From bd080674bfd1babf3c13e1488874eeaf581ed256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=90=A6?= Date: Wed, 23 Sep 2026 16:22:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=AD=E9=9F=B3=E5=BC=80=E6=96=B9?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E5=A4=8D=E5=88=86=E8=B4=A6=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E3=80=81=E8=AF=86=E5=9B=BE=E6=88=90=E6=96=B9=E3=80=81=E8=AF=86?= =?UTF-8?q?=E5=9B=BE=E4=BC=A0=E6=96=B9=E3=80=81=E4=BC=98=E5=8C=96=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E5=8D=A1=E7=89=87=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/Code-Standards.md | 2 +- .cursor/rules/Code-Standards.mdc | 2 +- .cursor/rules/Page-Container-Drawer.mdc | 95 + api/businessApi.js | 4 + api/clinicAdmin.js | 5 + api/imageTransferOrder.js | 220 ++ api/prescriptionImageOcr.js | 41 + api/reception.js | 19 +- api/upload.js | 43 + pages.json | 48 +- pages/workbench/index.vue | 127 +- .../components/CallLogDrawer.vue | 48 +- .../components/UserPatientDetail.vue | 72 +- .../components/PrescriptionDetailPopup.vue | 45 +- .../sub_business_shared/withdrawal/apply.vue | 46 +- .../withdrawal/common/withdrawalRules.js | 81 + .../components/WithdrawalRulesDrawer.vue | 404 ++++ .../sub_business_shared/withdrawal/index.vue | 40 +- .../components/PrescriptionDetailPopup.vue | 45 +- .../sub_clinic_admin/withdrawal/apply.vue | 45 +- .../sub_clinic_admin/withdrawal/index.vue | 39 +- .../components/TransferDetailDrawer.vue | 50 +- .../sub_clinic_salesperson/my/index.vue | 3 +- .../sub_image_transfer/common/display.js | 50 + .../common/processRuleCascade.js | 144 ++ .../components/RejectReasonDrawer.vue | 258 +++ .../components/TransferDoctorPicker.vue | 211 ++ .../components/TransferPrescriptionPad.vue | 481 ++++ .../sub_image_transfer/order-detail.vue | 1808 +++++++++++++++ subPackages/sub_image_transfer/order-list.vue | 847 +++++++ subPackages/sub_image_transfer/upload.vue | 271 +++ .../utils/transferPrescriptionImport.js | 7 +- subPackages/sub_patient/patient_detail.vue | 9 +- subPackages/sub_platform_admin/my/index.vue | 3 +- subPackages/sub_salesperson/my/index.vue | 3 +- .../claim-qrcode-modal/claim-qrcode-modal.vue | 72 +- .../proxy-pay-qrcode-modal.vue | 5 +- .../components}/vip-gate/vip-gate.vue | 0 .../sub_workbench/create_user_patient.vue | 2 +- .../doctor_created_patient_list.vue | 2 +- .../image-transfer-prescription/index.vue | 170 ++ .../components/AiPrescriptionDrawer.vue | 341 ++- .../components/ChineseMedicineConfig.vue | 417 ++-- .../components/ImageOcrDrawer.vue | 1969 +++++++++++++++++ .../components/TransferPatientPanel.vue | 309 +++ .../modals/ChineseMedicineModal.vue | 56 +- .../components/modals/DiagnosisModal.vue | 26 +- .../sub_workbench/prescription_v2/index.vue | 900 ++++++-- .../utils/alignChineseDrugPrice.js | 143 ++ .../public_account_lock/index.vue | 35 +- .../sub_workbench/workbench_infoPatient.vue | 4 +- utils/imageTransferQuote.js | 62 + utils/vip.js | 2 +- xaa | 62 + 54 files changed, 9664 insertions(+), 529 deletions(-) create mode 100644 .cursor/rules/Page-Container-Drawer.mdc create mode 100644 api/imageTransferOrder.js create mode 100644 api/prescriptionImageOcr.js create mode 100644 subPackages/sub_business_shared/withdrawal/common/withdrawalRules.js create mode 100644 subPackages/sub_business_shared/withdrawal/components/WithdrawalRulesDrawer.vue create mode 100644 subPackages/sub_image_transfer/common/display.js create mode 100644 subPackages/sub_image_transfer/common/processRuleCascade.js create mode 100644 subPackages/sub_image_transfer/components/RejectReasonDrawer.vue create mode 100644 subPackages/sub_image_transfer/components/TransferDoctorPicker.vue create mode 100644 subPackages/sub_image_transfer/components/TransferPrescriptionPad.vue create mode 100644 subPackages/sub_image_transfer/order-detail.vue create mode 100644 subPackages/sub_image_transfer/order-list.vue create mode 100644 subPackages/sub_image_transfer/upload.vue rename {components => subPackages/sub_workbench/components}/claim-qrcode-modal/claim-qrcode-modal.vue (70%) rename {components => subPackages/sub_workbench/components}/proxy-pay-qrcode-modal/proxy-pay-qrcode-modal.vue (96%) rename {components => subPackages/sub_workbench/components}/vip-gate/vip-gate.vue (100%) create mode 100644 subPackages/sub_workbench/image-transfer-prescription/index.vue create mode 100644 subPackages/sub_workbench/prescription_v2/components/ImageOcrDrawer.vue create mode 100644 subPackages/sub_workbench/prescription_v2/components/TransferPatientPanel.vue create mode 100644 subPackages/sub_workbench/prescription_v2/utils/alignChineseDrugPrice.js create mode 100644 utils/imageTransferQuote.js create mode 100644 xaa diff --git a/.cursor/rules/Code-Standards.md b/.cursor/rules/Code-Standards.md index 600712f..bae60c7 100644 --- a/.cursor/rules/Code-Standards.md +++ b/.cursor/rules/Code-Standards.md @@ -6,7 +6,7 @@ alwaysApply: true 1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则 2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行 3. 有封装好的方法、组件需要复用,不要重复造轮子 -4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show) +4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show);**禁止 v-if 与 :show 同绑**,开/关拆分与 350ms 卸挂载对齐切换账号,详见 `Page-Container-Drawer.mdc` 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 再绑定 diff --git a/.cursor/rules/Code-Standards.mdc b/.cursor/rules/Code-Standards.mdc index 49395ef..5597283 100644 --- a/.cursor/rules/Code-Standards.mdc +++ b/.cursor/rules/Code-Standards.mdc @@ -6,7 +6,7 @@ alwaysApply: true 1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则 2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行 3. 有封装好的方法、组件需要复用,不要重复造轮子 -4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show) +4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面(记得使用v-if而不是v-show);**禁止 v-if 与 :show 同绑**,开/关拆分与 350ms 卸挂载对齐切换账号,详见 `Page-Container-Drawer.mdc` 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 再绑定 diff --git a/.cursor/rules/Page-Container-Drawer.mdc b/.cursor/rules/Page-Container-Drawer.mdc new file mode 100644 index 0000000..7128f12 --- /dev/null +++ b/.cursor/rules/Page-Container-Drawer.mdc @@ -0,0 +1,95 @@ +--- +description: 小程序抽屉 page-container 返回拦截(对齐切换账号 / 工作台切换诊所) +globs: **/*.{vue,js} +alwaysApply: false +--- + +# 小程序抽屉返回拦截(强制) + +标杆实现: + +- **切换账号**:`components/account-switch/account-switch-panel.vue` +- **工作台切换诊所**:`pages/workbench/index.vue`(门店选择 `storePageGuard` + `openShow`) + +新增/改抽屉、底部弹层、认领码等凡需拦返回的,**必须**按本规则,禁止再 `v-if` 与 `:show` 绑同一个变量。 + +## 核心原则 + +1. 微信**同一页只能有一个** `page-container`;多抽屉必须互斥(先卸再挂,或等约 350ms)。 +2. **挂载**与**可见**拆开: + - `pageGuard`(或 `storePageGuard`)→ `v-if`(控制是否挂载) + - `visible` / `panelShow` / `openShow` → `:show`(控制是否展示) +3. 必须用 `v-if`,禁止 `v-show` 控制 `page-container`。 +4. 关抽屉:先 `:show = false` 播离场,再 **约 350ms**(或 `@afterleave`)后才 `pageGuard = false` 卸节点。 +5. 开抽屉:先 `pageGuard = true`,`$nextTick` 后再 `:show = true`。 + +## 标准写法(对齐切换账号) + +```vue + + + +``` + +```js +// 打开 +open() { + this.clearReleaseTimer(); + this.pageGuard = true; + this.$nextTick(() => { + this.visible = true; + }); +}, +// 关闭 +close() { + this.visible = false; + this.scheduleReleaseGuard(); // 350ms 后再 pageGuard=false +}, +releasePageGuard() { + this.clearReleaseTimer(); + this.pageGuard = false; +}, +scheduleReleaseGuard() { + this.clearReleaseTimer(); + this.releaseTimer = setTimeout(() => { + this.pageGuard = false; + this.releaseTimer = null; + }, 350); +}, +``` + +## 禁止 + +```vue + + + + + +``` + +## 同页多抽屉互斥(工作台切换诊所已落地) + +打开 A 前必须先关掉 B 的 `page-container`: + +- 关本层:可先 `show=false` 再 `scheduleReleaseGuard(350)`。 +- **立刻要挂另一层**:必须 **同步** `pageGuard = false`(不要等 350ms),或等对方卸完再挂(约 360ms)。 +- 参考:`openStoreSelect` / `openApptDrawer` / `onBriefPopupChange`(工作台)。 + +## 组件内嵌套两层抽屉(如患者详情 + 回访) + +同一组件内也只能保留一个 `page-container`: + +1. 开内层前:外层 `panelShow=false` 且 **立刻** `pageGuard=false`(勿只靠 afterleave,晚到会冲掉恢复后的 guard)。 +2. 关内层后:再 `pageGuard=true` → `$nextTick` → `panelShow=true`。 +3. 恢复逻辑加防重入,避免 `update:visible` + `close` 双事件恢复两次。 + +## 与 Code-Standards 的关系 + +`Code-Standards.mdc` 第 4 条要求抽屉必须配 `page-container`;**具体拆分与 350ms 卸挂载以本规则 + 切换账号组件为准**。 diff --git a/api/businessApi.js b/api/businessApi.js index 6801ad5..3af3ce6 100644 --- a/api/businessApi.js +++ b/api/businessApi.js @@ -73,6 +73,10 @@ export function createBusinessApi(prefix, headerFlag) { getWithdrawableOrders(params) { return bizRequest({ url: `${prefix}-withdrawal/withdrawable-orders`, method: 'GET', data: params || {} }); }, + /** 提现规则文案 xk-api GET /{prefix}-withdrawal/rules */ + getWithdrawalRules() { + return bizRequest({ url: `${prefix}-withdrawal/rules`, method: 'GET', data: {} }); + }, passWithdrawal(data) { return bizRequest({ url: `${prefix}-withdrawal/pass-application`, method: 'POST', data }); }, diff --git a/api/clinicAdmin.js b/api/clinicAdmin.js index ec160ac..db58dc4 100644 --- a/api/clinicAdmin.js +++ b/api/clinicAdmin.js @@ -183,6 +183,11 @@ export function getWithdrawableOrders(params) { return clinicRequest({ url: `${PREFIX}-withdrawal/withdrawable-orders`, method: 'GET', data: params || {} }); } +/** 提现规则文案 xk-api GET /clinic-admin-withdrawal/rules */ +export function getWithdrawalRules() { + return clinicRequest({ url: `${PREFIX}-withdrawal/rules`, method: 'GET', data: {} }); +} + export function getReconciliationList(params) { return clinicRequest({ url: `${PREFIX}-reconciliation/list`, method: 'GET', data: params }); } diff --git a/api/imageTransferOrder.js b/api/imageTransferOrder.js new file mode 100644 index 0000000..e8830bc --- /dev/null +++ b/api/imageTransferOrder.js @@ -0,0 +1,220 @@ +/** + * 识图传方订单(xk-api) + * 医生:/image-transfer-order-wx/* 取值 unwrapXkApi + * 诊所管理员:/clinic-admin-image-transfer/* 请求头 _clinicAdmin + */ +import { req } from '@/common/js/index.js'; + +function clinicReq(options) { + return req.request({ + ...options, + header: { + ...(options.header || {}), + _clinicAdmin: '1', + }, + }); +} + +/** 平台管理员请求:带 _platformAdmin 头走平台传方接口 */ +function platformReq(options) { + return req.request({ + ...options, + header: { + ...(options.header || {}), + _platformAdmin: '1', + }, + }); +} + +/** 医生我的传方 xk-api GET /image-transfer-order-wx/list */ +export function listMyImageTransferOrders(data) { + return req.request({ + url: '/newApi/image-transfer-order-wx/list', + method: 'GET', + data, + }); +} + +/** 医生传方详情 xk-api GET /image-transfer-order-wx/detail */ +export function getMyImageTransferOrder(data) { + return req.request({ + url: '/newApi/image-transfer-order-wx/detail', + method: 'GET', + data, + }); +} + +/** 医生驳回后再交 xk-api POST /image-transfer-order-wx/resubmit */ +export function resubmitMyImageTransferOrder(data) { + return req.request({ + url: '/newApi/image-transfer-order-wx/resubmit', + method: 'POST', + data, + }); +} + +/** 诊所识图 OCR xk-api POST /clinic-admin-image-transfer/ocr-prescription-image */ +export function clinicOcrPrescriptionImage(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/ocr-prescription-image', + method: 'POST', + data, + }); +} + +/** 诊所药库搜索 xk-api GET /clinic-admin-image-transfer/product-list */ +export function clinicSearchTransferDrugs(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/product-list', + method: 'GET', + data, + }); +} + +/** 诊所提交识图传方 xk-api POST /clinic-admin-image-transfer/confirm */ +export function clinicConfirmImageTransfer(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/confirm', + method: 'POST', + data, + }); +} + +/** 诊所识图传方匹配就诊人 xk-api POST /clinic-admin-image-transfer/match-transfer-patient */ +export function clinicMatchTransferPatient(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/match-transfer-patient', + method: 'POST', + data, + }); +} + +/** 诊所识图历史列表 xk-api GET /clinic-admin-image-transfer/ai-list-generations */ +export function clinicAiListGenerations(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/ai-list-generations', + method: 'GET', + data, + }); +} + +/** 诊所识图历史详情 xk-api GET /clinic-admin-image-transfer/ai-generation-detail */ +export function clinicAiGenerationDetail(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/ai-generation-detail', + method: 'GET', + data, + }); +} + +/** 诊所传方列表 xk-api GET /clinic-admin-image-transfer/list */ +export function listStoreImageTransferOrders(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/list', + method: 'GET', + data, + }); +} + +/** 诊所传方详情 xk-api GET /clinic-admin-image-transfer/detail */ +export function getStoreImageTransferOrder(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/detail', + method: 'GET', + data, + }); +} + +/** 诊所驳回后再交 xk-api POST /clinic-admin-image-transfer/resubmit */ +export function resubmitStoreImageTransferOrder(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/resubmit', + method: 'POST', + data, + }); +} + +/** 医生传方报价预览 xk-api POST /image-transfer-order-wx/quote */ +export function quoteMyImageTransferOrder(data) { + return req.request({ + url: '/newApi/image-transfer-order-wx/quote', + method: 'POST', + data, + }); +} + +/** 诊所传方报价预览 xk-api POST /clinic-admin-image-transfer/quote */ +export function quoteStoreImageTransferOrder(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/quote', + method: 'POST', + data, + }); +} + +/** + * 诊所委托调剂规则级联列表 + * xk-api GET /clinic-admin-image-transfer/process-rule-list + * 入参与医生端一致:pid 拉下级,rule_id 拉备注 + */ +export function clinicProcessRuleList(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/process-rule-list', + method: 'GET', + data, + }); +} + +/** 平台传方列表 xk-api GET /platform-admin-image-transfer/list */ +export function listPlatformImageTransferOrders(data) { + return platformReq({ + url: '/newApi/platform-admin-image-transfer/list', + method: 'GET', + data, + }); +} + +/** 平台传方详情 xk-api GET /platform-admin-image-transfer/detail */ +export function getPlatformImageTransferOrder(data) { + return platformReq({ + url: '/newApi/platform-admin-image-transfer/detail', + method: 'GET', + data, + }); +} + +/** 平台审方通过 xk-api POST /platform-admin-image-transfer/approve */ +export function approvePlatformImageTransferOrder(data) { + return platformReq({ + url: '/newApi/platform-admin-image-transfer/approve', + method: 'POST', + data, + }); +} + +/** 平台审方驳回 xk-api POST /platform-admin-image-transfer/reject */ +export function rejectPlatformImageTransferOrder(data) { + return platformReq({ + url: '/newApi/platform-admin-image-transfer/reject', + method: 'POST', + data, + }); +} + +/** 平台失败重试推送 xk-api POST /platform-admin-image-transfer/retry */ +export function retryPlatformImageTransferOrder(data) { + return platformReq({ + url: '/newApi/platform-admin-image-transfer/retry', + method: 'POST', + data, + }); +} + +/** 诊所本店医生选项 xk-api GET /clinic-admin-image-transfer/doctor-option */ +export function clinicTransferDoctorOption(data) { + return clinicReq({ + url: '/newApi/clinic-admin-image-transfer/doctor-option', + method: 'GET', + data, + }); +} diff --git a/api/prescriptionImageOcr.js b/api/prescriptionImageOcr.js new file mode 100644 index 0000000..e4b6fde --- /dev/null +++ b/api/prescriptionImageOcr.js @@ -0,0 +1,41 @@ +/** + * 处方图 OCR / 识图传方(xk-api) + * 取值:unwrapXkApi → code / result / message + */ +import { req } from '@/common/js/index.js'; + +/** 处方图 OCR xk-api POST /doctor-reception-wx/ocr-prescription-image */ +export function ocrPrescriptionImageApi(data) { + return req.request({ + url: '/newApi/doctor-reception-wx/ocr-prescription-image', + method: 'POST', + data, + }); +} + +/** 确认识图传方 xk-api POST /doctor-reception-wx/confirm-transfer-image-ocr */ +export function confirmTransferImageOcrApi(data) { + return req.request({ + url: '/newApi/doctor-reception-wx/confirm-transfer-image-ocr', + method: 'POST', + data, + }); +} + +/** 识图传方匹配就诊人 xk-api POST /doctor-reception-wx/match-transfer-patient */ +export function matchTransferPatientApi(data) { + return req.request({ + url: '/newApi/doctor-reception-wx/match-transfer-patient', + method: 'POST', + data, + }); +} + +/** 医生导入传方准备 xk-api GET /transfer-prescription/prepare-doctor-import */ +export function prepareDoctorImportApi(transferId, registerId) { + return req.request({ + url: '/newApi/transfer-prescription/prepare-doctor-import', + method: 'GET', + data: { id: transferId, register_id: registerId }, + }); +} diff --git a/api/reception.js b/api/reception.js index c2fbbbe..8211875 100644 --- a/api/reception.js +++ b/api/reception.js @@ -189,6 +189,19 @@ export function getProductListDoctorReception(data) { }) } +/** + * 复用/常用方填方前对比中药快照价与当前门店价 + * 为什么:供货比例变更后关系表已更新,价格不一致时要让医生确认是否按最新价计算 + * xk-api POST /doctor-reception-wx/compare-chinese-drug-prices + */ +export function compareChineseDrugPricesApi(data) { + return req.request({ + url: '/newApi/doctor-reception-wx/compare-chinese-drug-prices', + method: 'POST', + data + }) +} + /** 中药全局快捷克数(与药品无关,每次打开中药抽屉拉取) */ export function getChineseMedicineQuickGramsApi() { return req.request({ @@ -622,7 +635,7 @@ export function aiGeneratePrescriptionApi(data) { /** * 语音转写(multipart:字段名 audio) - * @param {{ filePath: string, duration_ms?: number }} data + * @param {{ filePath: string, duration_ms?: number, audio_format?: string }} data */ export function aiVoiceAsrApi(data) { return req.request({ @@ -632,7 +645,9 @@ export function aiVoiceAsrApi(data) { filePath: data.filePath, name: 'audio', formData: { - duration_ms: String(data.duration_ms || 0) + duration_ms: String(data.duration_ms || 0), + // 显式声明:服务端优先采信,避免 MIME/扩展名误杀 + audio_format: String(data.audio_format || 'wav') } }) } diff --git a/api/upload.js b/api/upload.js index 4a58e83..340f2fa 100644 --- a/api/upload.js +++ b/api/upload.js @@ -204,6 +204,7 @@ const LEGACY_ADMIN_UPLOAD_URL = 'https://api.xiaokang88.com/open-api/upload/old- /** * 录入/管理端旧版图片上传(与 d-upload、select-avatar-image 一致) + * 注意:只认医生态 token;诊所管理员不要走这条 */ export function uploadLegacyAdminImage(params) { const { filePath, name = 'file', formData = { store_id: '11001' } } = params || {}; @@ -238,3 +239,45 @@ export function uploadLegacyAdminImage(params) { }); }); } + +/** + * 识图传方选图上传:按 loginMode 选接口 + * 诊所管理员必须走 clinic-admin-upload(带 _clinicAdmin + clinic_admin_token), + * 不能用旧 Yii + 医生态 token,否则会一直「上传失败」 + * @param {{ filePath: string }} params + * @returns {Promise} 可访问的图片 URL + */ +export async function uploadOcrImageByLoginMode(params) { + const filePath = params && params.filePath; + if (!filePath) { + throw new Error('缺少文件路径'); + } + const mode = uni.getStorageSync('loginMode'); + if (mode === 'clinic_admin') { + const res = await req.request({ + url: `${CLINIC_ADMIN_PREFIX}image`, + type: 'upload', + method: 'POST', + filePath, + name: 'file', + formData: {}, + header: { _clinicAdmin: '1' }, + }); + let body = res; + if (typeof body === 'string') { + try { + body = JSON.parse(body); + } catch (e) { + body = null; + } + } + // xk-api:code=0,url 在 result + if (body && Number(body.code) === 0 && body.result && body.result.url) { + return String(body.result.url); + } + const viaExtract = extractUploadUrl(body); + if (viaExtract) return String(viaExtract); + throw new Error((body && body.message) || '上传失败'); + } + return uploadLegacyAdminImage({ filePath }); +} diff --git a/pages.json b/pages.json index b663c68..bc816e9 100644 --- a/pages.json +++ b/pages.json @@ -4,8 +4,8 @@ "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue", "^d-(.*)": "@/components/d-$1/d-$1.vue", "^ai-generation-loading$": "@/components/ai-generation-loading/ai-generation-loading.vue", - "^proxy-pay-qrcode-modal$": "@/components/proxy-pay-qrcode-modal/proxy-pay-qrcode-modal.vue", - "^claim-qrcode-modal$": "@/components/claim-qrcode-modal/claim-qrcode-modal.vue" + "^proxy-pay-qrcode-modal$": "@/subPackages/sub_workbench/components/proxy-pay-qrcode-modal/proxy-pay-qrcode-modal.vue", + "^claim-qrcode-modal$": "@/subPackages/sub_workbench/components/claim-qrcode-modal/claim-qrcode-modal.vue" }, "pages": [{ "path": "pages/login/index", @@ -69,8 +69,8 @@ "path": "workbench_upInfo/index", "style": { "navigationBarTitleText": "上传资料", - "enablePullDownRefresh ": false, //此设置 是否开启当前页面下拉刷新。 - "disableScroll": false //此设置 是否允许页面整体上下滚动。 + "enablePullDownRefresh": false, + "disableScroll": false } }, @@ -141,6 +141,12 @@ } } }, + { + "path": "image-transfer-prescription/index", + "style": { + "navigationBarTitleText": "识图传方" + } + }, { "path": "workbench_code", "style": { @@ -512,6 +518,40 @@ "enablePullDownRefresh": false } }] + }, { + "root": "subPackages/sub_image_transfer", + "pages": [{ + "path": "upload", + "style": { + "navigationBarTitleText": "识图传方", + "usingComponents": { + "image-ocr-drawer": "/subPackages/sub_workbench/prescription_v2/components/ImageOcrDrawer" + }, + "componentPlaceholder": { + "image-ocr-drawer": "view" + } + } + }, { + "path": "order-list", + "style": { + "navigationBarTitleText": "传方订单", + "backgroundColor": "#E8F6F4", + "backgroundTextStyle": "dark" + } + }, { + "path": "order-detail", + "style": { + "navigationBarTitleText": "传方详情", + "usingComponents": { + "chinese-medicine-config": "/subPackages/sub_workbench/prescription_v2/components/ChineseMedicineConfig", + "chinese-medicine-modal": "/subPackages/sub_workbench/prescription_v2/components/modals/ChineseMedicineModal" + }, + "componentPlaceholder": { + "chinese-medicine-config": "view", + "chinese-medicine-modal": "view" + } + } + }] } ], "tabBar": { diff --git a/pages/workbench/index.vue b/pages/workbench/index.vue index c2d2b23..aa042fc 100644 --- a/pages/workbench/index.vue +++ b/pages/workbench/index.vue @@ -8,7 +8,7 @@ - + {{doctorInfo.store.store.name || '默认门店'}} @@ -164,6 +164,28 @@ > + + + + {{ imageTransferEntry.name }} + 拍照识方,按配置进接诊或提交传方 + + + + + + + {{ imageTransferOrderEntry.name }} + 查看自己上传的传方订单 + + + @@ -392,8 +414,17 @@ - - + + + + + 选择诊所 @@ -413,7 +444,7 @@ - 取消 + 取消 确定 @@ -489,7 +520,18 @@ const ONLINE_RECEPTION_ENTRY = { icon: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-doctor-wx/static/image/xsfz.png', url: '../../subPackages/sub_online_reception/pages/list', }; -const RECEPTION_GRID_ENTRIES = [OFFLINE_RECEPTION_ENTRY, ONLINE_RECEPTION_ENTRY]; +/** 医生识图传方:上传处方图 → OCR → 待导入传方 → 接诊中药回填 */ +const IMAGE_TRANSFER_ENTRY = { + name: '识图传方', + icon: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-doctor-wx/static/image/zxzx.png', + url: '../../subPackages/sub_workbench/image-transfer-prescription/index', +}; +const IMAGE_TRANSFER_ORDER_ENTRY = { + name: '我的传方', + icon: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-doctor-wx/static/image/zxzx.png', + url: '../../subPackages/sub_image_transfer/order-list?scope=mine', +}; +const RECEPTION_GRID_ENTRIES = [OFFLINE_RECEPTION_ENTRY, ONLINE_RECEPTION_ENTRY, IMAGE_TRANSFER_ENTRY, IMAGE_TRANSFER_ORDER_ENTRY]; const EMPTY_WORKBENCH_STATS = { prescription_count: 0, wait_accept: 0, @@ -514,6 +556,8 @@ export default { }, receptionGridEntries: RECEPTION_GRID_ENTRIES, offlineReceptionEntry: OFFLINE_RECEPTION_ENTRY, + imageTransferEntry: IMAGE_TRANSFER_ENTRY, + imageTransferOrderEntry: IMAGE_TRANSFER_ORDER_ENTRY, canUseOnlineConsultation: false, onlineConsultationConfigReady: false, states: { @@ -543,6 +587,9 @@ export default { page: 1, totalPage: 1, openShow: false, + /** 门店选择 page-container 挂载态,与 openShow 拆开 */ + storePageGuard: false, + storeReleaseTimer: null, keyword: "", store: "", storeList: [], @@ -625,6 +672,7 @@ export default { }, onUnload() { this.stopRefreshTimer(); + this.clearStoreReleaseTimer(); if (this.__doctorImRoomUpdateHandler) { uni.$off('doctor-im-room-update', this.__doctorImRoomUpdateHandler); this.__doctorImRoomUpdateHandler = null; @@ -831,7 +879,7 @@ export default { this.store_id = uni.getStorageSync('store_id') } - this.openShow = false + this.closeStoreSelect() this.onlineConsultationConfigReady = false this.refreshWorkbenchData() }) @@ -892,13 +940,76 @@ export default { if (Number.isNaN(num)) return '0.000' return num.toFixed(3) }, - /** AI 简报弹层开合:打开时隐藏自定义 tabbar,避免抽屉底部「知道了」被挡住 */ + /** AI 简报弹层开合:打开时隐藏自定义 tabbar;与门店/预约抽屉互斥 */ onBriefPopupChange(show) { this.briefPopupShow = !!show; + if (show) { + // 简报自带 page-container:开之前立刻卸门店拦截,不能等 350ms + this.openShow = false; + this.clearStoreReleaseTimer(); + this.storePageGuard = false; + this.apptDrawerShow = false; + } }, - /** 打开未来7天预约明细抽屉;date 为空看全部,点某天卡格预筛选该天 */ + /** + * 打开门店选择:先关预约抽屉/简报,再挂 page-container + * 为什么:微信同页只能一个 page-container,且门店弹层原先无返回拦截 + */ + openStoreSelect() { + // 预约/简报关后仍留约 350ms pageGuard,需等卸完再挂门店,否则同页双 page-container + const waitMs = (this.apptDrawerShow || this.briefPopupShow) ? 360 : 0; + this.apptDrawerShow = false; + if (this.briefPopupShow && this.$refs.aiBrief && this.$refs.aiBrief.closePopup) { + this.$refs.aiBrief.closePopup(); + } + const open = () => { + this.clearStoreReleaseTimer(); + this.storePageGuard = true; + this.$nextTick(() => { + this.openShow = true; + }); + }; + if (waitMs > 0) { + setTimeout(open, waitMs); + } else { + open(); + } + }, + /** 关闭门店选择:先卸 show,再延迟卸挂载 */ + closeStoreSelect() { + this.openShow = false; + this.scheduleStoreReleaseGuard(); + }, + releaseStorePageGuard() { + this.clearStoreReleaseTimer(); + this.storePageGuard = false; + }, + scheduleStoreReleaseGuard() { + this.clearStoreReleaseTimer(); + this.storeReleaseTimer = setTimeout(() => { + this.storePageGuard = false; + this.storeReleaseTimer = null; + }, 350); + }, + clearStoreReleaseTimer() { + if (this.storeReleaseTimer) { + clearTimeout(this.storeReleaseTimer); + this.storeReleaseTimer = null; + } + }, + /** 打开预约抽屉:立刻卸门店 pageGuard;若简报开着则等其卸完再开 */ openApptDrawer(date) { + this.openShow = false; + this.clearStoreReleaseTimer(); + this.storePageGuard = false; this.apptDefaultDate = date || ''; + if (this.briefPopupShow && this.$refs.aiBrief && this.$refs.aiBrief.closePopup) { + this.$refs.aiBrief.closePopup(); + setTimeout(() => { + this.apptDrawerShow = true; + }, 360); + return; + } this.apptDrawerShow = true; }, /** 抽屉内改期成功:预约分布/待接诊数已变化,静默刷新看板(不打断用户继续操作抽屉) */ diff --git a/subPackages/sub_business_shared/components/CallLogDrawer.vue b/subPackages/sub_business_shared/components/CallLogDrawer.vue index fa0f0c1..05e49f0 100644 --- a/subPackages/sub_business_shared/components/CallLogDrawer.vue +++ b/subPackages/sub_business_shared/components/CallLogDrawer.vue @@ -1,13 +1,14 @@