This commit is contained in:
2026-08-12 15:23:18 +08:00
parent a01d2bbc9a
commit c15d21b986
4 changed files with 17 additions and 18 deletions
@@ -107,7 +107,7 @@ describe('CustomerDetailView', () => {
mount(CustomerDetailView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
expect([...document.querySelectorAll('.detail-tabs button')].map((button) => button.textContent?.trim())).toEqual([
expect([...document.querySelectorAll('.detail-tabs button')].map((button) => button.getAttribute('aria-label'))).toEqual([
'基本信息', '业务概况', '运单记录', '计费规则',
])
expect(document.body.textContent).toContain('企业用户1人')
@@ -118,16 +118,14 @@ describe('CustomerDetailView', () => {
expect(document.body.textContent).toContain('已替换')
expect(document.body.textContent).toContain('12票')
const ordersTab = [...document.querySelectorAll<HTMLButtonElement>('.detail-tabs button')]
.find((button) => button.textContent?.trim() === '运单记录')
const ordersTab = document.querySelector<HTMLButtonElement>('.detail-tabs button[aria-label="运单记录"]')
ordersTab?.click()
await flushPromises()
expect(document.body.textContent).toContain('BY20260807001')
expect(document.body.textContent).toContain('海运整箱 FCL')
expect(listPlatformFirstLegOrders).toHaveBeenCalledWith({ tenantId: tenant.id, page: 1, size: 10 })
const billingTab = [...document.querySelectorAll<HTMLButtonElement>('.detail-tabs button')]
.find((button) => button.textContent?.trim() === '计费规则')
const billingTab = document.querySelector<HTMLButtonElement>('.detail-tabs button[aria-label="计费规则"]')
billingTab?.click()
await flushPromises()
expect(document.body.textContent).toContain('暂无尺寸计算规则查看权限')
@@ -143,8 +141,7 @@ describe('CustomerDetailView', () => {
mount(CustomerDetailView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
const ordersTab = [...document.querySelectorAll<HTMLButtonElement>('.detail-tabs button')]
.find((button) => button.textContent?.trim() === '运单记录')
const ordersTab = document.querySelector<HTMLButtonElement>('.detail-tabs button[aria-label="运单记录"]')
ordersTab?.click()
await flushPromises()
@@ -174,8 +171,7 @@ describe('CustomerDetailView', () => {
await flushPromises()
expect(listPlatformFirstLegOrders).toHaveBeenCalledTimes(2)
const ordersTab = [...document.querySelectorAll<HTMLButtonElement>('.detail-tabs button')]
.find((button) => button.textContent?.trim() === '运单记录')
const ordersTab = document.querySelector<HTMLButtonElement>('.detail-tabs button[aria-label="运单记录"]')
ordersTab?.click()
await flushPromises()
const retryButton = [...document.querySelectorAll<HTMLButtonElement>('button')]
@@ -2,9 +2,9 @@
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
ArrowLeft, BarChart3, Box, Building2, ClipboardList, Eye,
ArrowLeft, BarChart3, Building2, Eye,
FileCheck2, FileText, Pencil, Power,
Scale, Ship, UserRound, UsersRound,
Ship, UserRound, UsersRound,
} from '@lucide/vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
@@ -70,11 +70,11 @@ const previewUrl = ref('')
const previewKind = ref<'image' | 'pdf'>('image')
const tabList = ref<HTMLElement | null>(null)
const tabs: Array<{ key: DetailTab; label: string; icon: typeof ClipboardList }> = [
{ key: 'basic', label: '基本信息', icon: ClipboardList },
{ key: 'business', label: '业务概况', icon: BarChart3 },
{ key: 'orders', label: '运单记录', icon: Box },
{ key: 'billing', label: '计费规则', icon: Scale },
const tabs: Array<{ key: DetailTab; label: string; icon: string }> = [
{ key: 'basic', label: '基本信息', icon: '📋' },
{ key: 'business', label: '业务概况', icon: '📊' },
{ key: 'orders', label: '运单记录', icon: '📦' },
{ key: 'billing', label: '计费规则', icon: '⚖️' },
]
const approvedQualifications = computed(() => qualifications.value.filter((item) => item.status === 'APPROVED').length)
@@ -345,7 +345,7 @@ watch(orderPage, (page, previousPage) => {
</template>
<div ref="tabList" class="detail-tabs" role="tablist" aria-label="客户详情模块">
<button v-for="tab in tabs" :id="`customer-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeTab === tab.key }" :aria-selected="activeTab === tab.key" :aria-controls="`customer-panel-${tab.key}`" :tabindex="activeTab === tab.key ? 0 : -1" @click="selectTab(tab.key, $event.currentTarget as HTMLElement)" @keydown="handleTabKeydown($event, tab.key)"><component :is="tab.icon" :size="18" /><span>{{ tab.label }}</span></button>
<button v-for="tab in tabs" :id="`customer-tab-${tab.key}`" :key="tab.key" type="button" role="tab" :class="{ active: activeTab === tab.key }" :aria-label="tab.label" :aria-selected="activeTab === tab.key" :aria-controls="`customer-panel-${tab.key}`" :tabindex="activeTab === tab.key ? 0 : -1" @click="selectTab(tab.key, $event.currentTarget as HTMLElement)" @keydown="handleTabKeydown($event, tab.key)"><span class="detail-tab-icon" aria-hidden="true">{{ tab.icon }}</span><span>{{ tab.label }}</span></button>
</div>
<div v-if="customer" :id="`customer-panel-${activeTab}`" class="tab-panel" role="tabpanel" :aria-labelledby="`customer-tab-${activeTab}`" tabindex="0">
@@ -453,6 +453,7 @@ watch(orderPage, (page, previousPage) => {
.overview-strip small { margin-left:5px; color:#475467; font-size:12px; font-weight:600; }
.detail-tabs { display:flex; gap:0; overflow-x:auto; overflow-y:hidden; border-bottom:1px solid rgba(228,228,231,.5); border-radius:20px 20px 0 0; background:rgba(255,255,255,.95); box-shadow:0 -4px 16px rgba(0,0,0,.04); scrollbar-width:thin; }
.detail-tabs button { display:inline-flex; min-width:max-content; min-height:0; flex:0 0 auto; align-items:center; justify-content:center; gap:8px; padding:16px 28px; border:0; border-bottom:3px solid transparent; background:transparent; color:#71717a; font-size:15px; font-weight:600; white-space:nowrap; cursor:pointer; transition:all .3s cubic-bezier(.4,0,.2,1); }
.detail-tab-icon { font-size:18px; line-height:1; }
.detail-tabs button:hover { background:rgba(102,126,234,.05); color:#667eea; }
.detail-tabs button.active { border-bottom-color:#667eea; background:rgba(102,126,234,.08); color:#667eea; font-weight:800; }
.tab-panel { min-height:400px; padding-top:18px; }
@@ -187,7 +187,7 @@ const creditCodeEstablished = ref(false)
const unifiedSocialCreditCodePattern = /^[0-9A-Z]{18}$/
const customerColumns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 620, slotName: 'actions' },
{ key: 'actions', label: '操作', width: 500, slotName: 'actions' },
{ key: 'tenantCode', prop: 'tenantCode', label: '客户编号', width: 220, className: 'customer-code-column' },
{ key: 'company', label: '公司名称', width: 250, slotName: 'company' },
{ key: 'unifiedSocialCreditCode', prop: 'unifiedSocialCreditCode', label: '统一社会信用代码', width: 220, formatter: (row: PlatformTenantResource) => displayText(row.unifiedSocialCreditCode) },