From 2a1fe884d77cb9108df2a726f99da8d6a1f4d366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=90=A6?= Date: Wed, 19 Aug 2026 16:48:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=94=81=E5=AE=9A=E8=AF=8A=E6=89=80?= =?UTF-8?q?=E3=80=81=E5=85=AC=E8=B4=A6=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/businessApi.js | 3 + api/clinicAdmin.js | 5 + api/publicAccountPay.js | 78 ++ common/js/index.js | 120 +-- .../workbench-fav-editor.vue | 2 + pages.json | 13 + .../withdrawal/account-change/index.vue | 28 +- .../sub_business_shared/withdrawal/apply.vue | 267 +++++- .../sub_business_shared/withdrawal/index.vue | 98 ++- .../withdrawal/record-detail.vue | 127 +++ .../withdrawal/settlement/index.vue | 21 +- .../withdrawal/withdrawable-orders.vue | 107 +++ subPackages/sub_clinic_admin/home/index.vue | 5 +- .../withdrawal/account-change/index.vue | 24 +- .../sub_clinic_admin/withdrawal/apply.vue | 253 +++++- .../sub_clinic_admin/withdrawal/index.vue | 25 +- .../withdrawal/record-detail.vue | 127 +++ .../withdrawal/settlement/index.vue | 17 +- .../withdrawal/withdrawable-orders.vue | 106 +++ .../sub_clinic_salesperson/home/index.vue | 3 +- subPackages/sub_platform_admin/home/index.vue | 5 +- .../sub_salesperson/doctor-input/form.vue | 1 - subPackages/sub_salesperson/home/index.vue | 3 +- .../modals/ApplyPublicAccountPayDrawer.vue | 311 +++++++ .../sub_workbench/prescription_v2/index.vue | 181 +++- .../public_account_lock/index.vue | 821 ++++++++++++++++++ .../lock-warning-decor.vue | 212 +++++ utils/publicAccountLock.js | 77 ++ 28 files changed, 2859 insertions(+), 181 deletions(-) create mode 100644 api/publicAccountPay.js create mode 100644 subPackages/sub_business_shared/withdrawal/withdrawable-orders.vue create mode 100644 subPackages/sub_clinic_admin/withdrawal/withdrawable-orders.vue create mode 100644 subPackages/sub_workbench/prescription_v2/components/modals/ApplyPublicAccountPayDrawer.vue create mode 100644 subPackages/sub_workbench/public_account_lock/index.vue create mode 100644 subPackages/sub_workbench/public_account_lock/lock-warning-decor.vue create mode 100644 utils/publicAccountLock.js diff --git a/api/businessApi.js b/api/businessApi.js index 7b6c9bf..6801ad5 100644 --- a/api/businessApi.js +++ b/api/businessApi.js @@ -70,6 +70,9 @@ export function createBusinessApi(prefix, headerFlag) { submitWithdrawal(data) { return bizRequest({ url: `${prefix}-withdrawal/withdrawal`, method: 'POST', data }); }, + getWithdrawableOrders(params) { + return bizRequest({ url: `${prefix}-withdrawal/withdrawable-orders`, method: 'GET', data: params || {} }); + }, passWithdrawal(data) { return bizRequest({ url: `${prefix}-withdrawal/pass-application`, method: 'POST', data }); }, diff --git a/api/clinicAdmin.js b/api/clinicAdmin.js index 080132b..ec160ac 100644 --- a/api/clinicAdmin.js +++ b/api/clinicAdmin.js @@ -178,6 +178,11 @@ export function submitWithdrawal(data) { return clinicRequest({ url: `${PREFIX}-withdrawal/withdrawal`, method: 'POST', data }); } +/** 可提订单明细 xk-api GET /clinic-admin-withdrawal/withdrawable-orders */ +export function getWithdrawableOrders(params) { + return clinicRequest({ url: `${PREFIX}-withdrawal/withdrawable-orders`, method: 'GET', data: params || {} }); +} + export function getReconciliationList(params) { return clinicRequest({ url: `${PREFIX}-reconciliation/list`, method: 'GET', data: params }); } diff --git a/api/publicAccountPay.js b/api/publicAccountPay.js new file mode 100644 index 0000000..9000fae --- /dev/null +++ b/api/publicAccountPay.js @@ -0,0 +1,78 @@ +/** + * 公账支付(xk-api,取值 unwrapXkApi → code / result / message) + * 医生走 /public-account-pay;诊所管理员走 /clinic-admin-public-account-pay + */ +import { req } from '@/common/js/index.js' + +function isClinicAdmin() { + return uni.getStorageSync('loginMode') === 'clinic_admin' +} + +function dailyPrefix() { + return isClinicAdmin() + ? '/newApi/clinic-admin-public-account-pay/' + : '/newApi/public-account-pay/' +} + +function dailyHeader() { + return isClinicAdmin() ? { _clinicAdmin: '1' } : {} +} + +/** 申请公账支付 xk-api POST /public-account-pay/apply */ +export function applyPublicAccountPayApi(data) { + return req.request({ + url: '/newApi/public-account-pay/apply', + method: 'POST', + data, + }) +} + +/** 日汇总详情 xk-api GET .../daily-detail */ +export function getPublicAccountDailyBillInfoApi(id) { + return req.request({ + url: dailyPrefix() + 'daily-detail', + method: 'GET', + data: { id }, + header: dailyHeader(), + }) +} + +/** 补出日汇总 xk-api POST .../daily-generate */ +export function generatePublicAccountDailyBillApi(data) { + return req.request({ + url: dailyPrefix() + 'daily-generate', + method: 'POST', + data, + header: dailyHeader(), + }) +} + +/** 上传日汇总凭证 xk-api POST .../daily-upload */ +export function uploadPublicAccountDailyBillApi(data) { + return req.request({ + url: dailyPrefix() + 'daily-upload', + method: 'POST', + data, + header: dailyHeader(), + }) +} + +/** 催办确认到账 xk-api POST .../daily-urge */ +export function urgePublicAccountDailyBillApi(data) { + return req.request({ + url: dailyPrefix() + 'daily-urge', + method: 'POST', + data, + header: dailyHeader(), + }) +} + +/** 锁店现算状态(配色/日汇总状态/联系人)xk-api GET .../lock-status */ +export function getPublicAccountLockStatusApi() { + return req.request({ + url: dailyPrefix() + 'lock-status', + method: 'GET', + data: {}, + header: dailyHeader(), + }) +} diff --git a/common/js/index.js b/common/js/index.js index ac94352..0276f23 100644 --- a/common/js/index.js +++ b/common/js/index.js @@ -3,6 +3,7 @@ import errorCode from './errorCode.js'; import {Base64} from "js-base64"; import { getDoctorWxEnv } from '@/config/app-env.js'; import { resolveDoctorWxAuthToken } from '@/utils/doctorWxAuthToken.js'; +import { setPublicAccountLock } from '@/utils/publicAccountLock.js' const { legacyApiBase } = getDoctorWxEnv(); @@ -156,6 +157,20 @@ const resInterceptor = (response, conf = {}) => { if (res == null || typeof res !== 'object') { return res } + // 4101 锁店:先解密联系人(会写出 phone_txt 再脱敏 phone),再落库给锁店页拨号 + const bizCode = Number(res.code != null ? res.code : res.errcode) + if (bizCode === 4101) { + const payload = res.result || {} + if (payload.contact_phones) { + payload.contact_phones = getRes(payload.contact_phones, true) + } + setPublicAccountLock(payload) + return { + wakaryReqToReject: true, + msg: res.message || '公账日账单已逾期', + res: response + } + } if (res.data != null) { res.data = getRes(res.data) } @@ -163,7 +178,6 @@ const resInterceptor = (response, conf = {}) => { res.result = getRes(res.result) } // Laravel notAuth 返回 HTTP 200 + 业务码 401,需与 HTTP 401 走同一套登录失效处理 - const bizCode = Number(res.code != null ? res.code : res.errcode) if (bizCode === 401) { return handleAuthExpired(res.msg || res.message || '账号需重新登录', response, conf) } @@ -222,62 +236,68 @@ function _responseLog(res, conf = {}, describe = null) { } } +/** 解密后写出 {字段}_txt 明文,给锁店拨号等场景用;原字段仍脱敏 */ +const PHONE_TXT_KEYS = ['phone', 'mobile', 'call_mobile', 'express_mobile', 'patient_mobile', 'accept_tel'] + function getRes(obj, isDecode = true) { if (obj == null || typeof obj !== 'object') { return obj } + if (Array.isArray(obj)) { + for (let i = 0; i < obj.length; i++) { + if (obj[i] != null && typeof obj[i] === 'object') { + obj[i] = getRes(obj[i], isDecode) + } + } + return obj + } try { for (const key in obj) { - if (Array.isArray(obj[key])) { - obj[key] = getRes(obj[key]) - } else if (typeof obj[key] === 'object' && obj[key] !== null) { - obj[key] = getRes(obj[key]) - } else { - // 判断obj[key]是否在arr中 - if (arr.indexOf(key) !== -1) { - if (obj[key] == null || obj[key] === '') continue - let aseFile = '' - if (isDecode === true) { - aseFile = customBase64Decode(obj[key]) - } else { - aseFile = customBase64Encode(obj[key]) - } - const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test( - typeof aseFile === 'string' ? aseFile : '' - ) - if (isGarbled) { - aseFile = obj[key] - } - if (isDecode === true) { - if (sensitiveData.indexOf(key) !== -1) { - // 数据脱敏,自动匹配姓名、手机号、身份证 - switch (key) { - case 'express_name': - case 'express_region': - case 'accept_name': - case 'patient': - // 保留前两个字符,其余用星号代替 - aseFile = aseFile.slice(0, 1).padEnd(aseFile.length, '*'); - break; - case 'mobile': - case 'express_mobile': - case 'phone': - // 保留前三位和后四位,中间用星号代替 - aseFile = aseFile.slice(0, 3).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); - break; - case 'id_card': - case 'idcard': - // 保留前六位和后四位,中间用星号代替 - aseFile = aseFile.slice(0, 6).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); - break; - default: - // 默认情况下,全部用星号代替 - break; - } - } - } - obj[key] = aseFile + if (obj[key] != null && typeof obj[key] === 'object') { + obj[key] = getRes(obj[key], isDecode) + } else if (arr.indexOf(key) !== -1) { + if (obj[key] == null || obj[key] === '') continue + let aseFile = '' + if (isDecode === true) { + aseFile = customBase64Decode(obj[key]) + } else { + aseFile = customBase64Encode(obj[key]) } + const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test( + typeof aseFile === 'string' ? aseFile : '' + ) + if (isGarbled) { + aseFile = obj[key] + } + // 请求必须加密,不能跳过;解密后先留明文 _txt 再脱敏 + if (isDecode === true && PHONE_TXT_KEYS.indexOf(key) !== -1) { + const plain = String(aseFile || '') + if (/^1\d{10}$/.test(plain)) { + obj[key + '_txt'] = plain + } + } + if (isDecode === true && sensitiveData.indexOf(key) !== -1) { + switch (key) { + case 'express_name': + case 'express_region': + case 'accept_name': + case 'patient': + aseFile = aseFile.slice(0, 1).padEnd(aseFile.length, '*'); + break; + case 'mobile': + case 'express_mobile': + case 'phone': + aseFile = aseFile.slice(0, 3).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); + break; + case 'id_card': + case 'idcard': + aseFile = aseFile.slice(0, 6).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); + break; + default: + break; + } + } + obj[key] = aseFile } } return obj diff --git a/components/workbench-fav-editor/workbench-fav-editor.vue b/components/workbench-fav-editor/workbench-fav-editor.vue index 8705932..aeaf2f8 100644 --- a/components/workbench-fav-editor/workbench-fav-editor.vue +++ b/components/workbench-fav-editor/workbench-fav-editor.vue @@ -10,11 +10,13 @@ @afterleave="releasePageGuard" /> + 时间{{ item.created_at }} 备注{{ item.remark }} 查看订单详情 @@ -87,23 +87,39 @@ export default { this.reload() }, reload() { - this.load(1, false) + return this.load(1, false) }, load(page, append) { this.loading = true const params = { page, pageSize: 15 } if (this.sourceType) params.source_type = this.sourceType if (this.orderNo.trim()) params.order_no = this.orderNo.trim() - this.bizApi.getAccountChangeLogList(params).then(w => { + return this.bizApi.getAccountChangeLogList(params).then(w => { const items = (w.data && w.data.items) || [] this.list = append ? this.list.concat(items) : items this.page = page }).finally(() => { this.loading = false }) }, + canViewChangeOrder(item) { + const orders = item.orders || [] + return (Number(item.order_id) > 0) || orders.length > 0 + }, goOrderDetail(item) { - uni.navigateTo({ - url: `${this.bizRoot}/order/detail?id=${item.order_id}`, - }) + const orders = item.orders || [] + const target = orders.length > 0 ? orders[0] : item + const orderType = Number(target.order_type || item.order_type || 0) + const orderId = target.order_id || item.order_id + if (orderType === 1 && orderId) { + uni.navigateTo({ url: `${this.bizRoot}/order/detail?id=${orderId}` }) + return + } + if (orderType === 2 && orderId) { + uni.navigateTo({ + url: `/subPackages/sub_business_shared/user-patient/register-detail?id=${orderId}`, + }) + return + } + uni.showToast({ title: '暂不支持查看该订单详情', icon: 'none' }) }, }, } diff --git a/subPackages/sub_business_shared/withdrawal/apply.vue b/subPackages/sub_business_shared/withdrawal/apply.vue index ea8c550..3e3a5b5 100644 --- a/subPackages/sub_business_shared/withdrawal/apply.vue +++ b/subPackages/sub_business_shared/withdrawal/apply.vue @@ -1,12 +1,61 @@