diff --git a/App.vue b/App.vue index 6d885f3..906a6fb 100644 --- a/App.vue +++ b/App.vue @@ -100,6 +100,9 @@ if (uni.getStorageSync('token') && !isWebSocketConnected()) { this.initWebSocketConnection(); } + // 检测拨号返回:详情页 onCallbackCall 调用 makePhoneCall 前写入了 pendingCallback, + // 回到小程序时广播给当前详情页/抽屉组件,由其自动弹回访表单 + this.checkPendingCallback(); }, onHide: function() { // 应用隐藏时不断开WebSocket,保持连接以便接收消息 @@ -120,6 +123,32 @@ initWebSocket(); } }, + + /** + * 检测从拨号返回小程序的场景 + * 就诊人详情点击"回访"时写入 pendingCallback(upId/phone/ts); + * 用户回到小程序时若 5 分钟内有效,则广播事件触发回访表单抽屉, + * 之后立即清除 storage 标记,避免下次 onShow 重复弹窗 + */ + checkPendingCallback() { + let payload = null; + try { + payload = uni.getStorageSync('pendingCallback'); + } catch (e) { + payload = null; + } + if (!payload || typeof payload !== 'object') return; + const now = Date.now(); + const ts = Number(payload.ts) || 0; + // 时间窗 5 分钟:避免误判;超时直接丢弃 + if (ts && now - ts > 5 * 60 * 1000) { + try { uni.removeStorageSync('pendingCallback'); } catch (e) { /* ignore */ } + return; + } + // 广播后清理,由当前可见的详情页/抽屉组件接收处理 + try { uni.removeStorageSync('pendingCallback'); } catch (e) { /* ignore */ } + uni.$emit('patient:callbackReturned', payload); + }, async getInfo() { diff --git a/api/userPatientProfile.js b/api/userPatientProfile.js index 5802f76..5f4249c 100644 --- a/api/userPatientProfile.js +++ b/api/userPatientProfile.js @@ -130,3 +130,43 @@ export function getUserPatientOrderList(upId, params) { data: { up_id: upId, ...params }, }); } + +/** + * 就诊人回访记录列表 + * @param {number|string} upId 就诊人ID + * @param {object} params { pageSize } + */ +export function getPatientCallLogList(upId, params) { + const { prefix } = getProfileContext(); + return profileRequest({ + url: `${prefix}/call-log-list`, + method: 'GET', + data: { up_id: upId, ...(params || {}) }, + }); +} + +/** + * 创建就诊人回访记录 + * @param {number|string} upId 就诊人ID + * @param {object} payload { phone, call_at, result, tags, conclusion, remark, next_at } + */ +export function createPatientCallLog(upId, payload) { + const { prefix } = getProfileContext(); + return profileRequest({ + url: `${prefix}/call-log-create`, + method: 'POST', + data: { up_id: upId, ...(payload || {}) }, + }); +} + +/** + * 小程序回访字典(启用中的结果/标签 + 颜色) + * 返回结构:{ results: [{value,name,color}], tags: [...] } + */ +export function getPatientCallLogDict() { + const { prefix } = getProfileContext(); + return profileRequest({ + url: `${prefix}/call-log-dict`, + method: 'GET', + }); +} diff --git a/config/app-env.js b/config/app-env.js index 75abbb2..2098d35 100644 --- a/config/app-env.js +++ b/config/app-env.js @@ -13,14 +13,14 @@ const DEV = { 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 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 TEST = { legacyApiBase: 'https://xk.test.saas.api-yii.nailaoyun.cn/service/v1/', newApiBase: 'https://xk.test.saas.api.nailaoyun.cn/api/doctor/', // wsUrl: 'wss://api.ws.g.xiaokang88.com/ws', @@ -33,7 +33,11 @@ export function isDoctorWxDev() { } export function getDoctorWxEnv() { - return checkDev('dev') ? DEV : PROD; + let env = checkDev('dev') ? DEV : PROD; + if (checkDev('test')) { + env = TEST; + } + return env; } /** 开发 / 生产 WebSocket 地址(与 getDoctorWxEnv 同源,供 websocket 配置展示) */ diff --git a/subPackages/sub_business_shared/common/display.js b/subPackages/sub_business_shared/common/display.js index c8320ee..3678ec2 100644 --- a/subPackages/sub_business_shared/common/display.js +++ b/subPackages/sub_business_shared/common/display.js @@ -259,19 +259,24 @@ export function mapOrderProductItem(p, prescriptionType) { ? '*' + ((p.number != null ? p.number : 0) * dosage) : '×' + (p.number != null ? p.number : 0) const isTcm = prescriptionType === 1 + const unitName = + (drug.unit && drug.unit.name) || + (p.unit && p.unit.name) || + p.unit_name || + 'g' return { id: p.id, drugName: p.drug_name || drug.drug_name || '药品', drugNumber: drug.drug_number || '', imageUrl: resolveDrugImage(p.drug_image || drug.image), - specification: drug.specification || (isTcm ? 'g' : '暂无'), + specification: drug.specification || (isTcm ? unitName : '暂无'), manufacturer, qty, price: isTcm - ? (p.price != null && p.price !== '' ? p.price + ' 元/g' : '—') + ? (p.price != null && p.price !== '' ? p.price + ' 元/' + unitName : '—') : formatMoney(p.price), buyPrice: p.buy_price != null && p.buy_price !== '' - ? p.buy_price + ' 元/g' + ? p.buy_price + ' 元/' + unitName : '—', isTcm, } @@ -348,9 +353,13 @@ export function mapPrescriptionDetailView(item) { const name = d.name || d.drug_name || '—' const num = d.number != null ? d.number : '' const way = (d.use_way && d.use_way.name) || d.useWay || '煎服' + const unit = + (d.unit && d.unit.name) || + (d.use_unit && d.use_unit.name) || + 'g' return { name, - qty: num ? num + ' /g' : '', + qty: num !== '' ? num + ' /' + unit : '', usage: way, specification: d.specification || '', } diff --git a/subPackages/sub_business_shared/components/CallLogDrawer.vue b/subPackages/sub_business_shared/components/CallLogDrawer.vue new file mode 100644 index 0000000..fa0f0c1 --- /dev/null +++ b/subPackages/sub_business_shared/components/CallLogDrawer.vue @@ -0,0 +1,550 @@ +