fix(login): 密码登录补充密码非空前端校验
- doctor-login submitLogin 增加「请输入密码」前置校验, 与 getCode 一致,避免空密码误触发登录请求 - 配合后端 service_user 空密码回退 PC 医生/药师密码校验
This commit is contained in:
12
.cursor/rules/Code-Standards.md
Normal file
12
.cursor/rules/Code-Standards.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
|
||||||
|
2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行
|
||||||
|
3. 有封装好的方法、组件需要复用,不要重复造轮子
|
||||||
|
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show)
|
||||||
|
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
|
||||||
|
6. 微信小程序 `.vue` 模板(会编译成 WXML)禁止使用 `??`、`?.` 等现代运算符,否则会报 `unexpected token '?'`;模板里用 `||` / 显式判断,脚本里可用 `??`/`?.`
|
||||||
|
7. 微信小程序模板里 `:class` / `:style` 禁止写 `fn(arg)` 方法调用(如 `:class="sexClass(p)"` 会编译失败);用对象/数组字面量(如 `:class="{ male: p.sex == 1 }"`),或把结果先算进 data/computed 再绑定
|
||||||
@@ -8,3 +8,5 @@ alwaysApply: true
|
|||||||
3. 有封装好的方法、组件需要复用,不要重复造轮子
|
3. 有封装好的方法、组件需要复用,不要重复造轮子
|
||||||
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show)
|
4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show)
|
||||||
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
|
5. 数据库的(created_at、updated_at、deleted_at)统一使用时间戳,不要使用字符串,并且我在查询器中一级格式化成字符串了,无需再次格式化
|
||||||
|
6. 微信小程序 `.vue` 模板(会编译成 WXML)禁止使用 `??`、`?.` 等现代运算符,否则会报 `unexpected token '?'`;模板里用 `||` / 显式判断,脚本里可用 `??`/`?.`
|
||||||
|
7. 微信小程序模板里 `:class` / `:style` 禁止写 `fn(arg)` 方法调用(如 `:class="sexClass(p)"` 会编译失败);用对象/数组字面量(如 `:class="{ male: p.sex == 1 }"`),或把结果先算进 data/computed 再绑定
|
||||||
|
|||||||
3
.idea/inspectionProfiles/Project_Default.xml
generated
3
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -15,7 +15,7 @@
|
|||||||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="myValues">
|
<option name="myValues">
|
||||||
<value>
|
<value>
|
||||||
<list size="24">
|
<list size="25">
|
||||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
<item index="21" class="java.lang.String" itemvalue="u-subsection" />
|
<item index="21" class="java.lang.String" itemvalue="u-subsection" />
|
||||||
<item index="22" class="java.lang.String" itemvalue="u-tabbar" />
|
<item index="22" class="java.lang.String" itemvalue="u-tabbar" />
|
||||||
<item index="23" class="java.lang.String" itemvalue="u-navbar" />
|
<item index="23" class="java.lang.String" itemvalue="u-navbar" />
|
||||||
|
<item index="24" class="java.lang.String" itemvalue="u-field" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ const PREFIX = '/newApi/clinic-admin';
|
|||||||
/** 解析 Laravel jok 或 Yii 响应 */
|
/** 解析 Laravel jok 或 Yii 响应 */
|
||||||
export function unwrapClinicRes(res) {
|
export function unwrapClinicRes(res) {
|
||||||
if (!res) return null;
|
if (!res) return null;
|
||||||
if (res.code === 0 && res.result !== undefined) return res.result;
|
// code 可能是 0 / "0"
|
||||||
if (res.errcode != null && res.errcode !== -1) return res.data;
|
if (Number(res.code) === 0 && res.result !== undefined) return res.result;
|
||||||
|
if (res.errcode != null && Number(res.errcode) !== -1) return res.data;
|
||||||
if (res.result !== undefined) return res.result;
|
if (res.result !== undefined) return res.result;
|
||||||
return res.data != null ? res.data : res;
|
return res.data != null ? res.data : res;
|
||||||
}
|
}
|
||||||
@@ -20,7 +21,10 @@ function clinicRequest(options) {
|
|||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const data = unwrapClinicRes(res);
|
const data = unwrapClinicRes(res);
|
||||||
const ok = res && (res.code === 0 || (res.errcode != null && Number(res.errcode) !== -1));
|
const ok = !!(res && (
|
||||||
|
Number(res.code) === 0
|
||||||
|
|| (res.errcode != null && Number(res.errcode) !== -1)
|
||||||
|
));
|
||||||
return { res, data, ok };
|
return { res, data, ok };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +1,24 @@
|
|||||||
import { req } from '@/common/js/index.js';
|
/**
|
||||||
import { unwrapClinicRes } from '@/api/clinicAdmin.js';
|
* 兼容旧引用:平台门店 API 统一走 storeManage(按 loginMode 切前缀)
|
||||||
|
*/
|
||||||
const STORE_PREFIX = '/newApi/platform-admin-store';
|
export {
|
||||||
const CONFIG_PREFIX = '/newApi/platform-admin-salesperson-store-config';
|
getStoreList as getPlatformStoreList,
|
||||||
|
getStoreDetail as getPlatformStoreDetail,
|
||||||
function platformRequest(options) {
|
updateStore as updatePlatformStore,
|
||||||
return req.request({
|
openStorePcWindows,
|
||||||
...options,
|
openStoreQrCode,
|
||||||
header: {
|
updateStoreShippingFree,
|
||||||
...(options.header || {}),
|
updateStoreSubscribeStatus,
|
||||||
_platformAdmin: '1',
|
updateStoreSeeRate,
|
||||||
},
|
updateStoreAllowInsurance,
|
||||||
}).then((res) => {
|
updateStoreClinicType,
|
||||||
const data = unwrapClinicRes(res);
|
toggleSalespersonSeePrice,
|
||||||
const ok = res && (res.code === 0 || (res.errcode != null && Number(res.errcode) !== -1));
|
getStoreDrugsByType,
|
||||||
return { res, data, ok };
|
updateStoreDrugPrices,
|
||||||
});
|
getDoctorOption,
|
||||||
}
|
getInternetMedicalStoreOption,
|
||||||
|
bindOnlineConsultation,
|
||||||
export function getPlatformStoreList(params) {
|
} from './storeManage.js';
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/list`, method: 'GET', data: params });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPlatformStoreDetail(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/detail`, method: 'GET', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updatePlatformStore(data) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update`, method: 'POST', data });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deletePlatformStore(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/delete`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openStorePcWindows(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/open-pc-windows`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openStoreQrCode(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/open-qr-code`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreShippingFree(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-shipping-free`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreSubscribeStatus(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-subscribe-status`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreSeeRate(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-see-rate`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreAllowInsurance(id) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-allow-insurance-category`, method: 'POST', data: { id } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreClinicType(id, clinicType) {
|
|
||||||
return platformRequest({
|
|
||||||
url: `${STORE_PREFIX}/update-clinic-type`,
|
|
||||||
method: 'POST',
|
|
||||||
data: { id, clinic_type: clinicType },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleSalespersonSeePrice(storeId) {
|
|
||||||
return platformRequest({
|
|
||||||
url: `${CONFIG_PREFIX}/toggle-see-price`,
|
|
||||||
method: 'POST',
|
|
||||||
data: { store_id: storeId },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStoreDrugsByType(storeId, type) {
|
|
||||||
return platformRequest({
|
|
||||||
url: `${STORE_PREFIX}/get-store-drugs-by-type`,
|
|
||||||
method: 'GET',
|
|
||||||
data: { store_id: storeId, type },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreDrugPrices(data) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-store-drug-prices`, method: 'POST', data });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDoctorOption(storeId) {
|
|
||||||
return platformRequest({
|
|
||||||
url: `${STORE_PREFIX}/doctor-option`,
|
|
||||||
method: 'GET',
|
|
||||||
data: storeId ? { store_id: storeId } : {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getInternetMedicalStoreOption() {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/internet-medical-store-option`, method: 'GET' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bindOnlineConsultation(data) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/bind-online-consultation`, method: 'POST', data });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateStoreExternalField(data) {
|
|
||||||
return platformRequest({ url: `${STORE_PREFIX}/update-store-external-field`, method: 'POST', data });
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DRUG_TYPE_OPTIONS = [
|
export const DRUG_TYPE_OPTIONS = [
|
||||||
{ label: '中药', value: 1 },
|
{ label: '中药', value: 1 },
|
||||||
@@ -114,3 +28,13 @@ export const DRUG_TYPE_OPTIONS = [
|
|||||||
{ label: '非药品', value: 6 },
|
{ label: '非药品', value: 6 },
|
||||||
{ label: '医疗器械', value: 7 },
|
{ label: '医疗器械', value: 7 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** @deprecated 请使用 updateStore */
|
||||||
|
export async function updateStoreExternalField(data) {
|
||||||
|
const { updateStore } = await import('./storeManage.js');
|
||||||
|
return updateStore(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePlatformStore() {
|
||||||
|
return { ok: false, data: null, res: null };
|
||||||
|
}
|
||||||
|
|||||||
@@ -250,6 +250,18 @@ export function addWestPrescription(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按药品查可选配送仓(药店开方选仓)
|
||||||
|
* 走 PC 同源 admin JWT 接口;医生小程序侧若无权限需后端另开 doctor 路由,此处与 delivery-warehouse-drug 对齐
|
||||||
|
*/
|
||||||
|
export function getDeliveryWarehouseOptionsByDrugs(data) {
|
||||||
|
return req.request({
|
||||||
|
url: '/newApi/doctor-reception-wx/delivery-warehouse-options-by-drugs',
|
||||||
|
method: 'GET',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 检查中药相冲
|
// 检查中药相冲
|
||||||
export function checkChineseMedicineConflictApi(data) {
|
export function checkChineseMedicineConflictApi(data) {
|
||||||
return req.request({
|
return req.request({
|
||||||
|
|||||||
152
api/storeManage.js
Normal file
152
api/storeManage.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* 门店管理 API:按 loginMode 切换平台超管 / 业务员前缀
|
||||||
|
* 业务员与超管复用同一套门店列表/详情页
|
||||||
|
*/
|
||||||
|
import { req } from '@/common/js/index.js';
|
||||||
|
import { unwrapClinicRes } from '@/api/clinicAdmin.js';
|
||||||
|
|
||||||
|
function getStoreContext() {
|
||||||
|
const mode = uni.getStorageSync('loginMode');
|
||||||
|
if (mode === 'salesperson') {
|
||||||
|
return {
|
||||||
|
prefix: '/newApi/salesperson-store',
|
||||||
|
configPrefix: '',
|
||||||
|
headerKey: '_salesperson',
|
||||||
|
isSalesperson: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
prefix: '/newApi/platform-admin-store',
|
||||||
|
configPrefix: '/newApi/platform-admin-salesperson-store-config',
|
||||||
|
headerKey: '_platformAdmin',
|
||||||
|
isSalesperson: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeRequest(options) {
|
||||||
|
const ctx = getStoreContext();
|
||||||
|
return req.request({
|
||||||
|
...options,
|
||||||
|
header: {
|
||||||
|
...(options.header || {}),
|
||||||
|
[ctx.headerKey]: '1',
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
const data = unwrapClinicRes(res);
|
||||||
|
const ok = res && (res.code === 0 || (res.errcode != null && Number(res.errcode) !== -1));
|
||||||
|
return { res, data, ok };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 业务员是否允许编辑门店(系统配置开关) */
|
||||||
|
export function getSalespersonStoreEditPermission() {
|
||||||
|
return storeRequest({
|
||||||
|
url: '/newApi/salesperson-store/edit-permission',
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStoreList(params) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/list`, method: 'GET', data: params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStoreDetail(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/detail`, method: 'GET', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStore(data) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update`, method: 'POST', data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openStorePcWindows(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/open-pc-windows`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openStoreQrCode(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/open-qr-code`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreShippingFree(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update-shipping-free`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreSubscribeStatus(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update-subscribe-status`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreSeeRate(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update-see-rate`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreAllowInsurance(id) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update-allow-insurance-category`, method: 'POST', data: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreClinicType(id, clinicType) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({
|
||||||
|
url: `${prefix}/update-clinic-type`,
|
||||||
|
method: 'POST',
|
||||||
|
data: { id, clinic_type: clinicType },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toggleSalespersonSeePrice(storeId) {
|
||||||
|
const { configPrefix, isSalesperson } = getStoreContext();
|
||||||
|
if (isSalesperson || !configPrefix) {
|
||||||
|
return Promise.resolve({ ok: false, data: null, res: null });
|
||||||
|
}
|
||||||
|
return storeRequest({
|
||||||
|
url: `${configPrefix}/toggle-see-price`,
|
||||||
|
method: 'POST',
|
||||||
|
data: { store_id: storeId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStoreDrugsByType(storeId, type) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({
|
||||||
|
url: `${prefix}/get-store-drugs-by-type`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { store_id: storeId, type },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateStoreDrugPrices(data) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/update-store-drug-prices`, method: 'POST', data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDoctorOption(storeId) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({
|
||||||
|
url: `${prefix}/doctor-option`,
|
||||||
|
method: 'GET',
|
||||||
|
data: storeId ? { store_id: storeId } : {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInternetMedicalStoreOption() {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/internet-medical-store-option`, method: 'GET' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindOnlineConsultation(data) {
|
||||||
|
const { prefix } = getStoreContext();
|
||||||
|
return storeRequest({ url: `${prefix}/bind-online-consultation`, method: 'POST', data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSalespersonStoreMode() {
|
||||||
|
return uni.getStorageSync('loginMode') === 'salesperson';
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getStoreContext };
|
||||||
132
api/userPatientProfile.js
Normal file
132
api/userPatientProfile.js
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* 用户管理 / 就诊人档案 API(诊所管理员 / 平台超管)
|
||||||
|
*/
|
||||||
|
import { req } from '@/common/js/index.js';
|
||||||
|
import { unwrapClinicRes } from '@/api/clinicAdmin.js';
|
||||||
|
|
||||||
|
function getProfileContext() {
|
||||||
|
const mode = uni.getStorageSync('loginMode');
|
||||||
|
if (mode === 'clinic_admin') {
|
||||||
|
return {
|
||||||
|
prefix: '/newApi/clinic-admin-user-patient-profile',
|
||||||
|
headerKey: '_clinicAdmin',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
prefix: '/newApi/platform-admin-user-patient-profile',
|
||||||
|
headerKey: '_platformAdmin',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function profileRequest(options) {
|
||||||
|
const ctx = getProfileContext();
|
||||||
|
return req.request({
|
||||||
|
...options,
|
||||||
|
header: {
|
||||||
|
...(options.header || {}),
|
||||||
|
[ctx.headerKey]: '1',
|
||||||
|
},
|
||||||
|
}).then((res) => {
|
||||||
|
const data = unwrapClinicRes(res);
|
||||||
|
// code 可能是数字 0 或字符串 "0";勿用 === 导致 ok 为 false、列表被丢弃
|
||||||
|
const ok = !!(res && (
|
||||||
|
Number(res.code) === 0
|
||||||
|
|| (res.errcode != null && Number(res.errcode) !== -1)
|
||||||
|
));
|
||||||
|
return { res, data, ok };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 profile 接口包装中取出列表 items(兼容 result/data/items 多层)
|
||||||
|
*/
|
||||||
|
export function pickProfileItems(wrap) {
|
||||||
|
const raw = (wrap && wrap.data) || null;
|
||||||
|
const body = wrap && wrap.res;
|
||||||
|
const fromBody = body && (body.result || body.data);
|
||||||
|
let payload = raw;
|
||||||
|
// unwrap 后若仍带着 code/result,再剥一层
|
||||||
|
if (payload && payload.items == null && payload.result != null) {
|
||||||
|
payload = payload.result;
|
||||||
|
}
|
||||||
|
if (payload == null && fromBody) {
|
||||||
|
payload = fromBody;
|
||||||
|
}
|
||||||
|
if (Array.isArray(payload)) {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
if (payload && Array.isArray(payload.items)) {
|
||||||
|
return payload.items;
|
||||||
|
}
|
||||||
|
if (payload && Array.isArray(payload.list)) {
|
||||||
|
return payload.list;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserPatientList(params) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({ url: `${prefix}/list`, method: 'GET', data: params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 会员管理:微信用户分页列表 */
|
||||||
|
export function getWxUserList(params) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({ url: `${prefix}/user-list`, method: 'GET', data: params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 某微信用户下的就诊人列表 */
|
||||||
|
export function getPatientListByUser(userId) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/patient-list-by-user`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { user_id: userId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserPatientDetail(upId) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/detail`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { up_id: upId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserPatientRegisterList(upId, params) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/register-list`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { up_id: upId, ...params },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 就诊人单次挂号详情(基本信息 + 选药 + 回答记录) */
|
||||||
|
export function getUserPatientRegisterDetail(registerId) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/register-detail`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { register_id: registerId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserPatientPrescriptionList(upId, params) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/prescription-list`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { up_id: upId, ...params },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserPatientOrderList(upId, params) {
|
||||||
|
const { prefix } = getProfileContext();
|
||||||
|
return profileRequest({
|
||||||
|
url: `${prefix}/order-list`,
|
||||||
|
method: 'GET',
|
||||||
|
data: { up_id: upId, ...params },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ export const config = {
|
|||||||
|
|
||||||
const arr = [
|
const arr = [
|
||||||
'mobile',
|
'mobile',
|
||||||
|
'call_mobile',
|
||||||
'express_mobile',
|
'express_mobile',
|
||||||
'doctor',
|
'doctor',
|
||||||
'accept_tel',
|
'accept_tel',
|
||||||
@@ -23,6 +24,7 @@ const arr = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// 敏感数据(解密后做脱敏;需与下方 switch 分支一致)
|
// 敏感数据(解密后做脱敏;需与下方 switch 分支一致)
|
||||||
|
// 注意:call_mobile 只解密不脱敏,供回访拨号使用
|
||||||
const sensitiveData = [
|
const sensitiveData = [
|
||||||
'id_card',
|
'id_card',
|
||||||
'idcard',
|
'idcard',
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
|
|
||||||
<view class="pwd">
|
<view class="pwd">
|
||||||
<u-field v-model="smsCode"
|
<u-field v-model="smsCode"
|
||||||
|
type="number"
|
||||||
|
:maxlength="6"
|
||||||
placeholder="请填写验证码"
|
placeholder="请填写验证码"
|
||||||
:prefixIcon="require('../../static/image/pwd.png')"
|
:prefixIcon="require('../../static/image/pwd.png')"
|
||||||
:placeholder-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
:placeholder-style="{ fontSize: '32rpx', paddingLeft: '60rpx' }"
|
||||||
@@ -83,7 +85,7 @@
|
|||||||
:loading="wxLoading"
|
:loading="wxLoading"
|
||||||
:custom-style="{ background: '#07C160', color: '#fff' }"
|
:custom-style="{ background: '#07C160', color: '#fff' }"
|
||||||
>
|
>
|
||||||
微信一键登录
|
一键登录
|
||||||
</u-button>
|
</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -221,6 +223,10 @@ export default {
|
|||||||
this.$toast('请输入手机号');
|
this.$toast('请输入手机号');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.form.password) {
|
||||||
|
this.$toast('请输入密码');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.smsCode) {
|
if (!this.smsCode) {
|
||||||
this.$toast('请输入验证码');
|
this.$toast('请输入验证码');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -13,10 +13,19 @@ const DEV = {
|
|||||||
wsUrl: 'ws://127.0.0.1:12080/ws',
|
wsUrl: 'ws://127.0.0.1:12080/ws',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const PROD = {
|
||||||
|
// legacyApiBase: 'https://app.xiaokang88.com/service/v1/',
|
||||||
|
// newApiBase: 'https://api.xiaokang88.com/api/doctor/',
|
||||||
|
// wsUrl: 'wss://api.ws.g.xiaokang88.com/ws',
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const TEST = {
|
||||||
const PROD = {
|
const PROD = {
|
||||||
legacyApiBase: 'https://app.xiaokang88.com/service/v1/',
|
legacyApiBase: 'https://xk.test.saas.api-yii.nailaoyun.cn/service/v1/',
|
||||||
newApiBase: 'https://api.xiaokang88.com/api/doctor/',
|
newApiBase: 'https://xk.test.saas.api.nailaoyun.cn/api/doctor/',
|
||||||
wsUrl: 'wss://api.ws.g.xiaokang88.com/ws',
|
// wsUrl: 'wss://api.ws.g.xiaokang88.com/ws',
|
||||||
|
wsUrl: 'wss://xk.ws.nailaoyun.cn/ws',
|
||||||
|
// wsUrl: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isDoctorWxDev() {
|
export function isDoctorWxDev() {
|
||||||
|
|||||||
17
pages.json
17
pages.json
@@ -441,7 +441,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "order/detail",
|
"path": "order/detail",
|
||||||
"style": { "navigationBarTitleText": "订单详情", "navigationStyle": "custom" }
|
"style": {
|
||||||
|
"navigationBarTitleText": "订单详情",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"usingComponents": {
|
||||||
|
"order-price-percent-adjust-popup": "/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup"
|
||||||
|
},
|
||||||
|
"componentPlaceholder": {
|
||||||
|
"order-price-percent-adjust-popup": "view"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "order/trace/index",
|
"path": "order/trace/index",
|
||||||
@@ -471,7 +480,11 @@
|
|||||||
{ "path": "withdrawal/settlement/detail", "style": { "navigationBarTitleText": "结算明细", "navigationStyle": "custom" } },
|
{ "path": "withdrawal/settlement/detail", "style": { "navigationBarTitleText": "结算明细", "navigationStyle": "custom" } },
|
||||||
{ "path": "withdrawal/account-change/index", "style": { "navigationBarTitleText": "资金变动", "navigationStyle": "custom" } },
|
{ "path": "withdrawal/account-change/index", "style": { "navigationBarTitleText": "资金变动", "navigationStyle": "custom" } },
|
||||||
{ "path": "warehouse/index", "style": { "navigationBarTitleText": "仓库管理", "navigationStyle": "custom" } },
|
{ "path": "warehouse/index", "style": { "navigationBarTitleText": "仓库管理", "navigationStyle": "custom" } },
|
||||||
{ "path": "warehouse/detail", "style": { "navigationBarTitleText": "药品详情", "navigationStyle": "custom" } }
|
{ "path": "warehouse/detail", "style": { "navigationBarTitleText": "药品详情", "navigationStyle": "custom" } },
|
||||||
|
{ "path": "user-patient/index", "style": { "navigationBarTitleText": "会员管理", "navigationStyle": "custom" } },
|
||||||
|
{ "path": "user-patient/patients", "style": { "navigationBarTitleText": "就诊人列表", "navigationStyle": "custom" } },
|
||||||
|
{ "path": "user-patient/detail", "style": { "navigationBarTitleText": "就诊人详情", "navigationStyle": "custom" } },
|
||||||
|
{ "path": "user-patient/register-detail", "style": { "navigationBarTitleText": "挂号详情", "navigationStyle": "custom" } }
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
"root": "subPackages/sub_platform_admin",
|
"root": "subPackages/sub_platform_admin",
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<my-pharmacist v-if="parseInt(identity)==2" ref="myPharmacist" :info="userInfo" :avatar="getAvatar"></my-pharmacist>
|
<my-pharmacist v-if="parseInt(identity)==2" ref="myPharmacist" :info="userInfo" :avatar="getAvatar"></my-pharmacist>
|
||||||
<my-service v-if="parseInt(identity)>2" :info="userInfo" :avatar="getAvatar"></my-service>
|
<my-service v-if="parseInt(identity)>2" ref="myService" :info="userInfo" :avatar="getAvatar"></my-service>
|
||||||
<d-tabbar></d-tabbar>
|
<d-tabbar></d-tabbar>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -183,6 +183,9 @@
|
|||||||
if (parseInt(this.identity) === 2 && this.$refs.myPharmacist) {
|
if (parseInt(this.identity) === 2 && this.$refs.myPharmacist) {
|
||||||
this.$refs.myPharmacist.refreshWxBind()
|
this.$refs.myPharmacist.refreshWxBind()
|
||||||
}
|
}
|
||||||
|
if (parseInt(this.identity) > 2 && this.$refs.myService) {
|
||||||
|
this.$refs.myService.refreshWxBind()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async getInfo() {
|
async getInfo() {
|
||||||
let res = {}
|
let res = {}
|
||||||
|
|||||||
@@ -18,31 +18,50 @@
|
|||||||
</d-text>
|
</d-text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="p-32 white m-t-12">
|
<!-- 菜单卡:绑定微信 / 个人资料 / 设置 / 切换账号(对齐医生「我的」) -->
|
||||||
|
<view class="flex-col m-t-12 white p-row-32">
|
||||||
|
<view class="wx-bind-wrap">
|
||||||
<wx-bind-entry ref="wxBindEntry" />
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
</view>
|
</view>
|
||||||
<view class="p-32 white m-t-12" @click="$go('../../subPackages/sub_pharmacist/pharmacist_info?avatar='+avatar+'&info='+JSON.stringify(info))">
|
<view class="flex-row bottom-border flex-jus-sp p-col-32 flex-ali-center"
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
@click="$go('../../subPackages/sub_pharmacist/pharmacist_info?avatar='+avatar+'&info='+JSON.stringify(info))">
|
||||||
|
<view class="flex-row flex-ali-center">
|
||||||
<u-icon margin-left="20" label-size="32" label-color="#31353D"
|
<u-icon margin-left="20" label-size="32" label-color="#31353D"
|
||||||
:name="require('../../static/image/grzl.png')" label="个人资料" size="30"></u-icon>
|
:name="require('../../static/image/grzl.png')" label="个人资料" size="30"></u-icon>
|
||||||
|
</view>
|
||||||
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
<view class="flex-row bottom-border flex-jus-sp p-col-32 flex-ali-center"
|
||||||
<view class="p-32 white m-t-2" @click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
|
@click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
<u-icon margin-left="20" label-size="32" label-color="#31353D"
|
<u-icon margin-left="20" label-size="32" label-color="#31353D"
|
||||||
:name="require('../../static/image/set.png')" label="设置" size="30"></u-icon>
|
:name="require('../../static/image/set.png')" label="设置" size="30"></u-icon>
|
||||||
|
</view>
|
||||||
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="account-switch-wrap">
|
||||||
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 独立绿色退出按钮,不再藏在二级设置页 -->
|
||||||
|
<view class="logout-wrap p-row-32 m-t-12">
|
||||||
|
<u-button :throttle-time="0" shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
@click="logout">退出登录</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 药师「我的」:资料 + 绑定微信 + 切换账号 + 退出登录(对齐医生主页结构)
|
||||||
|
*/
|
||||||
|
import { logout } from '@/api/all.js'
|
||||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
export default {
|
export default {
|
||||||
components: { WxBindEntry },
|
components: { WxBindEntry, AccountSwitchEntry },
|
||||||
props:{
|
props:{
|
||||||
avatar:{
|
avatar:{
|
||||||
type:String,
|
type:String,
|
||||||
@@ -56,12 +75,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {};
|
||||||
|
|
||||||
};
|
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.refreshWxBind()
|
this.refreshWxBind()
|
||||||
@@ -72,6 +86,18 @@
|
|||||||
this.$refs.wxBindEntry.refresh()
|
this.$refs.wxBindEntry.refresh()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** 退出登录:清本地会话并回登录页 */
|
||||||
|
logout() {
|
||||||
|
logout({ store_id: uni.getStorageSync('store_id') || 11001 }).then((res) => {
|
||||||
|
if (res.errcode != -1) {
|
||||||
|
uni.clearStorage()
|
||||||
|
uni.clearStorageSync()
|
||||||
|
this.$go('/pages/login/index', 2)
|
||||||
|
} else {
|
||||||
|
this.$toast(res.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -85,4 +111,13 @@
|
|||||||
background-color: #F9FAFB;
|
background-color: #F9FAFB;
|
||||||
padding: 16rpx;
|
padding: 16rpx;
|
||||||
}
|
}
|
||||||
|
.wx-bind-wrap {
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
.account-switch-wrap {
|
||||||
|
padding-top: 8rpx;
|
||||||
|
}
|
||||||
|
.logout-wrap {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,17 +11,19 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 菜单卡:绑定微信 / 业务项 / 设置 / 切换账号 -->
|
||||||
<view class="p-row-32 b-r-8 white m-t-2">
|
<view class="p-row-32 b-r-8 white m-t-2">
|
||||||
<view class="p-col-32" @click="$go('../../subPackages/sub_my/my_commonReply/index')">
|
<view class="wx-bind-wrap p-col-24">
|
||||||
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
|
</view>
|
||||||
|
<view class="p-col-32 bottom-border" @click="$go('../../subPackages/sub_my/my_commonReply/index')">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
<view class="flex-row flex-jus-sp flex-ali-center">
|
||||||
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="chat" label="快捷回复" size="40"
|
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="chat" label="快捷回复" size="40"
|
||||||
color="#6C7380"></u-icon>
|
color="#6C7380"></u-icon>
|
||||||
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
<view class="p-col-32 bottom-border flex-jus-sp flex-row flex-ali-center" @click="$go()">
|
||||||
<view class="p-row-32 b-r-8 white m-t-2">
|
|
||||||
<view class="p-col-32 flex-jus-sp flex-row flex-ali-center bottom-border" @click="$go()">
|
|
||||||
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="kefu-ermai" label="联系客服" size="40"
|
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="kefu-ermai" label="联系客服" size="40"
|
||||||
color="#6C7380"></u-icon>
|
color="#6C7380"></u-icon>
|
||||||
<view class="flex-row flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
@@ -31,19 +33,35 @@
|
|||||||
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="p-col-32" @click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
|
<view class="p-col-32 bottom-border" @click="$go('../../subPackages/sub_pharmacist/pharmacist_setInfo')">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center">
|
<view class="flex-row flex-jus-sp flex-ali-center">
|
||||||
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="setting" label="设置" size="40"
|
<u-icon margin-left="20" label-size="32" label-color="#31353D" name="setting" label="设置" size="40"
|
||||||
color="#6C7380"></u-icon>
|
color="#6C7380"></u-icon>
|
||||||
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
<u-icon name="arrow-right" size="32" color="#A7ABB0"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="account-switch-wrap p-col-8">
|
||||||
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 独立绿色退出按钮 -->
|
||||||
|
<view class="logout-wrap p-row-32 m-t-12">
|
||||||
|
<u-button :throttle-time="0" shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
@click="logout">退出登录</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 导医/客服「我的」:绑定微信 + 切换账号 + 退出登录上提到主页(对齐医生)
|
||||||
|
*/
|
||||||
|
import { logout } from '@/api/all.js'
|
||||||
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
export default {
|
export default {
|
||||||
|
components: { WxBindEntry, AccountSwitchEntry },
|
||||||
props: {
|
props: {
|
||||||
avatar: {
|
avatar: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -60,12 +78,26 @@
|
|||||||
return {
|
return {
|
||||||
identity: (uni.getStorageSync('userInfo') || {}).role || 3,
|
identity: (uni.getStorageSync('userInfo') || {}).role || 3,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
onLoad() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** 父页 onShow 时刷新绑定微信入口展示状态 */
|
||||||
|
refreshWxBind() {
|
||||||
|
const ref = this.$refs.wxBindEntry
|
||||||
|
if (ref && typeof ref.refresh === 'function') {
|
||||||
|
ref.refresh()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
logout({ store_id: uni.getStorageSync('store_id') || 11001 }).then((res) => {
|
||||||
|
if (res.errcode != -1) {
|
||||||
|
uni.clearStorage()
|
||||||
|
uni.clearStorageSync()
|
||||||
|
this.$go('/pages/login/index', 2)
|
||||||
|
} else {
|
||||||
|
this.$toast(res.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
styleIsolation: 'shared'
|
styleIsolation: 'shared'
|
||||||
@@ -78,8 +110,16 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
::v-deep .u-badge {
|
.badge {
|
||||||
display: flex !important;
|
background-color: #F7F8FA;
|
||||||
position: relative;
|
border-radius: 32rpx;
|
||||||
|
width: 64rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
|
.wx-bind-wrap {
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
.logout-wrap {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -214,6 +214,8 @@ export function mapOrderListRow(item) {
|
|||||||
deliveryText: deliveryMethodText(item.delivery_method),
|
deliveryText: deliveryMethodText(item.delivery_method),
|
||||||
deliveryClass: 'tag-delivery',
|
deliveryClass: 'tag-delivery',
|
||||||
userNickname: user.nickname || '-',
|
userNickname: user.nickname || '-',
|
||||||
|
patientName: (item.user_patient && item.user_patient.name) || item.patient || '-',
|
||||||
|
upId: Number(item.up_id || (item.user_patient && item.user_patient.id) || 0),
|
||||||
payTime: formatDateTime(item.pay_time),
|
payTime: formatDateTime(item.pay_time),
|
||||||
createdAt: formatDateTime(item.created_at),
|
createdAt: formatDateTime(item.created_at),
|
||||||
storeName: store.name || '-',
|
storeName: store.name || '-',
|
||||||
@@ -346,7 +348,12 @@ export function mapPrescriptionDetailView(item) {
|
|||||||
const name = d.name || d.drug_name || '—'
|
const name = d.name || d.drug_name || '—'
|
||||||
const num = d.number != null ? d.number : ''
|
const num = d.number != null ? d.number : ''
|
||||||
const way = (d.use_way && d.use_way.name) || d.useWay || '煎服'
|
const way = (d.use_way && d.use_way.name) || d.useWay || '煎服'
|
||||||
return { name, qty: num ? num + ' /g' : '', usage: way }
|
return {
|
||||||
|
name,
|
||||||
|
qty: num ? num + ' /g' : '',
|
||||||
|
usage: way,
|
||||||
|
specification: d.specification || '',
|
||||||
|
}
|
||||||
})
|
})
|
||||||
: []
|
: []
|
||||||
recipes.push({
|
recipes.push({
|
||||||
@@ -369,6 +376,7 @@ export function mapPrescriptionDetailView(item) {
|
|||||||
name: d.name || d.drug_name || '—',
|
name: d.name || d.drug_name || '—',
|
||||||
qty: (d.number != null ? d.number : '') + unitName,
|
qty: (d.number != null ? d.number : '') + unitName,
|
||||||
usage: d.useWay || (d.use_way && d.use_way.name) || '',
|
usage: d.useWay || (d.use_way && d.use_way.name) || '',
|
||||||
|
specification: d.specification || '',
|
||||||
}],
|
}],
|
||||||
usage: '',
|
usage: '',
|
||||||
process: '',
|
process: '',
|
||||||
|
|||||||
105
subPackages/sub_business_shared/components/UserPatientCell.vue
Normal file
105
subPackages/sub_business_shared/components/UserPatientCell.vue
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 就诊人/微信用户摘要单元格,点击打开患者视角详情 -->
|
||||||
|
<view class="up-cell" @click.stop="handleOpen">
|
||||||
|
<view class="up-row">
|
||||||
|
<image class="avatar" :src="userAvatar" mode="aspectFill" />
|
||||||
|
<view class="text">
|
||||||
|
<text class="lbl">微信用户</text>
|
||||||
|
<text class="val">{{ userNickname }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="up-row">
|
||||||
|
<view class="avatar placeholder" />
|
||||||
|
<view class="text">
|
||||||
|
<text class="lbl">就诊人</text>
|
||||||
|
<text class="val link">{{ patientName }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 列表内紧凑展示微信用户 + 就诊人,点击抛出 open 事件供父级打开详情抽屉
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'UserPatientCell',
|
||||||
|
props: {
|
||||||
|
/** 订单行或用户管理行:需含 user / user_patient / up_id / patient */
|
||||||
|
row: { type: Object, default: () => ({}) },
|
||||||
|
},
|
||||||
|
emits: ['open'],
|
||||||
|
computed: {
|
||||||
|
user() {
|
||||||
|
return this.row.user || {};
|
||||||
|
},
|
||||||
|
patient() {
|
||||||
|
return this.row.user_patient || this.row.userPatient || this.row.patient || {};
|
||||||
|
},
|
||||||
|
upId() {
|
||||||
|
if (typeof this.patient === 'object' && this.patient) {
|
||||||
|
return Number(this.patient.id || this.row.up_id || 0);
|
||||||
|
}
|
||||||
|
return Number(this.row.up_id || 0);
|
||||||
|
},
|
||||||
|
userNickname() {
|
||||||
|
return this.user.nickname || '—';
|
||||||
|
},
|
||||||
|
userAvatar() {
|
||||||
|
return this.user.avatarurl || '/static/image/default-avatar.png';
|
||||||
|
},
|
||||||
|
patientName() {
|
||||||
|
if (typeof this.patient === 'string') return this.patient || '—';
|
||||||
|
return (this.patient && this.patient.name) || this.row.patient || '—';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleOpen() {
|
||||||
|
if (!this.upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit('open', { upId: this.upId, patientName: this.patientName });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.up-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
.up-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 48rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f3f4f6;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.avatar.placeholder {
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.lbl {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #9ca3af;
|
||||||
|
}
|
||||||
|
.val {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
.val.link {
|
||||||
|
color: #6acdbb;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
556
subPackages/sub_business_shared/components/UserPatientDetail.vue
Normal file
556
subPackages/sub_business_shared/components/UserPatientDetail.vue
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 患者视角详情:page-container 抽屉,避免误退出页面;用 v-if 控制挂载 -->
|
||||||
|
<page-container
|
||||||
|
v-if="visible"
|
||||||
|
:show="visible"
|
||||||
|
:overlay="true"
|
||||||
|
position="bottom"
|
||||||
|
:round="true"
|
||||||
|
@beforeleave="onBeforeLeave"
|
||||||
|
@clickoverlay="close"
|
||||||
|
>
|
||||||
|
<view class="drawer">
|
||||||
|
<view class="drawer-head">
|
||||||
|
<text class="title">患者视角 · {{ patientName || '就诊人' }}</text>
|
||||||
|
<text class="close" @click="close">关闭</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="loading" class="empty">加载中...</view>
|
||||||
|
<template v-else>
|
||||||
|
<!-- 资料卡:对齐就诊人详情页 -->
|
||||||
|
<view class="profile-card">
|
||||||
|
<view class="profile-row">
|
||||||
|
<image
|
||||||
|
class="avatar"
|
||||||
|
:src="profile.user.avatarurl || '/static/image/default-avatar.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
<view class="meta">
|
||||||
|
<text class="name">{{ profile.user.nickname || '微信用户' }}</text>
|
||||||
|
<text class="sub">
|
||||||
|
ID {{ profile.user.id || '—' }}
|
||||||
|
<template v-if="profile.user.mobile"> · {{ profile.user.mobile }}</template>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="patient-block">
|
||||||
|
<view class="patient-head">
|
||||||
|
<view class="patient-head-left">
|
||||||
|
<text class="p-name">{{ profile.patient.name || '—' }}</text>
|
||||||
|
<text v-if="sexText !== '—'" class="sex-tag" :class="sexTagClass">{{ sexText }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 回访:拨打就诊人/用户手机 -->
|
||||||
|
<view class="callback-btn" @click.stop="onCallbackCall">
|
||||||
|
<text class="callback-btn-text">回访</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="p-sub">
|
||||||
|
{{ profile.patient.age || '—' }}岁
|
||||||
|
<template v-if="profile.patient.mobile"> · {{ profile.patient.mobile }}</template>
|
||||||
|
</text>
|
||||||
|
<text v-if="profile.patient.id_card" class="p-sub">身份证 {{ profile.patient.id_card }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- Tab:下划线选中态 -->
|
||||||
|
<view class="tabs">
|
||||||
|
<view
|
||||||
|
v-for="(t, idx) in tabs"
|
||||||
|
:key="t.key"
|
||||||
|
class="tab"
|
||||||
|
:class="{ on: tabIndex === idx }"
|
||||||
|
@click="selectTab(idx)"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ t.label }}</text>
|
||||||
|
<view v-if="tabIndex === idx" class="tab-bar" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 四页左右滑:资料 / 挂号 / 处方 / 订单 -->
|
||||||
|
<swiper
|
||||||
|
class="tab-swiper"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
:current="tabIndex"
|
||||||
|
:duration="200"
|
||||||
|
@change="onTabSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="t in tabs" :key="'sw-' + t.key">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="tab-scroll"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
>
|
||||||
|
<!-- 资料:不绑 tabLoading,避免切列表时闪加载中 -->
|
||||||
|
<view v-if="t.key === 'info'" class="health-card">
|
||||||
|
<text class="section-title">健康信息</text>
|
||||||
|
<template v-if="health">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">既往史</text>
|
||||||
|
<text class="info-value">{{ historyText(health.person_status, health.person_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">过敏史</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.allergic_status) !== 0 }"
|
||||||
|
>{{ historyText(health.allergic_status, health.allergic_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">家族遗传史</text>
|
||||||
|
<text class="info-value">{{ historyText(health.family_status, health.family_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">肝功能异常</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.liver_function) !== 0 }"
|
||||||
|
>{{ functionText(health.liver_function, health.liver_index) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">肾功能异常</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.renal_function) !== 0 }"
|
||||||
|
>{{ functionText(health.renal_function, health.renal_index) }}</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<view v-else class="empty-inline">暂无健康问诊记录</view>
|
||||||
|
</view>
|
||||||
|
<template v-else>
|
||||||
|
<view v-if="tabLoading && activeTab === t.key" class="empty">加载中...</view>
|
||||||
|
<template v-else>
|
||||||
|
<view v-if="!listForTab(t.key).length" class="empty">暂无记录</view>
|
||||||
|
<view
|
||||||
|
v-for="item in listForTab(t.key)"
|
||||||
|
:key="t.key + '-' + item.id"
|
||||||
|
class="record-card"
|
||||||
|
@click="onRecordClick(item, t.key)"
|
||||||
|
>
|
||||||
|
<view class="record-main">
|
||||||
|
<template v-if="t.key === 'register'">
|
||||||
|
<text class="r-title">{{ item.order_no || item.order_number || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.status_text || '—' }} · {{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="t.key === 'prescription'">
|
||||||
|
<text class="r-title">{{ item.prescription_no || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.clinical_diagnose || '—' }} · {{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<text class="r-title">{{ item.order_no || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · ¥{{ item.total_pay_price || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
<text v-if="t.key === 'register'" class="arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 患者视角详情抽屉:微信用户/就诊人信息 + 挂号/处方/订单
|
||||||
|
* 对齐详情页展示与回访;四 Tab 支持点击与左右滑动(懒加载)
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
getUserPatientDetail,
|
||||||
|
getUserPatientRegisterList,
|
||||||
|
getUserPatientPrescriptionList,
|
||||||
|
getUserPatientOrderList,
|
||||||
|
} from '@/api/userPatientProfile.js';
|
||||||
|
|
||||||
|
/** 抽屉左右滑提示只展示一次 */
|
||||||
|
const SWIPE_TIP_STORAGE_KEY = 'user_patient_drawer_swipe_tip';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserPatientDetail',
|
||||||
|
props: {
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
upId: { type: [Number, String], default: 0 },
|
||||||
|
patientName: { type: String, default: '' },
|
||||||
|
},
|
||||||
|
emits: ['close'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
tabLoading: false,
|
||||||
|
activeTab: 'info',
|
||||||
|
tabIndex: 0,
|
||||||
|
tabSwiperChanging: false,
|
||||||
|
swiperHeightPx: 320,
|
||||||
|
tabs: [
|
||||||
|
{ key: 'info', label: '资料' },
|
||||||
|
{ key: 'register', label: '挂号' },
|
||||||
|
{ key: 'prescription', label: '处方' },
|
||||||
|
{ key: 'order', label: '订单' },
|
||||||
|
],
|
||||||
|
profile: {
|
||||||
|
user: {},
|
||||||
|
patient: {},
|
||||||
|
},
|
||||||
|
health: null,
|
||||||
|
registerList: [],
|
||||||
|
prescriptionList: [],
|
||||||
|
orderList: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sexText() {
|
||||||
|
const sex = this.profile.patient && this.profile.patient.sex;
|
||||||
|
if (Number(sex) === 1) return '男';
|
||||||
|
if (Number(sex) === 2) return '女';
|
||||||
|
return '—';
|
||||||
|
},
|
||||||
|
sexTagClass() {
|
||||||
|
const sex = this.profile.patient && this.profile.patient.sex;
|
||||||
|
if (Number(sex) === 1) return 'male';
|
||||||
|
if (Number(sex) === 2) return 'female';
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 回访拨号:只用就诊人 call_mobile(拦截器只解密不脱敏)
|
||||||
|
* 不用脱敏后的 mobile,也不回退微信用户号
|
||||||
|
*/
|
||||||
|
callbackPhone() {
|
||||||
|
const patient = this.profile.patient || {};
|
||||||
|
return String(patient.call_mobile || '').trim();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* v-if 挂载时 visible 已是 true,必须 immediate,否则 loadDetail 永不触发
|
||||||
|
*/
|
||||||
|
visible: {
|
||||||
|
immediate: true,
|
||||||
|
handler(val) {
|
||||||
|
if (val && this.upId) {
|
||||||
|
this.openAndLoad();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/** 抽屉打开时切换不同就诊人,重新拉详情 */
|
||||||
|
upId(val) {
|
||||||
|
if (this.visible && val) {
|
||||||
|
this.openAndLoad();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onBeforeLeave() {
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开抽屉:重置 Tab/列表并请求就诊人详情
|
||||||
|
*/
|
||||||
|
openAndLoad() {
|
||||||
|
this.activeTab = 'info';
|
||||||
|
this.tabIndex = 0;
|
||||||
|
this.health = null;
|
||||||
|
this.registerList = [];
|
||||||
|
this.prescriptionList = [];
|
||||||
|
this.orderList = [];
|
||||||
|
this.profile = { user: {}, patient: {} };
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
this.loadDetail();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 抽屉内 swiper 高度:80vh − 头 − 资料卡 − Tab(保证横向滑动可用)
|
||||||
|
*/
|
||||||
|
calcSwiperHeight() {
|
||||||
|
try {
|
||||||
|
const sys = uni.getSystemInfoSync() || {};
|
||||||
|
const winH = Number(sys.windowHeight) || 600;
|
||||||
|
const drawerH = Math.floor(winH * 0.8);
|
||||||
|
const rpx = (Number(sys.windowWidth) || 375) / 750;
|
||||||
|
const headH = Math.ceil(100 * rpx);
|
||||||
|
const profileH = Math.ceil(280 * rpx);
|
||||||
|
const tabsH = Math.ceil(90 * rpx);
|
||||||
|
const pad = Math.ceil(24 * rpx);
|
||||||
|
const h = drawerH - headH - profileH - tabsH - pad;
|
||||||
|
this.swiperHeightPx = h > 160 ? h : 160;
|
||||||
|
} catch (e) {
|
||||||
|
this.swiperHeightPx = 320;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 按 Tab key 取对应列表(四页同时渲染) */
|
||||||
|
listForTab(key) {
|
||||||
|
if (key === 'register') return this.registerList;
|
||||||
|
if (key === 'prescription') return this.prescriptionList;
|
||||||
|
if (key === 'order') return this.orderList;
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
/** 既往/过敏/家族:0 无,否则展示 history */
|
||||||
|
historyText(status, history) {
|
||||||
|
if (Number(status) === 0) return '无';
|
||||||
|
const t = String(history || '').trim();
|
||||||
|
return t || '有';
|
||||||
|
},
|
||||||
|
/** 肝/肾功能:0 正常,异常时附带指标文案 */
|
||||||
|
functionText(flag, indexText) {
|
||||||
|
if (Number(flag) === 0) return '正常';
|
||||||
|
const t = String(indexText || '').trim();
|
||||||
|
return t ? `异常 · ${t}` : '异常';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 回访:调起系统拨号;无号 toast,按钮始终显示
|
||||||
|
*/
|
||||||
|
onCallbackCall() {
|
||||||
|
const phone = this.callbackPhone;
|
||||||
|
if (!phone) {
|
||||||
|
uni.showToast({ title: '暂无手机号', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.makePhoneCall({
|
||||||
|
phoneNumber: phone,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 首次打开抽屉短暂提示可左右滑动
|
||||||
|
*/
|
||||||
|
maybeShowSwipeTip() {
|
||||||
|
let shown = '';
|
||||||
|
try {
|
||||||
|
shown = uni.getStorageSync(SWIPE_TIP_STORAGE_KEY);
|
||||||
|
} catch (e) {
|
||||||
|
shown = '';
|
||||||
|
}
|
||||||
|
if (shown) return;
|
||||||
|
uni.showToast({
|
||||||
|
title: '左右滑动可切换标签',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
uni.setStorageSync(SWIPE_TIP_STORAGE_KEY, '1');
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadDetail() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientDetail(this.upId);
|
||||||
|
if (wrap && wrap.ok && wrap.data) {
|
||||||
|
this.profile = {
|
||||||
|
user: wrap.data.user || {},
|
||||||
|
patient: wrap.data.patient || {},
|
||||||
|
};
|
||||||
|
this.health = wrap.data.health_inquiry || null;
|
||||||
|
}
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.maybeShowSwipeTip();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 点击顶部 Tab */
|
||||||
|
selectTab(index) {
|
||||||
|
if (this.tabSwiperChanging) return;
|
||||||
|
const tab = this.tabs[index];
|
||||||
|
if (!tab) return;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.switchTab(tab.key);
|
||||||
|
},
|
||||||
|
/** swiper 滑动切换 */
|
||||||
|
onTabSwiperChange(e) {
|
||||||
|
const index = e && e.detail ? e.detail.current : 0;
|
||||||
|
const tab = this.tabs[index];
|
||||||
|
if (!tab || tab.key === this.activeTab) {
|
||||||
|
this.tabIndex = index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tabSwiperChanging = true;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.switchTab(tab.key);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabSwiperChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 切换 Tab 并懒加载对应列表 */
|
||||||
|
async switchTab(key) {
|
||||||
|
this.activeTab = key;
|
||||||
|
const idx = this.tabs.findIndex((t) => t.key === key);
|
||||||
|
if (idx >= 0 && this.tabIndex !== idx) {
|
||||||
|
this.tabIndex = idx;
|
||||||
|
}
|
||||||
|
if (key === 'info') return;
|
||||||
|
if (key === 'register' && !this.registerList.length) {
|
||||||
|
await this.loadRegisters();
|
||||||
|
} else if (key === 'prescription' && !this.prescriptionList.length) {
|
||||||
|
await this.loadPrescriptions();
|
||||||
|
} else if (key === 'order' && !this.orderList.length) {
|
||||||
|
await this.loadOrders();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadRegisters() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientRegisterList(this.upId);
|
||||||
|
this.registerList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadPrescriptions() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientPrescriptionList(this.upId);
|
||||||
|
this.prescriptionList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadOrders() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientOrderList(this.upId);
|
||||||
|
this.orderList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 挂号行:关抽屉后进挂号详情(与详情页一致)
|
||||||
|
*/
|
||||||
|
onRecordClick(item, tabKey) {
|
||||||
|
if (tabKey !== 'register') return;
|
||||||
|
const rid = Number((item && item.id) || 0);
|
||||||
|
if (!rid) {
|
||||||
|
uni.showToast({ title: '缺少挂号信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
uni.setStorageSync('user_patient_register_detail', item || {});
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
this.close();
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/subPackages/sub_business_shared/user-patient/register-detail?id=${rid}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.drawer {
|
||||||
|
height: 80vh;
|
||||||
|
background: #f9fafb;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.drawer-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 28rpx 32rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.title { font-size: 30rpx; font-weight: 600; color: #111827; }
|
||||||
|
.close { font-size: 26rpx; color: #6acdbb; }
|
||||||
|
.profile-card {
|
||||||
|
margin: 20rpx 24rpx 0;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.profile-row { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
|
||||||
|
.avatar { width: 80rpx; height: 80rpx; border-radius: 50%; background: #e5e7eb; }
|
||||||
|
.meta { display: flex; flex-direction: column; min-width: 0; }
|
||||||
|
.name { font-size: 28rpx; font-weight: 600; color: #1f2937; }
|
||||||
|
.sub { font-size: 22rpx; color: #9ca3af; margin-top: 4rpx; }
|
||||||
|
.patient-block { padding-top: 12rpx; border-top: 1rpx solid #f3f4f6; }
|
||||||
|
.patient-head {
|
||||||
|
display: flex; align-items: center; justify-content: space-between; gap: 16rpx;
|
||||||
|
}
|
||||||
|
.patient-head-left { display: flex; align-items: center; gap: 12rpx; min-width: 0; flex: 1; }
|
||||||
|
.p-name { font-size: 28rpx; font-weight: 600; color: #111827; }
|
||||||
|
.sex-tag {
|
||||||
|
font-size: 20rpx; padding: 2rpx 12rpx; border-radius: 8rpx;
|
||||||
|
background: #e8f5f2; color: #2a9d8f;
|
||||||
|
}
|
||||||
|
.sex-tag.male { background: #e8f0fe; color: #3b82f6; }
|
||||||
|
.sex-tag.female { background: #fce7f3; color: #db2777; }
|
||||||
|
.callback-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 8rpx 22rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
border: 1rpx solid #6acdbb;
|
||||||
|
background: #e8f5f2;
|
||||||
|
}
|
||||||
|
.callback-btn-text { font-size: 24rpx; color: #1a9b88; font-weight: 500; }
|
||||||
|
.p-sub { display: block; font-size: 22rpx; color: #6b7280; margin-top: 6rpx; }
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
background: #fff;
|
||||||
|
margin: 16rpx 24rpx 0;
|
||||||
|
border-radius: 16rpx 16rpx 0 0;
|
||||||
|
padding: 0 8rpx;
|
||||||
|
border-bottom: 1rpx solid #eef2f1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 0 14rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.tab-text { font-size: 26rpx; color: #6b7280; }
|
||||||
|
.tab.on .tab-text { color: #1a9b88; font-weight: 600; }
|
||||||
|
.tab-bar {
|
||||||
|
position: absolute; bottom: 0; width: 48rpx; height: 4rpx;
|
||||||
|
border-radius: 4rpx; background: #6acdbb;
|
||||||
|
}
|
||||||
|
.tab-swiper {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.tab-scroll { width: 100%; box-sizing: border-box; }
|
||||||
|
.health-card {
|
||||||
|
background: #fff; border-radius: 0 0 16rpx 16rpx; padding: 8rpx 20rpx 20rpx;
|
||||||
|
}
|
||||||
|
.section-title {
|
||||||
|
display: block; font-size: 26rpx; font-weight: 600; color: #1f2937;
|
||||||
|
padding: 16rpx 0 8rpx;
|
||||||
|
}
|
||||||
|
.info-row {
|
||||||
|
display: flex; justify-content: space-between; align-items: flex-start;
|
||||||
|
padding: 16rpx 0; border-bottom: 1rpx solid #f3f4f6; gap: 24rpx;
|
||||||
|
}
|
||||||
|
.info-row:last-child { border-bottom: none; }
|
||||||
|
.info-label { font-size: 24rpx; color: #9ca3af; flex-shrink: 0; }
|
||||||
|
.info-value { font-size: 24rpx; color: #374151; text-align: right; flex: 1; word-break: break-all; }
|
||||||
|
.info-value.danger { color: #dc2626; font-weight: 600; }
|
||||||
|
.empty-inline { text-align: center; color: #9ca3af; padding: 40rpx 0; font-size: 24rpx; }
|
||||||
|
.record-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
.record-main { flex: 1; min-width: 0; }
|
||||||
|
.r-title { display: block; font-size: 26rpx; font-weight: 600; color: #111827; }
|
||||||
|
.r-sub { display: block; font-size: 22rpx; color: #6b7280; margin-top: 6rpx; }
|
||||||
|
.arrow { font-size: 36rpx; color: #d1d5db; line-height: 1; }
|
||||||
|
.empty { text-align: center; color: #9ca3af; padding: 48rpx 0; font-size: 26rpx; }
|
||||||
|
</style>
|
||||||
@@ -1,14 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="showBlock" class="express-panel">
|
<view v-if="showBlock" class="express-panel">
|
||||||
<view class="section-title">物流信息</view>
|
<view class="section-title">物流信息</view>
|
||||||
<template v-if="display.hasData">
|
<scroll-view v-if="packageList.length > 1" class="pkg-tabs" scroll-x>
|
||||||
<view class="row"><text>物流公司</text><text>{{ display.companyName }}</text></view>
|
<view
|
||||||
<view class="row"><text>运单号</text><text>{{ display.expressNo }}</text></view>
|
v-for="(pkg, idx) in packageList"
|
||||||
<view class="row"><text>最新状态</text><text class="highlight-text">{{ display.stateText }}</text></view>
|
:key="idx"
|
||||||
|
class="pkg-tab"
|
||||||
<view v-if="display.tracks.length" class="timeline">
|
:class="{ active: activePkg === idx }"
|
||||||
|
@click="activePkg = idx"
|
||||||
|
>
|
||||||
|
包裹{{ pkg.package_no || idx + 1 }}
|
||||||
|
<text v-if="pkg.warehouse_name" class="pkg-sub">{{ pkg.warehouse_name }}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<template v-if="currentDisplay.hasData">
|
||||||
|
<view class="row"><text>物流公司</text><text>{{ currentDisplay.companyName }}</text></view>
|
||||||
|
<view class="row"><text>运单号</text><text>{{ currentDisplay.expressNo }}</text></view>
|
||||||
|
<view class="row"><text>最新状态</text><text class="highlight-text">{{ currentDisplay.stateText }}</text></view>
|
||||||
|
<view v-if="currentDisplay.tracks.length" class="timeline">
|
||||||
<view class="timeline-title">物流追踪</view>
|
<view class="timeline-title">物流追踪</view>
|
||||||
<view v-for="(t, idx) in display.tracks" :key="idx" class="track-item">
|
<view v-for="(t, idx) in currentDisplay.tracks" :key="idx" class="track-item">
|
||||||
<view class="track-dot" :class="{ 'active': idx === 0 }" />
|
<view class="track-dot" :class="{ 'active': idx === 0 }" />
|
||||||
<view class="track-body">
|
<view class="track-body">
|
||||||
<text class="track-status" :class="{ 'active': idx === 0 }">{{ t.status }}</text>
|
<text class="track-status" :class="{ 'active': idx === 0 }">{{ t.status }}</text>
|
||||||
@@ -18,13 +29,14 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<view v-else class="empty-express">暂无物流信息</view>
|
<view v-else class="empty-express">
|
||||||
|
{{ currentPkg && Number(currentPkg.is_send) === 0 ? '本包裹尚未发货' : '暂无物流信息' }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapExpressDetail } from '@/subPackages/sub_business_shared/common/display.js'
|
import { mapExpressDetail } from '@/subPackages/sub_business_shared/common/display.js'
|
||||||
|
|
||||||
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -34,14 +46,38 @@ export default {
|
|||||||
express: { type: Object, default: null },
|
express: { type: Object, default: null },
|
||||||
show: { type: Boolean, default: true },
|
show: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return { activePkg: 0 }
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showBlock() {
|
showBlock() {
|
||||||
return this.show
|
return this.show
|
||||||
},
|
},
|
||||||
display() {
|
packageList() {
|
||||||
|
const pkgs = this.express && Array.isArray(this.express.packages) ? this.express.packages : []
|
||||||
|
if (pkgs.length) return pkgs
|
||||||
|
if (this.express && (this.express.express_no || this.express.detail)) {
|
||||||
|
return [{ package_no: 1, warehouse_name: '', is_send: 1, express: this.express }]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
currentPkg() {
|
||||||
|
return this.packageList[this.activePkg] || null
|
||||||
|
},
|
||||||
|
currentDisplay() {
|
||||||
|
const pkg = this.currentPkg
|
||||||
|
if (pkg && pkg.express) return mapExpressDetail(pkg.express)
|
||||||
|
if (pkg && Number(pkg.is_send) === 0) {
|
||||||
|
return { hasData: false, companyName: '', expressNo: '', stateText: '', tracks: [] }
|
||||||
|
}
|
||||||
return mapExpressDetail(this.express)
|
return mapExpressDetail(this.express)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
express() {
|
||||||
|
this.activePkg = 0
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -56,7 +92,6 @@ $color-border: #F2F3F5;
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding: 32rpx 40rpx;
|
padding: 32rpx 40rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
/* 移除圆角和阴影,采用通栏设计 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
@@ -66,6 +101,34 @@ $color-border: #F2F3F5;
|
|||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pkg-tabs {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $color-text-muted;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(106, 205, 187, 0.15);
|
||||||
|
color: $theme-primary;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-sub {
|
||||||
|
font-size: 20rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -130,7 +193,6 @@ $color-border: #F2F3F5;
|
|||||||
margin-left: 8rpx;
|
margin-left: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 最后一个元素去掉线 */
|
|
||||||
.track-item:last-child .track-body {
|
.track-item:last-child .track-body {
|
||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<drawer-page-container
|
<!-- 小程序抽屉须用 page-container + v-if,避免 u-popup 在订单详情页底内联 -->
|
||||||
v-model="show"
|
<page-container
|
||||||
title="处方详情"
|
v-if="show"
|
||||||
height="85%"
|
:show="show"
|
||||||
tall
|
:overlay="true"
|
||||||
:show-close="true"
|
position="bottom"
|
||||||
:closeable="true"
|
:round="true"
|
||||||
@close="onClose"
|
@beforeleave="onClose"
|
||||||
|
@clickoverlay="onClose"
|
||||||
>
|
>
|
||||||
|
<view class="rx-drawer">
|
||||||
|
<view class="rx-drawer-head">
|
||||||
|
<text class="rx-drawer-title">处方详情</text>
|
||||||
|
<text class="rx-drawer-close" @click="onClose">关闭</text>
|
||||||
|
</view>
|
||||||
<view class="popup-wrap">
|
<view class="popup-wrap">
|
||||||
<scroll-view v-if="view" scroll-y class="popup-scroll">
|
<scroll-view v-if="view" scroll-y class="popup-scroll">
|
||||||
<view class="container safe-area-inset-bottom">
|
<view class="container safe-area-inset-bottom">
|
||||||
@@ -21,7 +27,6 @@
|
|||||||
{{ view.typeText }}
|
{{ view.typeText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态医院与公章 -->
|
<!-- 动态医院与公章 -->
|
||||||
<view class="head_record">
|
<view class="head_record">
|
||||||
<image :src="view.sealImage || '/static/group/xkyard.png'" mode="aspectFit"></image>
|
<image :src="view.sealImage || '/static/group/xkyard.png'" mode="aspectFit"></image>
|
||||||
@@ -34,13 +39,11 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 时间 -->
|
<!-- 时间 -->
|
||||||
<view class="head_record_time">
|
<view class="head_record_time">
|
||||||
<text style="text-align: right;">开具日期:{{ view.createdAt }}</text>
|
<text style="text-align: right;">开具日期:{{ view.createdAt }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 患者信息 -->
|
<!-- 患者信息 -->
|
||||||
<view class="my_info">
|
<view class="my_info">
|
||||||
<view class="info">
|
<view class="info">
|
||||||
@@ -57,7 +60,6 @@
|
|||||||
<view class="name">类别:<text>{{ view.category }}</text></view>
|
<view class="name">类别:<text>{{ view.category }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<view class="info_item">
|
<view class="info_item">
|
||||||
<view class="name">科室:<text>{{ view.departName }}</text></view>
|
<view class="name">科室:<text>{{ view.departName }}</text></view>
|
||||||
@@ -66,13 +68,11 @@
|
|||||||
<view class="name">电话:<text>{{ view.patientMobile }}</text></view>
|
<view class="name">电话:<text>{{ view.patientMobile }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<view class="info_item">
|
<view class="info_item">
|
||||||
<view class="names">诊断:<text>{{ view.diagnose }}</text></view>
|
<view class="names">诊断:<text>{{ view.diagnose }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 中医专属字段 -->
|
<!-- 中医专属字段 -->
|
||||||
<view v-if="view.tcmSyndrome || view.tcmMethod || view.tcmDisease" class="info">
|
<view v-if="view.tcmSyndrome || view.tcmMethod || view.tcmDisease" class="info">
|
||||||
<view v-if="view.tcmSyndrome" class="info_item">
|
<view v-if="view.tcmSyndrome" class="info_item">
|
||||||
@@ -86,17 +86,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="bg">
|
<view class="bg">
|
||||||
<!-- 处方明细区 (Rp) -->
|
<!-- 处方明细区 (Rp) -->
|
||||||
<view class="my_medical">
|
<view class="my_medical">
|
||||||
<view class="title">Rp</view>
|
<view class="title">Rp</view>
|
||||||
|
|
||||||
<view v-for="(recipe, rIdx) in view.recipes" :key="rIdx" class="item">
|
<view v-for="(recipe, rIdx) in view.recipes" :key="rIdx" class="item">
|
||||||
<view class="name">
|
<view class="name">
|
||||||
<view class="_name" v-for="(drug, dIdx) in recipe.drugs" :key="dIdx">
|
<view class="_name" v-for="(drug, dIdx) in recipe.drugs" :key="dIdx">
|
||||||
<view class="text">
|
<view class="text">
|
||||||
<text>{{ drug.name }}</text>
|
<text>{{ drug.name }}</text>
|
||||||
|
<text v-if="drug.specification" class="drug-spec">{{ drug.specification }}</text>
|
||||||
<text class="_abbr" v-if="drug.usage">[{{ drug.usage }}]</text>
|
<text class="_abbr" v-if="drug.usage">[{{ drug.usage }}]</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="name_num">{{ drug.qty }}</text>
|
<text class="name_num">{{ drug.qty }}</text>
|
||||||
@@ -110,7 +109,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 医嘱 -->
|
<!-- 医嘱 -->
|
||||||
<view class="doctor_order">
|
<view class="doctor_order">
|
||||||
<view class="yz">
|
<view class="yz">
|
||||||
@@ -122,7 +120,6 @@
|
|||||||
<view class="titles">处方开具已完毕</view>
|
<view class="titles">处方开具已完毕</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态医生与药师签名区 -->
|
<!-- 动态医生与药师签名区 -->
|
||||||
<view class="my_doctor">
|
<view class="my_doctor">
|
||||||
<view class="doctor_info">
|
<view class="doctor_info">
|
||||||
@@ -152,13 +149,11 @@
|
|||||||
<text v-else class="name"></text>
|
<text v-else class="name"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 价格 -->
|
<!-- 价格 -->
|
||||||
<view class="price" v-if="view.totalPayPrice">
|
<view class="price" v-if="view.totalPayPrice">
|
||||||
价格 ¥{{ view.totalPayPrice }}
|
价格 ¥{{ view.totalPayPrice }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态有效期温馨提示 -->
|
<!-- 动态有效期温馨提示 -->
|
||||||
<view class="my_prompt">
|
<view class="my_prompt">
|
||||||
<view class="title">
|
<view class="title">
|
||||||
@@ -166,24 +161,25 @@
|
|||||||
</view>
|
</view>
|
||||||
<image src="/static/group/img-yzf.png" mode="aspectFit" v-if="view.status == 2"></image>
|
<image src="/static/group/img-yzf.png" mode="aspectFit" v-if="view.status == 2"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view v-else-if="loading" class="popup-loading">加载中...</view>
|
<view v-else-if="loading" class="popup-loading">加载中...</view>
|
||||||
<view v-else class="popup-loading">暂无数据</view>
|
<view v-else class="popup-loading">暂无数据</view>
|
||||||
</view>
|
</view>
|
||||||
</drawer-page-container>
|
</view>
|
||||||
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DrawerPageContainer from '@/subPackages/sub_business_shared/components/DrawerPageContainer.vue'
|
/**
|
||||||
|
* 处方详情抽屉:page-container 底部浮层(管理端看方)
|
||||||
|
*/
|
||||||
import { mapPrescriptionDetailView } from '@/subPackages/sub_business_shared/common/display.js'
|
import { mapPrescriptionDetailView } from '@/subPackages/sub_business_shared/common/display.js'
|
||||||
|
|
||||||
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
||||||
|
import { formatStoreNameWithHu } from '@/utils/formatStoreNameWithHu.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [businessMixin],
|
mixins: [businessMixin],
|
||||||
components: { DrawerPageContainer },
|
|
||||||
name: 'PrescriptionDetailPopup',
|
name: 'PrescriptionDetailPopup',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -193,7 +189,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 解析签名图片,自动兼容 base64 和 http 链接
|
/** 解析签名图片,自动兼容 base64 和 http 链接 */
|
||||||
checkImageUrl(url) {
|
checkImageUrl(url) {
|
||||||
if (!url) return ''
|
if (!url) return ''
|
||||||
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
|
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
|
||||||
@@ -205,20 +201,27 @@ export default {
|
|||||||
this.loading = true
|
this.loading = true
|
||||||
this.bizApi.getPrescriptionDetail(prescriptionId).then(w => {
|
this.bizApi.getPrescriptionDetail(prescriptionId).then(w => {
|
||||||
if (w.ok && w.data) {
|
if (w.ok && w.data) {
|
||||||
// 复用原有的处方解析逻辑
|
|
||||||
const mapped = mapPrescriptionDetailView(w.data)
|
const mapped = mapPrescriptionDetailView(w.data)
|
||||||
|
mapped.storeName = formatStoreNameWithHu(
|
||||||
// --- 动态注入你新版 UI 需要的独有字段 ---
|
w.data.store ? w.data.store.name : '未知诊所',
|
||||||
mapped.storeName = w.data.store ? w.data.store.name : '未知诊所'
|
w.data.is_online,
|
||||||
|
)
|
||||||
mapped.sealImage = w.data.store ? w.data.store.offical_seal : ''
|
mapped.sealImage = w.data.store ? w.data.store.offical_seal : ''
|
||||||
mapped.typeText = w.data.type == 1 ? '普通' : '常用'
|
mapped.typeText = w.data.type == 1 ? '普通' : '常用'
|
||||||
mapped.totalPayPrice = w.data.total_pay_price || '0.00'
|
mapped.totalPayPrice = w.data.total_pay_price || '0.00'
|
||||||
mapped.doctorSignImage = w.data.doctor_sign_image || ''
|
mapped.doctorSignImage =
|
||||||
mapped.pharmacistSignImage = w.data.Pharmacist_sign_image || ''
|
w.data.doctor_sign_image
|
||||||
|
|| (w.data.doctor_info && w.data.doctor_info.identity_info && w.data.doctor_info.identity_info.sign_image)
|
||||||
|
|| (w.data.doctorInfo && w.data.doctorInfo.identityInfo && w.data.doctorInfo.identityInfo.sign_image)
|
||||||
|
|| ''
|
||||||
|
mapped.pharmacistSignImage =
|
||||||
|
w.data.Pharmacist_sign_image
|
||||||
|
|| (w.data.pharmacist_info && w.data.pharmacist_info.identity && w.data.pharmacist_info.identity.sign_image)
|
||||||
|
|| (w.data.pharmacistInfo && w.data.pharmacistInfo.identity && w.data.pharmacistInfo.identity.sign_image)
|
||||||
|
|| ''
|
||||||
mapped.validHours = w.data.valid_hours || 72
|
mapped.validHours = w.data.valid_hours || 72
|
||||||
mapped.status = w.data.status
|
mapped.status = w.data.status
|
||||||
mapped.patientMobile = w.data.patient ? w.data.patient.mobile : ''
|
mapped.patientMobile = w.data.patient ? w.data.patient.mobile : ''
|
||||||
|
|
||||||
this.view = mapped
|
this.view = mapped
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -234,20 +237,37 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.rx-drawer {
|
||||||
|
height: 85vh;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.rx-drawer-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 28rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.rx-drawer-title { font-size: 32rpx; font-weight: 600; color: #1d2129; }
|
||||||
|
.rx-drawer-close { font-size: 28rpx; color: #6acdbb; }
|
||||||
.popup-wrap {
|
.popup-wrap {
|
||||||
height: 100%;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 60rpx; /* 给默认的关闭按钮留出空间 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-scroll {
|
.popup-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-loading {
|
.popup-loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #999;
|
||||||
@@ -445,6 +465,11 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.drug-spec {
|
||||||
|
color: #8A92A3;
|
||||||
|
font-size: 22rpx;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
._abbr {
|
._abbr {
|
||||||
color: #A7ABB0;
|
color: #A7ABB0;
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view>
|
||||||
<business-page-layout title="订单详情" :show-back="true">
|
<business-page-layout title="订单详情" :show-back="true">
|
||||||
<view class="detail-container">
|
<view class="detail-container">
|
||||||
<!-- 头部面板:通栏,展示金额与状态 -->
|
<!-- 头部面板:通栏,展示金额与状态 -->
|
||||||
@@ -17,7 +18,14 @@
|
|||||||
<view class="sub-block">
|
<view class="sub-block">
|
||||||
<view class="sub-block-title">基本信息</view>
|
<view class="sub-block-title">基本信息</view>
|
||||||
<view class="row"><text>订单类型</text><text>{{ detailExtra.orderTypeText }}</text></view>
|
<view class="row"><text>订单类型</text><text>{{ detailExtra.orderTypeText }}</text></view>
|
||||||
<view class="row"><text>患者</text><text>{{ info.patient || '-' }}</text></view>
|
<view class="row">
|
||||||
|
<text>患者</text>
|
||||||
|
<text
|
||||||
|
class="patient-link"
|
||||||
|
:class="{ disabled: !patientUpId }"
|
||||||
|
@click="openPatient"
|
||||||
|
>{{ info.patient || '-' }}</text>
|
||||||
|
</view>
|
||||||
<view class="row"><text>医生</text><text>{{ doctorName }}</text></view>
|
<view class="row"><text>医生</text><text>{{ doctorName }}</text></view>
|
||||||
<view class="row"><text>处方来源</text><text>{{ storeName }}</text></view>
|
<view class="row"><text>处方来源</text><text>{{ storeName }}</text></view>
|
||||||
<view class="row"><text>订单来源</text><text>{{ onlineText(info.is_online) }}</text></view>
|
<view class="row"><text>订单来源</text><text>{{ onlineText(info.is_online) }}</text></view>
|
||||||
@@ -50,7 +58,14 @@
|
|||||||
<!-- 配送信息面板 -->
|
<!-- 配送信息面板 -->
|
||||||
<view v-if="info.id" class="detail-panel">
|
<view v-if="info.id" class="detail-panel">
|
||||||
<view class="section-title">{{ receiverBlockTitle }}</view>
|
<view class="section-title">{{ receiverBlockTitle }}</view>
|
||||||
<view class="row"><text>{{ receiverLabel }}</text><text>{{ receiverName }}</text></view>
|
<view class="row">
|
||||||
|
<text>{{ receiverLabel }}</text>
|
||||||
|
<text
|
||||||
|
class="patient-link"
|
||||||
|
:class="{ disabled: !patientUpId }"
|
||||||
|
@click="openPatient"
|
||||||
|
>{{ receiverName }}</text>
|
||||||
|
</view>
|
||||||
<view class="row"><text>联系电话</text><text>{{ receiverMobile }}</text></view>
|
<view class="row"><text>联系电话</text><text>{{ receiverMobile }}</text></view>
|
||||||
<view class="row" v-if="info.delivery_method === 0">
|
<view class="row" v-if="info.delivery_method === 0">
|
||||||
<text>配送地址</text><text class="addr">{{ deliveryAddress }}</text>
|
<text>配送地址</text><text class="addr">{{ deliveryAddress }}</text>
|
||||||
@@ -148,7 +163,8 @@
|
|||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 处方弹窗 -->
|
</business-page-layout>
|
||||||
|
<!-- page-container 抽屉挂在页面根级,避免被 layout 裁切成页底内联 -->
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
<ship-order-popup ref="shipPopup" :biz-api="bizApi" @success="load" />
|
<ship-order-popup ref="shipPopup" :biz-api="bizApi" @success="load" />
|
||||||
<refund-order-popup ref="refundPopup" :biz-api="bizApi" @success="load" />
|
<refund-order-popup ref="refundPopup" :biz-api="bizApi" @success="load" />
|
||||||
@@ -161,7 +177,14 @@
|
|||||||
@close="closePriceAdjust"
|
@close="closePriceAdjust"
|
||||||
@confirm="onSharedPriceAdjustConfirm"
|
@confirm="onSharedPriceAdjustConfirm"
|
||||||
/>
|
/>
|
||||||
</business-page-layout>
|
<user-patient-detail
|
||||||
|
v-if="patientDetailVisible"
|
||||||
|
:visible="patientDetailVisible"
|
||||||
|
:up-id="patientDetailUpId"
|
||||||
|
:patient-name="patientDetailName"
|
||||||
|
@close="patientDetailVisible = false"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -185,6 +208,7 @@ import { formatMoney } from '@/subPackages/sub_business_shared/common/format.js'
|
|||||||
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
import businessMixin from '@/subPackages/sub_business_shared/common/businessMixin.js'
|
||||||
import { orderPriceAdjustMixin } from '@/subPackages/sub_business_shared/common/orderPriceAdjustMixin.js'
|
import { orderPriceAdjustMixin } from '@/subPackages/sub_business_shared/common/orderPriceAdjustMixin.js'
|
||||||
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue'
|
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue'
|
||||||
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [businessMixin, orderPriceAdjustMixin],
|
mixins: [businessMixin, orderPriceAdjustMixin],
|
||||||
@@ -196,6 +220,7 @@ export default {
|
|||||||
RefundOrderPopup,
|
RefundOrderPopup,
|
||||||
ErpSyncPopup,
|
ErpSyncPopup,
|
||||||
OrderPricePercentAdjustPopup,
|
OrderPricePercentAdjustPopup,
|
||||||
|
UserPatientDetail,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -203,6 +228,9 @@ export default {
|
|||||||
info: {},
|
info: {},
|
||||||
expressInfo: null,
|
expressInfo: null,
|
||||||
detailExtra: {},
|
detailExtra: {},
|
||||||
|
patientDetailVisible: false,
|
||||||
|
patientDetailUpId: 0,
|
||||||
|
patientDetailName: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -225,6 +253,13 @@ export default {
|
|||||||
const s = this.info.store
|
const s = this.info.store
|
||||||
return (s && s.name) ? s.name : '-'
|
return (s && s.name) ? s.name : '-'
|
||||||
},
|
},
|
||||||
|
/** 就诊人 ID:订单 up_id 或关联 user_patient.id */
|
||||||
|
patientUpId() {
|
||||||
|
const up = Number(this.info.up_id || 0)
|
||||||
|
if (up) return up
|
||||||
|
const rel = this.info.user_patient
|
||||||
|
return Number((rel && rel.id) || 0)
|
||||||
|
},
|
||||||
receiverBlockTitle() {
|
receiverBlockTitle() {
|
||||||
return this.info.delivery_method === 0 ? '收货人信息' : '就诊人信息'
|
return this.info.delivery_method === 0 ? '收货人信息' : '就诊人信息'
|
||||||
},
|
},
|
||||||
@@ -272,16 +307,54 @@ export default {
|
|||||||
this.id = Number(q.id || 0)
|
this.id = Number(q.id || 0)
|
||||||
this.load()
|
this.load()
|
||||||
},
|
},
|
||||||
|
/** 离开页关闭处方抽屉,避免 page-container 残留遮罩导致列表白屏 */
|
||||||
|
onHide() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
this.patientDetailVisible = false
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
this.patientDetailVisible = false
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** 关闭处方抽屉 */
|
||||||
|
closePrescriptionPopup() {
|
||||||
|
const pop = this.$refs.prescriptionPopup
|
||||||
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
|
pop.onClose()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开患者视角抽屉(与订单列表一致)
|
||||||
|
*/
|
||||||
|
openPatient() {
|
||||||
|
const upId = this.patientUpId
|
||||||
|
if (!upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.patientDetailUpId = upId
|
||||||
|
this.patientDetailName = this.info.patient || ''
|
||||||
|
this.patientDetailVisible = true
|
||||||
|
},
|
||||||
load() {
|
load() {
|
||||||
|
if (!this.id) {
|
||||||
|
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.bizApi.getOrderDetail(this.id).then(w => {
|
this.bizApi.getOrderDetail(this.id).then(w => {
|
||||||
if (!w.ok) return
|
if (!w.ok) {
|
||||||
|
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.info = w.data || {}
|
this.info = w.data || {}
|
||||||
this.detailExtra = mapOrderDetailDisplay(this.info)
|
this.detailExtra = mapOrderDetailDisplay(this.info)
|
||||||
this.loadPriceAdjustMeta(() => this.bizApi.getPriceAdjustConfig(this.info.store_id))
|
this.loadPriceAdjustMeta(() => this.bizApi.getPriceAdjustConfig(this.info.store_id))
|
||||||
if (this.info.delivery_method === 0) {
|
if (this.info.delivery_method === 0) {
|
||||||
this.loadExpress()
|
this.loadExpress()
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadExpress() {
|
loadExpress() {
|
||||||
@@ -584,6 +657,11 @@ $color-danger: #F53F3F;
|
|||||||
|
|
||||||
.price-main { font-size: 28rpx; color: $color-text-title; font-weight: 600; }
|
.price-main { font-size: 28rpx; color: $color-text-title; font-weight: 600; }
|
||||||
.price-link { color: $theme-primary; text-decoration: underline; }
|
.price-link { color: $theme-primary; text-decoration: underline; }
|
||||||
|
.patient-link {
|
||||||
|
color: $theme-primary;
|
||||||
|
text-align: right;
|
||||||
|
&.disabled { color: inherit; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ================= 底部动作栏 ================= */
|
/* ================= 底部动作栏 ================= */
|
||||||
.actions-bar {
|
.actions-bar {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view>
|
||||||
<business-page-layout title="商品订单" :show-back="true">
|
<business-page-layout title="商品订单" :show-back="true">
|
||||||
<view class="order-list-container">
|
<view class="order-list-container">
|
||||||
|
|
||||||
@@ -96,9 +97,13 @@
|
|||||||
|
|
||||||
<!-- 去掉臃肿的灰色岛,采用精致的文本网格 -->
|
<!-- 去掉臃肿的灰色岛,采用精致的文本网格 -->
|
||||||
<view class="info-grid">
|
<view class="info-grid">
|
||||||
<view class="info-item" v-if="rowDisplay(item).userNickname !== '-'">
|
<view
|
||||||
|
class="info-item"
|
||||||
|
v-if="rowDisplay(item).patientName !== '-' || rowDisplay(item).userNickname !== '-'"
|
||||||
|
@click.stop="openPatient(item)"
|
||||||
|
>
|
||||||
<text class="info-lbl">就诊人</text>
|
<text class="info-lbl">就诊人</text>
|
||||||
<text class="info-val">{{ rowDisplay(item).userNickname }}</text>
|
<text class="info-val link">{{ rowDisplay(item).patientName !== '-' ? rowDisplay(item).patientName : rowDisplay(item).userNickname }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="info-lbl">收件人</text>
|
<text class="info-lbl">收件人</text>
|
||||||
@@ -136,8 +141,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
|
||||||
</business-page-layout>
|
</business-page-layout>
|
||||||
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
|
<user-patient-detail
|
||||||
|
v-if="patientDetailVisible"
|
||||||
|
:visible="patientDetailVisible"
|
||||||
|
:up-id="patientDetailUpId"
|
||||||
|
:patient-name="patientDetailName"
|
||||||
|
@close="patientDetailVisible = false"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -145,6 +158,7 @@
|
|||||||
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue'
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue'
|
||||||
import CollapsibleFilterPanel from '@/subPackages/sub_business_shared/components/CollapsibleFilterPanel.vue'
|
import CollapsibleFilterPanel from '@/subPackages/sub_business_shared/components/CollapsibleFilterPanel.vue'
|
||||||
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
||||||
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
import { withWxKey } from '@/utils/wxListKey.js'
|
import { withWxKey } from '@/utils/wxListKey.js'
|
||||||
import { mapOrderListRow } from '@/subPackages/sub_business_shared/common/display.js'
|
import { mapOrderListRow } from '@/subPackages/sub_business_shared/common/display.js'
|
||||||
import { formatMoney } from '@/subPackages/sub_business_shared/common/format.js'
|
import { formatMoney } from '@/subPackages/sub_business_shared/common/format.js'
|
||||||
@@ -176,7 +190,7 @@ const PRESCRIPTION_TYPE_OPTIONS = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [businessMixin],
|
mixins: [businessMixin],
|
||||||
components: { BusinessPageLayout, CollapsibleFilterPanel, PrescriptionDetailPopup },
|
components: { BusinessPageLayout, CollapsibleFilterPanel, PrescriptionDetailPopup, UserPatientDetail },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: [],
|
list: [],
|
||||||
@@ -190,7 +204,10 @@ export default {
|
|||||||
dateStart: '',
|
dateStart: '',
|
||||||
dateEnd: '',
|
dateEnd: '',
|
||||||
statItems: [],
|
statItems: [],
|
||||||
navTopOffset: 0, // 新增:用于存放导航栏计算后的确切高度
|
navTopOffset: 0,
|
||||||
|
patientDetailVisible: false,
|
||||||
|
patientDetailUpId: 0,
|
||||||
|
patientDetailName: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -225,10 +242,24 @@ export default {
|
|||||||
})
|
})
|
||||||
this.checkDateEndAndReload()
|
this.checkDateEndAndReload()
|
||||||
},
|
},
|
||||||
|
/** 离开/隐藏时关掉处方抽屉,避免 page-container 残留白屏 */
|
||||||
|
onHide() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
},
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
this.load(this.page + 1, true)
|
this.load(this.page + 1, true)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** 关闭处方抽屉 */
|
||||||
|
closePrescriptionPopup() {
|
||||||
|
const pop = this.$refs.prescriptionPopup
|
||||||
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
|
pop.onClose()
|
||||||
|
}
|
||||||
|
},
|
||||||
applyOrderFilter(f) {
|
applyOrderFilter(f) {
|
||||||
this.orderNo = f.orderNo
|
this.orderNo = f.orderNo
|
||||||
this.statusIndex = f.statusIndex
|
this.statusIndex = f.statusIndex
|
||||||
@@ -337,6 +368,18 @@ export default {
|
|||||||
if (!pId) return
|
if (!pId) return
|
||||||
this.$refs.prescriptionPopup.open(pId)
|
this.$refs.prescriptionPopup.open(pId)
|
||||||
},
|
},
|
||||||
|
/** 打开患者视角详情抽屉 */
|
||||||
|
openPatient(item) {
|
||||||
|
const d = this.rowDisplay(item)
|
||||||
|
const upId = Number(d.upId || 0)
|
||||||
|
if (!upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.patientDetailUpId = upId
|
||||||
|
this.patientDetailName = d.patientName !== '-' ? d.patientName : ''
|
||||||
|
this.patientDetailVisible = true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -543,6 +586,9 @@ $color-border: #E5E6EB;
|
|||||||
.info-val {
|
.info-val {
|
||||||
color: $color-text-body;
|
color: $color-text-body;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
&.link {
|
||||||
|
color: $theme-primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.info-sub {
|
.info-sub {
|
||||||
color: $color-text-muted;
|
color: $color-text-muted;
|
||||||
|
|||||||
515
subPackages/sub_business_shared/user-patient/detail.vue
Normal file
515
subPackages/sub_business_shared/user-patient/detail.vue
Normal file
@@ -0,0 +1,515 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<business-page-layout :title="pageTitle" :show-back="true">
|
||||||
|
<view class="page">
|
||||||
|
<view v-if="loading" class="empty">加载中...</view>
|
||||||
|
<template v-else>
|
||||||
|
<!-- 顶部:用户 + 就诊人分区卡片(固定,不进 swiper) -->
|
||||||
|
<view class="profile-card">
|
||||||
|
<view class="profile-row">
|
||||||
|
<image class="avatar" :src="profile.user.avatarurl || '/static/image/default-avatar.png'" mode="aspectFill" />
|
||||||
|
<view class="meta">
|
||||||
|
<text class="name">{{ profile.user.nickname || '微信用户' }}</text>
|
||||||
|
<text class="sub">ID {{ profile.user.id || '—' }}<template v-if="profile.user.mobile"> · {{ profile.user.mobile }}</template></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="patient-block">
|
||||||
|
<view class="patient-head">
|
||||||
|
<view class="patient-head-left">
|
||||||
|
<text class="p-name">{{ profile.patient.name || '—' }}</text>
|
||||||
|
<text v-if="sexText !== '—'" class="sex-tag" :class="sexTagClass">{{ sexText }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 回访:拨打就诊人/用户手机 -->
|
||||||
|
<view class="callback-btn" @click.stop="onCallbackCall">
|
||||||
|
<text class="callback-btn-text">回访</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="p-sub">
|
||||||
|
{{ profile.patient.age || '—' }}岁
|
||||||
|
<template v-if="profile.patient.mobile"> · {{ profile.patient.mobile }}</template>
|
||||||
|
</text>
|
||||||
|
<!-- 门店后端不返回身份证时不展示 -->
|
||||||
|
<text v-if="profile.patient.id_card" class="p-sub">身份证 {{ profile.patient.id_card }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- Tab:下划线选中态 -->
|
||||||
|
<view class="tabs">
|
||||||
|
<view
|
||||||
|
v-for="(t, idx) in tabs"
|
||||||
|
:key="t.key"
|
||||||
|
class="tab"
|
||||||
|
:class="{ on: tabIndex === idx }"
|
||||||
|
@click="selectTab(idx)"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ t.label }}</text>
|
||||||
|
<view v-if="tabIndex === idx" class="tab-bar" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 四页左右滑:资料 / 挂号 / 处方 / 订单 -->
|
||||||
|
<swiper
|
||||||
|
class="tab-swiper"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
:current="tabIndex"
|
||||||
|
:duration="200"
|
||||||
|
@change="onTabSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="t in tabs" :key="'sw-' + t.key">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="tab-scroll"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
>
|
||||||
|
<!-- 资料 -->
|
||||||
|
<view v-if="t.key === 'info'" class="health-card">
|
||||||
|
<text class="section-title">健康信息</text>
|
||||||
|
<template v-if="health">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">既往史</text>
|
||||||
|
<text class="info-value">{{ historyText(health.person_status, health.person_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">过敏史</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.allergic_status) !== 0 }"
|
||||||
|
>{{ historyText(health.allergic_status, health.allergic_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">家族遗传史</text>
|
||||||
|
<text class="info-value">{{ historyText(health.family_status, health.family_history) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">肝功能异常</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.liver_function) !== 0 }"
|
||||||
|
>{{ functionText(health.liver_function, health.liver_index) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">肾功能异常</text>
|
||||||
|
<text
|
||||||
|
class="info-value"
|
||||||
|
:class="{ danger: Number(health.renal_function) !== 0 }"
|
||||||
|
>{{ functionText(health.renal_function, health.renal_index) }}</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<view v-else class="empty-inline">暂无健康问诊记录</view>
|
||||||
|
</view>
|
||||||
|
<!-- 挂号 / 处方 / 订单列表 -->
|
||||||
|
<template v-else>
|
||||||
|
<view v-if="tabLoading && activeTab === t.key" class="empty">加载中...</view>
|
||||||
|
<template v-else>
|
||||||
|
<view v-if="!listForTab(t.key).length" class="empty">暂无记录</view>
|
||||||
|
<view
|
||||||
|
v-for="item in listForTab(t.key)"
|
||||||
|
:key="t.key + '-' + item.id"
|
||||||
|
class="record-card"
|
||||||
|
@click="onRecordClick(item, t.key)"
|
||||||
|
>
|
||||||
|
<view class="record-main">
|
||||||
|
<template v-if="t.key === 'register'">
|
||||||
|
<text class="r-title">{{ item.order_no || item.order_number || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.status_text || '—' }} · {{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="t.key === 'prescription'">
|
||||||
|
<text class="r-title">{{ item.prescription_no || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · {{ item.doctor || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.clinical_diagnose || '—' }} · {{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<text class="r-title">{{ item.order_no || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.store || '—' }} · ¥{{ item.total_pay_price || '—' }}</text>
|
||||||
|
<text class="r-sub">{{ item.created_at || '' }}</text>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
<text class="arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</business-page-layout>
|
||||||
|
<!-- page-container 抽屉挂在页面根级 -->
|
||||||
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 就诊人详情:资料(健康问诊)+ 挂号/处方/订单
|
||||||
|
* 顶部资料卡固定,下方四 Tab 支持点击与左右滑动切换(懒加载列表)
|
||||||
|
*/
|
||||||
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue';
|
||||||
|
import PrescriptionDetailPopup from '@/subPackages/sub_business_shared/order/components/PrescriptionDetailPopup.vue';
|
||||||
|
import {
|
||||||
|
getUserPatientDetail,
|
||||||
|
getUserPatientRegisterList,
|
||||||
|
getUserPatientPrescriptionList,
|
||||||
|
getUserPatientOrderList,
|
||||||
|
} from '@/api/userPatientProfile.js';
|
||||||
|
|
||||||
|
/** 就诊人详情左右滑提示只展示一次 */
|
||||||
|
const SWIPE_TIP_STORAGE_KEY = 'user_patient_detail_swipe_tip';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BusinessPageLayout, PrescriptionDetailPopup },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
upId: 0,
|
||||||
|
patientName: '',
|
||||||
|
loading: false,
|
||||||
|
tabLoading: false,
|
||||||
|
activeTab: 'info',
|
||||||
|
tabIndex: 0,
|
||||||
|
tabSwiperChanging: false,
|
||||||
|
swiperHeightPx: 400,
|
||||||
|
tabs: [
|
||||||
|
{ key: 'info', label: '资料' },
|
||||||
|
{ key: 'register', label: '挂号' },
|
||||||
|
{ key: 'prescription', label: '处方' },
|
||||||
|
{ key: 'order', label: '订单' },
|
||||||
|
],
|
||||||
|
profile: { user: {}, patient: {} },
|
||||||
|
health: null,
|
||||||
|
registerList: [],
|
||||||
|
prescriptionList: [],
|
||||||
|
orderList: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageTitle() {
|
||||||
|
return this.patientName ? `就诊人 · ${this.patientName}` : '就诊人详情';
|
||||||
|
},
|
||||||
|
sexText() {
|
||||||
|
const sex = this.profile.patient && this.profile.patient.sex;
|
||||||
|
if (Number(sex) === 1) return '男';
|
||||||
|
if (Number(sex) === 2) return '女';
|
||||||
|
return '—';
|
||||||
|
},
|
||||||
|
sexTagClass() {
|
||||||
|
const sex = this.profile.patient && this.profile.patient.sex;
|
||||||
|
if (Number(sex) === 1) return 'male';
|
||||||
|
if (Number(sex) === 2) return 'female';
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 回访拨号:只用就诊人 call_mobile(拦截器只解密不脱敏)
|
||||||
|
* 不用脱敏后的 mobile,也不回退微信用户号
|
||||||
|
*/
|
||||||
|
callbackPhone() {
|
||||||
|
const patient = this.profile.patient || {};
|
||||||
|
return String(patient.call_mobile || '').trim();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
this.upId = Number((options && options.up_id) || 0);
|
||||||
|
try {
|
||||||
|
this.patientName = decodeURIComponent((options && options.name) || '');
|
||||||
|
} catch (e) {
|
||||||
|
this.patientName = (options && options.name) || '';
|
||||||
|
}
|
||||||
|
if (this.upId) {
|
||||||
|
this.loadDetail();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReady() {
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
},
|
||||||
|
/** 离开页时关掉处方抽屉,避免 page-container 残留遮罩 */
|
||||||
|
onHide() {
|
||||||
|
this.closePrescriptionPopup();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.closePrescriptionPopup();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 按窗口高度算死 swiper:窗口 − 状态栏 − 导航 − 资料卡预估 − Tab − 边距
|
||||||
|
*/
|
||||||
|
calcSwiperHeight() {
|
||||||
|
try {
|
||||||
|
const sys = uni.getSystemInfoSync() || {};
|
||||||
|
const winH = Number(sys.windowHeight) || 600;
|
||||||
|
const statusBar = Number(sys.statusBarHeight) || 0;
|
||||||
|
const navbar = Number(this.$navbarHeight) || 44;
|
||||||
|
const rpx = (Number(sys.windowWidth) || 375) / 750;
|
||||||
|
const pagePad = Math.ceil(24 * rpx);
|
||||||
|
const profileH = Math.ceil(280 * rpx);
|
||||||
|
const tabsH = Math.ceil(90 * rpx);
|
||||||
|
const h = winH - statusBar - navbar - pagePad - profileH - tabsH;
|
||||||
|
this.swiperHeightPx = h > 180 ? h : 180;
|
||||||
|
} catch (e) {
|
||||||
|
this.swiperHeightPx = 400;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 按 Tab key 取对应列表(swiper 四页同时渲染时不能只用 activeTab) */
|
||||||
|
listForTab(key) {
|
||||||
|
if (key === 'register') return this.registerList;
|
||||||
|
if (key === 'prescription') return this.prescriptionList;
|
||||||
|
if (key === 'order') return this.orderList;
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
/** 关闭处方抽屉,防止 page-container 残留导致返回后白屏 */
|
||||||
|
closePrescriptionPopup() {
|
||||||
|
const pop = this.$refs.prescriptionPopup;
|
||||||
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
|
pop.onClose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 回访:调起系统拨号盘拨打 callbackPhone
|
||||||
|
* 无号时 toast,不隐藏按钮以免布局跳动
|
||||||
|
*/
|
||||||
|
onCallbackCall() {
|
||||||
|
const phone = this.callbackPhone;
|
||||||
|
if (!phone) {
|
||||||
|
uni.showToast({ title: '暂无手机号', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uni.makePhoneCall({
|
||||||
|
phoneNumber: phone,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 既往/过敏/家族:0 无,否则展示 history */
|
||||||
|
historyText(status, history) {
|
||||||
|
if (Number(status) === 0) return '无';
|
||||||
|
const t = String(history || '').trim();
|
||||||
|
return t || '有';
|
||||||
|
},
|
||||||
|
/** 肝/肾功能:0 正常,异常时附带指标文案 */
|
||||||
|
functionText(flag, indexText) {
|
||||||
|
if (Number(flag) === 0) return '正常';
|
||||||
|
const t = String(indexText || '').trim();
|
||||||
|
return t ? `异常 · ${t}` : '异常';
|
||||||
|
},
|
||||||
|
async loadDetail() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientDetail(this.upId);
|
||||||
|
if (wrap && wrap.ok && wrap.data) {
|
||||||
|
this.profile = {
|
||||||
|
user: wrap.data.user || {},
|
||||||
|
patient: wrap.data.patient || {},
|
||||||
|
};
|
||||||
|
this.health = wrap.data.health_inquiry || null;
|
||||||
|
if (!this.patientName && this.profile.patient.name) {
|
||||||
|
this.patientName = this.profile.patient.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.maybeShowSwipeTip();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 首次进入短暂提示可左右滑动(独立缓存键,与挂号详情互不影响)
|
||||||
|
*/
|
||||||
|
maybeShowSwipeTip() {
|
||||||
|
let shown = '';
|
||||||
|
try {
|
||||||
|
shown = uni.getStorageSync(SWIPE_TIP_STORAGE_KEY);
|
||||||
|
} catch (e) {
|
||||||
|
shown = '';
|
||||||
|
}
|
||||||
|
if (shown) return;
|
||||||
|
uni.showToast({
|
||||||
|
title: '左右滑动可切换标签',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
uni.setStorageSync(SWIPE_TIP_STORAGE_KEY, '1');
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 点击顶部 Tab */
|
||||||
|
selectTab(index) {
|
||||||
|
if (this.tabSwiperChanging) return;
|
||||||
|
const tab = this.tabs[index];
|
||||||
|
if (!tab) return;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.switchTab(tab.key);
|
||||||
|
},
|
||||||
|
/** swiper 滑动切换 */
|
||||||
|
onTabSwiperChange(e) {
|
||||||
|
const index = e && e.detail ? e.detail.current : 0;
|
||||||
|
const tab = this.tabs[index];
|
||||||
|
if (!tab || tab.key === this.activeTab) {
|
||||||
|
this.tabIndex = index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tabSwiperChanging = true;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.switchTab(tab.key);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabSwiperChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 切换 Tab 并懒加载对应列表 */
|
||||||
|
async switchTab(key) {
|
||||||
|
this.activeTab = key;
|
||||||
|
const idx = this.tabs.findIndex((t) => t.key === key);
|
||||||
|
if (idx >= 0 && this.tabIndex !== idx) {
|
||||||
|
this.tabIndex = idx;
|
||||||
|
}
|
||||||
|
if (key === 'info') return;
|
||||||
|
if (key === 'register' && !this.registerList.length) {
|
||||||
|
await this.loadRegisters();
|
||||||
|
} else if (key === 'prescription' && !this.prescriptionList.length) {
|
||||||
|
await this.loadPrescriptions();
|
||||||
|
} else if (key === 'order' && !this.orderList.length) {
|
||||||
|
await this.loadOrders();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadRegisters() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientRegisterList(this.upId);
|
||||||
|
this.registerList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadPrescriptions() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientPrescriptionList(this.upId);
|
||||||
|
this.prescriptionList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadOrders() {
|
||||||
|
this.tabLoading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientOrderList(this.upId);
|
||||||
|
this.orderList = (wrap && wrap.ok && wrap.data && wrap.data.items) || [];
|
||||||
|
} finally {
|
||||||
|
this.tabLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 列表项点击:处方弹窗 / 订单页 / 挂号详情页
|
||||||
|
* @param item 列表行
|
||||||
|
* @param tabKey 所在 Tab(避免滑动瞬间 activeTab 未同步)
|
||||||
|
*/
|
||||||
|
onRecordClick(item, tabKey) {
|
||||||
|
const key = tabKey || this.activeTab;
|
||||||
|
if (key === 'prescription') {
|
||||||
|
const pid = Number((item && item.id) || 0);
|
||||||
|
if (!pid) {
|
||||||
|
uni.showToast({ title: '缺少处方信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$refs.prescriptionPopup && this.$refs.prescriptionPopup.open(pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key === 'order') {
|
||||||
|
const oid = Number((item && item.id) || 0);
|
||||||
|
if (!oid) {
|
||||||
|
uni.showToast({ title: '缺少订单信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 诊所端进本包订单详情,平台端进 shared;避免跨包 + page-container 残留导致列表白屏
|
||||||
|
const mode = uni.getStorageSync('loginMode') || '';
|
||||||
|
const detailUrl = mode === 'clinic_admin'
|
||||||
|
? `/subPackages/sub_clinic_admin/order/detail?id=${oid}`
|
||||||
|
: `/subPackages/sub_business_shared/order/detail?id=${oid}`;
|
||||||
|
uni.navigateTo({ url: detailUrl });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key === 'register') {
|
||||||
|
// 轻量详情页用列表字段即可,暂存避免 URL 过长
|
||||||
|
try {
|
||||||
|
uni.setStorageSync('user_patient_register_detail', item || {});
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/subPackages/sub_business_shared/user-patient/register-detail?id=${Number((item && item.id) || 0)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page { padding: 24rpx 24rpx 0; background: #f5f7f8; box-sizing: border-box; }
|
||||||
|
.profile-card {
|
||||||
|
background: #fff; border-radius: 20rpx; padding: 28rpx 24rpx; margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.profile-row { display: flex; align-items: center; gap: 20rpx; margin-bottom: 20rpx; }
|
||||||
|
.avatar { width: 72rpx; height: 72rpx; border-radius: 50%; background: #e8f5f2; }
|
||||||
|
.meta { display: flex; flex-direction: column; min-width: 0; }
|
||||||
|
.name { font-size: 28rpx; font-weight: 600; color: #1f2937; }
|
||||||
|
.sub { font-size: 22rpx; color: #9ca3af; margin-top: 6rpx; }
|
||||||
|
.patient-block { padding-top: 20rpx; border-top: 1rpx solid #eef2f1; }
|
||||||
|
.patient-head {
|
||||||
|
display: flex; align-items: center; justify-content: space-between; gap: 16rpx;
|
||||||
|
}
|
||||||
|
.patient-head-left { display: flex; align-items: center; gap: 12rpx; min-width: 0; flex: 1; }
|
||||||
|
.p-name { font-size: 30rpx; font-weight: 600; color: #111827; }
|
||||||
|
.sex-tag {
|
||||||
|
font-size: 20rpx; padding: 2rpx 12rpx; border-radius: 8rpx;
|
||||||
|
background: #e8f5f2; color: #2a9d8f;
|
||||||
|
}
|
||||||
|
.callback-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 8rpx 22rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
border: 1rpx solid #6acdbb;
|
||||||
|
background: #e8f5f2;
|
||||||
|
}
|
||||||
|
.callback-btn-text { font-size: 24rpx; color: #1a9b88; font-weight: 500; }
|
||||||
|
.sex-tag.male { background: #e8f0fe; color: #3b82f6; }
|
||||||
|
.sex-tag.female { background: #fce7f3; color: #db2777; }
|
||||||
|
.p-sub { display: block; font-size: 24rpx; color: #6b7280; margin-top: 8rpx; }
|
||||||
|
.tabs {
|
||||||
|
display: flex; background: #fff; border-radius: 16rpx 16rpx 0 0;
|
||||||
|
margin-bottom: 0; padding: 0 8rpx; border-bottom: 1rpx solid #eef2f1;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
flex: 1; display: flex; flex-direction: column; align-items: center;
|
||||||
|
padding: 22rpx 0 16rpx; position: relative;
|
||||||
|
}
|
||||||
|
.tab-text { font-size: 26rpx; color: #6b7280; }
|
||||||
|
.tab.on .tab-text { color: #1a9b88; font-weight: 600; }
|
||||||
|
.tab-bar {
|
||||||
|
position: absolute; bottom: 0; width: 48rpx; height: 4rpx;
|
||||||
|
border-radius: 4rpx; background: #6acdbb;
|
||||||
|
}
|
||||||
|
.tab-swiper { width: 100%; }
|
||||||
|
.tab-scroll { width: 100%; box-sizing: border-box; }
|
||||||
|
.health-card {
|
||||||
|
background: #fff; border-radius: 0 0 16rpx 16rpx; padding: 24rpx;
|
||||||
|
}
|
||||||
|
.section-title {
|
||||||
|
display: block; font-size: 26rpx; font-weight: 600; color: #1f2937; margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
.info-row {
|
||||||
|
display: flex; justify-content: space-between; align-items: flex-start;
|
||||||
|
padding: 16rpx 0; border-bottom: 1rpx solid #f3f4f6; gap: 24rpx;
|
||||||
|
}
|
||||||
|
.info-row:last-child { border-bottom: none; }
|
||||||
|
.info-label { font-size: 24rpx; color: #9ca3af; flex-shrink: 0; }
|
||||||
|
.info-value { font-size: 24rpx; color: #374151; text-align: right; flex: 1; word-break: break-all; }
|
||||||
|
.info-value.danger { color: #dc2626; font-weight: 600; }
|
||||||
|
.empty-inline { text-align: center; color: #9ca3af; padding: 40rpx 0; font-size: 24rpx; }
|
||||||
|
.record-card {
|
||||||
|
background: #fff; border-radius: 14rpx; padding: 22rpx 20rpx; margin-bottom: 12rpx;
|
||||||
|
display: flex; align-items: center; gap: 12rpx;
|
||||||
|
}
|
||||||
|
.record-main { flex: 1; min-width: 0; }
|
||||||
|
.r-title { display: block; font-size: 26rpx; font-weight: 600; color: #111827; }
|
||||||
|
.r-sub { display: block; font-size: 22rpx; color: #6b7280; margin-top: 6rpx; }
|
||||||
|
.arrow { font-size: 36rpx; color: #d1d5db; line-height: 1; padding-left: 4rpx; }
|
||||||
|
.empty { text-align: center; color: #9ca3af; padding: 80rpx 0; font-size: 28rpx; }
|
||||||
|
</style>
|
||||||
144
subPackages/sub_business_shared/user-patient/index.vue
Normal file
144
subPackages/sub_business_shared/user-patient/index.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<business-page-layout title="会员管理" :show-back="true">
|
||||||
|
<view class="page">
|
||||||
|
<view class="search-bar">
|
||||||
|
<input
|
||||||
|
class="search-input"
|
||||||
|
v-model="keyword"
|
||||||
|
placeholder="昵称 / 手机 / 就诊人"
|
||||||
|
confirm-type="search"
|
||||||
|
@confirm="reload"
|
||||||
|
/>
|
||||||
|
<view class="search-btn" @click="reload">查询</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="loading" class="empty">加载中...</view>
|
||||||
|
<view v-else-if="!list.length" class="empty">暂无用户</view>
|
||||||
|
<view
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
class="card"
|
||||||
|
@click="goPatients(item)"
|
||||||
|
>
|
||||||
|
<view class="user-row">
|
||||||
|
<image class="avatar" :src="avatarSrc(item.avatarurl)" mode="aspectFill" />
|
||||||
|
<view class="text">
|
||||||
|
<text class="name">{{ item.nickname || '—' }}</text>
|
||||||
|
<text class="sub">
|
||||||
|
ID:{{ item.id || '—' }}
|
||||||
|
<template v-if="item.mobile"> · {{ item.mobile }}</template>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<text class="chevron">›</text>
|
||||||
|
</view>
|
||||||
|
<!-- 就诊人标签化:一行多枚、自动换行 -->
|
||||||
|
<view v-if="item.patients && item.patients.length" class="patients">
|
||||||
|
<view
|
||||||
|
v-for="p in item.patients"
|
||||||
|
:key="p.up_id"
|
||||||
|
class="patient-tag"
|
||||||
|
>
|
||||||
|
<text class="tag-text">{{ patientTagText(p) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="patients empty-p">暂无就诊人</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</business-page-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 会员管理用户列表:就诊人以标签展示;点用户进入就诊人列表页
|
||||||
|
*/
|
||||||
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue';
|
||||||
|
import { getWxUserList, pickProfileItems } from '@/api/userPatientProfile.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BusinessPageLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
keyword: '',
|
||||||
|
list: [],
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onShow() {
|
||||||
|
this.reload();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
avatarSrc(raw) {
|
||||||
|
const s = String(raw || '').trim();
|
||||||
|
return s || '/static/image/default-avatar.png';
|
||||||
|
},
|
||||||
|
/** 标签文案:姓名 或 姓名 · 脱敏手机 */
|
||||||
|
patientTagText(p) {
|
||||||
|
const name = (p && p.name) || '—';
|
||||||
|
const mobile = (p && p.mobile) ? String(p.mobile).trim() : '';
|
||||||
|
return mobile ? `${name} · ${mobile}` : name;
|
||||||
|
},
|
||||||
|
async reload() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
// 无关键字时不要传 keyword,uni 请求会把 undefined 序列化成字符串 "undefined"
|
||||||
|
const params = {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
};
|
||||||
|
const kw = (this.keyword || '').trim();
|
||||||
|
if (kw) {
|
||||||
|
params.keyword = kw;
|
||||||
|
}
|
||||||
|
const wrap = await getWxUserList(params);
|
||||||
|
this.list = pickProfileItems(wrap);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 跳转就诊人列表页 */
|
||||||
|
goPatients(item) {
|
||||||
|
const userId = Number((item && item.id) || 0);
|
||||||
|
if (!userId) {
|
||||||
|
uni.showToast({ title: '缺少用户信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nick = encodeURIComponent((item && item.nickname) || '');
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/subPackages/sub_business_shared/user-patient/patients?user_id=${userId}&nickname=${nick}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page { padding: 24rpx; }
|
||||||
|
.search-bar { display: flex; gap: 12rpx; margin-bottom: 20rpx; }
|
||||||
|
.search-input {
|
||||||
|
flex: 1; background: #fff; border-radius: 12rpx;
|
||||||
|
padding: 16rpx 20rpx; font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.search-btn {
|
||||||
|
background: #6acdbb; color: #fff; padding: 16rpx 28rpx;
|
||||||
|
border-radius: 12rpx; font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: #fff; border-radius: 16rpx; padding: 20rpx 20rpx 16rpx; margin-bottom: 14rpx;
|
||||||
|
}
|
||||||
|
.user-row { display: flex; align-items: center; gap: 14rpx; }
|
||||||
|
.avatar { width: 56rpx; height: 56rpx; border-radius: 50%; background: #e8f5f2; flex-shrink: 0; }
|
||||||
|
.text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4rpx; }
|
||||||
|
.name { font-size: 28rpx; color: #111827; font-weight: 500; }
|
||||||
|
.sub { font-size: 22rpx; color: #9ca3af; }
|
||||||
|
.chevron { font-size: 32rpx; color: #d1d5db; line-height: 1; }
|
||||||
|
.patients {
|
||||||
|
margin-top: 14rpx; padding-top: 14rpx; border-top: 1rpx solid #eef2f1;
|
||||||
|
display: flex; flex-wrap: wrap; gap: 12rpx;
|
||||||
|
}
|
||||||
|
.patient-tag {
|
||||||
|
padding: 6rpx 16rpx; border-radius: 8rpx;
|
||||||
|
background: #f0faf7; border: 1rpx solid #6acdbb;
|
||||||
|
}
|
||||||
|
.tag-text { font-size: 22rpx; color: #1a9b88; line-height: 1.4; }
|
||||||
|
.empty-p { font-size: 22rpx; color: #d1d5db; }
|
||||||
|
.empty { text-align: center; color: #9ca3af; padding: 80rpx 0; font-size: 28rpx; }
|
||||||
|
</style>
|
||||||
118
subPackages/sub_business_shared/user-patient/patients.vue
Normal file
118
subPackages/sub_business_shared/user-patient/patients.vue
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<business-page-layout :title="pageTitle" :show-back="true">
|
||||||
|
<view class="page">
|
||||||
|
<view v-if="loading" class="empty">加载中...</view>
|
||||||
|
<!-- 小程序端:勿把 v-else-if 与 v-for 写在同级,否则有数据也可能只显示空态 -->
|
||||||
|
<block v-else>
|
||||||
|
<view v-if="!patientList.length" class="empty">暂无就诊人</view>
|
||||||
|
<view
|
||||||
|
v-for="p in patientList"
|
||||||
|
:key="p.up_id"
|
||||||
|
class="card"
|
||||||
|
@click="goDetail(p)"
|
||||||
|
>
|
||||||
|
<view class="card-main">
|
||||||
|
<view class="name-row">
|
||||||
|
<text class="pname">{{ p.name || p.patient_name || '—' }}</text>
|
||||||
|
<!-- 小程序 :class 不支持 sexClass(p) 方法调用,用对象字面量 -->
|
||||||
|
<text
|
||||||
|
v-if="p.sex == 1 || p.sex == 2"
|
||||||
|
class="sex-tag"
|
||||||
|
:class="{ male: p.sex == 1, female: p.sex == 2 }"
|
||||||
|
>{{ p.sex == 1 ? '男' : '女' }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="psub">
|
||||||
|
<template v-if="p.mobile || p.patient_mobile">{{ p.mobile || p.patient_mobile }}</template>
|
||||||
|
<template v-else>暂无手机号</template>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<text class="arrow">›</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
</business-page-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 某微信用户下的就诊人列表页:左主信息 + 右箭头,点进入详情
|
||||||
|
*/
|
||||||
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue';
|
||||||
|
import { getPatientListByUser, pickProfileItems } from '@/api/userPatientProfile.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BusinessPageLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
userId: 0,
|
||||||
|
nickname: '',
|
||||||
|
patientList: [],
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageTitle() {
|
||||||
|
return this.nickname ? `就诊人 · ${this.nickname}` : '就诊人列表';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.userId = Number((options && options.user_id) || 0);
|
||||||
|
try {
|
||||||
|
this.nickname = decodeURIComponent((options && options.nickname) || '');
|
||||||
|
} catch (e) {
|
||||||
|
this.nickname = (options && options.nickname) || '';
|
||||||
|
}
|
||||||
|
this.reload();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async reload() {
|
||||||
|
if (!this.userId) {
|
||||||
|
this.patientList = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getPatientListByUser(this.userId);
|
||||||
|
// 有 items 就展示,避免 ok 误判把有效数据丢掉
|
||||||
|
this.patientList = pickProfileItems(wrap);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('就诊人列表加载失败', e);
|
||||||
|
this.patientList = [];
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goDetail(p) {
|
||||||
|
const upId = Number((p && (p.up_id || p.id)) || 0);
|
||||||
|
if (!upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = encodeURIComponent((p && (p.name || p.patient_name)) || '');
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/subPackages/sub_business_shared/user-patient/detail?up_id=${upId}&name=${name}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page { padding: 24rpx; }
|
||||||
|
.card {
|
||||||
|
background: #fff; border-radius: 16rpx; padding: 24rpx 20rpx; margin-bottom: 14rpx;
|
||||||
|
display: flex; align-items: center; gap: 12rpx;
|
||||||
|
}
|
||||||
|
.card-main { flex: 1; min-width: 0; }
|
||||||
|
.name-row { display: flex; align-items: center; gap: 12rpx; }
|
||||||
|
.pname { font-size: 30rpx; color: #111827; font-weight: 500; }
|
||||||
|
.sex-tag {
|
||||||
|
font-size: 20rpx; padding: 2rpx 12rpx; border-radius: 8rpx;
|
||||||
|
background: #e8f5f2; color: #2a9d8f;
|
||||||
|
}
|
||||||
|
.sex-tag.male { background: #e8f0fe; color: #3b82f6; }
|
||||||
|
.sex-tag.female { background: #fce7f3; color: #db2777; }
|
||||||
|
.psub { display: block; font-size: 24rpx; color: #9ca3af; margin-top: 8rpx; }
|
||||||
|
.arrow { font-size: 36rpx; color: #d1d5db; line-height: 1; }
|
||||||
|
.empty { text-align: center; color: #9ca3af; padding: 80rpx 0; font-size: 28rpx; }
|
||||||
|
</style>
|
||||||
440
subPackages/sub_business_shared/user-patient/register-detail.vue
Normal file
440
subPackages/sub_business_shared/user-patient/register-detail.vue
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
<template>
|
||||||
|
<business-page-layout title="挂号详情" :show-back="true">
|
||||||
|
<view class="page">
|
||||||
|
<view v-if="loading" class="empty">加载中...</view>
|
||||||
|
<template v-else-if="!detail.id">
|
||||||
|
<view class="empty">暂无挂号信息</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<!-- 有药品/回答时才出 Tab;仅基本信息时不展示 Tab 栏 -->
|
||||||
|
<view v-if="visibleTabs.length > 1" class="tabs">
|
||||||
|
<view
|
||||||
|
v-for="(t, idx) in visibleTabs"
|
||||||
|
:key="t.key"
|
||||||
|
class="tab"
|
||||||
|
:class="{ on: tabIndex === idx }"
|
||||||
|
@click="selectTab(idx)"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ t.label }}</text>
|
||||||
|
<view v-if="tabIndex === idx" class="tab-bar" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 多 Tab:固定高度 swiper,避免与外层页面滚动抢手势 -->
|
||||||
|
<swiper
|
||||||
|
v-if="visibleTabs.length > 1"
|
||||||
|
class="tab-swiper"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
:current="tabIndex"
|
||||||
|
:duration="200"
|
||||||
|
@change="onTabSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="t in visibleTabs" :key="'sw-' + t.key">
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="tab-scroll"
|
||||||
|
:style="{ height: swiperHeightPx + 'px' }"
|
||||||
|
>
|
||||||
|
<view v-if="t.key === 'basic'" class="card">
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">订单号</text>
|
||||||
|
<text class="value">{{ detail.order_no || detail.order_number || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">诊所</text>
|
||||||
|
<text class="value">{{ detail.store || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">医生</text>
|
||||||
|
<text class="value">{{ detail.doctor || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">挂号类型</text>
|
||||||
|
<text class="value">{{ detail.type_text || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">状态</text>
|
||||||
|
<text class="value accent">{{ detail.status_text || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">金额</text>
|
||||||
|
<text class="value">¥{{ formatPrice(detail.price) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">支付</text>
|
||||||
|
<text class="value">{{ Number(detail.is_pay) === 1 ? '已支付' : '未支付' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">创建时间</text>
|
||||||
|
<text class="value">{{ detail.created_at || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="detail.pay_time" class="row">
|
||||||
|
<text class="label">支付时间</text>
|
||||||
|
<text class="value">{{ detail.pay_time }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="t.key === 'drugs'" class="card">
|
||||||
|
<view v-if="!drugRows.length" class="empty-inline">暂无选药</view>
|
||||||
|
<view v-for="row in drugRows" :key="row._wxKey" class="drug-row">
|
||||||
|
<image
|
||||||
|
v-if="row.image"
|
||||||
|
class="drug-thumb"
|
||||||
|
:src="row.image"
|
||||||
|
mode="aspectFill"
|
||||||
|
@click="previewDrug(row.image)"
|
||||||
|
/>
|
||||||
|
<view v-else class="drug-thumb drug-thumb-placeholder">
|
||||||
|
<text class="drug-thumb-letter">{{ drugThumbLetter(row.name) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="drug-meta">
|
||||||
|
<text class="drug-name">{{ row.name || '药品' }}</text>
|
||||||
|
<text class="drug-spec">{{ row.specification || '暂无规格' }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="drug-qty">×{{ row.quantity }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="t.key === 'answers'" class="card">
|
||||||
|
<view v-if="!answerRecords.length" class="empty-inline">暂无回答</view>
|
||||||
|
<view v-for="(a, aidx) in answerRecords" :key="'a-' + aidx" class="row answer-row">
|
||||||
|
<text class="label">{{ a.question || '问题' }}</text>
|
||||||
|
<text class="value">{{ a.answer || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
<!-- 仅基本信息:无滑动 -->
|
||||||
|
<view v-else class="card">
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">订单号</text>
|
||||||
|
<text class="value">{{ detail.order_no || detail.order_number || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">诊所</text>
|
||||||
|
<text class="value">{{ detail.store || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">医生</text>
|
||||||
|
<text class="value">{{ detail.doctor || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">挂号类型</text>
|
||||||
|
<text class="value">{{ detail.type_text || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">状态</text>
|
||||||
|
<text class="value accent">{{ detail.status_text || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">金额</text>
|
||||||
|
<text class="value">¥{{ formatPrice(detail.price) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">支付</text>
|
||||||
|
<text class="value">{{ Number(detail.is_pay) === 1 ? '已支付' : '未支付' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row">
|
||||||
|
<text class="label">创建时间</text>
|
||||||
|
<text class="value">{{ detail.created_at || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="detail.pay_time" class="row">
|
||||||
|
<text class="label">支付时间</text>
|
||||||
|
<text class="value">{{ detail.pay_time }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</business-page-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 挂号详情:基本信息 + 条件 Tab(本次选药 / 回答记录)
|
||||||
|
* 多 Tab 时用固定高度 swiper 左右滑切换,并在首次进入时短暂提示
|
||||||
|
*/
|
||||||
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue';
|
||||||
|
import { getUserPatientRegisterDetail } from '@/api/userPatientProfile.js';
|
||||||
|
|
||||||
|
/** 左右滑切换提示只展示一次,避免每次进入打扰 */
|
||||||
|
const SWIPE_TIP_STORAGE_KEY = 'user_patient_register_detail_swipe_tip';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { BusinessPageLayout },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
registerId: 0,
|
||||||
|
activeTab: 'basic',
|
||||||
|
/** swiper 当前下标,与 activeTab / visibleTabs 同步 */
|
||||||
|
tabIndex: 0,
|
||||||
|
/** 点击 Tab 与 swiper change 互斥,避免回环 */
|
||||||
|
tabSwiperChanging: false,
|
||||||
|
/** 按窗口算出的 swiper 像素高度,保证横向滑动可用 */
|
||||||
|
swiperHeightPx: 400,
|
||||||
|
detail: {},
|
||||||
|
registerInfo: null,
|
||||||
|
answerRecords: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/** 药品行:多选西药优先,否则单药 */
|
||||||
|
drugRows() {
|
||||||
|
const info = this.registerInfo;
|
||||||
|
if (!info || typeof info !== 'object') return [];
|
||||||
|
const arr = info.selected_western_drugs;
|
||||||
|
const fallbackQty = info.number != null && info.number !== '' ? Number(info.number) : 1;
|
||||||
|
if (Array.isArray(arr) && arr.length) {
|
||||||
|
return arr.map((d, idx) => {
|
||||||
|
const q = d && d.quantity != null ? Number(d.quantity) : fallbackQty;
|
||||||
|
const id = d && (d.drug_id || d.id);
|
||||||
|
return {
|
||||||
|
_wxKey: 'd-' + (id != null ? id : idx),
|
||||||
|
name: (d && d.name) || '',
|
||||||
|
specification: (d && (d.specification || d.spec)) || '',
|
||||||
|
image: (d && (d.image || d.drug_image)) || '',
|
||||||
|
quantity: q >= 1 ? q : 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (info.drug && info.drug.name) {
|
||||||
|
return [{
|
||||||
|
_wxKey: 'd-' + (info.drug.drug_id || info.drug.id || 0),
|
||||||
|
name: info.drug.name,
|
||||||
|
specification: info.drug.specification || info.drug.spec || '',
|
||||||
|
image: info.drug.image || info.drug.drug_image || '',
|
||||||
|
quantity: fallbackQty >= 1 ? fallbackQty : 1,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
hasDrugs() {
|
||||||
|
return this.drugRows.length > 0;
|
||||||
|
},
|
||||||
|
hasAnswers() {
|
||||||
|
return Array.isArray(this.answerRecords) && this.answerRecords.length > 0;
|
||||||
|
},
|
||||||
|
/** 仅展示有数据的 Tab;基本信息始终保留 */
|
||||||
|
visibleTabs() {
|
||||||
|
const all = [
|
||||||
|
{ key: 'basic', label: '基本信息' },
|
||||||
|
{ key: 'drugs', label: '本次挂号选择药品', show: this.hasDrugs },
|
||||||
|
{ key: 'answers', label: '回答记录', show: this.hasAnswers },
|
||||||
|
];
|
||||||
|
return all.filter((t) => t.show !== false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onLoad(options) {
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
const id = Number((options && options.id) || 0);
|
||||||
|
this.registerId = id;
|
||||||
|
let cached = {};
|
||||||
|
try {
|
||||||
|
cached = uni.getStorageSync('user_patient_register_detail') || {};
|
||||||
|
} catch (e) {
|
||||||
|
cached = {};
|
||||||
|
}
|
||||||
|
// 缓存作乐观展示;id 不一致则只用 URL 中的 id
|
||||||
|
if (cached && Number(cached.id) === id) {
|
||||||
|
this.detail = cached;
|
||||||
|
} else {
|
||||||
|
this.detail = { id };
|
||||||
|
}
|
||||||
|
if (id) {
|
||||||
|
this.loadDetail(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onReady() {
|
||||||
|
// 布局就绪后再算一次,避免状态栏/导航高度未就绪
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 按窗口高度算死 swiper 高度:窗口 − 状态栏 − 导航 − Tab − 页边距
|
||||||
|
* 微信小程序 swiper 无明确高度时横向滑动常失效
|
||||||
|
*/
|
||||||
|
calcSwiperHeight() {
|
||||||
|
try {
|
||||||
|
const sys = uni.getSystemInfoSync() || {};
|
||||||
|
const winH = Number(sys.windowHeight) || 600;
|
||||||
|
const statusBar = Number(sys.statusBarHeight) || 0;
|
||||||
|
const navbar = Number(this.$navbarHeight) || 44;
|
||||||
|
const rpx = (Number(sys.windowWidth) || 375) / 750;
|
||||||
|
const pagePad = Math.ceil(24 * rpx);
|
||||||
|
const tabsH = Math.ceil(88 * rpx);
|
||||||
|
const h = winH - statusBar - navbar - pagePad * 2 - tabsH;
|
||||||
|
this.swiperHeightPx = h > 200 ? h : 200;
|
||||||
|
} catch (e) {
|
||||||
|
this.swiperHeightPx = 400;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatPrice(v) {
|
||||||
|
const n = Number(v);
|
||||||
|
return Number.isFinite(n) ? n.toFixed(2) : '0.00';
|
||||||
|
},
|
||||||
|
/** 无图时取药名首字作占位,与接诊面板一致 */
|
||||||
|
drugThumbLetter(name) {
|
||||||
|
const s = String(name || '').trim();
|
||||||
|
return s ? s.charAt(0) : '药';
|
||||||
|
},
|
||||||
|
previewDrug(url) {
|
||||||
|
if (!url) return;
|
||||||
|
uni.previewImage({ urls: [url], current: url });
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 点击顶部 Tab:同步 activeTab + tabIndex(与工作台 swiper 防抖一致)
|
||||||
|
*/
|
||||||
|
selectTab(index) {
|
||||||
|
if (this.tabSwiperChanging) return;
|
||||||
|
const tab = this.visibleTabs[index];
|
||||||
|
if (!tab) return;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.activeTab = tab.key;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 左右滑动 swiper:同步 Tab 下标与 activeTab
|
||||||
|
*/
|
||||||
|
onTabSwiperChange(e) {
|
||||||
|
const index = e && e.detail ? e.detail.current : 0;
|
||||||
|
const tab = this.visibleTabs[index];
|
||||||
|
if (!tab || tab.key === this.activeTab) return;
|
||||||
|
this.tabSwiperChanging = true;
|
||||||
|
this.tabIndex = index;
|
||||||
|
this.activeTab = tab.key;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabSwiperChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 按 activeTab 校正 tabIndex(数据加载后 Tab 集合可能变化)
|
||||||
|
*/
|
||||||
|
syncTabIndexFromActive() {
|
||||||
|
const keys = this.visibleTabs.map((t) => t.key);
|
||||||
|
let idx = keys.indexOf(this.activeTab);
|
||||||
|
if (idx < 0) {
|
||||||
|
this.activeTab = 'basic';
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
this.tabIndex = idx;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 多 Tab 时首次进入短暂提示「可左右滑动」;本地标记只提示一次
|
||||||
|
*/
|
||||||
|
maybeShowSwipeTip() {
|
||||||
|
if (this.visibleTabs.length <= 1) return;
|
||||||
|
let shown = '';
|
||||||
|
try {
|
||||||
|
shown = uni.getStorageSync(SWIPE_TIP_STORAGE_KEY);
|
||||||
|
} catch (e) {
|
||||||
|
shown = '';
|
||||||
|
}
|
||||||
|
if (shown) return;
|
||||||
|
uni.showToast({
|
||||||
|
title: '左右滑动可切换标签',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
uni.setStorageSync(SWIPE_TIP_STORAGE_KEY, '1');
|
||||||
|
} catch (e) {
|
||||||
|
// 写缓存失败不影响主流程
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** 拉取挂号详情并同步 Tab 可见性 */
|
||||||
|
async loadDetail(id) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const wrap = await getUserPatientRegisterDetail(id);
|
||||||
|
if (!wrap || !wrap.ok || !wrap.data) {
|
||||||
|
uni.showToast({ title: '挂号详情加载失败', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = wrap.data;
|
||||||
|
this.detail = {
|
||||||
|
id: data.id,
|
||||||
|
order_no: data.order_no,
|
||||||
|
order_number: data.order_number,
|
||||||
|
store: data.store,
|
||||||
|
doctor: data.doctor,
|
||||||
|
type: data.type,
|
||||||
|
type_text: data.type_text,
|
||||||
|
status_text: data.status_text,
|
||||||
|
price: data.price,
|
||||||
|
is_pay: data.is_pay,
|
||||||
|
created_at: data.created_at,
|
||||||
|
pay_time: data.pay_time,
|
||||||
|
};
|
||||||
|
this.registerInfo = data.register_info || null;
|
||||||
|
this.answerRecords = Array.isArray(data.answer_records) ? data.answer_records : [];
|
||||||
|
this.syncTabIndexFromActive();
|
||||||
|
this.calcSwiperHeight();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.maybeShowSwipeTip();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page {
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8rpx 24rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
padding: 0 4rpx;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
position: relative;
|
||||||
|
padding: 8rpx 0 12rpx;
|
||||||
|
}
|
||||||
|
.tab-text { font-size: 28rpx; color: #6b7280; }
|
||||||
|
.tab.on .tab-text { color: #1a9b88; font-weight: 600; }
|
||||||
|
.tab-bar {
|
||||||
|
position: absolute; left: 20%; right: 20%; bottom: 0;
|
||||||
|
height: 6rpx; border-radius: 6rpx; background: #6acdbb;
|
||||||
|
}
|
||||||
|
.tab-swiper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.tab-scroll {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: #fff; border-radius: 20rpx; padding: 8rpx 24rpx;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex; justify-content: space-between; align-items: flex-start;
|
||||||
|
padding: 24rpx 0; border-bottom: 1rpx solid #f3f4f6; gap: 24rpx;
|
||||||
|
}
|
||||||
|
.row:last-child { border-bottom: none; }
|
||||||
|
.answer-row .label { max-width: 46%; }
|
||||||
|
.label { font-size: 26rpx; color: #9ca3af; flex-shrink: 0; }
|
||||||
|
.value { font-size: 26rpx; color: #1f2937; text-align: right; flex: 1; word-break: break-all; }
|
||||||
|
.value.accent { color: #1a9b88; font-weight: 600; }
|
||||||
|
.drug-row {
|
||||||
|
display: flex; align-items: flex-start; gap: 16rpx;
|
||||||
|
padding: 24rpx 0; border-bottom: 1rpx solid #f3f4f6;
|
||||||
|
}
|
||||||
|
.drug-row:last-child { border-bottom: none; }
|
||||||
|
.drug-thumb {
|
||||||
|
width: 72rpx; height: 72rpx; border-radius: 10rpx; background: #f3f4f6; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.drug-thumb-placeholder {
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
.drug-thumb-letter { font-size: 28rpx; color: #6b7280; font-weight: 600; }
|
||||||
|
.drug-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6rpx; }
|
||||||
|
.drug-name { font-size: 28rpx; color: #1f2937; font-weight: 500; }
|
||||||
|
.drug-spec { font-size: 22rpx; color: #9ca3af; }
|
||||||
|
.drug-qty { font-size: 28rpx; color: #1f2937; font-weight: 600; flex-shrink: 0; }
|
||||||
|
.empty { text-align: center; color: #9ca3af; padding: 80rpx 0; font-size: 28rpx; }
|
||||||
|
.empty-inline { text-align: center; color: #9ca3af; padding: 48rpx 0; font-size: 26rpx; }
|
||||||
|
</style>
|
||||||
@@ -214,6 +214,8 @@ export function mapOrderListRow(item) {
|
|||||||
deliveryText: deliveryMethodText(item.delivery_method),
|
deliveryText: deliveryMethodText(item.delivery_method),
|
||||||
deliveryClass: 'tag-delivery',
|
deliveryClass: 'tag-delivery',
|
||||||
userNickname: user.nickname || '-',
|
userNickname: user.nickname || '-',
|
||||||
|
patientName: (item.user_patient && item.user_patient.name) || item.patient || '-',
|
||||||
|
upId: Number(item.up_id || (item.user_patient && item.user_patient.id) || 0),
|
||||||
payTime: formatDateTime(item.pay_time),
|
payTime: formatDateTime(item.pay_time),
|
||||||
createdAt: formatDateTime(item.created_at),
|
createdAt: formatDateTime(item.created_at),
|
||||||
storeName: store.name || '-',
|
storeName: store.name || '-',
|
||||||
@@ -346,7 +348,12 @@ export function mapPrescriptionDetailView(item) {
|
|||||||
const name = d.name || d.drug_name || '—'
|
const name = d.name || d.drug_name || '—'
|
||||||
const num = d.number != null ? d.number : ''
|
const num = d.number != null ? d.number : ''
|
||||||
const way = (d.use_way && d.use_way.name) || d.useWay || '煎服'
|
const way = (d.use_way && d.use_way.name) || d.useWay || '煎服'
|
||||||
return { name, qty: num ? num + ' /g' : '', usage: way }
|
return {
|
||||||
|
name,
|
||||||
|
qty: num ? num + ' /g' : '',
|
||||||
|
usage: way,
|
||||||
|
specification: d.specification || '',
|
||||||
|
}
|
||||||
})
|
})
|
||||||
: []
|
: []
|
||||||
recipes.push({
|
recipes.push({
|
||||||
@@ -369,6 +376,7 @@ export function mapPrescriptionDetailView(item) {
|
|||||||
name: d.name || d.drug_name || '—',
|
name: d.name || d.drug_name || '—',
|
||||||
qty: (d.number != null ? d.number : '') + unitName,
|
qty: (d.number != null ? d.number : '') + unitName,
|
||||||
usage: d.useWay || (d.use_way && d.use_way.name) || '',
|
usage: d.useWay || (d.use_way && d.use_way.name) || '',
|
||||||
|
specification: d.specification || '',
|
||||||
}],
|
}],
|
||||||
usage: '',
|
usage: '',
|
||||||
process: '',
|
process: '',
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ const FINANCE_MENUS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const BUSINESS_MENUS = [
|
const BUSINESS_MENUS = [
|
||||||
{ key: 'order', label: '商品订单', desc: '全平台订单概览', icon: 'bag-fill', theme: GLOBAL_THEMES[3], path: '/subPackages/sub_clinic_admin/order/index' },
|
{ key: 'order', label: '商品订单', desc: '本诊所订单概览', icon: 'bag-fill', theme: GLOBAL_THEMES[3], path: '/subPackages/sub_clinic_admin/order/index' },
|
||||||
|
{ key: 'user-patient', label: '会员管理', desc: '本诊所微信用户与就诊人', icon: 'account', theme: GLOBAL_THEMES[0], path: '/subPackages/sub_business_shared/user-patient/index' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const SALESPERSON_MENUS = [
|
const SALESPERSON_MENUS = [
|
||||||
|
|||||||
@@ -1,52 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<clinic-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
<clinic-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||||
<view class="mine-container">
|
<view class="mine-container">
|
||||||
|
<!-- 资料区:圆形头像 + 姓名 + 角色(对齐医生「我的」) -->
|
||||||
<!-- 个人信息面板(通栏) -->
|
|
||||||
<view class="profile-panel">
|
<view class="profile-panel">
|
||||||
<!-- 超椭圆高质感头像 -->
|
<view class="avatar-circle">
|
||||||
<view class="avatar-squircle">
|
|
||||||
<text>{{ avatarText }}</text>
|
<text>{{ avatarText }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
<text class="user-name">{{ displayName }}</text>
|
<text class="user-name">{{ displayName }}</text>
|
||||||
<!-- 角色标签:替代普通文本,提升专业感 -->
|
|
||||||
<view class="role-badge">
|
<view class="role-badge">
|
||||||
<text class="role-text">诊所管理员</text>
|
<text class="role-text">诊所管理员</text>
|
||||||
</view>
|
</view>
|
||||||
|
<text v-if="storeName && storeName !== '-'" class="store-line">{{ storeName }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 白色菜单卡:绑定微信 + 切换账号 -->
|
||||||
<!-- <!– 信息与设置面板(通栏) –>-->
|
|
||||||
<!-- <view class="setting-panel">-->
|
|
||||||
<!-- <view class="setting-item">-->
|
|
||||||
<!-- <text class="item-label">所属诊所</text>-->
|
|
||||||
<!-- <text class="item-value">{{ storeName }}</text>-->
|
|
||||||
<!-- </view>-->
|
|
||||||
<!-- </view>-->
|
|
||||||
|
|
||||||
<!-- 操作面板(通栏) -->
|
|
||||||
<view class="setting-panel">
|
<view class="setting-panel">
|
||||||
<wx-bind-entry ref="wxBindEntry" />
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
<account-switch-entry />
|
<account-switch-entry />
|
||||||
<view
|
</view>
|
||||||
class="setting-item action-item"
|
<!-- 独立绿色退出按钮 -->
|
||||||
hover-class="item-hover"
|
<view class="logout-wrap">
|
||||||
:hover-stay-time="100"
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
@click="logout"
|
@click="logout"
|
||||||
>
|
>退出登录</u-button>
|
||||||
<text class="danger-text">退出登录</text>
|
|
||||||
<!-- 保留原有的 u-icon -->
|
|
||||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
|
||||||
</clinic-page-layout>
|
</clinic-page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 诊所/门店管理员「我的」:布局对齐医生(菜单卡 + 绿色退出)
|
||||||
|
*/
|
||||||
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
||||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
@@ -77,7 +66,7 @@ export default {
|
|||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '确定退出登录?',
|
content: '确定退出登录?',
|
||||||
confirmColor: '#f53f3f', // 将确认按钮改为警示红,提升用户体验
|
confirmColor: '#6ACDBB',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
clearLoginStorage()
|
clearLoginStorage()
|
||||||
@@ -90,15 +79,11 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
/* 保持与工作台完全一致的色彩变量 */
|
|
||||||
$theme-primary: #6acdbb;
|
$theme-primary: #6acdbb;
|
||||||
$theme-dark: #4db6a8;
|
$theme-dark: #4db6a8;
|
||||||
$color-page-bg: #F5F7F8;
|
$color-page-bg: #F5F7F8;
|
||||||
$color-text-title: #222B2A;
|
$color-text-title: #222B2A;
|
||||||
$color-text-body: #4E5969;
|
|
||||||
$color-text-muted: #86909C;
|
$color-text-muted: #86909C;
|
||||||
$color-border: #F2F3F5;
|
|
||||||
$color-danger: #F53F3F; /* 现代系统标准红 */
|
|
||||||
|
|
||||||
.mine-container {
|
.mine-container {
|
||||||
padding: 0 0 60rpx;
|
padding: 0 0 60rpx;
|
||||||
@@ -106,21 +91,17 @@ $color-danger: #F53F3F; /* 现代系统标准红 */
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 个人信息面板:通栏纯白 */
|
|
||||||
.profile-panel {
|
.profile-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
padding: 60rpx 40rpx 50rpx; /* 顶部增加呼吸感 */
|
padding: 60rpx 40rpx 50rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
|
.avatar-circle {
|
||||||
/* 超椭圆头像:抛弃死板的纯圆,注入品牌色和微弱发光质感 */
|
|
||||||
.avatar-squircle {
|
|
||||||
width: 128rpx;
|
width: 128rpx;
|
||||||
height: 128rpx;
|
height: 128rpx;
|
||||||
border-radius: 40rpx; /* 超椭圆倒角 */
|
border-radius: 50%;
|
||||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
@@ -131,23 +112,18 @@ $color-danger: #F53F3F; /* 现代系统标准红 */
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-info {
|
.user-info {
|
||||||
margin-left: 32rpx;
|
margin-left: 32rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-name {
|
.user-name {
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: $color-text-title;
|
color: $color-text-title;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
letter-spacing: 1rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 角色标签:用线框+背景色取代纯文本,更具系统身份感 */
|
|
||||||
.role-badge {
|
.role-badge {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
background-color: rgba(106, 205, 187, 0.1);
|
background-color: rgba(106, 205, 187, 0.1);
|
||||||
@@ -155,62 +131,22 @@ $color-danger: #F53F3F; /* 现代系统标准红 */
|
|||||||
padding: 4rpx 16rpx;
|
padding: 4rpx 16rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.role-text {
|
.role-text {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: $theme-dark;
|
color: $theme-dark;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
.store-line {
|
||||||
/* 设置项面板(通栏组) */
|
margin-top: 12rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $color-text-muted;
|
||||||
|
}
|
||||||
.setting-panel {
|
.setting-panel {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
padding: 0 40rpx;
|
padding: 8rpx 40rpx 16rpx;
|
||||||
}
|
}
|
||||||
|
.logout-wrap {
|
||||||
/* 单个设置项 */
|
padding: 0 40rpx 32rpx;
|
||||||
.setting-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 36rpx 0;
|
|
||||||
border-bottom: 1rpx solid $color-border;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 移除组内最后一个元素的底边框 */
|
|
||||||
.setting-panel .setting-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-label {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: $color-text-title;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-value {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: $color-text-muted;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 交互列表项 */
|
|
||||||
.action-item {
|
|
||||||
/* 扩大点击热区补偿 padding */
|
|
||||||
margin: 0 -40rpx;
|
|
||||||
padding: 36rpx 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-hover {
|
|
||||||
background-color: #F7F8FA;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 退出登录专用红色 */
|
|
||||||
.danger-text {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: $color-danger;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,14 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="showBlock" class="express-panel">
|
<view v-if="showBlock" class="express-panel">
|
||||||
<view class="section-title">物流信息</view>
|
<view class="section-title">物流信息</view>
|
||||||
<template v-if="display.hasData">
|
<!-- 多包裹 Tab -->
|
||||||
<view class="row"><text>物流公司</text><text>{{ display.companyName }}</text></view>
|
<scroll-view v-if="packageList.length > 1" class="pkg-tabs" scroll-x>
|
||||||
<view class="row"><text>运单号</text><text>{{ display.expressNo }}</text></view>
|
<view
|
||||||
<view class="row"><text>最新状态</text><text class="highlight-text">{{ display.stateText }}</text></view>
|
v-for="(pkg, idx) in packageList"
|
||||||
|
:key="idx"
|
||||||
<view v-if="display.tracks.length" class="timeline">
|
class="pkg-tab"
|
||||||
|
:class="{ active: activePkg === idx }"
|
||||||
|
@click="activePkg = idx"
|
||||||
|
>
|
||||||
|
包裹{{ pkg.package_no || idx + 1 }}
|
||||||
|
<text v-if="pkg.warehouse_name" class="pkg-sub">{{ pkg.warehouse_name }}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<template v-if="currentDisplay.hasData">
|
||||||
|
<view class="row"><text>物流公司</text><text>{{ currentDisplay.companyName }}</text></view>
|
||||||
|
<view class="row"><text>运单号</text><text>{{ currentDisplay.expressNo }}</text></view>
|
||||||
|
<view class="row"><text>最新状态</text><text class="highlight-text">{{ currentDisplay.stateText }}</text></view>
|
||||||
|
<view v-if="currentDisplay.tracks.length" class="timeline">
|
||||||
<view class="timeline-title">物流追踪</view>
|
<view class="timeline-title">物流追踪</view>
|
||||||
<view v-for="(t, idx) in display.tracks" :key="idx" class="track-item">
|
<view v-for="(t, idx) in currentDisplay.tracks" :key="idx" class="track-item">
|
||||||
<view class="track-dot" :class="{ 'active': idx === 0 }" />
|
<view class="track-dot" :class="{ 'active': idx === 0 }" />
|
||||||
<view class="track-body">
|
<view class="track-body">
|
||||||
<text class="track-status" :class="{ 'active': idx === 0 }">{{ t.status }}</text>
|
<text class="track-status" :class="{ 'active': idx === 0 }">{{ t.status }}</text>
|
||||||
@@ -18,7 +30,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<view v-else class="empty-express">暂无物流信息</view>
|
<view v-else class="empty-express">
|
||||||
|
{{ currentPkg && Number(currentPkg.is_send) === 0 ? '本包裹尚未发货' : '暂无物流信息' }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -31,14 +45,39 @@ export default {
|
|||||||
express: { type: Object, default: null },
|
express: { type: Object, default: null },
|
||||||
show: { type: Boolean, default: true },
|
show: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return { activePkg: 0 }
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
showBlock() {
|
showBlock() {
|
||||||
return this.show
|
return this.show
|
||||||
},
|
},
|
||||||
display() {
|
/** 优先 packages,否则当作单包 */
|
||||||
|
packageList() {
|
||||||
|
const pkgs = this.express && Array.isArray(this.express.packages) ? this.express.packages : []
|
||||||
|
if (pkgs.length) return pkgs
|
||||||
|
if (this.express && (this.express.express_no || this.express.detail)) {
|
||||||
|
return [{ package_no: 1, warehouse_name: '', is_send: 1, express: this.express }]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
currentPkg() {
|
||||||
|
return this.packageList[this.activePkg] || null
|
||||||
|
},
|
||||||
|
currentDisplay() {
|
||||||
|
const pkg = this.currentPkg
|
||||||
|
if (pkg && pkg.express) return mapExpressDetail(pkg.express)
|
||||||
|
if (pkg && Number(pkg.is_send) === 0) {
|
||||||
|
return { hasData: false, companyName: '', expressNo: '', stateText: '', tracks: [] }
|
||||||
|
}
|
||||||
return mapExpressDetail(this.express)
|
return mapExpressDetail(this.express)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
express() {
|
||||||
|
this.activePkg = 0
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -53,7 +92,6 @@ $color-border: #F2F3F5;
|
|||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding: 32rpx 40rpx;
|
padding: 32rpx 40rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
/* 移除圆角和阴影,采用通栏设计 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
@@ -63,6 +101,34 @@ $color-border: #F2F3F5;
|
|||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pkg-tabs {
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $color-text-muted;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(106, 205, 187, 0.15);
|
||||||
|
color: $theme-primary;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-sub {
|
||||||
|
font-size: 20rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -127,7 +193,6 @@ $color-border: #F2F3F5;
|
|||||||
margin-left: 8rpx;
|
margin-left: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 最后一个元素去掉线 */
|
|
||||||
.track-item:last-child .track-body {
|
.track-item:last-child .track-body {
|
||||||
border-left-color: transparent;
|
border-left-color: transparent;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<drawer-page-container
|
<!-- 小程序抽屉须用 page-container + v-if,避免 u-popup 在订单详情页底内联 -->
|
||||||
v-model="show"
|
<page-container
|
||||||
title="处方详情"
|
v-if="show"
|
||||||
height="85%"
|
:show="show"
|
||||||
tall
|
:overlay="true"
|
||||||
:show-close="true"
|
position="bottom"
|
||||||
:closeable="true"
|
:round="true"
|
||||||
@close="onClose"
|
@beforeleave="onClose"
|
||||||
|
@clickoverlay="onClose"
|
||||||
>
|
>
|
||||||
|
<view class="rx-drawer">
|
||||||
|
<view class="rx-drawer-head">
|
||||||
|
<text class="rx-drawer-title">处方详情</text>
|
||||||
|
<text class="rx-drawer-close" @click="onClose">关闭</text>
|
||||||
|
</view>
|
||||||
<view class="popup-wrap">
|
<view class="popup-wrap">
|
||||||
<scroll-view v-if="view" scroll-y class="popup-scroll">
|
<scroll-view v-if="view" scroll-y class="popup-scroll">
|
||||||
<view class="container safe-area-inset-bottom">
|
<view class="container safe-area-inset-bottom">
|
||||||
<!-- 处方头部 -->
|
|
||||||
<view class="head">
|
<view class="head">
|
||||||
<view class="head_record_top">
|
<view class="head_record_top">
|
||||||
<view class="record">
|
<view class="record">
|
||||||
@@ -21,8 +26,6 @@
|
|||||||
{{ view.typeText }}
|
{{ view.typeText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态医院与公章 -->
|
|
||||||
<view class="head_record">
|
<view class="head_record">
|
||||||
<image :src="view.sealImage || '/static/group/xkyard.png'" mode="aspectFit"></image>
|
<image :src="view.sealImage || '/static/group/xkyard.png'" mode="aspectFit"></image>
|
||||||
<view class="record_yard">
|
<view class="record_yard">
|
||||||
@@ -34,14 +37,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 时间 -->
|
|
||||||
<view class="head_record_time">
|
<view class="head_record_time">
|
||||||
<text style="text-align: right;">开具日期:{{ view.createdAt }}</text>
|
<text style="text-align: right;">开具日期:{{ view.createdAt }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 患者信息 -->
|
|
||||||
<view class="my_info">
|
<view class="my_info">
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<view class="info_item">
|
<view class="info_item">
|
||||||
@@ -57,7 +56,6 @@
|
|||||||
<view class="name">类别:<text>{{ view.category }}</text></view>
|
<view class="name">类别:<text>{{ view.category }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<view class="info_item">
|
<view class="info_item">
|
||||||
<view class="name">科室:<text>{{ view.departName }}</text></view>
|
<view class="name">科室:<text>{{ view.departName }}</text></view>
|
||||||
@@ -66,14 +64,11 @@
|
|||||||
<view class="name">电话:<text>{{ view.patientMobile }}</text></view>
|
<view class="name">电话:<text>{{ view.patientMobile }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<view class="info_item">
|
<view class="info_item">
|
||||||
<view class="names">诊断:<text>{{ view.diagnose }}</text></view>
|
<view class="names">诊断:<text>{{ view.diagnose }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 中医专属字段 -->
|
|
||||||
<view v-if="view.tcmSyndrome || view.tcmMethod || view.tcmDisease" class="info">
|
<view v-if="view.tcmSyndrome || view.tcmMethod || view.tcmDisease" class="info">
|
||||||
<view v-if="view.tcmSyndrome" class="info_item">
|
<view v-if="view.tcmSyndrome" class="info_item">
|
||||||
<view class="names">中医证候:<text>{{ view.tcmSyndrome }}</text></view>
|
<view class="names">中医证候:<text>{{ view.tcmSyndrome }}</text></view>
|
||||||
@@ -86,17 +81,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="bg">
|
<view class="bg">
|
||||||
<!-- 处方明细区 (Rp) -->
|
|
||||||
<view class="my_medical">
|
<view class="my_medical">
|
||||||
<view class="title">Rp</view>
|
<view class="title">Rp</view>
|
||||||
|
|
||||||
<view v-for="(recipe, rIdx) in view.recipes" :key="rIdx" class="item">
|
<view v-for="(recipe, rIdx) in view.recipes" :key="rIdx" class="item">
|
||||||
<view class="name">
|
<view class="name">
|
||||||
<view class="_name" v-for="(drug, dIdx) in recipe.drugs" :key="dIdx">
|
<view class="_name" v-for="(drug, dIdx) in recipe.drugs" :key="dIdx">
|
||||||
<view class="text">
|
<view class="text">
|
||||||
<text>{{ drug.name }}</text>
|
<text>{{ drug.name }}</text>
|
||||||
|
<text v-if="drug.specification" class="drug-spec">{{ drug.specification }}</text>
|
||||||
<text class="_abbr" v-if="drug.usage">[{{ drug.usage }}]</text>
|
<text class="_abbr" v-if="drug.usage">[{{ drug.usage }}]</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="name_num">{{ drug.qty }}</text>
|
<text class="name_num">{{ drug.qty }}</text>
|
||||||
@@ -110,8 +103,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 医嘱 -->
|
|
||||||
<view class="doctor_order">
|
<view class="doctor_order">
|
||||||
<view class="yz">
|
<view class="yz">
|
||||||
<text class="order_info_left">医嘱:</text>
|
<text class="order_info_left">医嘱:</text>
|
||||||
@@ -122,8 +113,6 @@
|
|||||||
<view class="titles">处方开具已完毕</view>
|
<view class="titles">处方开具已完毕</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态医生与药师签名区 -->
|
|
||||||
<view class="my_doctor">
|
<view class="my_doctor">
|
||||||
<view class="doctor_info">
|
<view class="doctor_info">
|
||||||
<view class="info">
|
<view class="info">
|
||||||
@@ -152,36 +141,34 @@
|
|||||||
<text v-else class="name"></text>
|
<text v-else class="name"></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 价格 -->
|
|
||||||
<view class="price" v-if="view.totalPayPrice">
|
<view class="price" v-if="view.totalPayPrice">
|
||||||
价格 ¥{{ view.totalPayPrice }}
|
价格 ¥{{ view.totalPayPrice }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 动态有效期温馨提示 -->
|
|
||||||
<view class="my_prompt">
|
<view class="my_prompt">
|
||||||
<view class="title">
|
<view class="title">
|
||||||
温馨提示:请遵医嘱服药!处方{{ view.validHours }}小时有效!
|
温馨提示:请遵医嘱服药!处方{{ view.validHours }}小时有效!
|
||||||
</view>
|
</view>
|
||||||
<image src="/static/group/img-yzf.png" mode="aspectFit" v-if="view.status == 2"></image>
|
<image src="/static/group/img-yzf.png" mode="aspectFit" v-if="view.status == 2"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view v-else-if="loading" class="popup-loading">加载中...</view>
|
<view v-else-if="loading" class="popup-loading">加载中...</view>
|
||||||
<view v-else class="popup-loading">暂无数据</view>
|
<view v-else class="popup-loading">暂无数据</view>
|
||||||
</view>
|
</view>
|
||||||
</drawer-page-container>
|
</view>
|
||||||
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import DrawerPageContainer from '@/subPackages/sub_business_shared/components/DrawerPageContainer.vue'
|
/**
|
||||||
|
* 处方详情抽屉:page-container 底部浮层(诊所管理端)
|
||||||
|
*/
|
||||||
import { getPrescriptionDetail } from '@/api/clinicAdmin.js'
|
import { getPrescriptionDetail } from '@/api/clinicAdmin.js'
|
||||||
import { mapPrescriptionDetailView } from '@/subPackages/sub_clinic_admin/common/display.js'
|
import { mapPrescriptionDetailView } from '@/subPackages/sub_clinic_admin/common/display.js'
|
||||||
|
import { formatStoreNameWithHu } from '@/utils/formatStoreNameWithHu.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { DrawerPageContainer },
|
|
||||||
name: 'PrescriptionDetailPopup',
|
name: 'PrescriptionDetailPopup',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -191,7 +178,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 解析签名图片,自动兼容 base64 和 http 链接
|
/** 解析签名图片,自动兼容 base64 和 http 链接 */
|
||||||
checkImageUrl(url) {
|
checkImageUrl(url) {
|
||||||
if (!url) return ''
|
if (!url) return ''
|
||||||
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
|
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
|
||||||
@@ -203,20 +190,27 @@ export default {
|
|||||||
this.loading = true
|
this.loading = true
|
||||||
getPrescriptionDetail(prescriptionId).then(w => {
|
getPrescriptionDetail(prescriptionId).then(w => {
|
||||||
if (w.ok && w.data) {
|
if (w.ok && w.data) {
|
||||||
// 复用原有的处方解析逻辑
|
|
||||||
const mapped = mapPrescriptionDetailView(w.data)
|
const mapped = mapPrescriptionDetailView(w.data)
|
||||||
|
mapped.storeName = formatStoreNameWithHu(
|
||||||
// --- 动态注入你新版 UI 需要的独有字段 ---
|
w.data.store ? w.data.store.name : '未知诊所',
|
||||||
mapped.storeName = w.data.store ? w.data.store.name : '未知诊所'
|
w.data.is_online,
|
||||||
|
)
|
||||||
mapped.sealImage = w.data.store ? w.data.store.offical_seal : ''
|
mapped.sealImage = w.data.store ? w.data.store.offical_seal : ''
|
||||||
mapped.typeText = w.data.type == 1 ? '普通' : '常用'
|
mapped.typeText = w.data.type == 1 ? '普通' : '常用'
|
||||||
mapped.totalPayPrice = w.data.total_pay_price || '0.00'
|
mapped.totalPayPrice = w.data.total_pay_price || '0.00'
|
||||||
mapped.doctorSignImage = w.data.doctor_sign_image || ''
|
mapped.doctorSignImage =
|
||||||
mapped.pharmacistSignImage = w.data.Pharmacist_sign_image || ''
|
w.data.doctor_sign_image
|
||||||
|
|| (w.data.doctor_info && w.data.doctor_info.identity_info && w.data.doctor_info.identity_info.sign_image)
|
||||||
|
|| (w.data.doctorInfo && w.data.doctorInfo.identityInfo && w.data.doctorInfo.identityInfo.sign_image)
|
||||||
|
|| ''
|
||||||
|
mapped.pharmacistSignImage =
|
||||||
|
w.data.Pharmacist_sign_image
|
||||||
|
|| (w.data.pharmacist_info && w.data.pharmacist_info.identity && w.data.pharmacist_info.identity.sign_image)
|
||||||
|
|| (w.data.pharmacistInfo && w.data.pharmacistInfo.identity && w.data.pharmacistInfo.identity.sign_image)
|
||||||
|
|| ''
|
||||||
mapped.validHours = w.data.valid_hours || 72
|
mapped.validHours = w.data.valid_hours || 72
|
||||||
mapped.status = w.data.status
|
mapped.status = w.data.status
|
||||||
mapped.patientMobile = w.data.patient ? w.data.patient.mobile : ''
|
mapped.patientMobile = w.data.patient ? w.data.patient.mobile : ''
|
||||||
|
|
||||||
this.view = mapped
|
this.view = mapped
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
@@ -232,20 +226,37 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.rx-drawer {
|
||||||
|
height: 85vh;
|
||||||
|
background: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
border-radius: 24rpx 24rpx 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.rx-drawer-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 28rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.rx-drawer-title { font-size: 32rpx; font-weight: 600; color: #1d2129; }
|
||||||
|
.rx-drawer-close { font-size: 28rpx; color: #6acdbb; }
|
||||||
.popup-wrap {
|
.popup-wrap {
|
||||||
height: 100%;
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 60rpx; /* 给默认的关闭按钮留出空间 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-scroll {
|
.popup-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-loading {
|
.popup-loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #999;
|
||||||
@@ -443,6 +454,11 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.drug-spec {
|
||||||
|
color: #8A92A3;
|
||||||
|
font-size: 22rpx;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
._abbr {
|
._abbr {
|
||||||
color: #A7ABB0;
|
color: #A7ABB0;
|
||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view>
|
||||||
<clinic-page-layout title="订单详情" :show-back="true">
|
<clinic-page-layout title="订单详情" :show-back="true">
|
||||||
<view class="detail-container">
|
<view class="detail-container">
|
||||||
<!-- 头部面板:通栏,展示金额与状态 -->
|
<!-- 头部面板:通栏,展示金额与状态 -->
|
||||||
@@ -17,7 +18,14 @@
|
|||||||
<view class="sub-block">
|
<view class="sub-block">
|
||||||
<view class="sub-block-title">基本信息</view>
|
<view class="sub-block-title">基本信息</view>
|
||||||
<view class="row"><text>订单类型</text><text>{{ detailExtra.orderTypeText }}</text></view>
|
<view class="row"><text>订单类型</text><text>{{ detailExtra.orderTypeText }}</text></view>
|
||||||
<view class="row"><text>患者</text><text>{{ info.patient || '-' }}</text></view>
|
<view class="row">
|
||||||
|
<text>患者</text>
|
||||||
|
<text
|
||||||
|
class="patient-link"
|
||||||
|
:class="{ disabled: !patientUpId }"
|
||||||
|
@click="openPatient"
|
||||||
|
>{{ info.patient || '-' }}</text>
|
||||||
|
</view>
|
||||||
<view class="row"><text>医生</text><text>{{ doctorName }}</text></view>
|
<view class="row"><text>医生</text><text>{{ doctorName }}</text></view>
|
||||||
<view class="row"><text>处方来源</text><text>{{ storeName }}</text></view>
|
<view class="row"><text>处方来源</text><text>{{ storeName }}</text></view>
|
||||||
<view class="row"><text>订单来源</text><text>{{ onlineText(info.is_online) }}</text></view>
|
<view class="row"><text>订单来源</text><text>{{ onlineText(info.is_online) }}</text></view>
|
||||||
@@ -49,7 +57,14 @@
|
|||||||
<!-- 配送信息面板 -->
|
<!-- 配送信息面板 -->
|
||||||
<view v-if="info.id" class="detail-panel">
|
<view v-if="info.id" class="detail-panel">
|
||||||
<view class="section-title">{{ receiverBlockTitle }}</view>
|
<view class="section-title">{{ receiverBlockTitle }}</view>
|
||||||
<view class="row"><text>{{ receiverLabel }}</text><text>{{ receiverName }}</text></view>
|
<view class="row">
|
||||||
|
<text>{{ receiverLabel }}</text>
|
||||||
|
<text
|
||||||
|
class="patient-link"
|
||||||
|
:class="{ disabled: !patientUpId }"
|
||||||
|
@click="openPatient"
|
||||||
|
>{{ receiverName }}</text>
|
||||||
|
</view>
|
||||||
<view class="row"><text>联系电话</text><text>{{ receiverMobile }}</text></view>
|
<view class="row"><text>联系电话</text><text>{{ receiverMobile }}</text></view>
|
||||||
<view class="row" v-if="info.delivery_method === 0">
|
<view class="row" v-if="info.delivery_method === 0">
|
||||||
<text>配送地址</text><text class="addr">{{ deliveryAddress }}</text>
|
<text>配送地址</text><text class="addr">{{ deliveryAddress }}</text>
|
||||||
@@ -141,7 +156,8 @@
|
|||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 处方弹窗 -->
|
</clinic-page-layout>
|
||||||
|
<!-- page-container 抽屉挂在页面根级,避免被 layout 裁切成页底内联 -->
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
<order-price-percent-adjust-popup
|
<order-price-percent-adjust-popup
|
||||||
:visible="priceAdjustVisible"
|
:visible="priceAdjustVisible"
|
||||||
@@ -151,7 +167,14 @@
|
|||||||
@close="closePriceAdjust"
|
@close="closePriceAdjust"
|
||||||
@confirm="(p) => onPriceAdjustConfirm(p, { adjustOrderPercent })"
|
@confirm="(p) => onPriceAdjustConfirm(p, { adjustOrderPercent })"
|
||||||
/>
|
/>
|
||||||
</clinic-page-layout>
|
<user-patient-detail
|
||||||
|
v-if="patientDetailVisible"
|
||||||
|
:visible="patientDetailVisible"
|
||||||
|
:up-id="patientDetailUpId"
|
||||||
|
:patient-name="patientDetailName"
|
||||||
|
@close="patientDetailVisible = false"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -167,6 +190,7 @@ import {
|
|||||||
} from '@/api/clinicAdmin.js'
|
} from '@/api/clinicAdmin.js'
|
||||||
import { orderPriceAdjustMixin } from '@/subPackages/sub_business_shared/common/orderPriceAdjustMixin.js'
|
import { orderPriceAdjustMixin } from '@/subPackages/sub_business_shared/common/orderPriceAdjustMixin.js'
|
||||||
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue'
|
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue'
|
||||||
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
import {
|
import {
|
||||||
orderStatusText,
|
orderStatusText,
|
||||||
deliveryMethodText,
|
deliveryMethodText,
|
||||||
@@ -180,13 +204,22 @@ import { formatMoney } from '@/subPackages/sub_clinic_admin/common/format.js'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [orderPriceAdjustMixin],
|
mixins: [orderPriceAdjustMixin],
|
||||||
components: { ClinicPageLayout, ExpressTimeline, PrescriptionDetailPopup, OrderPricePercentAdjustPopup },
|
components: {
|
||||||
|
ClinicPageLayout,
|
||||||
|
ExpressTimeline,
|
||||||
|
PrescriptionDetailPopup,
|
||||||
|
OrderPricePercentAdjustPopup,
|
||||||
|
UserPatientDetail,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
id: 0,
|
id: 0,
|
||||||
info: {},
|
info: {},
|
||||||
expressInfo: null,
|
expressInfo: null,
|
||||||
detailExtra: {},
|
detailExtra: {},
|
||||||
|
patientDetailVisible: false,
|
||||||
|
patientDetailUpId: 0,
|
||||||
|
patientDetailName: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -209,6 +242,13 @@ export default {
|
|||||||
const s = this.info.store
|
const s = this.info.store
|
||||||
return (s && s.name) ? s.name : '-'
|
return (s && s.name) ? s.name : '-'
|
||||||
},
|
},
|
||||||
|
/** 就诊人 ID:订单 up_id 或关联 user_patient.id */
|
||||||
|
patientUpId() {
|
||||||
|
const up = Number(this.info.up_id || 0)
|
||||||
|
if (up) return up
|
||||||
|
const rel = this.info.user_patient
|
||||||
|
return Number((rel && rel.id) || 0)
|
||||||
|
},
|
||||||
receiverBlockTitle() {
|
receiverBlockTitle() {
|
||||||
return this.info.delivery_method === 0 ? '收货人信息' : '就诊人信息'
|
return this.info.delivery_method === 0 ? '收货人信息' : '就诊人信息'
|
||||||
},
|
},
|
||||||
@@ -244,16 +284,54 @@ export default {
|
|||||||
this.id = Number(q.id || 0)
|
this.id = Number(q.id || 0)
|
||||||
this.load()
|
this.load()
|
||||||
},
|
},
|
||||||
|
/** 离开页关闭处方抽屉,避免 page-container 残留遮罩导致列表白屏 */
|
||||||
|
onHide() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
this.patientDetailVisible = false
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
this.patientDetailVisible = false
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** 关闭处方抽屉 */
|
||||||
|
closePrescriptionPopup() {
|
||||||
|
const pop = this.$refs.prescriptionPopup
|
||||||
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
|
pop.onClose()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开患者视角抽屉(与订单列表一致)
|
||||||
|
*/
|
||||||
|
openPatient() {
|
||||||
|
const upId = this.patientUpId
|
||||||
|
if (!upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.patientDetailUpId = upId
|
||||||
|
this.patientDetailName = this.info.patient || ''
|
||||||
|
this.patientDetailVisible = true
|
||||||
|
},
|
||||||
load() {
|
load() {
|
||||||
|
if (!this.id) {
|
||||||
|
uni.showToast({ title: '缺少订单信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
getOrderDetail(this.id).then(w => {
|
getOrderDetail(this.id).then(w => {
|
||||||
if (!w.ok) return
|
if (!w.ok) {
|
||||||
|
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.info = w.data || {}
|
this.info = w.data || {}
|
||||||
this.detailExtra = mapOrderDetailDisplay(this.info)
|
this.detailExtra = mapOrderDetailDisplay(this.info)
|
||||||
this.loadPriceAdjustMeta(() => getPriceAdjustConfig(this.info.store_id))
|
this.loadPriceAdjustMeta(() => getPriceAdjustConfig(this.info.store_id))
|
||||||
if (this.info.delivery_method === 0) {
|
if (this.info.delivery_method === 0) {
|
||||||
this.loadExpress()
|
this.loadExpress()
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
uni.showToast({ title: '订单加载失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadExpress() {
|
loadExpress() {
|
||||||
@@ -539,6 +617,11 @@ $color-danger: #F53F3F;
|
|||||||
|
|
||||||
.price-main { font-size: 28rpx; color: $color-text-title; font-weight: 600; }
|
.price-main { font-size: 28rpx; color: $color-text-title; font-weight: 600; }
|
||||||
.price-link { color: $theme-primary; text-decoration: underline; }
|
.price-link { color: $theme-primary; text-decoration: underline; }
|
||||||
|
.patient-link {
|
||||||
|
color: $theme-primary;
|
||||||
|
text-align: right;
|
||||||
|
&.disabled { color: inherit; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ================= 底部动作栏 ================= */
|
/* ================= 底部动作栏 ================= */
|
||||||
.actions-bar {
|
.actions-bar {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view>
|
||||||
<clinic-page-layout title="商品订单" :show-back="true">
|
<clinic-page-layout title="商品订单" :show-back="true">
|
||||||
<view class="order-list-container">
|
<view class="order-list-container">
|
||||||
<!-- 顶部筛选 -->
|
<!-- 顶部筛选 -->
|
||||||
@@ -96,9 +97,13 @@
|
|||||||
|
|
||||||
<!-- 信息岛 (Info Island):聚合次要信息的高级灰底区块 -->
|
<!-- 信息岛 (Info Island):聚合次要信息的高级灰底区块 -->
|
||||||
<view class="info-island">
|
<view class="info-island">
|
||||||
<view class="island-row" v-if="rowDisplay(item).userNickname !== '-'">
|
<view
|
||||||
|
class="island-row"
|
||||||
|
v-if="rowDisplay(item).patientName !== '-' || rowDisplay(item).userNickname !== '-'"
|
||||||
|
@click.stop="openPatient(item)"
|
||||||
|
>
|
||||||
<text class="island-label">就诊人</text>
|
<text class="island-label">就诊人</text>
|
||||||
<text class="island-value">{{ rowDisplay(item).userNickname }}</text>
|
<text class="island-value link">{{ rowDisplay(item).patientName !== '-' ? rowDisplay(item).patientName : rowDisplay(item).userNickname }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="island-row">
|
<view class="island-row">
|
||||||
<text class="island-label">收件人</text>
|
<text class="island-label">收件人</text>
|
||||||
@@ -135,8 +140,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
|
||||||
</clinic-page-layout>
|
</clinic-page-layout>
|
||||||
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
|
<user-patient-detail
|
||||||
|
v-if="patientDetailVisible"
|
||||||
|
:visible="patientDetailVisible"
|
||||||
|
:up-id="patientDetailUpId"
|
||||||
|
:patient-name="patientDetailName"
|
||||||
|
@close="patientDetailVisible = false"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -144,6 +157,7 @@ import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-p
|
|||||||
import PageStickyHeader from '@/subPackages/sub_clinic_admin/components/PageStickyHeader.vue'
|
import PageStickyHeader from '@/subPackages/sub_clinic_admin/components/PageStickyHeader.vue'
|
||||||
import CollapsibleFilterPanel from '@/subPackages/sub_clinic_admin/components/CollapsibleFilterPanel.vue'
|
import CollapsibleFilterPanel from '@/subPackages/sub_clinic_admin/components/CollapsibleFilterPanel.vue'
|
||||||
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
||||||
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
import { getOrderList, getOrderStatusOption, getOrderSaleAmount } from '@/api/clinicAdmin.js'
|
import { getOrderList, getOrderStatusOption, getOrderSaleAmount } from '@/api/clinicAdmin.js'
|
||||||
import { withWxKey } from '@/utils/wxListKey.js'
|
import { withWxKey } from '@/utils/wxListKey.js'
|
||||||
import { mapOrderListRow } from '@/subPackages/sub_clinic_admin/common/display.js'
|
import { mapOrderListRow } from '@/subPackages/sub_clinic_admin/common/display.js'
|
||||||
@@ -174,7 +188,7 @@ const PRESCRIPTION_TYPE_OPTIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ClinicPageLayout, PageStickyHeader, CollapsibleFilterPanel, PrescriptionDetailPopup },
|
components: { ClinicPageLayout, PageStickyHeader, CollapsibleFilterPanel, PrescriptionDetailPopup, UserPatientDetail },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: [],
|
list: [],
|
||||||
@@ -188,6 +202,9 @@ export default {
|
|||||||
dateStart: '',
|
dateStart: '',
|
||||||
dateEnd: '',
|
dateEnd: '',
|
||||||
statItems: [],
|
statItems: [],
|
||||||
|
patientDetailVisible: false,
|
||||||
|
patientDetailUpId: 0,
|
||||||
|
patientDetailName: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -214,10 +231,24 @@ export default {
|
|||||||
})
|
})
|
||||||
this.checkDateEndAndReload()
|
this.checkDateEndAndReload()
|
||||||
},
|
},
|
||||||
|
/** 离开/隐藏时关掉处方抽屉,避免 page-container 残留白屏 */
|
||||||
|
onHide() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
this.closePrescriptionPopup()
|
||||||
|
},
|
||||||
onReachBottom() {
|
onReachBottom() {
|
||||||
this.load(this.page + 1, true)
|
this.load(this.page + 1, true)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/** 关闭处方抽屉 */
|
||||||
|
closePrescriptionPopup() {
|
||||||
|
const pop = this.$refs.prescriptionPopup
|
||||||
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
|
pop.onClose()
|
||||||
|
}
|
||||||
|
},
|
||||||
applyOrderFilter(f) {
|
applyOrderFilter(f) {
|
||||||
this.orderNo = f.orderNo
|
this.orderNo = f.orderNo
|
||||||
this.statusIndex = f.statusIndex
|
this.statusIndex = f.statusIndex
|
||||||
@@ -326,6 +357,17 @@ export default {
|
|||||||
if (!pId) return
|
if (!pId) return
|
||||||
this.$refs.prescriptionPopup.open(pId)
|
this.$refs.prescriptionPopup.open(pId)
|
||||||
},
|
},
|
||||||
|
openPatient(item) {
|
||||||
|
const d = this.rowDisplay(item)
|
||||||
|
const upId = Number(d.upId || 0)
|
||||||
|
if (!upId) {
|
||||||
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.patientDetailUpId = upId
|
||||||
|
this.patientDetailName = d.patientName !== '-' ? d.patientName : ''
|
||||||
|
this.patientDetailVisible = true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -569,6 +611,9 @@ $color-text-muted: #86909C;
|
|||||||
color: $color-text-body;
|
color: $color-text-body;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
&.link {
|
||||||
|
color: $theme-primary;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
|
|||||||
@@ -1,436 +1,188 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<clinic-salesperson-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
<clinic-salesperson-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||||
|
|
||||||
<view class="mine-container">
|
<view class="mine-container">
|
||||||
|
|
||||||
<view class="profile-panel">
|
<view class="profile-panel">
|
||||||
|
<view class="avatar-circle">
|
||||||
<view class="avatar-squircle">
|
|
||||||
|
|
||||||
<text>{{ avatarText }}</text>
|
<text>{{ avatarText }}</text>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
|
|
||||||
<text class="user-name">{{ displayName }}</text>
|
<text class="user-name">{{ displayName }}</text>
|
||||||
|
|
||||||
<view class="role-badge">
|
<view class="role-badge">
|
||||||
|
|
||||||
<text class="role-text">推广员</text>
|
<text class="role-text">推广员</text>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
<text v-if="phone" class="meta-line">{{ phone }}</text>
|
||||||
<text v-if="phone" class="phone-line">{{ phone }}</text>
|
|
||||||
|
|
||||||
<text v-if="storeName" class="store-line">{{ storeName }}</text>
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="storeName" class="setting-panel info-panel">
|
||||||
|
<view class="info-row">
|
||||||
|
|
||||||
<view class="setting-panel">
|
|
||||||
|
|
||||||
<view class="setting-item">
|
|
||||||
|
|
||||||
<text class="item-label">所属诊所</text>
|
<text class="item-label">所属诊所</text>
|
||||||
|
<text class="item-value">{{ storeName }}</text>
|
||||||
<text class="item-value">{{ storeName || '-' }}</text>
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<view class="setting-panel">
|
<view class="setting-panel">
|
||||||
|
|
||||||
<wx-bind-entry ref="wxBindEntry" />
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
|
|
||||||
<account-switch-entry />
|
<account-switch-entry />
|
||||||
|
</view>
|
||||||
<view
|
<view class="logout-wrap">
|
||||||
|
<u-button
|
||||||
class="setting-item action-item"
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
hover-class="item-hover"
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
|
||||||
:hover-stay-time="100"
|
|
||||||
|
|
||||||
@click="logout"
|
@click="logout"
|
||||||
|
>退出登录</u-button>
|
||||||
>
|
|
||||||
|
|
||||||
<text class="danger-text">退出登录</text>
|
|
||||||
|
|
||||||
<u-icon name="arrow-right" color="#C9CDD4" size="28" />
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
|
||||||
|
|
||||||
</clinic-salesperson-page-layout>
|
</clinic-salesperson-page-layout>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 推广员「我的」:对齐医生(菜单卡 + 绿色退出),保留所属诊所与绑定微信
|
||||||
|
*/
|
||||||
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
import ClinicSalespersonPageLayout from '@/subPackages/sub_clinic_salesperson/components/clinic-salesperson-page-layout.vue'
|
||||||
|
|
||||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
|
|
||||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
|
|
||||||
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
|
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
|
||||||
|
|
||||||
import { clearLoginStorage } from '@/utils/loginSession.js'
|
import { clearLoginStorage } from '@/utils/loginSession.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
components: { ClinicSalespersonPageLayout, AccountSwitchEntry, WxBindEntry },
|
components: { ClinicSalespersonPageLayout, AccountSwitchEntry, WxBindEntry },
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
displayName: '',
|
displayName: '',
|
||||||
|
|
||||||
phone: '',
|
phone: '',
|
||||||
|
|
||||||
storeName: '',
|
storeName: '',
|
||||||
|
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
||||||
avatarText() {
|
avatarText() {
|
||||||
|
|
||||||
return (this.displayName || '推').slice(0, 1)
|
return (this.displayName || '推').slice(0, 1)
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
|
|
||||||
this.loadProfile()
|
this.loadProfile()
|
||||||
|
|
||||||
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
if (this.$refs.wxBindEntry) this.$refs.wxBindEntry.refresh()
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
async loadProfile() {
|
async loadProfile() {
|
||||||
|
|
||||||
if (this.loading) return
|
if (this.loading) return
|
||||||
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const wrap = await getClinicSalespersonMyInfo()
|
const wrap = await getClinicSalespersonMyInfo()
|
||||||
|
|
||||||
if (!wrap || !wrap.ok) return
|
if (!wrap || !wrap.ok) return
|
||||||
|
|
||||||
const info = wrap.data || {}
|
const info = wrap.data || {}
|
||||||
|
|
||||||
this.displayName = info.salesperson?.nick_name || '推广员'
|
this.displayName = info.salesperson?.nick_name || '推广员'
|
||||||
|
|
||||||
this.phone = info.salesperson?.phone || ''
|
this.phone = info.salesperson?.phone || ''
|
||||||
|
|
||||||
this.storeName = info.store?.name || ''
|
this.storeName = info.store?.name || ''
|
||||||
|
|
||||||
uni.setStorageSync('clinic_salesperson_user', info)
|
uni.setStorageSync('clinic_salesperson_user', info)
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
|
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
|
|
||||||
title: '提示',
|
title: '提示',
|
||||||
|
|
||||||
content: '确定退出登录?',
|
content: '确定退出登录?',
|
||||||
|
confirmColor: '#6ACDBB',
|
||||||
confirmColor: '#f53f3f',
|
|
||||||
|
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
|
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
|
|
||||||
clearLoginStorage()
|
clearLoginStorage()
|
||||||
|
|
||||||
uni.reLaunch({ url: '/pages/login/index' })
|
uni.reLaunch({ url: '/pages/login/index' })
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
$theme-primary: #6acdbb;
|
$theme-primary: #6acdbb;
|
||||||
|
|
||||||
$theme-dark: #4db6a8;
|
$theme-dark: #4db6a8;
|
||||||
|
|
||||||
$color-page-bg: #f5f7f8;
|
$color-page-bg: #f5f7f8;
|
||||||
|
|
||||||
$color-text-title: #222b2a;
|
$color-text-title: #222b2a;
|
||||||
|
|
||||||
$color-text-muted: #86909c;
|
$color-text-muted: #86909c;
|
||||||
|
|
||||||
$color-border: #f2f3f5;
|
|
||||||
|
|
||||||
$color-danger: #f53f3f;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.mine-container {
|
.mine-container {
|
||||||
|
|
||||||
padding: 0 0 60rpx;
|
padding: 0 0 60rpx;
|
||||||
|
|
||||||
background-color: $color-page-bg;
|
background-color: $color-page-bg;
|
||||||
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.profile-panel {
|
.profile-panel {
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
||||||
padding: 60rpx 40rpx 50rpx;
|
padding: 60rpx 40rpx 50rpx;
|
||||||
|
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.avatar-circle {
|
||||||
|
|
||||||
|
|
||||||
.avatar-squircle {
|
|
||||||
|
|
||||||
width: 128rpx;
|
width: 128rpx;
|
||||||
|
|
||||||
height: 128rpx;
|
height: 128rpx;
|
||||||
|
border-radius: 50%;
|
||||||
border-radius: 40rpx;
|
|
||||||
|
|
||||||
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
background: linear-gradient(135deg, $theme-primary 0%, $theme-dark 100%);
|
||||||
|
|
||||||
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
box-shadow: 0 8rpx 24rpx rgba(106, 205, 187, 0.25);
|
||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
font-size: 52rpx;
|
font-size: 52rpx;
|
||||||
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.user-info {
|
.user-info {
|
||||||
|
|
||||||
margin-left: 32rpx;
|
margin-left: 32rpx;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.user-name {
|
.user-name {
|
||||||
|
|
||||||
font-size: 40rpx;
|
font-size: 40rpx;
|
||||||
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|
||||||
color: $color-text-title;
|
color: $color-text-title;
|
||||||
|
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.role-badge {
|
.role-badge {
|
||||||
|
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
|
|
||||||
background-color: rgba(106, 205, 187, 0.1);
|
background-color: rgba(106, 205, 187, 0.1);
|
||||||
|
|
||||||
border: 1rpx solid rgba(106, 205, 187, 0.2);
|
border: 1rpx solid rgba(106, 205, 187, 0.2);
|
||||||
|
|
||||||
padding: 4rpx 16rpx;
|
padding: 4rpx 16rpx;
|
||||||
|
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
margin-bottom: 12rpx;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.role-text {
|
.role-text {
|
||||||
|
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
|
|
||||||
color: $theme-dark;
|
color: $theme-dark;
|
||||||
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.meta-line {
|
||||||
|
|
||||||
|
|
||||||
.phone-line,
|
|
||||||
|
|
||||||
.store-line {
|
|
||||||
|
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
|
|
||||||
color: $color-text-muted;
|
color: $color-text-muted;
|
||||||
|
margin-top: 4rpx;
|
||||||
margin-top: 8rpx;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.setting-panel {
|
.setting-panel {
|
||||||
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
|
padding: 8rpx 40rpx 16rpx;
|
||||||
padding: 0 40rpx;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.info-panel {
|
||||||
|
padding: 28rpx 40rpx;
|
||||||
|
}
|
||||||
.setting-item {
|
.info-row {
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
padding: 36rpx 0;
|
|
||||||
|
|
||||||
border-bottom: 1rpx solid $color-border;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.setting-panel .setting-item:last-child {
|
|
||||||
|
|
||||||
border-bottom: none;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.item-label {
|
.item-label {
|
||||||
|
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
|
|
||||||
color: $color-text-title;
|
color: $color-text-title;
|
||||||
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.item-value {
|
.item-value {
|
||||||
|
|
||||||
font-size: 30rpx;
|
font-size: 30rpx;
|
||||||
|
|
||||||
color: $color-text-muted;
|
color: $color-text-muted;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.logout-wrap {
|
||||||
|
padding: 0 40rpx 32rpx;
|
||||||
|
|
||||||
.action-item {
|
|
||||||
|
|
||||||
margin: 0 -40rpx;
|
|
||||||
|
|
||||||
padding: 36rpx 40rpx;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.item-hover {
|
|
||||||
|
|
||||||
background-color: #f7f8fa;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.danger-text {
|
|
||||||
|
|
||||||
font-size: 30rpx;
|
|
||||||
|
|
||||||
color: $color-danger;
|
|
||||||
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<image :src="lists.store.offical_seal||'/static/group/xkyard.png'" mode=""></image>
|
<image :src="lists.store.offical_seal||'/static/group/xkyard.png'" mode=""></image>
|
||||||
<view class="record_yard">
|
<view class="record_yard">
|
||||||
<view class="yard">
|
<view class="yard">
|
||||||
{{lists.store.name}}
|
{{ storeDisplayName }}
|
||||||
</view>
|
</view>
|
||||||
<view class="state">
|
<view class="state">
|
||||||
处方笺
|
处方笺
|
||||||
@@ -223,7 +223,15 @@
|
|||||||
prescripDetail,
|
prescripDetail,
|
||||||
getUseWay
|
getUseWay
|
||||||
} from "@/api/all.js";
|
} from "@/api/all.js";
|
||||||
|
import { formatStoreNameWithHu } from '@/utils/formatStoreNameWithHu.js';
|
||||||
export default {
|
export default {
|
||||||
|
computed: {
|
||||||
|
/** 在线处方诊所名加(互) */
|
||||||
|
storeDisplayName() {
|
||||||
|
const store = this.lists && this.lists.store ? this.lists.store : {}
|
||||||
|
return formatStoreNameWithHu(store.name, this.lists && this.lists.is_online)
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
statusName: {
|
statusName: {
|
||||||
|
|||||||
@@ -131,15 +131,35 @@
|
|||||||
<text class="block-label">主诉</text>
|
<text class="block-label">主诉</text>
|
||||||
<text class="block-content">{{ parsedObj.chief_complaint }}</text>
|
<text class="block-content">{{ parsedObj.chief_complaint }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="registerCardDrugNamesLine" class="info-row">
|
<view v-if="registerCardDrugRows.length" class="drug-list-block">
|
||||||
<text class="label">所选药品</text>
|
<text class="block-label">所选药品</text>
|
||||||
<text class="value">{{ registerCardDrugNamesLine }}</text>
|
<view v-for="row in registerCardDrugRows" :key="row._wxKey" class="info-row drug-spec-row">
|
||||||
|
<image
|
||||||
|
v-if="row.image"
|
||||||
|
class="drug-thumb"
|
||||||
|
:src="row.image"
|
||||||
|
mode="aspectFill"
|
||||||
|
@click.stop="$emit('previewImage', row.image)"
|
||||||
|
/>
|
||||||
|
<view class="drug-name-wrap">
|
||||||
|
<text class="label">{{ row.name || '药品' }}</text>
|
||||||
|
<text v-if="row.specification" class="spec-text">{{ row.specification }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="parsedObj.number != null && parsedObj.number !== ''" class="info-row">
|
<text class="value">×{{ row.quantity }}</text>
|
||||||
<text class="label">数量</text>
|
|
||||||
<text class="value">各 {{ parsedObj.number }} 盒</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="registerCardDrugRows.length && canAddExperienceToRx && !isMine"
|
||||||
|
class="card-footer card-footer--compact"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="action-btn action-btn--ghost"
|
||||||
|
@click.stop="$emit('addRegisterDrugsToRx')"
|
||||||
|
>
|
||||||
|
添加到处方单
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<!-- 11 复诊用药 -->
|
<!-- 11 复诊用药 -->
|
||||||
@@ -154,8 +174,11 @@
|
|||||||
<view v-if="parsedObj.question" class="text-block">
|
<view v-if="parsedObj.question" class="text-block">
|
||||||
<text class="block-content">{{ parsedObj.question }}</text>
|
<text class="block-content">{{ parsedObj.question }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-for="row in followUpDrugs" :key="row._wxKey" class="info-row">
|
<view v-for="row in followUpDrugs" :key="row._wxKey" class="info-row drug-spec-row">
|
||||||
|
<view class="drug-name-wrap">
|
||||||
<text class="label">{{ row.name || '药品' }}</text>
|
<text class="label">{{ row.name || '药品' }}</text>
|
||||||
|
<text v-if="row.specification" class="spec-text">{{ row.specification }}</text>
|
||||||
|
</view>
|
||||||
<text class="value">×{{ row.quantity != null ? row.quantity : 1 }}</text>
|
<text class="value">×{{ row.quantity != null ? row.quantity : 1 }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="followUpAnsweredLabel" class="info-row">
|
<view v-if="followUpAnsweredLabel" class="info-row">
|
||||||
@@ -166,6 +189,17 @@
|
|||||||
<text class="follow-up-pending-tip">请在挂号后的用药确认页完成选择,此处不可更改</text>
|
<text class="follow-up-pending-tip">请在挂号后的用药确认页完成选择,此处不可更改</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="followUpDrugs.length && canAddExperienceToRx && !isMine"
|
||||||
|
class="card-footer card-footer--compact"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="action-btn action-btn--ghost"
|
||||||
|
@click.stop="$emit('addRegisterDrugsToRx')"
|
||||||
|
>
|
||||||
|
添加到处方单
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<!-- 11 就诊经历 -->
|
<!-- 11 就诊经历 -->
|
||||||
@@ -202,7 +236,7 @@
|
|||||||
class="action-btn action-btn--ghost"
|
class="action-btn action-btn--ghost"
|
||||||
@click.stop="$emit('addExperienceToRx', experienceRxPayload)"
|
@click.stop="$emit('addExperienceToRx', experienceRxPayload)"
|
||||||
>
|
>
|
||||||
加入处方
|
添加到处方单
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
@@ -370,16 +404,47 @@ export default {
|
|||||||
if (typeof c === 'object') return JSON.stringify(c);
|
if (typeof c === 'object') return JSON.stringify(c);
|
||||||
return String(c);
|
return String(c);
|
||||||
},
|
},
|
||||||
registerCardDrugNamesLine() {
|
registerCardDrugRows() {
|
||||||
if (this.msg.message_type !== 10) return '';
|
if (this.msg.message_type !== 10) return [];
|
||||||
const pc = this.parsedObj;
|
const pc = this.parsedObj;
|
||||||
if (!pc || typeof pc !== 'object') return '';
|
if (!pc || typeof pc !== 'object') return [];
|
||||||
|
const fallbackQty = pc.number != null && pc.number !== '' ? Number(pc.number) : 1;
|
||||||
const arr = pc.selected_western_drugs;
|
const arr = pc.selected_western_drugs;
|
||||||
if (Array.isArray(arr) && arr.length) {
|
if (Array.isArray(arr) && arr.length) {
|
||||||
return arr.map((d) => d && d.name).filter(Boolean).join('、');
|
return withWxKey(
|
||||||
|
arr.map((d) => {
|
||||||
|
const q = d && d.quantity != null ? Number(d.quantity) : fallbackQty;
|
||||||
|
return {
|
||||||
|
name: (d && d.name) || '',
|
||||||
|
specification: (d && (d.specification || d.spec || d.drug_spec)) || '',
|
||||||
|
image: (d && (d.image || d.drug_image)) || '',
|
||||||
|
quantity: q >= 1 ? q : 1,
|
||||||
|
id: d && (d.drug_id || d.id),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
'id',
|
||||||
|
'rd'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (pc.drug && pc.drug.name) return pc.drug.name;
|
if (pc.drug && pc.drug.name) {
|
||||||
return '';
|
return withWxKey(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: pc.drug.name,
|
||||||
|
specification: pc.drug.specification || pc.drug.spec || '',
|
||||||
|
image: pc.drug.image || pc.drug.drug_image || '',
|
||||||
|
quantity: fallbackQty >= 1 ? fallbackQty : 1,
|
||||||
|
id: pc.drug.drug_id || pc.drug.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'id',
|
||||||
|
'rd'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
registerCardDrugNamesLine() {
|
||||||
|
return this.registerCardDrugRows.map((r) => r.name).filter(Boolean).join('、');
|
||||||
},
|
},
|
||||||
followUpAnsweredLabel() {
|
followUpAnsweredLabel() {
|
||||||
const pc = this.parsedObj;
|
const pc = this.parsedObj;
|
||||||
@@ -391,7 +456,14 @@ export default {
|
|||||||
},
|
},
|
||||||
followUpDrugs() {
|
followUpDrugs() {
|
||||||
const drugs = (this.parsedObj && this.parsedObj.drugs) || [];
|
const drugs = (this.parsedObj && this.parsedObj.drugs) || [];
|
||||||
return withWxKey(drugs, 'id', 'fd');
|
return withWxKey(
|
||||||
|
drugs.map((d) => ({
|
||||||
|
...d,
|
||||||
|
specification: d.specification || d.spec || d.drug_spec || '',
|
||||||
|
})),
|
||||||
|
'id',
|
||||||
|
'fd'
|
||||||
|
);
|
||||||
},
|
},
|
||||||
transferQuestionsWithKey() {
|
transferQuestionsWithKey() {
|
||||||
const qs = (this.parsedObj && this.parsedObj.questions) || [];
|
const qs = (this.parsedObj && this.parsedObj.questions) || [];
|
||||||
@@ -683,6 +755,40 @@ $radius-bubble: 12rpx;
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.drug-spec-row {
|
||||||
|
align-items: flex-start;
|
||||||
|
.drug-thumb {
|
||||||
|
width: 64rpx;
|
||||||
|
height: 64rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: #f3f4f6;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
.drug-name-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 4rpx;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.spec-text {
|
||||||
|
display: block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9ca3af;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.drug-list-block {
|
||||||
|
.block-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
.desc-text {
|
.desc-text {
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
|||||||
@@ -1 +1,184 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view v-if="info" class="panel">
|
||||||
|
<view class="head-row" v-if="info.user_patient">
|
||||||
|
<image class="avatar" :src="patientAvatarSrc" mode="aspectFill" />
|
||||||
|
<view class="head-meta">
|
||||||
|
<view class="row name-row">
|
||||||
|
<text class="v name">{{ info.user_patient.name }}</text>
|
||||||
|
<text class="sub" v-if="info.user_patient.age">{{ info.user_patient.age }}岁</text>
|
||||||
|
</view>
|
||||||
|
<view class="row" v-if="info.order_no">
|
||||||
|
<text class="k">订单号</text>
|
||||||
|
<text class="v">{{ info.order_no }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="row" v-else-if="info.order_no">
|
||||||
|
<text class="k">订单号</text>
|
||||||
|
<text class="v">{{ info.order_no }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="row" v-if="healthLine"><text class="k">健康问卷</text><text class="v">{{ healthLine }}</text></view>
|
||||||
|
<view v-if="registerOrder" class="block">
|
||||||
|
<text class="block-title">本次挂号</text>
|
||||||
|
<view v-if="registerOrder.order_no" class="row">
|
||||||
|
<text class="k">挂号单号</text>
|
||||||
|
<text class="v order-font">{{ registerOrder.order_no }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="registerOrder.price != null && registerOrder.price !== ''" class="row">
|
||||||
|
<text class="k">费用</text>
|
||||||
|
<text class="v price">¥{{ registerOrder.price }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="registerOrder.status != null && registerOrder.status !== ''" class="row">
|
||||||
|
<text class="k">挂号状态</text>
|
||||||
|
<text class="v">{{ registerStatusText(registerOrder.status) }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="registerOrder.pay_status != null && registerOrder.pay_status !== ''" class="row">
|
||||||
|
<text class="k">支付状态</text>
|
||||||
|
<text class="v">{{ registerOrder.pay_status }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-for="(sub, six) in childOrders" :key="six" class="sub-order">
|
||||||
|
<text class="sub-order-label">子订单</text>
|
||||||
|
<text class="sub-order-no">{{ sub.order_no || sub.no || '—' }}</text>
|
||||||
|
<text v-if="sub.status != null" class="sub-order-st">{{ sub.status }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 在线问诊顶部患者信息面板:头像 + 基本信息 + 本次挂号摘要
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'PatientDetailPanel',
|
||||||
|
props: {
|
||||||
|
info: { type: Object, default: null }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/** 有就诊人头像用真实图,否则按性别默认 nan/nv */
|
||||||
|
patientAvatarSrc() {
|
||||||
|
const up = this.info && this.info.user_patient;
|
||||||
|
if (up && up.avatar) return up.avatar;
|
||||||
|
const sex = up ? Number(up.sex) : 0;
|
||||||
|
return require(`@/static/image/${sex % 2 === 0 ? 'nv' : 'nan'}.png`);
|
||||||
|
},
|
||||||
|
healthLine() {
|
||||||
|
const h = this.info && this.info.user_patient_health_inquiry;
|
||||||
|
if (!h) return '';
|
||||||
|
if (typeof h === 'string') return h;
|
||||||
|
return h.summary || h.chief_complaint || '';
|
||||||
|
},
|
||||||
|
registerOrder() {
|
||||||
|
const i = this.info;
|
||||||
|
if (!i) return null;
|
||||||
|
return i.register_order || null;
|
||||||
|
},
|
||||||
|
childOrders() {
|
||||||
|
const ro = this.registerOrder;
|
||||||
|
if (!ro) return [];
|
||||||
|
const raw = ro.child_orders || ro.sub_orders || ro.orders;
|
||||||
|
return Array.isArray(raw) ? raw : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
registerStatusText(status) {
|
||||||
|
const map = { 0: '待就诊', 1: '已缴费', 2: '已就诊', 3: '已取消', 4: '已退费' };
|
||||||
|
const n = Number(status);
|
||||||
|
return map[n] != null ? map[n] : String(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.panel {
|
||||||
|
padding: 16rpx 24rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
margin: 16rpx 24rpx;
|
||||||
|
}
|
||||||
|
.head-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 72rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #e8f5f2;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.head-meta {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.name-row {
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
.k {
|
||||||
|
color: #888;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
.v {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.v.name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
margin-left: 16rpx;
|
||||||
|
color: #666;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.block {
|
||||||
|
margin-top: 16rpx;
|
||||||
|
padding-top: 16rpx;
|
||||||
|
border-top: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
.block-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
.order-font {
|
||||||
|
font-family: Courier, monospace;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
color: #ef4444;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.sub-order {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
padding: 12rpx;
|
||||||
|
background: #f9fafb;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
.sub-order-label {
|
||||||
|
color: #999;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
.sub-order-no {
|
||||||
|
flex: 1;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.sub-order-st {
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="bar" v-if="visible">
|
<view class="bar" v-if="visible">
|
||||||
<button v-if="showAccept" class="btn primary" size="mini" @click="$emit('accept')">接诊</button>
|
<button v-if="showAccept" class="btn primary" @click="$emit('accept')">接诊</button>
|
||||||
<button v-if="showRefuse" class="btn warn" size="mini" @click="$emit('refuse')">拒诊</button>
|
<button v-if="showRefuse" class="btn warn" @click="$emit('refuse')">拒诊</button>
|
||||||
<button v-if="showPrescription" class="btn primary" size="mini" @click="$emit('prescription')">开方</button>
|
<button v-if="showPrescription" class="btn primary" @click="$emit('prescription')">开方</button>
|
||||||
<button v-if="showEnd" class="btn plain" size="mini" @click="$emit('end')">结束诊断</button>
|
<button v-if="showEnd" class="btn plain" @click="$emit('end')">结束诊断</button>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/** 与 RegisterStatusEnum 对齐:1=已支付待接诊 2=接诊中 */
|
/** 与 RegisterStatusEnum 对齐:1=已支付待接诊 2=接诊中;按钮等宽铺满避免挤在左侧 */
|
||||||
export default {
|
export default {
|
||||||
name: 'ReceptionActionBar',
|
name: 'ReceptionActionBar',
|
||||||
props: {
|
props: {
|
||||||
@@ -39,14 +39,25 @@ export default {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.bar {
|
.bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
align-items: center;
|
||||||
padding: 16rpx 24rpx;
|
justify-content: space-between;
|
||||||
gap: 16rpx;
|
padding: 20rpx 24rpx;
|
||||||
|
gap: 24rpx;
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
border-top: 1rpx solid #eee;
|
border-top: 1rpx solid #eee;
|
||||||
}
|
}
|
||||||
.btn {
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 30rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
border: none;
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.primary {
|
.primary {
|
||||||
background: #6acdbb !important;
|
background: #6acdbb !important;
|
||||||
@@ -60,5 +71,8 @@ export default {
|
|||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
color: #333 !important;
|
color: #333 !important;
|
||||||
border: 1rpx solid #ddd !important;
|
border: 1rpx solid #ddd !important;
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
@playAudio="playAudio"
|
@playAudio="playAudio"
|
||||||
@viewPrescription="onBubbleViewPrescription"
|
@viewPrescription="onBubbleViewPrescription"
|
||||||
@addExperienceToRx="onAddExperienceToPrescription"
|
@addExperienceToRx="onAddExperienceToPrescription"
|
||||||
|
@addRegisterDrugsToRx="onRegisterDrugsBulkToPrescription"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -70,8 +71,8 @@
|
|||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<ReceptionActionBar
|
<ReceptionActionBar
|
||||||
:register-status="Number(patientInfo && patientInfo.status) || 0"
|
:register-status="registerStatusNum"
|
||||||
:room-ended="roomEnded"
|
:room-ended="actionBarRoomEnded"
|
||||||
@accept="onAccept"
|
@accept="onAccept"
|
||||||
@refuse="onRefuseTap"
|
@refuse="onRefuseTap"
|
||||||
@prescription="goPrescriptionV2"
|
@prescription="goPrescriptionV2"
|
||||||
@@ -384,31 +385,58 @@ export default {
|
|||||||
navbarTitle() {
|
navbarTitle() {
|
||||||
return this.patientName || '在线问诊';
|
return this.patientName || '在线问诊';
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 患者气泡头像:有就诊人头像用真实图,否则按性别默认 nan/nv
|
||||||
|
*/
|
||||||
patientAvatar() {
|
patientAvatar() {
|
||||||
return this.patientInfo?.user_patient?.avatar || '';
|
const up = this.patientInfo && this.patientInfo.user_patient;
|
||||||
|
if (up && up.avatar) return up.avatar;
|
||||||
|
const sex = up ? Number(up.sex) : 0;
|
||||||
|
return require(`@/static/image/${sex % 2 === 0 ? 'nv' : 'nan'}.png`);
|
||||||
},
|
},
|
||||||
doctorAvatar() {
|
doctorAvatar() {
|
||||||
return uni.getStorageSync('doctor_avatar') || '';
|
return uni.getStorageSync('doctor_avatar') || '';
|
||||||
},
|
},
|
||||||
|
/** 当前挂号状态(与 RegisterStatusEnum 对齐:1=待接诊 2=接诊中) */
|
||||||
|
registerStatusNum() {
|
||||||
|
return Number(this.patientInfo && this.patientInfo.status) || 0;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 问诊会话是否已结束(锁输入、隐藏操作栏)
|
||||||
|
* 待接诊(1)/接诊中(2) 均不以复用房间的历史结束态为准:
|
||||||
|
* 否则会藏掉接诊按钮,或接诊后房间未同步导致无法发消息
|
||||||
|
*/
|
||||||
roomEnded() {
|
roomEnded() {
|
||||||
|
const st = this.registerStatusNum;
|
||||||
|
if (st === 1 || st === 2) return false;
|
||||||
return this.roomChatStatus === 1 || this.isConsultationEnded;
|
return this.roomChatStatus === 1 || this.isConsultationEnded;
|
||||||
},
|
},
|
||||||
inputLocked() {
|
/**
|
||||||
if (this.roomEnded) return true;
|
* 传给 ReceptionActionBar:待接诊/接诊中强制不隐藏操作栏
|
||||||
const st = Number(this.patientInfo && this.patientInfo.status);
|
*/
|
||||||
if (!st) return false;
|
actionBarRoomEnded() {
|
||||||
|
const st = this.registerStatusNum;
|
||||||
if (st === 1 || st === 2) return false;
|
if (st === 1 || st === 2) return false;
|
||||||
|
return this.roomEnded;
|
||||||
|
},
|
||||||
|
inputLocked() {
|
||||||
|
const st = this.registerStatusNum;
|
||||||
|
// 待接诊、接诊中均可发消息,不因房间残留结束态锁定
|
||||||
|
if (st === 1 || st === 2) return false;
|
||||||
|
if (this.roomEnded) return true;
|
||||||
|
if (!st) return false;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
lockReason() {
|
lockReason() {
|
||||||
|
const st = this.registerStatusNum;
|
||||||
|
if (st === 1 || st === 2) return '';
|
||||||
if (this.roomChatStatus === 1 || this.isConsultationEnded) return '问诊已结束';
|
if (this.roomChatStatus === 1 || this.isConsultationEnded) return '问诊已结束';
|
||||||
const st = Number(this.patientInfo && this.patientInfo.status);
|
|
||||||
if (st === 7 || st === 8) return '已拒诊';
|
if (st === 7 || st === 8) return '已拒诊';
|
||||||
if (st >= 3) return '当前状态不可发送消息';
|
if (st >= 3) return '当前状态不可发送消息';
|
||||||
return '不可发送';
|
return '不可发送';
|
||||||
},
|
},
|
||||||
statusLine() {
|
statusLine() {
|
||||||
const st = Number(this.patientInfo && this.patientInfo.status);
|
const st = this.registerStatusNum;
|
||||||
const m = {
|
const m = {
|
||||||
0: '待支付',
|
0: '待支付',
|
||||||
1: '待接诊',
|
1: '待接诊',
|
||||||
@@ -421,7 +449,7 @@ export default {
|
|||||||
return m[st] || '问诊中';
|
return m[st] || '问诊中';
|
||||||
},
|
},
|
||||||
canAddExperienceDrugFromChat() {
|
canAddExperienceDrugFromChat() {
|
||||||
const st = Number(this.patientInfo && this.patientInfo.status);
|
const st = this.registerStatusNum;
|
||||||
return st === 2 && !this.inputLocked && !!this.registerId;
|
return st === 2 && !this.inputLocked && !!this.registerId;
|
||||||
},
|
},
|
||||||
registerOrderBlock() {
|
registerOrderBlock() {
|
||||||
@@ -586,7 +614,11 @@ export default {
|
|||||||
const res = await getRoomStatusApi({ room_id: this.roomId });
|
const res = await getRoomStatusApi({ room_id: this.roomId });
|
||||||
const raw = this.unwrap(res) || {};
|
const raw = this.unwrap(res) || {};
|
||||||
this.roomChatStatus = Number(raw.status) || 0;
|
this.roomChatStatus = Number(raw.status) || 0;
|
||||||
if (this.roomChatStatus === 1) this.isConsultationEnded = true;
|
const st = this.registerStatusNum;
|
||||||
|
// 待接诊/接诊中:房间可能仍是上一单结束态,不据此标记本单已结束
|
||||||
|
if (this.roomChatStatus === 1 && st !== 1 && st !== 2) {
|
||||||
|
this.isConsultationEnded = true;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
@@ -694,31 +726,57 @@ export default {
|
|||||||
if (tempIndex !== -1) {
|
if (tempIndex !== -1) {
|
||||||
const patched = ensureWxKey({ ...message, isTemporary: false }, 'id', `m${tempIndex}`);
|
const patched = ensureWxKey({ ...message, isTemporary: false }, 'id', `m${tempIndex}`);
|
||||||
this.$set(this.messages, tempIndex, patched);
|
this.$set(this.messages, tempIndex, patched);
|
||||||
|
if (message.message_type === chatConfig.messageTypes['end-consultation']) {
|
||||||
|
this.applyEndConsultationMessage(message);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const existingIndex = this.messages.findIndex((m) => m.id === message.id);
|
const existingIndex = this.messages.findIndex((m) => m.id === message.id);
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
this.$set(this.messages, existingIndex, ensureWxKey(message, 'id', `m${existingIndex}`));
|
this.$set(this.messages, existingIndex, ensureWxKey(message, 'id', `m${existingIndex}`));
|
||||||
|
if (message.message_type === chatConfig.messageTypes['end-consultation']) {
|
||||||
|
this.applyEndConsultationMessage(message);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.messages.push(ensureWxKey(message, 'id', `m${this.messages.length}`));
|
this.messages.push(ensureWxKey(message, 'id', `m${this.messages.length}`));
|
||||||
|
// 结束问诊卡片:待接诊忽略历史结束态;接诊中仅认本单创建之后的结束消息
|
||||||
if (message.message_type === chatConfig.messageTypes['end-consultation']) {
|
if (message.message_type === chatConfig.messageTypes['end-consultation']) {
|
||||||
this.isConsultationEnded = true;
|
this.applyEndConsultationMessage(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 根据结束问诊消息更新本单结束态,避免医患复用房间时历史卡片误锁待接诊
|
||||||
|
*/
|
||||||
|
applyEndConsultationMessage(message) {
|
||||||
|
const st = this.registerStatusNum;
|
||||||
|
if (st === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (st === 2) {
|
||||||
|
const msgTs = Number(message.created_at || message.timestamp || 0);
|
||||||
|
const regTs = Number(this.patientInfo && this.patientInfo.created_at) || 0;
|
||||||
|
const msgSec = msgTs > 1e12 ? Math.floor(msgTs / 1000) : msgTs;
|
||||||
|
if (regTs > 0 && msgSec < regTs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isConsultationEnded = true;
|
||||||
|
},
|
||||||
isMsgMine(msg) {
|
isMsgMine(msg) {
|
||||||
const senderId = msg.sender_user_id || '';
|
const senderId = msg.sender_user_id || '';
|
||||||
return senderId === this.currentUserId;
|
return senderId === this.currentUserId;
|
||||||
},
|
},
|
||||||
getMsgAvatar(msg) {
|
getMsgAvatar(msg) {
|
||||||
const sid = msg.sender_user_id || '';
|
const sid = msg.sender_user_id || '';
|
||||||
const fallback = 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/29bdb9560340373bb3096394a845afa5.jpg';
|
const assistantFallback = 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/29bdb9560340373bb3096394a845afa5.jpg';
|
||||||
if (sid === 'doctor_assistant') return fallback;
|
if (sid === 'doctor_assistant') return assistantFallback;
|
||||||
if (this.isMsgMine(msg)) {
|
if (this.isMsgMine(msg)) {
|
||||||
return this.doctorAvatar || fallback;
|
return this.doctorAvatar || assistantFallback;
|
||||||
}
|
}
|
||||||
return this.patientAvatar || fallback;
|
// 患者侧:真实头像或性别默认(patientAvatar 已处理)
|
||||||
|
return this.patientAvatar;
|
||||||
},
|
},
|
||||||
toMessageMs(t) {
|
toMessageMs(t) {
|
||||||
const n = Number(t) || 0;
|
const n = Number(t) || 0;
|
||||||
@@ -1372,8 +1430,12 @@ export default {
|
|||||||
async onAccept() {
|
async onAccept() {
|
||||||
try {
|
try {
|
||||||
await receptionApi(this.registerId);
|
await receptionApi(this.registerId);
|
||||||
|
// 接诊成功后清掉上一单遗留的结束态,并刷新房间状态
|
||||||
|
this.isConsultationEnded = false;
|
||||||
|
this.roomChatStatus = 0;
|
||||||
uni.showToast({ title: '接诊成功', icon: 'none' });
|
uni.showToast({ title: '接诊成功', icon: 'none' });
|
||||||
await this.loadPatientDetail();
|
await this.loadPatientDetail();
|
||||||
|
await this.refreshRoomStatus();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
uni.showToast({ title: (e && e.msg) || '接诊失败', icon: 'none' });
|
uni.showToast({ title: (e && e.msg) || '接诊失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<image src="../../static/image/dy.png" class="m-t-8 b-r-8"></image>
|
<image src="../../static/image/dy.png" class="m-t-8 b-r-8"></image>
|
||||||
<d-text text="设置" className="content-c fs-32"></d-text>
|
<d-text text="设置" className="content-c fs-32"></d-text>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 协议与客服:退出/切账号已上提到「我的」主页 -->
|
||||||
<view class="p-row-32 white m-t-48">
|
<view class="p-row-32 white m-t-48">
|
||||||
<view @click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')"
|
<view @click="$go('/subPackages/sub_agreement/agreement?type=service_user_agreement')"
|
||||||
class="bottom-border flex-row flex-jus-sp flex-ali-center p-col-32">
|
class="bottom-border flex-row flex-jus-sp flex-ali-center p-col-32">
|
||||||
@@ -17,44 +18,22 @@
|
|||||||
<d-text text="隐私权政策" className="content-c fs-32"></d-text>
|
<d-text text="隐私权政策" className="content-c fs-32"></d-text>
|
||||||
<u-icon name="arrow-right" color="#A7ABB0" size="32"></u-icon>
|
<u-icon name="arrow-right" color="#A7ABB0" size="32"></u-icon>
|
||||||
</view>
|
</view>
|
||||||
<view class="bottom-border flex-row flex-jus-sp flex-ali-center p-col-32">
|
<view class="flex-row flex-jus-sp flex-ali-center p-col-32">
|
||||||
<d-text text="客服热线" className="content-c fs-32"></d-text>
|
<d-text text="客服热线" className="content-c fs-32"></d-text>
|
||||||
40056256325
|
40056256325
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="p-row-32 m-t-8 white">
|
|
||||||
<account-switch-entry />
|
|
||||||
</view>
|
|
||||||
<view class="p-row-32 m-t-8" style="height: 80rpx;">
|
|
||||||
<u-button :throttle-time="0" shape="circle" :custom-style="{backgroundColor:'#6ACDBB',color:'#fff',heigth:'86rpx'}" @click="logout">
|
|
||||||
退出登录</u-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {
|
/**
|
||||||
logout
|
* 药师/导医客服设置页:仅保留协议与客服热线
|
||||||
} from '@/api/all.js'
|
*/
|
||||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
|
||||||
export default {
|
export default {
|
||||||
components: { AccountSwitchEntry },
|
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
logout() {
|
|
||||||
logout({store_id: uni.getStorageSync('store_id') || 11001,}).then((res) => {
|
|
||||||
if (res.errcode != -1) {
|
|
||||||
uni.clearStorage()
|
|
||||||
uni.clearStorageSync()
|
|
||||||
this.$go('/pages/login/index', 2)
|
|
||||||
} else {
|
|
||||||
this.$toast(res.msg);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
@@ -68,11 +47,4 @@
|
|||||||
height: 160rpx;
|
height: 160rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-t-8 {
|
|
||||||
::v-deep button {
|
|
||||||
color: #fff;
|
|
||||||
background: #6ACDBB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ export default {
|
|||||||
icon: 'bag-fill',
|
icon: 'bag-fill',
|
||||||
theme: { bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' } // 安全绿
|
theme: { bg: 'linear-gradient(135deg, #F0FDF4, #DCFCE7)', color: '#22C55E' } // 安全绿
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'user-patient', label: '会员管理', desc: '微信用户与就诊人', path: `${shared}/user-patient/index`,
|
||||||
|
icon: 'account',
|
||||||
|
theme: { bg: 'linear-gradient(135deg, #FFF7ED, #FFEDD5)', color: '#F97316' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'store-manage', label: '门店管理', desc: '诊所列表与价格配置', path: `${root}/store/index`,
|
key: 'store-manage', label: '门店管理', desc: '诊所列表与价格配置', path: `${root}/store/index`,
|
||||||
icon: 'home-fill',
|
icon: 'home-fill',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||||
<view class="mine-container">
|
<view class="mine-container">
|
||||||
<view class="profile-panel">
|
<view class="profile-panel">
|
||||||
<view class="avatar-squircle"><text>{{ avatarText }}</text></view>
|
<view class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
<text class="user-name">{{ displayName }}</text>
|
<text class="user-name">{{ displayName }}</text>
|
||||||
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
||||||
@@ -11,15 +11,23 @@
|
|||||||
<view class="setting-panel">
|
<view class="setting-panel">
|
||||||
<wx-bind-entry ref="wxBindEntry" />
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
<account-switch-entry />
|
<account-switch-entry />
|
||||||
<view class="setting-item action-item" @click="logout">
|
|
||||||
<text class="danger-text">退出登录</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
<view class="logout-wrap">
|
||||||
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
@click="logout"
|
||||||
|
>退出登录</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</business-page-layout>
|
</business-page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 平台管理员「我的」:对齐医生(菜单卡 + 绿色退出),保留绑定微信
|
||||||
|
*/
|
||||||
import BusinessPageLayout from '@/subPackages/sub_platform_admin/components/business-page-layout.vue'
|
import BusinessPageLayout from '@/subPackages/sub_platform_admin/components/business-page-layout.vue'
|
||||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
@@ -42,7 +50,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
logout() {
|
logout() {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示', content: '确定退出登录?',
|
title: '提示', content: '确定退出登录?', confirmColor: '#6ACDBB',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
clearLoginStorage()
|
clearLoginStorage()
|
||||||
@@ -55,14 +63,17 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mine-container { min-height: 100vh; background: #F5F7F8; }
|
.mine-container { min-height: 100vh; background: #F5F7F8; padding-bottom: 60rpx; }
|
||||||
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
||||||
.avatar-squircle { width: 128rpx; height: 128rpx; border-radius: 40rpx; background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx; display: flex; align-items: center; justify-content: center; }
|
.avatar-circle {
|
||||||
|
width: 128rpx; height: 128rpx; border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx;
|
||||||
|
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||||
|
}
|
||||||
.user-info { margin-left: 32rpx; }
|
.user-info { margin-left: 32rpx; }
|
||||||
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
||||||
.role-badge { display: inline-block; background: rgba(106,205,187,.1); padding: 4rpx 16rpx; border-radius: 8rpx; }
|
.role-badge { display: inline-block; background: rgba(106,205,187,.1); padding: 4rpx 16rpx; border-radius: 8rpx; }
|
||||||
.role-text { font-size: 22rpx; color: #4db6a8; }
|
.role-text { font-size: 22rpx; color: #4db6a8; }
|
||||||
.setting-panel { background: #fff; padding: 0 40rpx; margin-bottom: 24rpx; }
|
.setting-panel { background: #fff; padding: 8rpx 40rpx 16rpx; margin-bottom: 24rpx; }
|
||||||
.setting-item { padding: 36rpx 0; border-bottom: 1rpx solid #F2F3F5; }
|
.logout-wrap { padding: 0 40rpx 32rpx; }
|
||||||
.danger-text { color: #F53F3F; font-size: 30rpx; }
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,9 +7,20 @@
|
|||||||
<text class="addr">{{ store.position || '暂无地址' }}</text>
|
<text class="addr">{{ store.position || '暂无地址' }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section">
|
<!-- 只读信息:业务员无编辑权限时展示配置摘要 -->
|
||||||
|
<view v-if="!canEdit" class="section">
|
||||||
|
<text class="section-title">门店信息</text>
|
||||||
|
<view class="info-row"><text>推广员可见价格</text><text>{{ Number(store.salesperson_see_price) === 1 ? '是' : '否' }}</text></view>
|
||||||
|
<view class="info-row"><text>包邮</text><text>{{ Number(store.is_shipping_free) === 1 ? '是' : '否' }}</text></view>
|
||||||
|
<view class="info-row"><text>订阅价格波动</text><text>{{ Number(store.subscribe_price_change) === 0 ? '是' : '否' }}</text></view>
|
||||||
|
<view class="info-row"><text>查看毛利率</text><text>{{ Number(store.see_rate) === 1 ? '是' : '否' }}</text></view>
|
||||||
|
<view class="info-row"><text>开方可选医保</text><text>{{ Number(store.allow_insurance_category) === 1 ? '是' : '否' }}</text></view>
|
||||||
|
<view class="info-row"><text>诊所类型</text><text>{{ clinicTypeText(store.clinic_type) }}</text></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="canEdit" class="section">
|
||||||
<text class="section-title">门店配置</text>
|
<text class="section-title">门店配置</text>
|
||||||
<view class="switch-row">
|
<view v-if="!isSalesperson" class="switch-row">
|
||||||
<text>推广员可见价格</text>
|
<text>推广员可见价格</text>
|
||||||
<switch :checked="Number(store.salesperson_see_price) === 1" color="#6ACDBB" @change="toggleSeePrice" />
|
<switch :checked="Number(store.salesperson_see_price) === 1" color="#6ACDBB" @change="toggleSeePrice" />
|
||||||
</view>
|
</view>
|
||||||
@@ -40,13 +51,13 @@
|
|||||||
<view class="section">
|
<view class="section">
|
||||||
<text class="section-title">快捷操作</text>
|
<text class="section-title">快捷操作</text>
|
||||||
<view class="action-grid">
|
<view class="action-grid">
|
||||||
<view class="action-btn" @click="goEdit">编辑资料</view>
|
<view v-if="canEdit" class="action-btn" @click="goEdit">编辑资料</view>
|
||||||
<view class="action-btn" @click="goDrugPrice">修改药品价格</view>
|
<view v-if="canEdit" class="action-btn" @click="goDrugPrice">修改药品价格</view>
|
||||||
<view class="action-btn" @click="goConsultation">在线复诊配置</view>
|
<view v-if="canEdit" class="action-btn" @click="goConsultation">在线复诊配置</view>
|
||||||
<view class="action-btn" @click="goPromoters">管理推广员</view>
|
<view v-if="canEdit && !isSalesperson" class="action-btn" @click="goPromoters">管理推广员</view>
|
||||||
<view class="action-btn" @click="openQr">诊所二维码</view>
|
<view class="action-btn" @click="openQr">诊所二维码</view>
|
||||||
<view class="action-btn" @click="goBankCardReport">{{ bankCardReportButtonText }}</view>
|
<view class="action-btn" @click="goBankCardReport">{{ bankCardReportButtonText }}</view>
|
||||||
<view v-if="!hasAdminAccount" class="action-btn warn" @click="openPc">开通后台</view>
|
<view v-if="canEdit && !hasAdminAccount" class="action-btn warn" @click="openPc">开通后台</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -60,9 +71,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 门店详情:超管可编辑;业务员默认只读,系统配置开启后显示编辑入口
|
||||||
|
*/
|
||||||
import StorePageLayout from '@/subPackages/sub_platform_admin/components/store-page-layout.vue';
|
import StorePageLayout from '@/subPackages/sub_platform_admin/components/store-page-layout.vue';
|
||||||
import {
|
import {
|
||||||
getPlatformStoreDetail,
|
getStoreDetail,
|
||||||
|
getSalespersonStoreEditPermission,
|
||||||
toggleSalespersonSeePrice,
|
toggleSalespersonSeePrice,
|
||||||
updateStoreShippingFree,
|
updateStoreShippingFree,
|
||||||
updateStoreSubscribeStatus,
|
updateStoreSubscribeStatus,
|
||||||
@@ -71,7 +86,8 @@ import {
|
|||||||
updateStoreClinicType,
|
updateStoreClinicType,
|
||||||
openStorePcWindows,
|
openStorePcWindows,
|
||||||
openStoreQrCode,
|
openStoreQrCode,
|
||||||
} from '@/api/platformStore.js';
|
isSalespersonStoreMode,
|
||||||
|
} from '@/api/storeManage.js';
|
||||||
import { openQrCodePreview, buildClinicQrPayload } from '@/utils/qrCodePreview.js';
|
import { openQrCodePreview, buildClinicQrPayload } from '@/utils/qrCodePreview.js';
|
||||||
|
|
||||||
const ROOT = '/subPackages/sub_platform_admin/store';
|
const ROOT = '/subPackages/sub_platform_admin/store';
|
||||||
@@ -84,9 +100,16 @@ export default {
|
|||||||
storeId: 0,
|
storeId: 0,
|
||||||
store: {},
|
store: {},
|
||||||
clinicTypeLabels: ['未设置', '西医诊所', '中医诊所'],
|
clinicTypeLabels: ['未设置', '西医诊所', '中医诊所'],
|
||||||
|
isSalesperson: false,
|
||||||
|
/** 业务员编辑开关;超管始终 true */
|
||||||
|
salespersonEditEnabled: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
canEdit() {
|
||||||
|
if (!this.isSalesperson) return true;
|
||||||
|
return !!this.salespersonEditEnabled;
|
||||||
|
},
|
||||||
clinicTypeIndex() {
|
clinicTypeIndex() {
|
||||||
return Number(this.store.clinic_type) || 0;
|
return Number(this.store.clinic_type) || 0;
|
||||||
},
|
},
|
||||||
@@ -104,15 +127,28 @@ export default {
|
|||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
this.storeId = Number(options.id || 0);
|
this.storeId = Number(options.id || 0);
|
||||||
this.loadDetail();
|
this.isSalesperson = isSalespersonStoreMode();
|
||||||
|
this.bootstrap();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clinicTypeText(type) {
|
clinicTypeText(type) {
|
||||||
return this.clinicTypeLabels[Number(type) || 0] || '未设置';
|
return this.clinicTypeLabels[Number(type) || 0] || '未设置';
|
||||||
},
|
},
|
||||||
|
async bootstrap() {
|
||||||
|
if (this.isSalesperson) {
|
||||||
|
try {
|
||||||
|
const wrap = await getSalespersonStoreEditPermission();
|
||||||
|
const data = wrap && wrap.ok ? wrap.data : null;
|
||||||
|
this.salespersonEditEnabled = !!(data && data.salesperson_store_edit_enabled);
|
||||||
|
} catch (e) {
|
||||||
|
this.salespersonEditEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.loadDetail();
|
||||||
|
},
|
||||||
async loadDetail() {
|
async loadDetail() {
|
||||||
if (!this.storeId) return;
|
if (!this.storeId) return;
|
||||||
const wrap = await getPlatformStoreDetail(this.storeId);
|
const wrap = await getStoreDetail(this.storeId);
|
||||||
if (wrap && wrap.ok) {
|
if (wrap && wrap.ok) {
|
||||||
this.store = wrap.data || {};
|
this.store = wrap.data || {};
|
||||||
}
|
}
|
||||||
@@ -204,10 +240,11 @@ export default {
|
|||||||
.sub, .addr { display: block; font-size: 24rpx; margin-top: 8rpx; opacity: 0.9; }
|
.sub, .addr { display: block; font-size: 24rpx; margin-top: 8rpx; opacity: 0.9; }
|
||||||
.section { background: #fff; border-radius: 20rpx; padding: 24rpx; margin-bottom: 20rpx; }
|
.section { background: #fff; border-radius: 20rpx; padding: 24rpx; margin-bottom: 20rpx; }
|
||||||
.section-title { display: block; font-size: 28rpx; font-weight: 600; margin-bottom: 16rpx; }
|
.section-title { display: block; font-size: 28rpx; font-weight: 600; margin-bottom: 16rpx; }
|
||||||
.switch-row, .picker-row {
|
.switch-row, .picker-row, .info-row {
|
||||||
display: flex; justify-content: space-between; align-items: center;
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
padding: 20rpx 0; border-bottom: 1rpx solid #f0f0f0; font-size: 28rpx;
|
padding: 20rpx 0; border-bottom: 1rpx solid #f0f0f0; font-size: 28rpx;
|
||||||
}
|
}
|
||||||
|
.info-row text:last-child { color: #6b7280; }
|
||||||
.picker-val { color: #6ACDBB; }
|
.picker-val { color: #6ACDBB; }
|
||||||
.action-grid { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
.action-grid { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||||
.action-btn {
|
.action-btn {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<store-page-layout title="门店管理" :show-back="true">
|
<store-page-layout :title="pageTitle" :show-back="true">
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="filter-panel">
|
<view class="filter-panel">
|
||||||
<input class="filter-input" v-model="filters.name" placeholder="诊所名称" />
|
<input class="filter-input" v-model="filters.name" placeholder="诊所名称" />
|
||||||
@@ -39,8 +39,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 门店/诊所列表:超管看全量,业务员看自己推广码绑定的诊所
|
||||||
|
*/
|
||||||
import StorePageLayout from '@/subPackages/sub_platform_admin/components/store-page-layout.vue';
|
import StorePageLayout from '@/subPackages/sub_platform_admin/components/store-page-layout.vue';
|
||||||
import { getPlatformStoreList } from '@/api/platformStore.js';
|
import { getStoreList, isSalespersonStoreMode } from '@/api/storeManage.js';
|
||||||
|
|
||||||
const ROOT = '/subPackages/sub_platform_admin/store';
|
const ROOT = '/subPackages/sub_platform_admin/store';
|
||||||
|
|
||||||
@@ -51,9 +54,16 @@ export default {
|
|||||||
filters: { name: '', shouzimu: '', mobile: '' },
|
filters: { name: '', shouzimu: '', mobile: '' },
|
||||||
list: [],
|
list: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
isSalesperson: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
pageTitle() {
|
||||||
|
return this.isSalesperson ? '我的诊所' : '门店管理';
|
||||||
|
},
|
||||||
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
|
this.isSalesperson = isSalespersonStoreMode();
|
||||||
this.reload();
|
this.reload();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -65,7 +75,7 @@ export default {
|
|||||||
async reload() {
|
async reload() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const wrap = await getPlatformStoreList({
|
const wrap = await getStoreList({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
...this.filters,
|
...this.filters,
|
||||||
@@ -99,20 +109,22 @@ export default {
|
|||||||
.filter-btn {
|
.filter-btn {
|
||||||
background: #6ACDBB; color: #fff; padding: 16rpx 32rpx; border-radius: 12rpx; font-size: 26rpx;
|
background: #6ACDBB; color: #fff; padding: 16rpx 32rpx; border-radius: 12rpx; font-size: 26rpx;
|
||||||
}
|
}
|
||||||
.empty { text-align: center; color: #999; padding: 80rpx 0; }
|
.empty { text-align: center; color: #999; padding: 80rpx 0; font-size: 28rpx; }
|
||||||
.card {
|
.card {
|
||||||
background: #fff; border-radius: 20rpx; padding: 28rpx; margin-bottom: 20rpx;
|
background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx;
|
||||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.04);
|
|
||||||
}
|
}
|
||||||
.card-hover { opacity: 0.92; }
|
.card-hover { opacity: 0.92; }
|
||||||
.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8rpx; }
|
.card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
|
||||||
.name { font-size: 30rpx; font-weight: 600; flex: 1; }
|
.name { font-size: 30rpx; font-weight: 600; color: #1f2937; }
|
||||||
.type-tag { font-size: 22rpx; color: #6ACDBB; background: #f0fbf8; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
.type-tag { font-size: 22rpx; color: #6ACDBB; background: #f0fbf8; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
||||||
.addr { font-size: 24rpx; color: #666; display: block; margin-bottom: 12rpx; }
|
.addr { display: block; font-size: 24rpx; color: #6b7280; margin-bottom: 12rpx; }
|
||||||
.meta-row { display: flex; justify-content: space-between; font-size: 22rpx; color: #999; margin-bottom: 12rpx; }
|
.meta-row { display: flex; gap: 24rpx; font-size: 22rpx; color: #9ca3af; margin-bottom: 12rpx; }
|
||||||
.tag-row { display: flex; gap: 12rpx; flex-wrap: wrap; }
|
.tag-row { display: flex; flex-wrap: wrap; gap: 8rpx; }
|
||||||
.mini-tag { font-size: 22rpx; color: #999; background: #f5f5f5; padding: 4rpx 12rpx; border-radius: 8rpx; }
|
.mini-tag {
|
||||||
.mini-tag.on { color: #22C55E; background: #F0FDF4; }
|
font-size: 20rpx; padding: 4rpx 10rpx; border-radius: 6rpx;
|
||||||
.mini-tag.warn { color: #D97706; background: #FEF3C7; }
|
background: #f3f4f6; color: #9ca3af;
|
||||||
.mini-tag.bank { color: #3B82F6; background: #EFF6FF; }
|
}
|
||||||
|
.mini-tag.on { background: #ecfdf5; color: #059669; }
|
||||||
|
.mini-tag.warn { background: #fef3c7; color: #d97706; }
|
||||||
|
.mini-tag.bank { background: #eff6ff; color: #3b82f6; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export default {
|
|||||||
{ key: 'doctor-form', label: '医生录入', desc: '新增合作医生', path: '/subPackages/sub_salesperson/doctor-input/form', inputType: 'doctor', isForm: true, icon: 'plus-circle-fill', theme: this.themes.doctor },
|
{ key: 'doctor-form', label: '医生录入', desc: '新增合作医生', path: '/subPackages/sub_salesperson/doctor-input/form', inputType: 'doctor', isForm: true, icon: 'plus-circle-fill', theme: this.themes.doctor },
|
||||||
]},
|
]},
|
||||||
{ key: 'list', title: '档案管理', items: [
|
{ key: 'list', title: '档案管理', items: [
|
||||||
|
{ key: 'my-store', label: '我的诊所', desc: '查看所属正式诊所', path: '/subPackages/sub_platform_admin/store/index', icon: 'home', theme: this.themes.store },
|
||||||
{ key: 'store-list', label: '门店列表', desc: '查看已录门店', path: '/subPackages/sub_salesperson/store-input/index', icon: 'home-fill', theme: this.themes.store },
|
{ key: 'store-list', label: '门店列表', desc: '查看已录门店', path: '/subPackages/sub_salesperson/store-input/index', icon: 'home-fill', theme: this.themes.store },
|
||||||
{ key: 'doctor-list', label: '医生列表', desc: '查看已录医生', path: '/subPackages/sub_salesperson/doctor-input/index', icon: 'account-fill', theme: this.themes.doctor },
|
{ key: 'doctor-list', label: '医生列表', desc: '查看已录医生', path: '/subPackages/sub_salesperson/doctor-input/index', icon: 'account-fill', theme: this.themes.doctor },
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
<business-page-layout title="我的" :show-tabbar="true" :tab-index="1">
|
||||||
<view class="mine-container">
|
<view class="mine-container">
|
||||||
<view class="profile-panel">
|
<view class="profile-panel">
|
||||||
<view class="avatar-squircle"><text>{{ avatarText }}</text></view>
|
<view class="avatar-circle"><text>{{ avatarText }}</text></view>
|
||||||
<view class="user-info">
|
<view class="user-info">
|
||||||
<text class="user-name">{{ displayName }}</text>
|
<text class="user-name">{{ displayName }}</text>
|
||||||
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
<view class="role-badge"><text class="role-text">{{ roleText }}</text></view>
|
||||||
@@ -11,13 +11,23 @@
|
|||||||
<view class="setting-panel">
|
<view class="setting-panel">
|
||||||
<wx-bind-entry ref="wxBindEntry" />
|
<wx-bind-entry ref="wxBindEntry" />
|
||||||
<account-switch-entry />
|
<account-switch-entry />
|
||||||
<view class="setting-item" @click="logout"><text class="danger-text">退出登录</text></view>
|
</view>
|
||||||
|
<view class="logout-wrap">
|
||||||
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', height: '96rpx' }"
|
||||||
|
@click="logout"
|
||||||
|
>退出登录</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</business-page-layout>
|
</business-page-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
/**
|
||||||
|
* 业务员「我的」:对齐医生(菜单卡 + 绿色退出),保留绑定微信
|
||||||
|
*/
|
||||||
import BusinessPageLayout from '@/subPackages/sub_salesperson/components/business-page-layout.vue'
|
import BusinessPageLayout from '@/subPackages/sub_salesperson/components/business-page-layout.vue'
|
||||||
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
import AccountSwitchEntry from '@/components/account-switch/account-switch-entry.vue'
|
||||||
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
import WxBindEntry from '@/components/wx-bind-entry/wx-bind-entry.vue'
|
||||||
@@ -38,7 +48,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
logout() {
|
logout() {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: '提示', content: '确定退出登录?',
|
title: '提示', content: '确定退出登录?', confirmColor: '#6ACDBB',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
clearLoginStorage()
|
clearLoginStorage()
|
||||||
@@ -51,12 +61,17 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.mine-container { min-height: 100vh; background: #F5F7F8; }
|
.mine-container { min-height: 100vh; background: #F5F7F8; padding-bottom: 60rpx; }
|
||||||
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
.profile-panel { display: flex; align-items: center; background: #fff; padding: 60rpx 40rpx; margin-bottom: 24rpx; }
|
||||||
.avatar-squircle { width: 128rpx; height: 128rpx; border-radius: 40rpx; background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx; display: flex; align-items: center; justify-content: center; }
|
.avatar-circle {
|
||||||
|
width: 128rpx; height: 128rpx; border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #6acdbb, #4db6a8); color: #fff; font-size: 52rpx;
|
||||||
|
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||||
|
}
|
||||||
.user-info { margin-left: 32rpx; }
|
.user-info { margin-left: 32rpx; }
|
||||||
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
.user-name { font-size: 40rpx; font-weight: 700; display: block; margin-bottom: 12rpx; }
|
||||||
|
.role-badge { display: inline-block; background: rgba(106,205,187,.1); padding: 4rpx 16rpx; border-radius: 8rpx; }
|
||||||
.role-text { font-size: 22rpx; color: #4db6a8; }
|
.role-text { font-size: 22rpx; color: #4db6a8; }
|
||||||
.setting-panel { background: #fff; padding: 36rpx 40rpx; }
|
.setting-panel { background: #fff; padding: 8rpx 40rpx 16rpx; margin-bottom: 24rpx; }
|
||||||
.danger-text { color: #F53F3F; }
|
.logout-wrap { padding: 0 40rpx 32rpx; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
>
|
>
|
||||||
<text class="selected-chip-name">{{ sel.drug_name || sel.name || '—' }}</text>
|
<text class="selected-chip-name">{{ sel.drug_name || sel.name || '—' }}</text>
|
||||||
<text class="selected-chip-dose">{{ String(sel.number || 0) }}{{ sel.unit && sel.unit.name ? sel.unit.name : 'g' }}</text>
|
<text class="selected-chip-dose">{{ String(sel.number || 0) }}{{ sel.unit && sel.unit.name ? sel.unit.name : 'g' }}</text>
|
||||||
|
<text v-if="getWayLabel(sel)" class="selected-chip-way">{{ getWayLabel(sel) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="selected-chip-close" :data-wx-key="sel._wxKey" @click.stop="handleSelectedChipRemove">
|
<view class="selected-chip-close" :data-wx-key="sel._wxKey" @click.stop="handleSelectedChipRemove">
|
||||||
<u-icon name="close" size="22" color="#8A92A3"></u-icon>
|
<u-icon name="close" size="22" color="#8A92A3"></u-icon>
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
<view class="flex-row flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
<view class="selected-indicator" v-if="isInSelected(item)"></view>
|
<view class="selected-indicator" v-if="isInSelected(item)"></view>
|
||||||
<d-text :text="getDrugName(item)" className="fs-32 font-bold color-title"></d-text>
|
<d-text :text="getDrugName(item)" className="fs-32 font-bold color-title"></d-text>
|
||||||
|
<text v-if="Number(item.has_delivery_warehouse) === 1" class="multi-wh-tag m-l-12">多仓</text>
|
||||||
</view>
|
</view>
|
||||||
<d-text
|
<d-text
|
||||||
v-if="showDrugPrice"
|
v-if="showDrugPrice"
|
||||||
@@ -98,6 +100,43 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 先煎/后下:气泡标签卡片点选(不用 ActionSheet,选项超过 6 个会失败) -->
|
||||||
|
<view v-if="drugUseWayList.length" class="way-block m-b-16">
|
||||||
|
<view
|
||||||
|
class="way-row flex-row flex-ali-center flex-jus-sp"
|
||||||
|
@click.stop="toggleWayPanel"
|
||||||
|
:data-index="index"
|
||||||
|
>
|
||||||
|
<d-text text="煎法:" className="fs-28 color-title"></d-text>
|
||||||
|
<view class="way-picker flex-row flex-ali-center">
|
||||||
|
<text class="fs-26" :class="getWayLabel(item) ? 'color-way' : 'color-sub'">
|
||||||
|
{{ getWayDisplayName(item) }}
|
||||||
|
</text>
|
||||||
|
<u-icon
|
||||||
|
:name="wayPanelDrugIndex === index ? 'arrow-up' : 'arrow-right'"
|
||||||
|
size="24"
|
||||||
|
color="#8A92A3"
|
||||||
|
class="m-l-8"
|
||||||
|
></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="wayPanelDrugIndex === index"
|
||||||
|
class="way-bubble-card"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-for="w in drugUseWayList"
|
||||||
|
:key="w.id"
|
||||||
|
class="way-bubble-tag"
|
||||||
|
:class="{ active: isWayActive(item, w) }"
|
||||||
|
@click.stop="selectWayTag"
|
||||||
|
:data-index="index"
|
||||||
|
:data-way-id="w.id"
|
||||||
|
>{{ w.name }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="quantity-input-row flex-row flex-ali-center flex-jus-sp pt-20 border-t-dashed">
|
<view class="quantity-input-row flex-row flex-ali-center flex-jus-sp pt-20 border-t-dashed">
|
||||||
<view class="quantity-input-left flex-row flex-ali-center">
|
<view class="quantity-input-left flex-row flex-ali-center">
|
||||||
<d-text text="用量:" className="fs-28 color-title"></d-text>
|
<d-text text="用量:" className="fs-28 color-title"></d-text>
|
||||||
@@ -223,6 +262,11 @@ export default {
|
|||||||
showDrugPrice: {
|
showDrugPrice: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
/** 药材级先煎/后下选项(父页 drugUseList.drug_use_way) */
|
||||||
|
drugUseWayList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -237,13 +281,16 @@ export default {
|
|||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
hasMore: false,
|
hasMore: false,
|
||||||
// 常用克数(后端全局配置,每次打开抽屉拉取)
|
// 常用克数(后端全局配置,每次打开抽屉拉取)
|
||||||
commonQuantities: []
|
commonQuantities: [],
|
||||||
|
/** 当前展开煎法气泡的药材下标,-1 表示收起 */
|
||||||
|
wayPanelDrugIndex: -1
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(newVal) {
|
value(newVal) {
|
||||||
this.show = newVal;
|
this.show = newVal;
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
this.selectedDrugs = this.mapSelectedDrugs(JSON.parse(JSON.stringify(this.currentDrugs)));
|
this.selectedDrugs = this.mapSelectedDrugs(JSON.parse(JSON.stringify(this.currentDrugs)));
|
||||||
this.fetchQuickGrams();
|
this.fetchQuickGrams();
|
||||||
const kw = (this.initialSearchKey || '').trim();
|
const kw = (this.initialSearchKey || '').trim();
|
||||||
@@ -256,10 +303,13 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.resetList();
|
this.resetList();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show(newVal) {
|
show(newVal) {
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
this.$emit('input', false);
|
this.$emit('input', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,6 +384,7 @@ export default {
|
|||||||
this.drugList = [];
|
this.drugList = [];
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.hasMore = false;
|
this.hasMore = false;
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 加载药品列表
|
* 加载药品列表
|
||||||
@@ -388,17 +439,27 @@ export default {
|
|||||||
return matchByIndexId || matchByEntityId || matchByDrugId;
|
return matchByIndexId || matchByEntityId || matchByDrugId;
|
||||||
});
|
});
|
||||||
this.$set(drug, '_quantity', selected ? selected.number : 0);
|
this.$set(drug, '_quantity', selected ? selected.number : 0);
|
||||||
|
// 恢复已选煎法(先煎/后下)
|
||||||
|
const wayId = selected
|
||||||
|
? Number(selected.way_id ?? selected.use_ways?.id ?? 0)
|
||||||
|
: Number(drug.way_id || (drug.drug && drug.drug.way_id) || 0);
|
||||||
|
this.$set(drug, '_way_id', wayId || 0);
|
||||||
return drug;
|
return drug;
|
||||||
});
|
});
|
||||||
if (this.page > 1) {
|
if (this.page > 1) {
|
||||||
this.drugList = this.drugList.concat(merged);
|
this.drugList = this.drugList.concat(merged);
|
||||||
} else {
|
} else {
|
||||||
|
// 重新搜索/首屏加载后列表下标变化,收起煎法气泡
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
this.drugList = merged;
|
this.drugList = merged;
|
||||||
}
|
}
|
||||||
this.drugList.forEach(drug => {
|
this.drugList.forEach(drug => {
|
||||||
if (typeof drug._quantity === 'undefined') {
|
if (typeof drug._quantity === 'undefined') {
|
||||||
this.$set(drug, '_quantity', 0);
|
this.$set(drug, '_quantity', 0);
|
||||||
}
|
}
|
||||||
|
if (typeof drug._way_id === 'undefined') {
|
||||||
|
this.$set(drug, '_way_id', Number(drug.way_id || (drug.drug && drug.drug.way_id) || 0));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取药品列表失败:', error);
|
console.error('获取药品列表失败:', error);
|
||||||
@@ -420,6 +481,7 @@ export default {
|
|||||||
clearTimeout(this.searchTimer);
|
clearTimeout(this.searchTimer);
|
||||||
this.searchTimer = setTimeout(() => {
|
this.searchTimer = setTimeout(() => {
|
||||||
const name = (this.searchKey || '').trim();
|
const name = (this.searchKey || '').trim();
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
if (!name) {
|
if (!name) {
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.hasMore = false;
|
this.hasMore = false;
|
||||||
@@ -529,6 +591,10 @@ export default {
|
|||||||
const indexId = drug.id;
|
const indexId = drug.id;
|
||||||
const entityId = (drug.drug && drug.drug.id) || drug.id;
|
const entityId = (drug.drug && drug.drug.id) || drug.id;
|
||||||
const wxKeyBase = indexId != null && indexId !== '' ? String(indexId) : String(entityId || '');
|
const wxKeyBase = indexId != null && indexId !== '' ? String(indexId) : String(entityId || '');
|
||||||
|
const wayId = Number(
|
||||||
|
drug._way_id ?? (drug.drug && drug.drug.way_id) ?? drug.way_id ?? 0
|
||||||
|
);
|
||||||
|
const wayHit = (this.drugUseWayList || []).find((w) => Number(w.id) === wayId);
|
||||||
return {
|
return {
|
||||||
index_id: indexId,
|
index_id: indexId,
|
||||||
id: entityId,
|
id: entityId,
|
||||||
@@ -536,12 +602,80 @@ export default {
|
|||||||
number: drug._quantity,
|
number: drug._quantity,
|
||||||
price: drug.price || 0,
|
price: drug.price || 0,
|
||||||
buy_price: drug.buy_price ?? 0,
|
buy_price: drug.buy_price ?? 0,
|
||||||
way_id: (drug.drug && drug.drug.way_id) || drug.way_id || 0,
|
way_id: wayId || 0,
|
||||||
|
use_ways: wayHit || null,
|
||||||
select_number: 1,
|
select_number: 1,
|
||||||
_wxKey: wxKeyBase ? `sch-${wxKeyBase}` : `sch${this.selectedDrugs.length}`,
|
_wxKey: wxKeyBase ? `sch-${wxKeyBase}` : `sch${this.selectedDrugs.length}`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 展示非默认煎法文案;煎服/0 不展示(chip)或用「煎服(默认)」在选择行
|
||||||
|
*/
|
||||||
|
getWayLabel(drugOrSel) {
|
||||||
|
if (!drugOrSel) return '';
|
||||||
|
const wayId = Number(
|
||||||
|
drugOrSel._way_id ?? drugOrSel.way_id ?? drugOrSel.use_ways?.id ?? 0
|
||||||
|
);
|
||||||
|
if (!wayId) return '';
|
||||||
|
const hit = (this.drugUseWayList || []).find((w) => Number(w.id) === wayId);
|
||||||
|
const name =
|
||||||
|
(hit && hit.name) ||
|
||||||
|
(drugOrSel.use_ways && drugOrSel.use_ways.name) ||
|
||||||
|
'';
|
||||||
|
if (!name || name === '煎服') return '';
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 煎法行展示文案:有先煎/后下等显示标签名,否则「煎服(默认)」
|
||||||
|
*/
|
||||||
|
getWayDisplayName(drugOrSel) {
|
||||||
|
return this.getWayLabel(drugOrSel) || '煎服(默认)';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 判断气泡标签是否为当前药材选中煎法
|
||||||
|
*/
|
||||||
|
isWayActive(drug, wayItem) {
|
||||||
|
if (!drug || !wayItem) return false;
|
||||||
|
const wayId = Number(drug._way_id ?? drug.way_id ?? 0);
|
||||||
|
return wayId > 0 && wayId === Number(wayItem.id);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 展开/收起当前药材的煎法气泡卡片(同时只开一行)
|
||||||
|
*/
|
||||||
|
toggleWayPanel(event) {
|
||||||
|
const index = Number(event.currentTarget.dataset.index);
|
||||||
|
if (Number.isNaN(index)) return;
|
||||||
|
if (!(this.drugUseWayList || []).length) {
|
||||||
|
this.$toast('暂无煎法选项');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.wayPanelDrugIndex = this.wayPanelDrugIndex === index ? -1 : index;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 气泡标签点选煎法:写入 _way_id 并同步已选处方后收起
|
||||||
|
*/
|
||||||
|
selectWayTag(event) {
|
||||||
|
const ds = event.currentTarget.dataset || {};
|
||||||
|
const index = Number(ds.index);
|
||||||
|
const wayId = Number(ds.wayId ?? ds['way-id'] ?? 0);
|
||||||
|
const drug = this.drugList[index];
|
||||||
|
if (!drug || Number.isNaN(index)) return;
|
||||||
|
const picked = (this.drugUseWayList || []).find((w) => Number(w.id) === wayId) || null;
|
||||||
|
this.$set(drug, '_way_id', wayId || 0);
|
||||||
|
this.$set(drug, 'way_id', wayId || 0);
|
||||||
|
const existIndex = this.findSelectedIndex(drug);
|
||||||
|
if (existIndex !== -1) {
|
||||||
|
this.$set(this.selectedDrugs[existIndex], 'way_id', wayId || 0);
|
||||||
|
this.$set(this.selectedDrugs[existIndex], 'use_ways', picked);
|
||||||
|
this.emitSelect();
|
||||||
|
} else if (drug._quantity > 0) {
|
||||||
|
this.tryAutoAddDrug(drug, { showToast: false });
|
||||||
|
}
|
||||||
|
this.wayPanelDrugIndex = -1;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* emit select 事件,传出当前完整的 selectedDrugs(深拷贝)
|
* emit select 事件,传出当前完整的 selectedDrugs(深拷贝)
|
||||||
*/
|
*/
|
||||||
@@ -563,6 +697,11 @@ export default {
|
|||||||
const isNew = existIndex === -1;
|
const isNew = existIndex === -1;
|
||||||
if (existIndex !== -1) {
|
if (existIndex !== -1) {
|
||||||
this.$set(this.selectedDrugs[existIndex], 'number', drug._quantity);
|
this.$set(this.selectedDrugs[existIndex], 'number', drug._quantity);
|
||||||
|
// 保持煎法与列表一致
|
||||||
|
const wayId = Number(drug._way_id ?? drug.way_id ?? 0);
|
||||||
|
this.$set(this.selectedDrugs[existIndex], 'way_id', wayId);
|
||||||
|
const wayHit = (this.drugUseWayList || []).find((w) => Number(w.id) === wayId);
|
||||||
|
this.$set(this.selectedDrugs[existIndex], 'use_ways', wayHit || null);
|
||||||
} else {
|
} else {
|
||||||
this.selectedDrugs.push(this.buildSelectedItem(drug));
|
this.selectedDrugs.push(this.buildSelectedItem(drug));
|
||||||
}
|
}
|
||||||
@@ -818,6 +957,7 @@ export default {
|
|||||||
.color-title { color: #2A2E35; }
|
.color-title { color: #2A2E35; }
|
||||||
.color-sub { color: #8A92A3; }
|
.color-sub { color: #8A92A3; }
|
||||||
.color-price { color: #F53F3F; }
|
.color-price { color: #F53F3F; }
|
||||||
|
.color-way { color: #5b8ff9; }
|
||||||
.fs-26 { font-size: 26rpx; }
|
.fs-26 { font-size: 26rpx; }
|
||||||
.fs-28 { font-size: 28rpx; }
|
.fs-28 { font-size: 28rpx; }
|
||||||
.fs-32 { font-size: 32rpx; }
|
.fs-32 { font-size: 32rpx; }
|
||||||
@@ -904,7 +1044,48 @@ export default {
|
|||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #00A88A;
|
color: #00A88A;
|
||||||
margin-left: 8rpx;
|
margin-left: 8rpx;
|
||||||
white-space: nowrap;
|
}
|
||||||
|
.selected-chip-way {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #5b8ff9;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.way-block {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.way-row {
|
||||||
|
padding: 8rpx 0;
|
||||||
|
}
|
||||||
|
.way-picker {
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
background: #F7F8FA;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 1rpx solid #E2E8F0;
|
||||||
|
}
|
||||||
|
/* 煎法气泡卡片:标签直接点选,不限 ActionSheet 6 项 */
|
||||||
|
.way-bubble-card {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
border: 1rpx solid #E2E8F0;
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(42, 46, 53, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
.way-bubble-tag {
|
||||||
|
padding: 10rpx 24rpx;
|
||||||
|
background-color: #F4F6F8;
|
||||||
|
color: #6C7380;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.way-bubble-tag.active {
|
||||||
|
background-color: #00A88A;
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected-chip-close {
|
.selected-chip-close {
|
||||||
@@ -1065,4 +1246,17 @@ export default {
|
|||||||
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 配送仓绑定药品标识 */
|
||||||
|
.multi-wh-tag {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
line-height: 30rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #2b6de5;
|
||||||
|
background: rgba(43, 109, 229, 0.08);
|
||||||
|
border: 1rpx solid rgba(43, 109, 229, 0.45);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -44,7 +44,10 @@
|
|||||||
></image>
|
></image>
|
||||||
<view class="flex-1 m-l-24 flex-col">
|
<view class="flex-1 m-l-24 flex-col">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-start">
|
<view class="flex-row flex-jus-sp flex-ali-start">
|
||||||
<text class="fs-30 font-bold color-title line-clamp-2 w-70">{{item.drug.drug_name || item.drug.name}}</text>
|
<view class="flex-row flex-ali-center w-70">
|
||||||
|
<text class="fs-30 font-bold color-title line-clamp-2">{{item.drug.drug_name || item.drug.name}}</text>
|
||||||
|
<text v-if="Number(item.has_delivery_warehouse) === 1" class="multi-wh-tag m-l-8">多仓</text>
|
||||||
|
</view>
|
||||||
<text class="fs-32 font-bold color-price">¥{{parseFloat(item.price || 0).toFixed(2)}}</text>
|
<text class="fs-32 font-bold color-price">¥{{parseFloat(item.price || 0).toFixed(2)}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
|
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
|
||||||
@@ -357,4 +360,17 @@ export default {
|
|||||||
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 配送仓绑定药品标识 */
|
||||||
|
.multi-wh-tag {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
line-height: 30rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #2b6de5;
|
||||||
|
background: rgba(43, 109, 229, 0.08);
|
||||||
|
border: 1rpx solid rgba(43, 109, 229, 0.45);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<!-- 用 page-container 防止右滑误退出开方页 -->
|
||||||
|
<page-container
|
||||||
|
v-if="show"
|
||||||
|
:show="show"
|
||||||
|
:overlay="false"
|
||||||
|
@leave="handleClose"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
<u-popup
|
||||||
|
v-model="show"
|
||||||
|
mode="bottom"
|
||||||
|
:closeable="true"
|
||||||
|
@close="handleClose"
|
||||||
|
:safe-area-inset-bottom="true"
|
||||||
|
:mask-close-able="true"
|
||||||
|
z-index="10080"
|
||||||
|
height="75%"
|
||||||
|
>
|
||||||
|
<view class="warehouse-select-drawer page-bg">
|
||||||
|
<view class="modal-header white">
|
||||||
|
<d-text text="选择配送仓库" className="fs-32 font-bold color-title"></d-text>
|
||||||
|
</view>
|
||||||
|
<scroll-view class="scroll-container" scroll-y>
|
||||||
|
<view v-if="currentRow" class="step-body">
|
||||||
|
<view class="step-progress">第 {{ stepIndex + 1 }} / {{ localRows.length }} 个药品</view>
|
||||||
|
<!-- 当前药品:图 / 名 / 规格 -->
|
||||||
|
<view class="drug-info flex-row flex-ali-start">
|
||||||
|
<image
|
||||||
|
v-if="currentRow.image"
|
||||||
|
class="drug-img"
|
||||||
|
:src="currentRow.image"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
<view v-else class="drug-img drug-img-placeholder">暂无图</view>
|
||||||
|
<view class="drug-meta">
|
||||||
|
<view class="drug-name">{{ currentRow.drug_name }}</view>
|
||||||
|
<view class="drug-spec">规格:{{ currentRow.specification || '--' }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-list">
|
||||||
|
<view
|
||||||
|
v-for="opt in currentRow.options"
|
||||||
|
:key="opt.warehouse_id"
|
||||||
|
class="wh-card"
|
||||||
|
:class="{ 'is-active': currentRow.warehouse_id === opt.warehouse_id }"
|
||||||
|
@click="selectWarehouse(opt.warehouse_id)"
|
||||||
|
>
|
||||||
|
<view class="wh-name">{{ opt.warehouse_name }}</view>
|
||||||
|
<view class="wh-meta">
|
||||||
|
供货价 {{ opt.quote }} · 库存 {{ opt.available_stock }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-tip">当前药品无需选择配送仓库</view>
|
||||||
|
</scroll-view>
|
||||||
|
<!-- 底部:外包 flex 半宽,避免 u-button custom-style 的 flex 在小程序被忽略 -->
|
||||||
|
<view class="modal-footer white shadow-up">
|
||||||
|
<view class="footer-btn-wrap">
|
||||||
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
@click="onLeftClick"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="leftBtnStyle"
|
||||||
|
>{{ leftBtnText }}</u-button>
|
||||||
|
</view>
|
||||||
|
<view class="footer-btn-wrap">
|
||||||
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
@click="onRightClick"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="rightBtnStyle"
|
||||||
|
>{{ rightBtnText }}</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 在线复诊开方:按药分步卡片选配送仓
|
||||||
|
* 默认选中每药 options[0](与 auto 最低 quote 一致)
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'WarehouseSelectDrawer',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
/** [{ drug_id, drug_name, image, specification, options, warehouse_id? }] */
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localRows: [],
|
||||||
|
stepIndex: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
show: {
|
||||||
|
get() {
|
||||||
|
return this.value;
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
this.$emit('input', v);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
currentRow() {
|
||||||
|
return this.localRows[this.stepIndex] || null;
|
||||||
|
},
|
||||||
|
isFirstStep() {
|
||||||
|
return this.stepIndex <= 0;
|
||||||
|
},
|
||||||
|
isLastStep() {
|
||||||
|
return this.stepIndex >= Math.max(this.localRows.length - 1, 0);
|
||||||
|
},
|
||||||
|
leftBtnText() {
|
||||||
|
return this.isFirstStep ? '取消' : '上一步';
|
||||||
|
},
|
||||||
|
rightBtnText() {
|
||||||
|
return this.isLastStep ? '确认' : '下一步';
|
||||||
|
},
|
||||||
|
leftBtnStyle() {
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '88rpx',
|
||||||
|
margin: '0',
|
||||||
|
background: '#F5F6F8',
|
||||||
|
color: '#333',
|
||||||
|
border: 'none',
|
||||||
|
fontSize: '30rpx',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
rightBtnStyle() {
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '88rpx',
|
||||||
|
margin: '0',
|
||||||
|
background: '#2B6DE5',
|
||||||
|
color: '#fff',
|
||||||
|
border: 'none',
|
||||||
|
fontSize: '30rpx',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(val) {
|
||||||
|
if (val) {
|
||||||
|
// 打开时默认选中每药最低价仓(options[0]);规格/图/名可从 option 回退
|
||||||
|
this.localRows = (this.rows || []).map((row) => {
|
||||||
|
const first =
|
||||||
|
Array.isArray(row.options) && row.options[0] ? row.options[0] : null;
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
warehouse_id:
|
||||||
|
row.warehouse_id ||
|
||||||
|
(first && first.warehouse_id) ||
|
||||||
|
0,
|
||||||
|
options: Array.isArray(row.options) ? row.options : [],
|
||||||
|
drug_name: row.drug_name || (first && first.drug_name) || '',
|
||||||
|
image: row.image || (first && first.image) || '',
|
||||||
|
specification:
|
||||||
|
row.specification || (first && first.specification) || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.stepIndex = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 点击卡片选中当前步骤的仓 */
|
||||||
|
selectWarehouse(warehouseId) {
|
||||||
|
if (!this.currentRow) return;
|
||||||
|
this.$set
|
||||||
|
? this.$set(this.currentRow, 'warehouse_id', warehouseId)
|
||||||
|
: (this.currentRow.warehouse_id = warehouseId);
|
||||||
|
},
|
||||||
|
onLeftClick() {
|
||||||
|
if (this.isFirstStep) {
|
||||||
|
this.handleClose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stepIndex -= 1;
|
||||||
|
},
|
||||||
|
onRightClick() {
|
||||||
|
if (!this.currentRow || !this.currentRow.warehouse_id) {
|
||||||
|
uni.showToast({ title: '请先选择配送仓库', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isLastStep) {
|
||||||
|
this.stepIndex += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handleConfirm();
|
||||||
|
},
|
||||||
|
handleClose() {
|
||||||
|
this.show = false;
|
||||||
|
this.$emit('cancel');
|
||||||
|
},
|
||||||
|
handleConfirm() {
|
||||||
|
const map = {};
|
||||||
|
for (const row of this.localRows) {
|
||||||
|
if (!row.warehouse_id) {
|
||||||
|
uni.showToast({ title: `请为【${row.drug_name}】选择配送仓库`, icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map[row.drug_id] = row.warehouse_id;
|
||||||
|
}
|
||||||
|
this.show = false;
|
||||||
|
this.$emit('confirm', map);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.warehouse-select-drawer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.modal-header {
|
||||||
|
padding: 32rpx 32rpx 16rpx;
|
||||||
|
}
|
||||||
|
.scroll-container {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
padding: 0 24rpx 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.step-progress {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #8a92a3;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.drug-info {
|
||||||
|
padding: 20rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
background: #f5f6f8;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
.drug-img {
|
||||||
|
width: 112rpx;
|
||||||
|
height: 112rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #e5e6eb;
|
||||||
|
}
|
||||||
|
.drug-img-placeholder {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #8a92a3;
|
||||||
|
}
|
||||||
|
.drug-meta {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.drug-name {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
.drug-spec {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #8a92a3;
|
||||||
|
}
|
||||||
|
.card-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
.wh-card {
|
||||||
|
min-width: 280rpx;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 48%;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
border: 2rpx solid #e5e6eb;
|
||||||
|
background: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.wh-card.is-active {
|
||||||
|
border-color: #2b6de5;
|
||||||
|
background: rgba(43, 109, 229, 0.06);
|
||||||
|
}
|
||||||
|
.wh-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
.wh-meta {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #8a92a3;
|
||||||
|
}
|
||||||
|
.empty-tip {
|
||||||
|
padding: 48rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #8a92a3;
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.footer-btn-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.shadow-up {
|
||||||
|
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -46,7 +46,10 @@
|
|||||||
></image>
|
></image>
|
||||||
<view class="flex-1 m-l-24 flex-col">
|
<view class="flex-1 m-l-24 flex-col">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-start">
|
<view class="flex-row flex-jus-sp flex-ali-start">
|
||||||
<text class="fs-30 font-bold color-title line-clamp-2 w-70">{{item.drug.drug_name || item.drug.name}}</text>
|
<view class="flex-row flex-ali-center w-70">
|
||||||
|
<text class="fs-30 font-bold color-title line-clamp-2">{{item.drug.drug_name || item.drug.name}}</text>
|
||||||
|
<text v-if="Number(item.has_delivery_warehouse) === 1" class="multi-wh-tag m-l-8">多仓</text>
|
||||||
|
</view>
|
||||||
<text class="fs-32 font-bold color-price">¥{{parseFloat(item.price || 0).toFixed(2)}}</text>
|
<text class="fs-32 font-bold color-price">¥{{parseFloat(item.price || 0).toFixed(2)}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
|
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
|
||||||
@@ -390,4 +393,17 @@ export default {
|
|||||||
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
padding: 24rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 配送仓绑定药品标识 */
|
||||||
|
.multi-wh-tag {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
line-height: 30rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #2b6de5;
|
||||||
|
background: rgba(43, 109, 229, 0.08);
|
||||||
|
border: 1rpx solid rgba(43, 109, 229, 0.45);
|
||||||
|
border-radius: 999rpx;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -279,7 +279,13 @@
|
|||||||
<!-- 中药极简高密度标签云设计 -->
|
<!-- 中药极简高密度标签云设计 -->
|
||||||
<view class="white radius-12 p-32" v-if="currentDrugs.length > 0">
|
<view class="white radius-12 p-32" v-if="currentDrugs.length > 0">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||||
|
<view class="flex-col">
|
||||||
<text class="fs-28 font-bold color-title">药材清单 (共{{currentDrugs.length}}味)</text>
|
<text class="fs-28 font-bold color-title">药材清单 (共{{currentDrugs.length}}味)</text>
|
||||||
|
<text
|
||||||
|
v-if="!isSpecialPrescriptionCartLocked"
|
||||||
|
class="fs-22 text-gray m-t-4"
|
||||||
|
>先煎/后下请在「选择中药」中设置</text>
|
||||||
|
</view>
|
||||||
<view class="flex-row">
|
<view class="flex-row">
|
||||||
<template v-if="!isSpecialPrescriptionCartLocked">
|
<template v-if="!isSpecialPrescriptionCartLocked">
|
||||||
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
|
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
|
||||||
@@ -302,6 +308,8 @@
|
|||||||
@longpress.stop="!isSpecialPrescriptionCartLocked && onTcmTagLongPress">
|
@longpress.stop="!isSpecialPrescriptionCartLocked && onTcmTagLongPress">
|
||||||
<text class="tcm-name">{{it.drug_name || it.name}}</text>
|
<text class="tcm-name">{{it.drug_name || it.name}}</text>
|
||||||
<text class="tcm-weight">{{String(it.number)}}{{it.unit ? it.unit.name : 'g'}}</text>
|
<text class="tcm-weight">{{String(it.number)}}{{it.unit ? it.unit.name : 'g'}}</text>
|
||||||
|
<!-- 展示药材级先煎/后下(在选择中药弹窗设置) -->
|
||||||
|
<text v-if="getTcmWayLabel(it)" class="tcm-way">{{ getTcmWayLabel(it) }}</text>
|
||||||
<text
|
<text
|
||||||
v-if="seeRate == 1 && getChineseItemMargin(it) !== '--'"
|
v-if="seeRate == 1 && getChineseItemMargin(it) !== '--'"
|
||||||
class="tcm-margin"
|
class="tcm-margin"
|
||||||
@@ -546,6 +554,7 @@
|
|||||||
:see-rate="seeRate"
|
:see-rate="seeRate"
|
||||||
:salesperson-transfer-mode="isSalespersonTransferMode"
|
:salesperson-transfer-mode="isSalespersonTransferMode"
|
||||||
:show-drug-price="showSalespersonTransferPrice"
|
:show-drug-price="showSalespersonTransferPrice"
|
||||||
|
:drug-use-way-list="drugUseList.drug_use_way || []"
|
||||||
@select="handleSelectChineseDrug"
|
@select="handleSelectChineseDrug"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -556,6 +565,14 @@
|
|||||||
@confirm="handleTransferPatientConfirm"
|
@confirm="handleTransferPatientConfirm"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 在线复诊:卡片选配送仓 -->
|
||||||
|
<WarehouseSelectDrawer
|
||||||
|
v-model="showWarehouseSelectDrawer"
|
||||||
|
:rows="warehouseSelectRows"
|
||||||
|
@confirm="onWarehouseSelectConfirm"
|
||||||
|
@cancel="onWarehouseSelectCancel"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 简单产品选择弹窗 -->
|
<!-- 简单产品选择弹窗 -->
|
||||||
<SimpleProductModal
|
<SimpleProductModal
|
||||||
v-model="showSimpleProductModal"
|
v-model="showSimpleProductModal"
|
||||||
@@ -602,6 +619,7 @@
|
|||||||
// 注意:这部分保持你原有的逻辑代码,不进行任何修改,确保业务完全正常
|
// 注意:这部分保持你原有的逻辑代码,不进行任何修改,确保业务完全正常
|
||||||
import {
|
import {
|
||||||
addWestPrescription,
|
addWestPrescription,
|
||||||
|
getDeliveryWarehouseOptionsByDrugs,
|
||||||
checkChineseMedicineConflictApi,
|
checkChineseMedicineConflictApi,
|
||||||
getDiseaseList,
|
getDiseaseList,
|
||||||
getDrugUseList,
|
getDrugUseList,
|
||||||
@@ -634,6 +652,7 @@ import WesternMedicineUsageModal from './components/modals/WesternMedicineUsageM
|
|||||||
import ChineseMedicineConfig from './components/ChineseMedicineConfig.vue';
|
import ChineseMedicineConfig from './components/ChineseMedicineConfig.vue';
|
||||||
import TraditionalTermPickerModal from './components/modals/TraditionalTermPickerModal.vue';
|
import TraditionalTermPickerModal from './components/modals/TraditionalTermPickerModal.vue';
|
||||||
import TransferPatientDrawer from './components/modals/TransferPatientDrawer.vue';
|
import TransferPatientDrawer from './components/modals/TransferPatientDrawer.vue';
|
||||||
|
import WarehouseSelectDrawer from './components/modals/WarehouseSelectDrawer.vue';
|
||||||
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue';
|
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue';
|
||||||
import { applyRatioToDrugs, formatPriceDiscountLabel, normalizeQuickOptions } from '@/subPackages/sub_business_shared/common/pricePercentAdjust.js';
|
import { applyRatioToDrugs, formatPriceDiscountLabel, normalizeQuickOptions } from '@/subPackages/sub_business_shared/common/pricePercentAdjust.js';
|
||||||
import {
|
import {
|
||||||
@@ -665,7 +684,8 @@ const PRESCRIPTION_OVERLAY_KEYS = [
|
|||||||
'showDiagnosisModal',
|
'showDiagnosisModal',
|
||||||
'showDoctorOrderModal',
|
'showDoctorOrderModal',
|
||||||
'showSaveCommonPrescriptionModal',
|
'showSaveCommonPrescriptionModal',
|
||||||
'showTransferPatientDrawer'
|
'showTransferPatientDrawer',
|
||||||
|
'showWarehouseSelectDrawer',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -681,6 +701,7 @@ export default {
|
|||||||
ChineseMedicineConfig,
|
ChineseMedicineConfig,
|
||||||
TraditionalTermPickerModal,
|
TraditionalTermPickerModal,
|
||||||
TransferPatientDrawer,
|
TransferPatientDrawer,
|
||||||
|
WarehouseSelectDrawer,
|
||||||
OrderPricePercentAdjustPopup,
|
OrderPricePercentAdjustPopup,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -738,6 +759,8 @@ export default {
|
|||||||
commonPrescriptionName: '',
|
commonPrescriptionName: '',
|
||||||
isCommonPrescription: false,
|
isCommonPrescription: false,
|
||||||
_presetActiveCategory: null,
|
_presetActiveCategory: null,
|
||||||
|
/** URL/传方指定了分类时,禁止被诊所类型默认覆盖 */
|
||||||
|
_categoryLockedByQuery: false,
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
identity: uni.getStorageSync('role'),
|
identity: uni.getStorageSync('role'),
|
||||||
registerStoreInfo: null,
|
registerStoreInfo: null,
|
||||||
@@ -817,6 +840,11 @@ export default {
|
|||||||
salespersonSeePrice: 0,
|
salespersonSeePrice: 0,
|
||||||
showTransferPatientDrawer: false,
|
showTransferPatientDrawer: false,
|
||||||
transferDrawerSubmitting: false,
|
transferDrawerSubmitting: false,
|
||||||
|
/** 在线复诊选仓抽屉 */
|
||||||
|
showWarehouseSelectDrawer: false,
|
||||||
|
warehouseSelectRows: [],
|
||||||
|
/** resolvePharmacyWarehouseMap 的 Promise resolve;false=取消,null=无需选,map=已选 */
|
||||||
|
_warehouseSelectResolve: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -1016,6 +1044,7 @@ export default {
|
|||||||
if (this.isSalespersonTransferMode) {
|
if (this.isSalespersonTransferMode) {
|
||||||
this.registerId = SALESPERSON_TRANSFER_STORAGE_KEY;
|
this.registerId = SALESPERSON_TRANSFER_STORAGE_KEY;
|
||||||
this._presetActiveCategory = 1;
|
this._presetActiveCategory = 1;
|
||||||
|
this._categoryLockedByQuery = true;
|
||||||
// 传方模式默认自制剂 + 剂包
|
// 传方模式默认自制剂 + 剂包
|
||||||
this.chineseConfig = {
|
this.chineseConfig = {
|
||||||
...this.chineseConfig,
|
...this.chineseConfig,
|
||||||
@@ -1034,7 +1063,10 @@ export default {
|
|||||||
}
|
}
|
||||||
if (options.initial_category !== undefined && options.initial_category !== '') {
|
if (options.initial_category !== undefined && options.initial_category !== '') {
|
||||||
const n = parseInt(options.initial_category, 10);
|
const n = parseInt(options.initial_category, 10);
|
||||||
if (!Number.isNaN(n)) this._presetActiveCategory = n;
|
if (!Number.isNaN(n)) {
|
||||||
|
this._presetActiveCategory = n;
|
||||||
|
this._categoryLockedByQuery = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.initializePageData();
|
this.initializePageData();
|
||||||
},
|
},
|
||||||
@@ -1083,6 +1115,8 @@ export default {
|
|||||||
await this.loadRegisterOrderType();
|
await this.loadRegisterOrderType();
|
||||||
await this.loadBasicConfigData();
|
await this.loadBasicConfigData();
|
||||||
await this.loadRegisterStoreInfo();
|
await this.loadRegisterStoreInfo();
|
||||||
|
// 无 URL/上次切换/草稿时,按诊所类型兜底中药或西药 Tab
|
||||||
|
this.applyDefaultCategoryByClinicType();
|
||||||
}
|
}
|
||||||
this.loadCurrentCategoryDrugs();
|
this.loadCurrentCategoryDrugs();
|
||||||
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
|
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
|
||||||
@@ -1147,6 +1181,7 @@ export default {
|
|||||||
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
|
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
|
||||||
this.activeCategory = this._presetActiveCategory;
|
this.activeCategory = this._presetActiveCategory;
|
||||||
this._presetActiveCategory = null;
|
this._presetActiveCategory = null;
|
||||||
|
// 保留 _categoryLockedByQuery,避免随后被诊所类型默认覆盖
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const savedCategory = PrescriptionStorage.getActiveCategory(this.registerId);
|
const savedCategory = PrescriptionStorage.getActiveCategory(this.registerId);
|
||||||
@@ -1163,6 +1198,28 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
||||||
|
* 按诊所类型设中药(1)/西药(2)。优先级:URL > 上次切换 > 草稿 > clinic_type
|
||||||
|
*/
|
||||||
|
applyDefaultCategoryByClinicType() {
|
||||||
|
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
||||||
|
if (!this.registerId) return;
|
||||||
|
// 最后切换的分类优先,不再用诊所类型覆盖
|
||||||
|
if (PrescriptionStorage.getActiveCategory(this.registerId)) return;
|
||||||
|
// 任一类有草稿则沿用 restore 结果
|
||||||
|
if (PrescriptionStorage.hasDraftWithDrugs(this.registerId)) return;
|
||||||
|
const clinicType = Number(this.registerStoreInfo?.clinic_type ?? 0);
|
||||||
|
let next = 0;
|
||||||
|
if (clinicType === 2) {
|
||||||
|
next = 1; // 中医诊所 → 中药
|
||||||
|
} else if (clinicType === 1) {
|
||||||
|
next = 2; // 西医诊所 → 西(中成)药
|
||||||
|
}
|
||||||
|
if (!next || next === this.activeCategory) return;
|
||||||
|
this.activeCategory = next;
|
||||||
|
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||||
|
},
|
||||||
loadCurrentCategoryDrugs() {
|
loadCurrentCategoryDrugs() {
|
||||||
const data = PrescriptionStorage.loadPrescriptionData(this.activeCategory, this.registerId);
|
const data = PrescriptionStorage.loadPrescriptionData(this.activeCategory, this.registerId);
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -2215,8 +2272,120 @@ export default {
|
|||||||
await this.executeSendPrescription(doctorSecondSign, 0, null);
|
await this.executeSendPrescription(doctorSecondSign, 0, null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 主界面药材清单展示先煎/后下文案(在选择中药弹窗设置;默认煎服不展示)
|
||||||
|
*/
|
||||||
|
getTcmWayLabel(drug) {
|
||||||
|
if (!drug) return '';
|
||||||
|
const wayId = Number(drug.way_id ?? drug.use_ways?.id ?? 0);
|
||||||
|
if (!wayId) return '';
|
||||||
|
const list = (this.drugUseList && this.drugUseList.drug_use_way) || [];
|
||||||
|
const hit = list.find((w) => Number(w.id) === wayId);
|
||||||
|
const name = (hit && hit.name) || (drug.use_ways && drug.use_ways.name) || '';
|
||||||
|
if (!name || name === '煎服') return '';
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 在线复诊:有仓药绑定时弹卡片选仓;默认最低价;取消返回 false
|
||||||
|
*/
|
||||||
|
async resolvePharmacyWarehouseMap() {
|
||||||
|
if (!this.isOnlineRevisit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const drugs = this.currentDrugs || [];
|
||||||
|
const drugIds = [];
|
||||||
|
const needQtyMap = {};
|
||||||
|
for (const d of drugs) {
|
||||||
|
const id = Number(d?.id || 0);
|
||||||
|
const qty = Number(d?.select_number || d?.number || 0);
|
||||||
|
if (id <= 0 || qty <= 0) continue;
|
||||||
|
drugIds.push(id);
|
||||||
|
needQtyMap[id] = (needQtyMap[id] || 0) + qty;
|
||||||
|
}
|
||||||
|
if (!drugIds.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await getDeliveryWarehouseOptionsByDrugs({
|
||||||
|
drug_ids: drugIds.join(','),
|
||||||
|
need_qty_map: needQtyMap,
|
||||||
|
});
|
||||||
|
const map = (res && (res.result || res.data || res)) || {};
|
||||||
|
const rows = [];
|
||||||
|
for (const d of drugs) {
|
||||||
|
const id = Number(d?.id || 0);
|
||||||
|
if (id <= 0) continue;
|
||||||
|
const options = map[String(id)] || map[id] || [];
|
||||||
|
if (!Array.isArray(options) || !options.length) continue;
|
||||||
|
rows.push({
|
||||||
|
drug_id: id,
|
||||||
|
// 优先用仓选项接口带回的药品信息,避免处方草稿未存规格/图
|
||||||
|
drug_name: String(
|
||||||
|
(options[0] && options[0].drug_name) ||
|
||||||
|
d.drug_name ||
|
||||||
|
d.name ||
|
||||||
|
`药品#${id}`,
|
||||||
|
),
|
||||||
|
image: String(
|
||||||
|
(options[0] && options[0].image) ||
|
||||||
|
d.image ||
|
||||||
|
d._image ||
|
||||||
|
(d.drug && d.drug.image) ||
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
specification: String(
|
||||||
|
(options[0] && options[0].specification) ||
|
||||||
|
d.specification ||
|
||||||
|
(d.drug && d.drug.specification) ||
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
options: options.map((o) => ({
|
||||||
|
warehouse_id: Number(o.warehouse_id),
|
||||||
|
warehouse_name: String(o.warehouse_name || ''),
|
||||||
|
quote: String(o.quote ?? '0'),
|
||||||
|
available_stock: Number(o.available_stock ?? 0),
|
||||||
|
// 保留药品展示字段,供抽屉从 options[0] 回退读取
|
||||||
|
drug_name: String(o.drug_name || ''),
|
||||||
|
image: String(o.image || ''),
|
||||||
|
specification: String(o.specification || ''),
|
||||||
|
})),
|
||||||
|
warehouse_id: Number(options[0].warehouse_id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!rows.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this.warehouseSelectRows = rows;
|
||||||
|
this.showWarehouseSelectDrawer = true;
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this._warehouseSelectResolve = resolve;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载配送仓库失败', e);
|
||||||
|
this.$toast((e && e.message) || '加载配送仓库失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onWarehouseSelectConfirm(map) {
|
||||||
|
const resolve = this._warehouseSelectResolve;
|
||||||
|
this._warehouseSelectResolve = null;
|
||||||
|
this.showWarehouseSelectDrawer = false;
|
||||||
|
if (typeof resolve === 'function') resolve(map || {});
|
||||||
|
},
|
||||||
|
onWarehouseSelectCancel() {
|
||||||
|
const resolve = this._warehouseSelectResolve;
|
||||||
|
this._warehouseSelectResolve = null;
|
||||||
|
this.showWarehouseSelectDrawer = false;
|
||||||
|
if (typeof resolve === 'function') resolve(false);
|
||||||
|
},
|
||||||
async executeSendPrescription(doctorSecondSign = 0, sendMode = 0, customStoreId = null) {
|
async executeSendPrescription(doctorSecondSign = 0, sendMode = 0, customStoreId = null) {
|
||||||
try {
|
try {
|
||||||
|
// 在线复诊有绑仓时先卡片选仓
|
||||||
|
const warehouseMap = await this.resolvePharmacyWarehouseMap();
|
||||||
|
if (warehouseMap === false) {
|
||||||
|
this.isSubmitting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.activeCategory === 2) {
|
if (this.activeCategory === 2) {
|
||||||
if (!this.patientInfo || !this.patientId) {
|
if (!this.patientInfo || !this.patientId) {
|
||||||
if (this.patientId && !this.patientInfo) await this.loadPatientInfo();
|
if (this.patientId && !this.patientInfo) await this.loadPatientInfo();
|
||||||
@@ -2269,6 +2438,13 @@ export default {
|
|||||||
price_discount: this.priceDiscount,
|
price_discount: this.priceDiscount,
|
||||||
special_prescription_id: Number(this.appliedSpecialPrescriptionId) || 0,
|
special_prescription_id: Number(this.appliedSpecialPrescriptionId) || 0,
|
||||||
};
|
};
|
||||||
|
if (warehouseMap && typeof warehouseMap === 'object') {
|
||||||
|
// 列表格式避免数字键对象在 form/部分环境序列化丢失
|
||||||
|
params.warehouse_map = Object.keys(warehouseMap).map((drugId) => ({
|
||||||
|
drug_id: Number(drugId),
|
||||||
|
warehouse_id: Number(warehouseMap[drugId]),
|
||||||
|
}));
|
||||||
|
}
|
||||||
if (this.activeCategory === 1) {
|
if (this.activeCategory === 1) {
|
||||||
params.package_method_id = this.chineseConfig.packageMethodId || null; params.process_rule_id = this.chineseConfig.processRuleId || null; params.process_rule_note_id = this.chineseConfig.processRuleNoteId || null; params.child_process_rule_id = this.chineseConfig.childProcessRuleId || null; params.process_rule_type = this.chineseConfig.ruleType || 1; params.processing_fee = this.processingFee; params.dosage = this.chineseConfig.dosage || 7; params.day_dosage = this.chineseConfig.dayDosage || 2;
|
params.package_method_id = this.chineseConfig.packageMethodId || null; params.process_rule_id = this.chineseConfig.processRuleId || null; params.process_rule_note_id = this.chineseConfig.processRuleNoteId || null; params.child_process_rule_id = this.chineseConfig.childProcessRuleId || null; params.process_rule_type = this.chineseConfig.ruleType || 1; params.processing_fee = this.processingFee; params.dosage = this.chineseConfig.dosage || 7; params.day_dosage = this.chineseConfig.dayDosage || 2;
|
||||||
if (this.isOnlineRevisit) {
|
if (this.isOnlineRevisit) {
|
||||||
@@ -2718,6 +2894,12 @@ export default {
|
|||||||
color: #00A88A;
|
color: #00A88A;
|
||||||
margin-left: 12rpx;
|
margin-left: 12rpx;
|
||||||
}
|
}
|
||||||
|
.tcm-way {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #5b8ff9;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
.tcm-margin {
|
.tcm-margin {
|
||||||
font-size: 22rpx;
|
font-size: 22rpx;
|
||||||
color: #ff9800;
|
color: #ff9800;
|
||||||
|
|||||||
12
utils/formatStoreNameWithHu.js
Normal file
12
utils/formatStoreNameWithHu.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 在线处方诊所名追加「(互)」:is_online 为 1/2/3,线下 0 不加
|
||||||
|
*/
|
||||||
|
export function formatStoreNameWithHu(storeName, isOnline) {
|
||||||
|
const name = String(storeName || '').trim()
|
||||||
|
if (!name) return ''
|
||||||
|
const online = Number(isOnline)
|
||||||
|
if (online === 1 || online === 2 || online === 3) {
|
||||||
|
return name.endsWith('(互)') ? name : `${name}(互)`
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ export function checkDev(key = '') {
|
|||||||
// 判断某些模块是否开启
|
// 判断某些模块是否开启
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'dev':
|
case 'dev':
|
||||||
// return false;
|
return false;
|
||||||
return true;
|
// return true;
|
||||||
case 'open-im':
|
case 'open-im':
|
||||||
// return false;
|
// return false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user