fix(admin): keep fleet arrangement references fully readable
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
import { mount, enableAutoUnmount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { compileStyle, parse } from 'vue/compiler-sfc'
|
||||
import FleetArrangementSummary from './FleetArrangementSummary.vue'
|
||||
import type { FleetArrangement } from '../fleetArrangement'
|
||||
|
||||
const state = vi.hoisted(() => ({ writableLinks: true, push: vi.fn() }))
|
||||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => state.writableLinks }) }))
|
||||
vi.mock('vue-router', () => ({ useRouter: () => ({ push: state.push }) }))
|
||||
enableAutoUnmount(afterEach)
|
||||
beforeEach(() => { state.writableLinks = true; state.push.mockClear() })
|
||||
const arrangement: FleetArrangement = {
|
||||
sourceType: 'FLEET_CAPACITY_OFFER', businessType: 'LOCAL_LOOSEBOARD', businessOrderNo: 'LB-ONT-20261007-309123', bookingNo: 'FB-2107769452587171841', tripNo: 'TR-76DB64B4457C4206',
|
||||
plannedDepartureAt: '2026-10-07T13:46:07Z', originWarehouseName: '洛杉矶仓', destinationLabel: 'ONT8', deliveryMode: 'SELF_DELIVERY', deliveryInstructions: '交货说明完整保留', fulfillmentStage: 'WAITING_INBOUND', stageOccurredAt: null,
|
||||
agreedFreightAmount: 30, currency: 'CNY', incomingNo: 'WT-ONT-20261007-12345678901234567890', dispatchNo: 'DP-ONT-20261007-12345678901234567890', deliveryNo: 'DL-ONT-20261007-12345678901234567890',
|
||||
signedPackages: 0, unsignedPackages: null, returnedPackages: 0, exceptionSummary: null, readOnlyReason: '此订单由拼车承运实际履约推进',
|
||||
}
|
||||
function render() { return mount(FleetArrangementSummary, { props: { arrangement }, global: { plugins: [ElementPlus] } }) }
|
||||
it('retains all long business numbers, labels and authorized navigation', async () => {
|
||||
const wrapper = render()
|
||||
const rows = wrapper.findAll('.fleet-arrangement-fields > div')
|
||||
expect(rows).toHaveLength(14)
|
||||
for (const [label, value] of [['订单号', arrangement.businessOrderNo], ['外接预订号', arrangement.bookingNo], ['车次号', arrangement.tripNo], ['来货单号', arrangement.incomingNo], ['排车单号', arrangement.dispatchNo], ['派送单号', arrangement.deliveryNo]]) {
|
||||
expect(rows.find(row => row.get('dt').text() === label)!.get('dd').text()).toBe(value)
|
||||
}
|
||||
expect(rows.find(row => row.get('dt').text() === '已签 / 未签 / 退回箱数')!.get('dd').text()).toBe('0 / — / 0')
|
||||
const links = wrapper.findAll('button.fleet-arrangement-reference')
|
||||
expect(links.map(link => link.text())).toEqual([arrangement.tripNo, arrangement.dispatchNo])
|
||||
await links[0]!.trigger('click'); await links[1]!.trigger('click')
|
||||
expect(state.push).toHaveBeenNthCalledWith(1, { name: 'overseas-master-data-trip-detail', params: { tripNo: arrangement.tripNo } })
|
||||
expect(state.push).toHaveBeenNthCalledWith(2, { name: 'overseas-dispatch-detail', params: { dispatchNo: arrangement.dispatchNo } })
|
||||
})
|
||||
it('keeps full reference text without links when target permissions are absent', () => {
|
||||
state.writableLinks = false
|
||||
const wrapper = render()
|
||||
expect(wrapper.findAll('button')).toHaveLength(0)
|
||||
expect(wrapper.text()).toContain(arrangement.tripNo); expect(wrapper.text()).toContain(arrangement.dispatchNo)
|
||||
})
|
||||
it('uses a scoped two-column layout and wrap-capable reference controls rather than truncation', () => {
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/modules/overseas-business/components/FleetArrangementSummary.vue'), 'utf8')
|
||||
const style = parse(source).descriptor.styles[0]!
|
||||
expect(style.scoped).toBe(true)
|
||||
const compiled = compileStyle({ source: style.content, filename: 'FleetArrangementSummary.vue', id: 'data-v-arrangement', scoped: true })
|
||||
expect(compiled.errors).toEqual([])
|
||||
const rules = new Map<string, Record<string, string>>()
|
||||
compiled.rawResult!.root.walkRules(rule => {
|
||||
const declarations: Record<string, string> = {}
|
||||
rule.walkDecls(declaration => { declarations[declaration.prop] = declaration.value })
|
||||
rules.set(rule.selector, declarations)
|
||||
})
|
||||
const grid = [...rules].find(([selector]) => selector.includes('.fleet-arrangement-fields.density-fields'))!
|
||||
expect(grid[0]).toContain('[data-v-arrangement]')
|
||||
expect(grid[1]).toMatchObject({ 'grid-template-columns': 'repeat(2, minmax(0, 1fr))', '--admin-field-label-width': '160px' })
|
||||
const button = [...rules].find(([selector]) => selector.endsWith('.fleet-arrangement-reference[data-v-arrangement]'))!
|
||||
expect(button[1]).toMatchObject({ 'max-width': '100%', 'min-width': '0', height: 'auto', 'white-space': 'normal', 'text-align': 'left' })
|
||||
const text = [...rules].find(([selector]) => selector.endsWith(' span'))!
|
||||
expect(text[1]).toMatchObject({ 'white-space': 'normal', 'overflow-wrap': 'anywhere', 'min-width': '0' })
|
||||
expect(style.content).not.toMatch(/text-overflow:\s*ellipsis|overflow:\s*hidden|white-space:\s*nowrap/)
|
||||
})
|
||||
@@ -9,10 +9,10 @@ const time = (value: string | null) => value ? new Date(value).toLocaleString('z
|
||||
<template>
|
||||
<section aria-label="承运安排">
|
||||
<p>{{ arrangement.readOnlyReason }}</p>
|
||||
<dl class="density-fields">
|
||||
<dl class="density-fields fleet-arrangement-fields">
|
||||
<div><dt>订单号</dt><dd>{{ arrangement.businessOrderNo }}</dd></div>
|
||||
<div><dt>外接预订号</dt><dd>{{ arrangement.bookingNo }}</dd></div>
|
||||
<div><dt>车次号</dt><dd><el-button v-if="access.can('platform:fleet:trip:list')" link type="primary" @click="router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: arrangement.tripNo } })">{{ arrangement.tripNo }}</el-button><span v-else>{{ arrangement.tripNo }}</span></dd></div>
|
||||
<div><dt>车次号</dt><dd><el-button v-if="access.can('platform:fleet:trip:list')" class="fleet-arrangement-reference" link type="primary" @click="router.push({ name: 'overseas-master-data-trip-detail', params: { tripNo: arrangement.tripNo } })">{{ arrangement.tripNo }}</el-button><span v-else>{{ arrangement.tripNo }}</span></dd></div>
|
||||
<div><dt>计划发车</dt><dd>{{ time(arrangement.plannedDepartureAt) }}</dd></div>
|
||||
<div><dt>起运仓</dt><dd>{{ arrangement.originWarehouseName || '—' }}</dd></div>
|
||||
<div><dt>目的地</dt><dd>{{ arrangement.destinationLabel || '—' }}</dd></div>
|
||||
@@ -21,7 +21,7 @@ const time = (value: string | null) => value ? new Date(value).toLocaleString('z
|
||||
<div><dt>阶段时间</dt><dd>{{ time(arrangement.stageOccurredAt) }}</dd></div>
|
||||
<div><dt>约定运费(非实收)</dt><dd>{{ arrangement.agreedFreightAmount == null ? '—' : `${Number(arrangement.agreedFreightAmount).toFixed(2)} ${arrangement.currency || ''}` }}</dd></div>
|
||||
<div><dt>来货单号</dt><dd>{{ arrangement.incomingNo || '—' }}</dd></div>
|
||||
<div><dt>排车单号</dt><dd><el-button v-if="arrangement.dispatchNo && access.can('platform:overseas-warehouse:dispatch:list')" link type="primary" @click="router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: arrangement.dispatchNo } })">{{ arrangement.dispatchNo }}</el-button><span v-else>{{ arrangement.dispatchNo || '—' }}</span></dd></div>
|
||||
<div><dt>排车单号</dt><dd><el-button v-if="arrangement.dispatchNo && access.can('platform:overseas-warehouse:dispatch:list')" class="fleet-arrangement-reference" link type="primary" @click="router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: arrangement.dispatchNo } })">{{ arrangement.dispatchNo }}</el-button><span v-else>{{ arrangement.dispatchNo || '—' }}</span></dd></div>
|
||||
<div><dt>派送单号</dt><dd>{{ arrangement.deliveryNo || '—' }}</dd></div>
|
||||
<div><dt>已签 / 未签 / 退回箱数</dt><dd>{{ arrangement.signedPackages ?? '—' }} / {{ arrangement.unsignedPackages ?? '—' }} / {{ arrangement.returnedPackages ?? '—' }}</dd></div>
|
||||
</dl>
|
||||
@@ -29,3 +29,10 @@ const time = (value: string | null) => value ? new Date(value).toLocaleString('z
|
||||
<p v-if="arrangement.exceptionSummary" role="status">履约说明:{{ arrangement.exceptionSummary }}</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fleet-arrangement-fields.density-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); --admin-field-label-width: 160px; }
|
||||
.fleet-arrangement-fields dt { overflow-wrap: anywhere; }
|
||||
.fleet-arrangement-fields .fleet-arrangement-reference { max-width: 100%; min-width: 0; height: auto; justify-content: flex-start; white-space: normal; text-align: left; line-height: inherit; }
|
||||
.fleet-arrangement-reference :deep(span) { display: block; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user