From fd22dc7ad41bc733e2b1dad0ef7ec95de9848b76 Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Wed, 22 Apr 2026 17:42:36 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=B7=A5=E5=8D=95bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/styles/el-ui.scss | 5 + src/template/IoT卡导入模板.xlsx | Bin 0 -> 16307 bytes src/template/设备导入模板.xlsx | Bin 0 -> 8950 bytes .../account-management/account/index.vue | 51 ++------- .../components/dialogs/DailyRecordsDialog.vue | 3 - .../composables/useAssetInfo.ts | 27 +++-- .../asset-management/device-list/index.vue | 57 +++++++--- .../iot-card-management/index.vue | 102 ++++++++++++------ .../task-management/device-task/index.vue | 77 ++----------- .../task-management/iot-card-task/index.vue | 62 ++--------- src/views/common/account-list.vue | 21 +--- src/views/finance/agent-recharge/detail.vue | 11 -- src/views/finance/refund/detail.vue | 23 ++-- src/views/finance/refund/index.vue | 40 +++++-- 14 files changed, 199 insertions(+), 280 deletions(-) create mode 100644 src/template/IoT卡导入模板.xlsx create mode 100644 src/template/设备导入模板.xlsx diff --git a/src/assets/styles/el-ui.scss b/src/assets/styles/el-ui.scss index 85bb447..aec4ef9 100644 --- a/src/assets/styles/el-ui.scss +++ b/src/assets/styles/el-ui.scss @@ -280,6 +280,11 @@ transform: scale(0.6) !important; } } + + .el-checkbox__input.is-disabled .el-checkbox__inner { + background-color: var(--el-color-primary) !important; + border-color: var(--el-color-primary) !important; + } } .el-notification .el-notification__icon { diff --git a/src/template/IoT卡导入模板.xlsx b/src/template/IoT卡导入模板.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8529fd0ff32fe21bd75d22f2f444f6907a259bc3 GIT binary patch literal 16307 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 literal 0 HcmV?d00001 diff --git a/src/template/设备导入模板.xlsx b/src/template/设备导入模板.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..48b209eba83a22c58ca7faf62d6920cd44577096 GIT binary patch literal 8950 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 { + const showDialog = async (type: string, row?: any) => { dialogVisible.value = true dialogType.value = type @@ -411,6 +411,10 @@ formRef.value.resetFields() } + if (type === 'add') { + await loadAllRoles() + } + if (type === 'edit' && row) { formData.id = row.id formData.username = row.username @@ -590,7 +594,8 @@ try { const params: any = { page: 1, - page_size: keyword ? 100 : 20 // 搜索时加载更多,默认20条 + page_size: keyword ? 100 : 20, // 搜索时加载更多,默认20条 + role_type: 1 } if (keyword) { params.role_name = keyword @@ -604,48 +609,14 @@ } } - // 计算属性:新建账号时可选的角色(根据账号类型过滤) + // 计算属性:新建账号时可选的角色 const availableRolesForCreate = computed(() => { - let roles = allRoles.value - - // 根据账号类型过滤角色 - if (formData.user_type === 1) { - // 超级管理员:不能分配角色 - return [] - } else if (formData.user_type === 2) { - // 平台用户:只显示平台角色 - roles = roles.filter((role) => role.role_type === 1) - } else if (formData.user_type === 3) { - // 代理账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } else if (formData.user_type === 4) { - // 企业账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } - - return roles + return allRoles.value }) - // 计算属性:过滤后的可分配角色(根据账号类型过滤) + // 计算属性:过滤后的可分配角色 const filteredAvailableRoles = computed(() => { - let roles = allRoles.value - - // 根据账号类型过滤角色 - if (currentAccountType.value === 1) { - // 超级管理员:不能分配任何角色 - return [] - } else if (currentAccountType.value === 3) { - // 代理账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } else if (currentAccountType.value === 4) { - // 企业账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } else if (currentAccountType.value === 2) { - // 平台用户:只显示平台角色 - roles = roles.filter((role) => role.role_type === 1) - } - - return roles + return allRoles.value }) // 搜索防抖定时器 diff --git a/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue b/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue index 8345c4c..034c747 100644 --- a/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue +++ b/src/views/asset-management/asset-information/components/dialogs/DailyRecordsDialog.vue @@ -6,9 +6,6 @@ {{ data.package_name }} - - {{ data.package_usage_id }} - {{ formatDataSize(data.total_usage_mb) }} diff --git a/src/views/asset-management/asset-information/composables/useAssetInfo.ts b/src/views/asset-management/asset-information/composables/useAssetInfo.ts index fe7facc..7a8fae7 100644 --- a/src/views/asset-management/asset-information/composables/useAssetInfo.ts +++ b/src/views/asset-management/asset-information/composables/useAssetInfo.ts @@ -171,20 +171,25 @@ export function useAssetInfo() { const loadCurrentPackage = async (identifier: string) => { try { currentPackageLoading.value = true - // 清空之前的错误信息 currentPackageErrorMsg.value = '' - const response = await AssetService.getCurrentPackage(identifier) - if (response.code === 0) { - if (response.data) { - const pkg = response.data + const [currentPkgResponse, packageListResponse] = await Promise.all([ + AssetService.getCurrentPackage(identifier), + AssetService.getAssetPackages(identifier, { page: 1, page_size: 50 }) + ]) + + const activePackage = packageListResponse.data?.items?.find((p: any) => p.status === 1) + const activePaidAmount = activePackage?.paid_amount || 0 + + if (currentPkgResponse.code === 0) { + if (currentPkgResponse.data) { + const pkg = currentPkgResponse.data - // 转换为组件期望的格式 currentPackage.value = { id: pkg.package_usage_id || 0, package_id: pkg.package_id || 0, package_name: pkg.package_name || '', - package_price: 0, // API 没有返回价格 + package_price: activePaidAmount, package_total_data: pkg.data_limit_mb || 0, real_data_used: pkg.data_usage_mb || 0, real_data_remaining: (pkg.data_limit_mb || 0) - (pkg.data_usage_mb || 0), @@ -196,10 +201,10 @@ export function useAssetInfo() { expire_time: pkg.expires_at || '', status: pkg.status || 0, status_name: pkg.status_name, - duration_days: undefined, // API 没有返回时长 - virtual_ratio: pkg.virtual_ratio, // 虚流量比例 - package_type: pkg.package_type, // 套餐类型 - enable_virtual_data: pkg.enable_virtual_data, // 是否启用虚流量 + duration_days: undefined, + virtual_ratio: pkg.virtual_ratio, + package_type: pkg.package_type, + enable_virtual_data: pkg.enable_virtual_data, data_limit_mb: pkg.data_limit_mb, virtual_limit_mb: pkg.virtual_limit_mb, virtual_remain_mb: pkg.virtual_remain_mb, diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index 517398e..ec46db4 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -338,34 +338,47 @@ label-width="80" > - - + + + + + + + + + + + - + ({ - iot_card_id: [{ required: true, message: '请选择IoT卡', trigger: 'change' }], + search_type: [{ required: true, message: '请选择搜索类型', trigger: 'change' }], + iot_card_id: [{ required: true, message: '请选择ICCID', trigger: 'change' }], slot_position: [{ required: true, message: '请选择插槽位置', trigger: 'change' }] }) @@ -855,10 +871,14 @@ deviceCards.value = [] deviceCardsDialogVisible.value = true // 重置绑定卡表单 + bindCardForm.search_type = 'iccid' bindCardForm.iot_card_id = undefined bindCardForm.slot_position = 1 - // 只加载设备卡列表 - await loadDeviceCards(device.virtual_no) + // 加载未绑定设备的卡列表 + await Promise.all([ + loadDeviceCards(device.virtual_no), + loadDefaultIotCards() + ]) } // 加载设备绑定的卡列表 @@ -897,7 +917,7 @@ } } - // 搜索IoT卡(根据ICCID) + // 搜索IoT卡(根据ICCID或运营商名称) const searchIotCards = async (query: string) => { if (!query) { await loadDefaultIotCards() @@ -906,12 +926,17 @@ iotCardSearchLoading.value = true iotCardHasSearched.value = true try { - const res = await CardService.getStandaloneIotCards({ + const params: any = { page: 1, page_size: 20, - iccid: query, is_standalone: true - }) + } + if (bindCardForm.search_type === 'iccid') { + params.iccid = query + } else if (bindCardForm.search_type === 'carrier_name') { + params.carrier_name = query + } + const res = await CardService.getStandaloneIotCards(params) if (res.code === 0 && res.data?.items) { iotCardList.value = res.data.items } diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue index e1e1f6f..3706dcc 100644 --- a/src/views/asset-management/iot-card-management/index.vue +++ b/src/views/asset-management/iot-card-management/index.vue @@ -562,7 +562,7 @@ import { h } from 'vue' import { useRouter } from 'vue-router' import { RoutesAlias } from '@/router/routesAlias' - import { CardService, ShopService, PackageSeriesService, AssetService } from '@/api/modules' + import { CardService, ShopService, PackageSeriesService, AssetService, CarrierService } from '@/api/modules' import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus' import { Loading } from '@element-plus/icons-vue' import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue' @@ -627,6 +627,38 @@ failed_items: null }) + // 运营商搜索相关 + const carrierLoading = ref(false) + const searchCarrierOptions = ref([]) + + // 加载搜索栏运营商选项(默认加载10条) + const loadSearchCarrierOptions = async (carrierName?: string) => { + try { + const params: any = { + page: 1, + page_size: 10 + } + if (carrierName) { + params.carrier_name = carrierName + } + const res = await CarrierService.getCarriers(params) + if (res.code === 0) { + searchCarrierOptions.value = res.data.items + } + } catch (error) { + console.error('加载运营商选项失败:', error) + } + } + + // 搜索运营商(用于搜索栏) + const handleSearchCarrier = (query: string) => { + if (query) { + loadSearchCarrierOptions(query) + } else { + loadSearchCarrierOptions() + } + } + // 卡详情弹窗相关 const cardDetailDialogVisible = ref(false) const cardDetailLoading = ref(false) @@ -730,8 +762,8 @@ // 搜索表单初始值 const initialSearchState: { status: undefined | number - carrier_id: undefined | number iccid: string + carrier_name: string msisdn: string virtual_no: string device_virtual_no: string @@ -740,8 +772,8 @@ [key: string]: any } = { status: undefined, - carrier_id: undefined, iccid: '', + carrier_name: '', msisdn: '', virtual_no: '', device_virtual_no: '', @@ -863,20 +895,6 @@ { label: '已停用', value: 4 } ] }, - { - label: '运营商', - prop: 'carrier_id', - type: 'select', - config: { - clearable: true, - placeholder: '全部' - }, - options: () => [ - { label: '中国移动', value: 1 }, - { label: '中国联通', value: 2 }, - { label: '中国电信', value: 3 } - ] - }, { label: 'ICCID', prop: 'iccid', @@ -886,6 +904,24 @@ placeholder: '请输入ICCID' } }, + { + label: '运营商名称', + prop: 'carrier_name', + type: 'select', + config: { + clearable: true, + filterable: true, + remote: true, + remoteMethod: handleSearchCarrier, + loading: carrierLoading.value, + placeholder: '请选择或搜索运营商名称' + }, + options: () => + searchCarrierOptions.value.map((c) => ({ + label: c.carrier_name, + value: c.carrier_name + })) + }, { label: '卡接入号', prop: 'msisdn', @@ -1180,20 +1216,22 @@ const getActions = (row: StandaloneIotCard) => { const actions: any[] = [] - if (hasAuth('iot_card:start_card')) { - actions.push({ - label: '启用此卡', - handler: () => handleCardOperation('start-card', row.iccid), - type: 'primary' - }) - } - - if (hasAuth('iot_card:stop_card')) { - actions.push({ - label: '停用此卡', - handler: () => handleCardOperation('stop-card', row.iccid), - type: 'primary' - }) + if (row.network_status === 1) { + if (hasAuth('iot_card:stop_card')) { + actions.push({ + label: '停用此卡', + handler: () => handleCardOperation('stop-card', row.iccid), + type: 'primary' + }) + } + } else { + if (hasAuth('iot_card:start_card')) { + actions.push({ + label: '启用此卡', + handler: () => handleCardOperation('start-card', row.iccid), + type: 'primary' + }) + } } if (hasAuth('iot_card:realname_policy')) { @@ -1216,6 +1254,7 @@ } onMounted(() => { + loadSearchCarrierOptions() getTableData() }) @@ -1228,7 +1267,6 @@ page_size: pagination.pageSize, ...formFilters } - // 清理空值 Object.keys(params).forEach((key) => { if (params[key] === '' || params[key] === undefined) { delete params[key] diff --git a/src/views/asset-management/task-management/device-task/index.vue b/src/views/asset-management/task-management/device-task/index.vue index fcd315c..337d9ad 100644 --- a/src/views/asset-management/task-management/device-task/index.vue +++ b/src/views/asset-management/task-management/device-task/index.vue @@ -61,7 +61,7 @@

2. 仅支持 Excel 格式(.xlsx),单次最多导入 1000 条

3. 列格式请设置为文本格式,避免长数字被转为科学计数法

4. 重要:列顺序固定,不可调整。系统按位置读取,不识别列名

-

5. 必填列:虚拟号(第1列)

+

5. 必填列:虚拟号(第1列)

6. 可选列:设备名称、设备型号、设备类型、IMEI、制造商、最大SIM槽数(默认4,有效范围1-4)、卡1~卡4 @@ -443,77 +443,16 @@ // 下载模板 const downloadTemplate = async () => { try { - // 动态导入 xlsx 库 - const XLSX = await import('xlsx') - - // 创建示例数据(按固定列顺序) - const templateData = [ - { - 虚拟号: '862639070731999', - 设备名称: '智能水表01', - 设备型号: 'WM-2000', - 设备类型: '智能水表', - IMEI: '860123456789012', - 制造商: '华为', - 最大SIM槽数: 4, - '卡1 ICCID': '89860123456789012345', - '卡2 ICCID': '', - '卡3 ICCID': '', - '卡4 ICCID': '' - }, - { - 虚拟号: '862639070750932', - 设备名称: 'GPS定位器01', - 设备型号: 'GPS-3000', - 设备类型: '定位设备', - IMEI: '860123456789013', - 制造商: '小米', - 最大SIM槽数: 2, - '卡1 ICCID': '89860123456789012346', - '卡2 ICCID': '89860123456789012347', - '卡3 ICCID': '', - '卡4 ICCID': '' - } - ] - - // 创建工作簿 - const wb = XLSX.utils.book_new() - const ws = XLSX.utils.json_to_sheet(templateData) - - // 设置列宽 - ws['!cols'] = [ - { wch: 20 }, // 虚拟号 - { wch: 15 }, // 设备名称 - { wch: 15 }, // 设备型号 - { wch: 15 }, // 设备类型 - { wch: 18 }, // IMEI - { wch: 12 }, // 制造商 - { wch: 15 }, // 最大SIM槽数 - { wch: 25 }, // 卡1 ICCID - { wch: 25 }, // 卡2 ICCID - { wch: 25 }, // 卡3 ICCID - { wch: 25 } // 卡4 ICCID - ] - - // 将所有单元格设置为文本格式 - const range = XLSX.utils.decode_range(ws['!ref'] || 'A1') - for (let R = range.s.r; R <= range.e.r; ++R) { - for (let C = range.s.c; C <= range.e.c; ++C) { - const cellAddress = XLSX.utils.encode_cell({ r: R, c: C }) - if (!ws[cellAddress]) continue - ws[cellAddress].t = 's' // 设置为字符串类型 - } - } - - // 添加工作表到工作簿 - XLSX.utils.book_append_sheet(wb, ws, '设备导入模板') - - // 导出文件 - XLSX.writeFile(wb, '设备导入模板.xlsx') - + const link = document.createElement('a') + link.href = new URL('@/template/设备导入模板.xlsx', import.meta.url).href + link.download = '设备导入模板.xlsx' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) ElMessage.success('设备导入模板下载成功') } catch (error) { console.error('下载模板失败:', error) + ElMessage.error('下载模板失败') } } diff --git a/src/views/asset-management/task-management/iot-card-task/index.vue b/src/views/asset-management/task-management/iot-card-task/index.vue index b364654..e7f90a9 100644 --- a/src/views/asset-management/task-management/iot-card-task/index.vue +++ b/src/views/asset-management/task-management/iot-card-task/index.vue @@ -105,10 +105,8 @@

1. 请先下载 Excel 模板文件,按照模板格式填写IoT卡信息

2. 仅支持 Excel 格式(.xlsx),单次最多导入 1000 条

3. 列格式请设置为文本格式,避免长数字被转为科学计数法

-

4. 必填字段:ICCID、MSISDN(手机号)、virtual_no(虚拟号)

-

5. Excel 文件必须包含 virtual_no 列,否则整批导入将失败

-

6. virtual_no 为空将导入失败

-

7. 必须选择运营商

+

4. 必填字段:ICCID、MSISDN

+

5. 必须选择运营商

@@ -642,58 +640,16 @@ // 下载模板 const downloadTemplate = async () => { try { - // 动态导入 xlsx 库 - const XLSX = await import('xlsx') - - // 创建示例数据(按固定列顺序:ICCID、MSISDN、虚拟号) - const templateData = [ - { - ICCID: '89860123456789012345', - 'MSISDN(接入号)': '13800138000', - 虚拟号: 'V001' - }, - { - ICCID: '89860123456789012346', - 'MSISDN(接入号)': '13800138001', - 虚拟号: 'V002' - }, - { - ICCID: '89860123456789012347', - 'MSISDN(接入号)': '13800138002', - 虚拟号: 'V003' - } - ] - - // 创建工作簿 - const wb = XLSX.utils.book_new() - const ws = XLSX.utils.json_to_sheet(templateData) - - // 设置列宽 - ws['!cols'] = [ - { wch: 25 }, // ICCID - { wch: 20 }, // MSISDN(接入号) - { wch: 15 } // 虚拟号 - ] - - // 将所有单元格设置为文本格式,防止科学计数法 - const range = XLSX.utils.decode_range(ws['!ref'] || 'A1') - for (let R = range.s.r; R <= range.e.r; ++R) { - for (let C = range.s.c; C <= range.e.c; ++C) { - const cellAddress = XLSX.utils.encode_cell({ r: R, c: C }) - if (!ws[cellAddress]) continue - ws[cellAddress].t = 's' // 设置为字符串类型 - } - } - - // 添加工作表到工作簿 - XLSX.utils.book_append_sheet(wb, ws, 'IoT卡导入模板') - - // 导出文件 - XLSX.writeFile(wb, 'IoT卡导入模板.xlsx') - + const link = document.createElement('a') + link.href = new URL('@/template/IoT卡导入模板.xlsx', import.meta.url).href + link.download = 'IoT卡导入模板.xlsx' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) ElMessage.success('IoT卡导入模板下载成功') } catch (error) { console.error('下载模板失败:', error) + ElMessage.error('下载模板失败') } } diff --git a/src/views/common/account-list.vue b/src/views/common/account-list.vue index 628fad2..d370510 100644 --- a/src/views/common/account-list.vue +++ b/src/views/common/account-list.vue @@ -582,7 +582,8 @@ try { const params: any = { page: 1, - page_size: 20 + page_size: 20, + role_type: 1 } if (keyword) { params.role_name = keyword @@ -607,23 +608,9 @@ }, 300) } - // 计算属性:过滤后的可分配角色(根据账号类型过滤) + // 计算属性:过滤后的可分配角色 const filteredAvailableRoles = computed(() => { - let roles = allRoles.value - - // 根据账号类型过滤角色 - if (currentAccountType.value === 3) { - // 代理账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } else if (currentAccountType.value === 2) { - // 平台用户:只显示平台角色 - roles = roles.filter((role) => role.role_type === 1) - } else if (currentAccountType.value === 4) { - // 企业账号:只显示客户角色 - roles = roles.filter((role) => role.role_type === 2) - } - - return roles + return allRoles.value }) // 计算属性:过滤后的已分配角色 diff --git a/src/views/finance/agent-recharge/detail.vue b/src/views/finance/agent-recharge/detail.vue index f932d50..c4d7018 100644 --- a/src/views/finance/agent-recharge/detail.vue +++ b/src/views/finance/agent-recharge/detail.vue @@ -94,9 +94,7 @@ title: '订单信息', fields: [ { label: '充值单号', prop: 'recharge_no' }, - { label: '充值记录ID', prop: 'id' }, { label: '店铺名称', prop: 'shop_name' }, - { label: '店铺ID', prop: 'shop_id' }, { label: '充值金额', formatter: (_, data) => formatCurrency(data.amount) @@ -122,15 +120,6 @@ prop: 'payment_channel', formatter: (value) => value || '-' }, - { - label: '支付配置ID', - prop: 'payment_config_id', - formatter: (value) => value || '-' - }, - { - label: '代理钱包ID', - prop: 'agent_wallet_id' - }, { label: '第三方支付流水号', prop: 'payment_transaction_id', diff --git a/src/views/finance/refund/detail.vue b/src/views/finance/refund/detail.vue index af0cda4..9858a0b 100644 --- a/src/views/finance/refund/detail.vue +++ b/src/views/finance/refund/detail.vue @@ -14,12 +14,6 @@ {{ getStatusText(refund.status) }} - {{ refund.order_id }} - {{ refund.shop_id }} - - - {{ refund.package_usage_id || '-' }} - {{ formatCurrency(refund.requested_refund_amount) }} @@ -42,34 +36,29 @@ - + {{ refund.refund_reason || '-' }} - + {{ refund.reject_reason || '-' }} - - {{ refund.remark || '-' }} + + {{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }} - {{ refund.creator }} - - {{ refund.processor_id || '-' }} + + {{ refund.remark || '-' }} {{ formatDateTime(refund.created_at) }} - - {{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }} - {{ formatDateTime(refund.updated_at) }} - {{ refund.updater }}
diff --git a/src/views/finance/refund/index.vue b/src/views/finance/refund/index.vue index a634f48..a8eb03d 100644 --- a/src/views/finance/refund/index.vue +++ b/src/views/finance/refund/index.vue @@ -63,11 +63,12 @@ placeholder="请输入订单号搜索" style="width: 100%" @focus="handleOrderSelectFocus" + @change="handleOrderSelectChange" > @@ -75,21 +76,18 @@ - - + @@ -434,8 +432,7 @@ const createRules = reactive({ order_id: [{ required: true, message: '请输入订单ID', trigger: 'blur' }], - requested_refund_amount: [{ required: true, message: '请输入申请退款金额', trigger: 'blur' }], - actual_received_amount: [{ required: true, message: '请输入实收金额', trigger: 'blur' }] + requested_refund_amount: [{ required: true, message: '请输入申请退款金额', trigger: 'blur' }] }) const approveRules = reactive({}) @@ -485,6 +482,15 @@ const refundList = ref([]) + // 选中的订单实收金额 + const selectedOrderActualPaid = computed(() => { + if (!createForm.order_id) return 0 + const selectedOrder = orderSearchOptions.value.find((o) => o.id === createForm.order_id) + if (!selectedOrder?.actual_paid_amount) return 0 + const amount = selectedOrder.actual_paid_amount / 100 + return isNaN(amount) ? 0 : amount + }) + // 格式化货币 - 将分转换为元 const formatCurrency = (amount: number): string => { return `¥${(amount / 100).toFixed(2)}` @@ -684,6 +690,18 @@ } } + // 订单选择变化 - 自动填充实收金额 + const handleOrderSelectChange = (orderId: number | null) => { + if (!orderId) { + createForm.actual_received_amount = 0 + return + } + const selectedOrder = orderSearchOptions.value.find((o) => o.id === orderId) + if (selectedOrder) { + createForm.actual_received_amount = selectedOrder.actual_paid_amount / 100 + } + } + // 获取退款申请列表 const getTableData = async () => { loading.value = true