This commit is contained in:
2026-08-14 12:50:42 +08:00
parent 87c6d2c4ca
commit 590a457dcd
36 changed files with 242 additions and 95 deletions
@@ -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>