diff --git a/src/modules/finance/views/FinanceBillsView.test.mjs b/src/modules/finance/views/FinanceBillsView.test.mjs index 84d81f7..7ad3278 100644 --- a/src/modules/finance/views/FinanceBillsView.test.mjs +++ b/src/modules/finance/views/FinanceBillsView.test.mjs @@ -121,6 +121,14 @@ test('keeps actions first and exposes list, balance, detail, confirmation, recei assert.match(viewSource, new RegExp(expected)) } assert.match(viewSource, /auth\.hasPermission\('finance:bill:confirm'\)/) + assert.match(viewSource, /row\.customerConfirmationStatus === 'PENDING_CONFIRMATION' && canConfirm/) + assert.match(viewSource, /size="small" type="success" plain/) + assert.match(viewSource, /confirmStatement\(row as FinanceBillSummary\)/) + assert.match(viewSource, /detail\.customerConfirmationStatus === 'PENDING_CONFIRMATION' && canConfirm/) + assert.match(viewSource, /confirmStatement\(detail\)/) + assert.match(viewSource, /confirmMerchantFinanceStatement\(statementId, statement\.version, key\)/) + assert.match(viewSource, /ElMessage\.success\('对账单已确认'\)/) + assert.match(viewSource, /ElMessage\.error\(error instanceof ApiError \? error\.message : '对账单确认失败,请重试'\)/) for (const forbidden of ['发起争议', '新增收款', '核销', '修改金额']) { assert.doesNotMatch(viewSource, new RegExp(forbidden)) } @@ -134,3 +142,10 @@ test('keeps the existing customer finance view permission mapping', () => { assert.match(permissionSource, /code: 'finance:bill:confirm'/) assert.match(permissionSource, /'finance:bill:confirm': \['merchant:finance:statement:confirm'\]/) }) + +test('grants the customer confirmation action only from its independent backend authority', () => { + const allowed = (codes) => permissionContracts.hasMappedMerchantPermission('finance:bill:confirm', new Set(codes)) + assert.equal(allowed([]), false) + assert.equal(allowed(['merchant:finance:statement:list', 'merchant:finance:statement:view']), false) + assert.equal(allowed(['merchant:finance:statement:confirm']), true) +}) diff --git a/src/modules/finance/views/FinanceBillsView.vue b/src/modules/finance/views/FinanceBillsView.vue index d3e055b..87be647 100644 --- a/src/modules/finance/views/FinanceBillsView.vue +++ b/src/modules/finance/views/FinanceBillsView.vue @@ -16,7 +16,7 @@ const bills = ref([]) const detail = ref(null) const selectedBillId = ref('') const detailVisible = ref(false) -const confirming = ref(false) +const confirmingBillId = ref('') const confirmationKeys = new Map() const page = ref(1) const pageSize = ref(20) @@ -112,19 +112,19 @@ async function loadDetail(billId: string) { } } -async function confirmStatement() { - if (!detail.value || detail.value.customerConfirmationStatus !== 'PENDING_CONFIRMATION') return +async function confirmStatement(statement: FinanceBillSummary) { + if (!canConfirm.value || statement.customerConfirmationStatus !== 'PENDING_CONFIRMATION' || confirmingBillId.value) return try { await ElMessageBox.confirm( - `确认对账单 ${detail.value.billNo} 的金额与费用明细无误?确认后平台将记录当前登录成员和确认时间。`, + `确认对账单 ${statement.billNo}(¥ ${formatAmount(statement.totalAmount)})的金额与费用明细无误?确认后平台将记录当前登录成员和确认时间。`, '确认客户对账单', { type: 'warning', confirmButtonText: '确认无误', cancelButtonText: '取消' }, ) } catch { return } - confirming.value = true - const statementId = detail.value.id + confirmingBillId.value = statement.id + const statementId = statement.id let key = confirmationKeys.get(statementId) if (!key) { const randomPart = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}` @@ -132,14 +132,16 @@ async function confirmStatement() { confirmationKeys.set(statementId, key) } try { - await confirmMerchantFinanceStatement(statementId, detail.value.version, key) + await confirmMerchantFinanceStatement(statementId, statement.version, key) confirmationKeys.delete(statementId) ElMessage.success('对账单已确认') - await Promise.all([loadDetail(statementId), loadBills(page.value)]) + const refreshes: Array> = [loadBills(page.value)] + if (detailVisible.value && selectedBillId.value === statementId) refreshes.push(loadDetail(statementId)) + await Promise.all(refreshes) } catch (error) { ElMessage.error(error instanceof ApiError ? error.message : '对账单确认失败,请重试') } finally { - confirming.value = false + confirmingBillId.value = '' } } @@ -181,8 +183,8 @@ onMounted(() => { void loadBills(1) }) 重试 - - + + @@ -203,7 +205,7 @@ onMounted(() => { void loadBills(1) })