优化
This commit is contained in:
@@ -46,9 +46,9 @@ describe('finance api', () => {
|
||||
it('uses the formal G1 account list path and fixed page DTO', async () => {
|
||||
const get = vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'success', requestId: 'req-g1', traceId: 'trace-g1', data: [{ customerId: 10001, customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00' }], page: { page: 2, pageSize: 20, total: 21 } })
|
||||
|
||||
const result = await listFinanceAccounts({ page: 2, size: 20, keyword: '客户A', status: 'ACTIVE' })
|
||||
const result = await listFinanceAccounts({ page: 2, size: 20, customerId: '10001', status: 'ACTIVE' })
|
||||
|
||||
expect(get).toHaveBeenCalledWith('/platform/finance/v1/accounts', { params: { page: 2, pageSize: 20, keyword: '客户A', status: 'ACTIVE' } })
|
||||
expect(get).toHaveBeenCalledWith('/platform/finance/v1/accounts', { params: { page: 2, pageSize: 20, customerId: '10001', status: 'ACTIVE' } })
|
||||
expect(result).toEqual({ records: [{ customerId: '10001', customerCode: 'C-10001', customerName: '客户A', contactName: '张三', contactPhone: '13800000000', outstandingReceivable: 300, pendingBilling: 50, receivedInPeriod: 100, outstandingPayable: 80, status: 'ACTIVE', lastChangedAt: '2026-08-11T10:00:00' }], total: 21, pages: 2 })
|
||||
})
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ export async function listFinanceFeePool(customerId?: string, direction?: 'RECEI
|
||||
})
|
||||
}
|
||||
|
||||
export async function listFinanceAccounts(params: { page?: number; size?: number; keyword?: string; status?: string } = {}): Promise<{ records: FinanceAccountSummary[]; total: number; pages: number }> {
|
||||
const query = { page: params.page ?? 1, pageSize: params.size ?? 20, keyword: params.keyword || undefined, status: params.status || undefined }
|
||||
export async function listFinanceAccounts(params: { page?: number; size?: number; customerId?: string; status?: string } = {}): Promise<{ records: FinanceAccountSummary[]; total: number; pages: number }> {
|
||||
const query = { page: params.page ?? 1, pageSize: params.size ?? 20, customerId: params.customerId || undefined, status: params.status || undefined }
|
||||
const response = await http.get<ApiEnvelope<RawResource[]> & { page?: RawResource }, ApiEnvelope<RawResource[]> & { page?: RawResource }>(`${basePath}/accounts`, { params: query })
|
||||
const data = unwrapApiResponse(response)
|
||||
const records = data.map((item) => ({ customerId: stringValue(item.customerId), customerCode: stringValue(item.customerCode), customerName: stringValue(item.customerName), contactName: stringValue(item.contactName), contactPhone: stringValue(item.contactPhone), outstandingReceivable: numberValue(item.outstandingReceivable), pendingBilling: numberValue(item.pendingBilling), receivedInPeriod: numberValue(item.receivedInPeriod), outstandingPayable: numberValue(item.outstandingPayable), status: stringValue(item.status), lastChangedAt: stringValue(item.lastChangedAt) }))
|
||||
|
||||
@@ -57,7 +57,12 @@ describe('finance five-page delivery contract', () => {
|
||||
expect(page).toContain('listFinanceAccountLedger')
|
||||
expect(page).not.toContain('getFinanceCustomerAccount')
|
||||
expect(page).not.toContain('接口尚未提供')
|
||||
expect(page.indexOf("key: 'actions'")).toBeLessThan(page.indexOf("key: 'customerName'"))
|
||||
expect(page).toContain("key: 'customerId', type: 'select', label: '客户名称'")
|
||||
expect(page).toContain('options: customers.value.filter')
|
||||
expect(page).toContain('label: item.tenantName')
|
||||
expect(page).not.toContain('tenantCode')
|
||||
const columns = page.slice(page.indexOf('const columns: DataTableColumn[]'))
|
||||
expect(columns.indexOf("key: 'actions'")).toBeLessThan(columns.indexOf("key: 'customerName'"))
|
||||
for (const state of ['客户账户加载失败', '暂无客户账户', '无权访问客户账户', '账户流水加载失败']) expect(page).toContain(state)
|
||||
})
|
||||
|
||||
|
||||
@@ -40,12 +40,21 @@ describe('FinanceCustomerAccountsView delivery', () => {
|
||||
for (const forbidden of ['手动充值', '钱包', '外币', '发票', '利润']) expect(page).not.toContain(forbidden)
|
||||
})
|
||||
|
||||
it('selects an active customer by name from master data', () => {
|
||||
expect(page).toContain("key: 'customerId', type: 'select'")
|
||||
expect(page).toContain("label: '客户名称'")
|
||||
expect(page).toContain('item.tenantName')
|
||||
expect(page).not.toContain('tenantCode')
|
||||
expect(page).toContain('listPlatformTenants')
|
||||
expect(page).not.toContain("key: 'keyword', type: 'input'")
|
||||
})
|
||||
|
||||
it('supports a refreshable customer detail route without inventing an API parameter', () => {
|
||||
expect(routes).toContain("path: 'finance/customer-accounts/:customerId'")
|
||||
expect(page).toContain("name: 'finance-customer-account-detail'")
|
||||
expect(page).toContain('restoreAccountDetail(customerId)')
|
||||
expect(page).toContain("listFinanceAccounts({ page: currentPage, size: 200 })")
|
||||
expect(page).not.toContain('listFinanceAccounts({ customerId')
|
||||
expect(page).toContain('customerId: String(filters.value.customerId ||')
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ArrowLeft, Download, Eye, Pencil, Save } from '@lucide/vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
@@ -19,7 +20,10 @@ import type { FinanceAccountSummary, FinanceCustomerTerms, FinanceLedgerEntry }
|
||||
const access = useAccessPreviewStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const filters = ref<QueryModel>({ keyword: '', status: '' })
|
||||
const filters = ref<QueryModel>({ customerId: '', status: '' })
|
||||
const customers = ref<PlatformTenantResource[]>([])
|
||||
const customersLoading = ref(false)
|
||||
const customersError = ref<ApiErrorDetails | null>(null)
|
||||
const rows = ref<FinanceAccountSummary[]>([])
|
||||
const loading = ref(false)
|
||||
const loadError = ref<ApiErrorDetails | null>(null)
|
||||
@@ -41,12 +45,14 @@ const ledgerPageSize = ref(20)
|
||||
const ledgerTotal = ref(0)
|
||||
|
||||
const canView = computed(() => access.can('platform:finance:customer-account:list'))
|
||||
const canListCustomers = computed(() => canView.value || access.can('platform:customer:list'))
|
||||
const canViewTerms = computed(() => access.can('platform:finance:customer-terms:view'))
|
||||
const canUpdateTerms = computed(() => canViewTerms.value && access.can('platform:finance:customer-terms:update'))
|
||||
const queryFields: QueryField[] = [
|
||||
{ key: 'keyword', type: 'input', label: '关键词', width: 260, placeholder: '客户编号/客户名称/联系人/手机号', props: { 'aria-label': '关键词' } },
|
||||
const stateError = computed(() => customersError.value || loadError.value)
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{ key: 'customerId', type: 'select', label: '客户名称', width: 260, placeholder: '全部客户', props: { 'aria-label': '客户名称', filterable: true, clearable: true, loading: customersLoading.value }, options: customers.value.filter((item) => item.status === 'ACTIVE').map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'status', type: 'select', label: '状态', width: 150, placeholder: '全部状态', options: [{ label: '正常', value: 'ACTIVE' }, { label: '停用', value: 'DISABLED' }] },
|
||||
]
|
||||
])
|
||||
const columns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', width: 150, fixed: 'left', slotName: 'actions' },
|
||||
{ key: 'customerName', label: '客户', minWidth: 180 },
|
||||
@@ -84,12 +90,21 @@ async function load(model: QueryModel = filters.value) {
|
||||
loading.value = true
|
||||
loadError.value = null
|
||||
try {
|
||||
const result = await listFinanceAccounts({ page: page.value, size: pageSize.value, keyword: String(filters.value.keyword || '').trim(), status: String(filters.value.status || '') })
|
||||
const result = await listFinanceAccounts({ page: page.value, size: pageSize.value, customerId: String(filters.value.customerId || '').trim() || undefined, status: String(filters.value.status || '') })
|
||||
rows.value = result.records
|
||||
total.value = result.total
|
||||
} catch (error) { loadError.value = getApiErrorDetails(error, '客户账户加载失败,请重试') }
|
||||
finally { loading.value = false }
|
||||
}
|
||||
async function loadCustomers() {
|
||||
customersLoading.value = true; customersError.value = null
|
||||
try {
|
||||
const records: PlatformTenantResource[] = []; let currentPage = 1; let pages = 1
|
||||
do { const result = await listPlatformTenants({ page: currentPage, size: 200, status: 'ACTIVE' }); records.push(...result.records); pages = Math.max(1, result.pages); currentPage += 1 } while (currentPage <= pages)
|
||||
customers.value = records
|
||||
} catch (error) { customersError.value = getApiErrorDetails(error, '客户列表加载失败') }
|
||||
finally { customersLoading.value = false }
|
||||
}
|
||||
function search(model: QueryModel) { page.value = 1; void load(model) }
|
||||
async function openAccount(row: Record<string, unknown>, edit: boolean) {
|
||||
selected.value = row as unknown as FinanceAccountSummary
|
||||
@@ -160,7 +175,7 @@ async function restoreAccountDetail(customerId: string) {
|
||||
onMounted(() => {
|
||||
const customerId = String(route.params.customerId || '')
|
||||
if (route.name === 'finance-customer-account-detail' && customerId) void restoreAccountDetail(customerId)
|
||||
else void load()
|
||||
else { if (canListCustomers.value) void loadCustomers(); void load() }
|
||||
})
|
||||
watch(() => [route.name, String(route.params.customerId || '')], ([name, customerId], previous) => {
|
||||
if (!previous) return
|
||||
@@ -174,8 +189,8 @@ watch(() => [route.name, String(route.params.customerId || '')], ([name, custome
|
||||
<el-result v-if="!canView" icon="warning" title="无权访问客户账户" sub-title="请联系管理员开通客户账户查看权限" />
|
||||
<ListPageLayout v-else-if="!detailOpen" aria-label="客户账户">
|
||||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" ariaLabel="客户账户筛选" :fields="queryFields" preference-key="finance-customer-accounts-query" @search="search" /></template>
|
||||
<template v-if="loadError" #state><LoadErrorState title="客户账户加载失败" :message="loadError.message" :loading="loading" @retry="load()"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState></template>
|
||||
<section v-if="!loadError" class="surface-panel table-panel">
|
||||
<template v-if="stateError" #state><LoadErrorState title="客户账户加载失败" :message="stateError.message" :loading="loading || customersLoading" @retry="customersError ? loadCustomers() : load()"><template #details><ApiErrorMeta :details="stateError" /></template></LoadErrorState></template>
|
||||
<section v-if="!stateError" class="surface-panel table-panel">
|
||||
<DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="finance-customer-accounts-table" aria-label="客户账户列表" row-key="customerId" empty-text="暂无客户账户" @update:page="load()" @update:page-size="page = 1; load()">
|
||||
<template #toolbar-actions><el-button :disabled="loading || !rows.length" @click="exportAccounts"><Download :size="16" />导出</el-button></template>
|
||||
<template #actions="{ row }"><div class="row-actions"><el-button link type="primary" @click="openAccount(row, false)"><Eye :size="15" />查看</el-button><el-button v-if="canUpdateTerms" link type="primary" @click="openAccount(row, true)"><Pencil :size="15" />账期</el-button></div></template>
|
||||
|
||||
@@ -78,7 +78,7 @@ const canCreateStatement = computed(() => access.can('platform:finance:reconcili
|
||||
const stateError = computed(() => customersError.value || loadError.value)
|
||||
const activeCustomers = computed(() => customers.value.filter((item) => item.status === 'ACTIVE'))
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{ key: 'customerId', type: 'select', label: '客户', width: 260, placeholder: '全部客户', props: { 'aria-label': '客户', filterable: true, clearable: true, loading: customersLoading.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'customerId', type: 'select', label: '客户名称', width: 260, placeholder: '全部客户', props: { 'aria-label': '客户名称', filterable: true, clearable: true, loading: customersLoading.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'direction', type: 'select', label: '收付方向', width: 140, placeholder: '全部', options: [{ label: '应收', value: 'RECEIVABLE' }, { label: '应付', value: 'PAYABLE' }] },
|
||||
{ key: 'reviewStatus', type: 'select', label: '审核状态', width: 150, placeholder: '全部状态', options: financeArapStatusOptions },
|
||||
{ key: 'settlementIncluded', type: 'select', label: '参与结算', width: 140, placeholder: '全部', options: [{ label: '参与', value: 'true' }, { label: '不参与', value: 'false' }] },
|
||||
@@ -103,7 +103,7 @@ const selectedAmount = computed(() => selectedRows.value.reduce((sum, row) => su
|
||||
async function loadCustomers() {
|
||||
if (!canListCustomers.value) return
|
||||
customersLoading.value = true; customersError.value = null
|
||||
try { const records: PlatformTenantResource[] = []; let currentPage = 1; let pages = 1; do { const result = await listPlatformTenants({ page: currentPage, size: 200 }); records.push(...result.records); pages = Math.max(1, result.pages); currentPage += 1 } while (currentPage <= pages); customers.value = records }
|
||||
try { const records: PlatformTenantResource[] = []; let currentPage = 1; let pages = 1; do { const result = await listPlatformTenants({ page: currentPage, size: 200, status: 'ACTIVE' }); records.push(...result.records); pages = Math.max(1, result.pages); currentPage += 1 } while (currentPage <= pages); customers.value = records }
|
||||
catch (error) { customersError.value = getApiErrorDetails(error, '客户列表加载失败') }
|
||||
finally { customersLoading.value = false }
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ const fields = computed<QueryField[]>(() => [
|
||||
{ label: '审核拒绝', value: 'REJECTED' }, { label: '已出账', value: 'ISSUED' }, { label: '已作废', value: 'VOIDED' },
|
||||
] },
|
||||
{ key: 'billNo', type: 'input', label: '对账单号', width: 210, placeholder: '请输入对账单号' },
|
||||
{ key: 'customerId', type: 'select', label: '客户', width: 240, placeholder: '全部客户', props: { filterable: true, clearable: true }, options: customers.value.filter((item) => item.status === 'ACTIVE').map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'customerId', type: 'select', label: '客户名称', width: 260, placeholder: '全部客户', props: { filterable: true, clearable: true }, options: customers.value.filter((item) => item.status === 'ACTIVE').map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'containerNo', type: 'input', label: '柜次号', width: 190, placeholder: canViewFeeSource.value ? '请输入柜次号' : '缺少费用来源查看权限', props: { disabled: !canViewFeeSource.value } },
|
||||
{ key: 'periodStartRange', type: 'daterange', label: '结算起始日', width: 280, props: { valueFormat: 'YYYY-MM-DD', startPlaceholder: '开始日期', endPlaceholder: '结束日期' } },
|
||||
{ key: 'periodEndRange', type: 'daterange', label: '结算结束日', width: 280, props: { valueFormat: 'YYYY-MM-DD', startPlaceholder: '开始日期', endPlaceholder: '结束日期' } },
|
||||
@@ -170,8 +170,9 @@ async function loadCustomers() {
|
||||
if (!canListCustomers.value) return
|
||||
customerError.value = null
|
||||
try {
|
||||
const result = await listPlatformTenants({ page: 1, size: 200 })
|
||||
customers.value = result.records
|
||||
const records: PlatformTenantResource[] = []; let currentPage = 1; let pages = 1
|
||||
do { const result = await listPlatformTenants({ page: currentPage, size: 200, status: 'ACTIVE' }); records.push(...result.records); pages = Math.max(1, result.pages); currentPage += 1 } while (currentPage <= pages)
|
||||
customers.value = records
|
||||
} catch (cause) {
|
||||
customerError.value = getApiErrorDetails(cause, '客户列表加载失败')
|
||||
}
|
||||
|
||||
@@ -83,10 +83,10 @@ const statusOptions = computed(() => [...new Set(rows.value.map((item) => item.s
|
||||
const fields = computed<QueryField[]>(() => [
|
||||
{ key: 'status', type: 'select', label: props.kind === 'receivable' ? '收款状态' : '付款状态', width: 180, placeholder: '全部状态', props: { clearable: true }, options: statusOptions.value.map((status) => ({ label: financeStatusLabel(status), value: status })) },
|
||||
...(props.kind === 'receivable'
|
||||
? [{ key: 'customerId', type: 'select' as const, label: '客户', width: 240, placeholder: '全部客户', props: { filterable: true, clearable: true, loading: !customers.value.length && !customerError.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) }]
|
||||
? [{ key: 'customerId', type: 'select' as const, label: '客户名称', width: 260, placeholder: '全部客户', props: { filterable: true, clearable: true, loading: !customers.value.length && !customerError.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) }]
|
||||
: [
|
||||
{ key: 'supplierName', type: 'select' as const, label: '供应商', width: 240, placeholder: '全部供应商', props: { filterable: true, clearable: true }, options: supplierNames.value.map((name) => ({ label: name, value: name })) },
|
||||
{ key: 'customerId', type: 'select' as const, label: '客户', width: 220, placeholder: '全部客户', props: { filterable: true, clearable: true, loading: !customers.value.length && !customerError.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
{ key: 'customerId', type: 'select' as const, label: '客户名称', width: 260, placeholder: '全部客户', props: { filterable: true, clearable: true, loading: !customers.value.length && !customerError.value }, options: activeCustomers.value.map((item) => ({ label: item.tenantName, value: String(item.id) })) },
|
||||
]),
|
||||
{ key: 'settlementNo', type: 'input', label: '结算单号', width: 220, placeholder: '请输入结算单号' },
|
||||
])
|
||||
@@ -150,7 +150,11 @@ function remainingAfter(amount: number) { return money(Math.max(0, Number(detail
|
||||
async function loadCustomers() {
|
||||
if (!canListCustomers.value) return
|
||||
customerError.value = null
|
||||
try { customers.value = (await listPlatformTenants({ page: 1, size: 200 })).records }
|
||||
try {
|
||||
const records: PlatformTenantResource[] = []; let currentPage = 1; let pages = 1
|
||||
do { const result = await listPlatformTenants({ page: currentPage, size: 200, status: 'ACTIVE' }); records.push(...result.records); pages = Math.max(1, result.pages); currentPage += 1 } while (currentPage <= pages)
|
||||
customers.value = records
|
||||
}
|
||||
catch (cause) { customerError.value = getApiErrorDetails(cause, '客户列表加载失败') }
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('FinanceManualPage delivery contract', () => {
|
||||
})
|
||||
|
||||
it('uses business selectors instead of asking operators for internal ids', () => {
|
||||
expect(source).toContain("type: 'select', label: '客户'")
|
||||
expect(source).toContain("type: 'select', label: '客户名称'")
|
||||
expect(source).toContain('placeholder="请选择客户"')
|
||||
expect(source).toContain('placeholder="选择订单 / 入库单"')
|
||||
expect(source).toContain('placeholder="选择费用来源"')
|
||||
@@ -79,6 +79,12 @@ describe('FinanceManualPage delivery contract', () => {
|
||||
expect(source).not.toContain(':disabled="customer.status !== \'ACTIVE\'"')
|
||||
})
|
||||
|
||||
it('keeps customer selection separate from business keyword matching', () => {
|
||||
expect(source).toContain("placeholder: '单号/费用项目/供应商'")
|
||||
expect(source).toContain('searchableRowValues(row)')
|
||||
expect(source).not.toContain('Object.values(row).some')
|
||||
})
|
||||
|
||||
it('exposes loading, empty and retry states for dependent business options', () => {
|
||||
expect(source).toContain('该客户暂无未结清账单')
|
||||
expect(source).toContain('该客户暂无可选订单')
|
||||
|
||||
@@ -102,8 +102,8 @@ const drawerTitle = computed(() => {
|
||||
})
|
||||
const rows = computed(() => serverPaged.value ? allRows.value : allRows.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value))
|
||||
const queryFields = computed<QueryField[]>(() => [
|
||||
{ key: 'keyword', type: 'input', label: '关键词', width: 220, placeholder: '客户/单号/费用项目', props: { 'aria-label': '关键词' } },
|
||||
{ key: 'customerId', type: 'select', label: '客户', width: 240, placeholder: '请选择客户', props: { 'aria-label': '客户', filterable: true, loading: customerOptionsLoading.value }, options: activeCustomers.value.map((customer) => ({ label: customer.tenantName, value: String(customer.id) })) },
|
||||
{ key: 'keyword', type: 'input', label: '业务关键词', width: 220, placeholder: '单号/费用项目/供应商', props: { 'aria-label': '业务关键词' } },
|
||||
{ key: 'customerId', type: 'select', label: '客户名称', width: 260, placeholder: '请选择客户', props: { 'aria-label': '客户名称', filterable: true, loading: customerOptionsLoading.value }, options: activeCustomers.value.map((customer) => ({ label: customer.tenantName, value: String(customer.id) })) },
|
||||
...(props.mode === 'bills' ? [{ key: 'status', type: 'select' as const, label: '状态', width: 160, placeholder: '全部状态', options: financeBillStatusOptions }] : []),
|
||||
])
|
||||
const columns = computed<DataTableColumn[]>(() => {
|
||||
@@ -128,6 +128,12 @@ function customerNameById(customerId: string | number | null) {
|
||||
return customers.value.find((customer) => String(customer.id) === String(customerId))?.tenantName || '客户信息暂不可用'
|
||||
}
|
||||
|
||||
function searchableRowValues(row: TableRow) {
|
||||
if (props.mode === 'bills') return [row.billNo]
|
||||
if (props.mode === 'receipts') return [row.settlementNo]
|
||||
return [row.settlementNo, row.referenceNo, row.feeName, row.supplierName]
|
||||
}
|
||||
|
||||
function emptyBillLine(): BillLineDraft {
|
||||
return { sourceFeeId: '', sourceFeeVersion: 1, feeItemName: '', amount: '', orderId: '', inboundId: '' }
|
||||
}
|
||||
@@ -339,7 +345,7 @@ async function load() {
|
||||
}
|
||||
const keyword = String(filters.value.keyword || '').trim().toLowerCase()
|
||||
const status = String(filters.value.status || '')
|
||||
allRows.value = source.filter((row) => (!keyword || Object.values(row).some((value) => String(value ?? '').toLowerCase().includes(keyword))) && (!status || row.status === status))
|
||||
allRows.value = source.filter((row) => (!keyword || searchableRowValues(row).some((value) => String(value ?? '').toLowerCase().includes(keyword))) && (!status || row.status === status))
|
||||
total.value = keyword || status ? allRows.value.length : responseTotal
|
||||
} catch (error) { allRows.value = []; total.value = 0; loadError.value = getApiErrorDetails(error, `${title.value}加载失败,请检查 Finance 接口后重试`) }
|
||||
finally { loading.value = false }
|
||||
|
||||
@@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest'
|
||||
|
||||
const settlement = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceFormalSettlementPage.vue'), 'utf8')
|
||||
const financeRoutes = readFileSync(resolve(process.cwd(), 'src/modules/finance/routes.ts'), 'utf8')
|
||||
const accounts = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerAccountsView.vue'), 'utf8')
|
||||
const statements = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerStatementsView.vue'), 'utf8')
|
||||
const arap = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerArapView.vue'), 'utf8')
|
||||
const manual = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceManualPage.vue'), 'utf8')
|
||||
@@ -36,6 +37,16 @@ describe('finance presentation governance', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('uses active-customer name selectors across all formal finance pages', () => {
|
||||
for (const source of [accounts, arap, statements, settlement]) {
|
||||
expect(source).toContain("type: 'select'")
|
||||
expect(source).toContain("label: '客户名称'")
|
||||
expect(source).toContain('label: item.tenantName')
|
||||
expect(source).not.toContain('tenantCode} · ${item.tenantName')
|
||||
}
|
||||
expect(accounts).not.toContain("key: 'keyword', type: 'input'")
|
||||
})
|
||||
|
||||
it('translates source codes and provides reason placeholders', () => {
|
||||
expect(arap).toContain('financeSourceTypeLabel(detail.sourceType)')
|
||||
expect(arap).toContain('financeOperationActionLabel(log.action)')
|
||||
|
||||
Reference in New Issue
Block a user