From 6846ea3e0fa9c8a63ef583a09f6fbf50281255fe Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Mon, 6 Jul 2026 00:37:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8A=9F=E8=83=BD=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/health/activity/addEdit.vue | 20 ++++++- src/pages/health/activity/list.vue | 7 --- src/pages/health/doctorRecord/addEdit.vue | 20 ++++++- src/pages/health/doctorRecord/costList.vue | 66 +++++++++++++++++++--- src/pages/health/medicineBasic/addEdit.vue | 20 ++++++- src/styles/health-common.scss | 10 +++- src/utils/upload.ts | 11 +++- 7 files changed, 132 insertions(+), 22 deletions(-) diff --git a/src/pages/health/activity/addEdit.vue b/src/pages/health/activity/addEdit.vue index 36f4f8a..8883df0 100644 --- a/src/pages/health/activity/addEdit.vue +++ b/src/pages/health/activity/addEdit.vue @@ -376,12 +376,26 @@ function chooseImage() { proxy.$refs['uToast'].show({ message: '正在上传中,请稍候', type: 'warning' }) return } + // H5 端传 camera 会触发 getUserMedia,可能卡死;微信小程序/APP 需要相机选项 + let sourceType + // #ifdef H5 + sourceType = ['album'] + // #endif + // #ifndef H5 + sourceType = ['album', 'camera'] + // #endif uni.chooseImage({ count: 9 - attachmentList.value.length, sizeType: ['compressed'], - sourceType: ['album', 'camera'], + sourceType, success: (res) => { uploadImages(res.tempFilePaths, 0) + }, + fail: (err) => { + // 用户取消选择不走 success,也不会报错,但某些浏览器会触发 fail + if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) { + proxy.$refs['uToast'].show({ message: '选择图片失败', type: 'warning' }) + } } }) } @@ -392,7 +406,9 @@ function uploadImages(paths, index) { return } uploading.value = true - uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: true }) + // H5 端 mask: true 会让页面无法点击,一旦上传出问题会导致用户被“锁住” + // 这里改为 mask: false,允许用户取消(会提示正在上传) + uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: false }) uploadFile({ filePath: paths[index], name: 'file' diff --git a/src/pages/health/activity/list.vue b/src/pages/health/activity/list.vue index 2e0e283..1f87820 100644 --- a/src/pages/health/activity/list.vue +++ b/src/pages/health/activity/list.vue @@ -523,13 +523,6 @@ function previewAttachment(urls, index) { font-weight: 600; } -// 活动特有 - 汇总按钮 -.operate .btn-summary { - background: rgba(19, 194, 194, 0.1); - color: #13c2c2; - border: 1rpx solid rgba(19, 194, 194, 0.3); -} - .attachment-list { display: flex; flex-wrap: wrap; diff --git a/src/pages/health/doctorRecord/addEdit.vue b/src/pages/health/doctorRecord/addEdit.vue index 66b80b6..eb0c3b5 100644 --- a/src/pages/health/doctorRecord/addEdit.vue +++ b/src/pages/health/doctorRecord/addEdit.vue @@ -432,12 +432,26 @@ function chooseImage() { proxy.$refs['uToast'].show({ message: '正在上传中,请稍候', type: 'warning' }) return } + // H5 端传 camera 会触发 getUserMedia,可能卡死;微信小程序/APP 需要相机选项 + let sourceType + // #ifdef H5 + sourceType = ['album'] + // #endif + // #ifndef H5 + sourceType = ['album', 'camera'] + // #endif uni.chooseImage({ count: 9 - attachmentList.value.length, sizeType: ['compressed'], - sourceType: ['album', 'camera'], + sourceType, success: (res) => { uploadImages(res.tempFilePaths, 0) + }, + fail: (err) => { + // 用户取消选择不走 success,也不会报错,但某些浏览器会触发 fail + if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) { + proxy.$refs['uToast'].show({ message: '选择图片失败', type: 'warning' }) + } } }) } @@ -449,7 +463,9 @@ function uploadImages(paths, index) { return } uploading.value = true - uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: true }) + // H5 端 mask: true 会让页面无法点击,一旦上传出问题会导致用户被“锁住” + // 这里改为 mask: false,允许用户取消(会提示正在上传) + uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: false }) uploadFile({ filePath: paths[index], name: 'file' diff --git a/src/pages/health/doctorRecord/costList.vue b/src/pages/health/doctorRecord/costList.vue index 8047d87..005e352 100644 --- a/src/pages/health/doctorRecord/costList.vue +++ b/src/pages/health/doctorRecord/costList.vue @@ -221,6 +221,35 @@ function handleDelete(item) { }) } +function buildCostDetailSummary() { + if (!listData.value.length) return '' + + // 按费用类型分组汇总 + const groupMap = new Map() + listData.value.forEach(item => { + const typeKey = item.type || '__other__' + const typeLabel = item.type ? (dictStr(item.type, costTypeList.value) || '未分类') : '未分类' + if (!groupMap.has(typeKey)) { + groupMap.set(typeKey, { label: typeLabel, total: 0, count: 0 }) + } + const g = groupMap.get(typeKey) + g.total += Number(item.totalCost || 0) + g.count += 1 + }) + + // 拼接汇总字符串,每行一个类型,末行合计 + const lines = [] + let grandTotal = 0 + let grandCount = 0 + groupMap.forEach(g => { + lines.push(`${g.label}:${g.total.toFixed(2)}元(${g.count}笔)`) + grandTotal += g.total + grandCount += g.count + }) + lines.push(`合计:${grandTotal.toFixed(2)}元(${grandCount}笔)`) + return lines.join('\n') +} + function handleUpdateCost() { if (!doctorRecord.value.id) { uni.showToast({ @@ -229,15 +258,38 @@ function handleUpdateCost() { }) return } - updateDoctorRecord({ - ...doctorRecord.value, - totalCost: Number(detailTotalCost.value) - }).then(() => { - doctorRecord.value.totalCost = Number(detailTotalCost.value) + if (!listData.value.length) { uni.showToast({ - title: '汇总成功', - icon: 'success' + title: '暂无费用明细,无法汇总', + icon: 'none' }) + return + } + const total = Number(detailTotalCost.value) + const costDetail = buildCostDetailSummary() + + // 会覆盖主表原有的费用明细字段,所以弹窗确认 + uni.showModal({ + title: '确认汇总', + content: `总费用:${total.toFixed(2)}元\n费用明细将按类型汇总并覆盖原有内容,是否继续?`, + confirmText: '确认汇总', + cancelText: '取消', + success: function (res) { + if (res.confirm) { + updateDoctorRecord({ + ...doctorRecord.value, + totalCost: total, + costDetail: costDetail + }).then(() => { + doctorRecord.value.totalCost = total + doctorRecord.value.costDetail = costDetail + uni.showToast({ + title: '汇总成功', + icon: 'success' + }) + }) + } + } }) } diff --git a/src/pages/health/medicineBasic/addEdit.vue b/src/pages/health/medicineBasic/addEdit.vue index 7f81b5a..b98688d 100644 --- a/src/pages/health/medicineBasic/addEdit.vue +++ b/src/pages/health/medicineBasic/addEdit.vue @@ -592,12 +592,26 @@ function chooseImage() { proxy.$refs['uToast'].show({ message: '正在上传中,请稍候', type: 'warning' }) return } + // H5 端传 camera 会触发 getUserMedia,可能卡死;微信小程序/APP 需要相机选项 + let sourceType + // #ifdef H5 + sourceType = ['album'] + // #endif + // #ifndef H5 + sourceType = ['album', 'camera'] + // #endif uni.chooseImage({ count: 9 - attachmentList.value.length, sizeType: ['compressed'], - sourceType: ['album', 'camera'], + sourceType, success: (res) => { uploadImages(res.tempFilePaths, 0) + }, + fail: (err) => { + // 用户取消选择不走 success,也不会报错,但某些浏览器会触发 fail + if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) { + proxy.$refs['uToast'].show({ message: '选择图片失败', type: 'warning' }) + } } }) } @@ -609,7 +623,9 @@ function uploadImages(paths, index) { return } uploading.value = true - uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: true }) + // H5 端 mask: true 会让页面无法点击,一旦上传出问题会导致用户被“锁住” + // 这里改为 mask: false,允许用户取消(会提示正在上传) + uni.showLoading({ title: `上传中 ${index + 1}/${paths.length}`, mask: false }) uploadFile({ filePath: paths[index], name: 'file' diff --git a/src/styles/health-common.scss b/src/styles/health-common.scss index 5456cce..0955b13 100644 --- a/src/styles/health-common.scss +++ b/src/styles/health-common.scss @@ -520,7 +520,8 @@ page { .btn-edit, .btn-delete, .btn-copy, - .btn-detail { + .btn-detail, + .btn-summary { display: flex; align-items: center; justify-content: center; @@ -560,6 +561,13 @@ page { color: $health-info; border: 1rpx solid $health-info-border; } + + /* 汇总按钮:与 btn-detail 同一色调(信息色),避免各页面重复定义 */ + .btn-summary { + background: $health-info-bg; + color: $health-info; + border: 1rpx solid $health-info-border; + } } } diff --git a/src/utils/upload.ts b/src/utils/upload.ts index e074689..cf6073a 100644 --- a/src/utils/upload.ts +++ b/src/utils/upload.ts @@ -31,7 +31,16 @@ const upload = (config: RequestUploadConfig): Promise> => { header: config.header, formData: config.formData, success: (res) => { - let result = JSON.parse(res.data) + let result + // 后端可能返回 JSON 字符串(正常)或 HTML 错误页(如 413 Payload Too Large) + // 必须包 try-catch,否则 JSON.parse 招错后 Promise 永远不 settle,mask loading 永不关闭 + try { + result = JSON.parse(res.data) + } catch (e) { + toast('上传失败:服务器响应非 JSON 格式(可能文件过大)') + reject('上传失败:响应解析失败') + return + } const code = result.code || 200 // @ts-ignore const msg = errorCode[code] || result.msg || errorCode['default']