style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -67,6 +67,42 @@
|
||||
.admin-density-dialog.el-dialog .el-empty__image { display: none; }
|
||||
.admin-density-dialog.el-dialog .el-empty__description { margin-top: 0; }
|
||||
|
||||
/* Opted-in detail and editor content reuse the existing compact drawer frame. */
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-code-block { margin:0; padding:var(--admin-space-3); border:1px solid var(--admin-table-border); border-radius:var(--admin-radius); background:var(--admin-surface); color:var(--cc-text); font:var(--admin-font-caption)/1.65 ui-monospace,SFMono-Regular,Consolas,monospace; white-space:pre-wrap; overflow-wrap:anywhere; }
|
||||
|
||||
/* Explicit section opt-in; existing dialogs retain their approved body layout. */
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section {
|
||||
min-width: 0;
|
||||
margin: 0 0 var(--admin-space-5);
|
||||
padding: var(--admin-space-4) var(--admin-space-5);
|
||||
border: 1px solid var(--admin-table-border);
|
||||
border-radius: var(--admin-dialog-section-radius);
|
||||
background: var(--admin-surface);
|
||||
}
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section:last-child { margin-bottom: 0; }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-sections { display: grid; gap: var(--admin-space-5); }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-sections > .density-dialog-section { margin: 0; }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section[data-tone="summary"] {
|
||||
border-color: var(--admin-dialog-summary-border);
|
||||
background: var(--admin-dialog-summary-bg);
|
||||
}
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section[data-tone="source"] {
|
||||
border-color: var(--admin-dialog-source-border);
|
||||
background: var(--admin-dialog-source-bg);
|
||||
}
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section[data-tone="work"] {
|
||||
border-color: var(--admin-dialog-work-border);
|
||||
background: var(--admin-dialog-work-bg);
|
||||
}
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section > .density-dialog-section__title {
|
||||
display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--admin-space-3);
|
||||
margin: 0 0 var(--admin-space-4); color: var(--cc-text); font-size: var(--admin-font-section); font-weight: 600;
|
||||
}
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section__title small { color: var(--cc-muted); font-size: var(--admin-font-caption); font-weight: 400; }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-note { margin: 0 0 var(--admin-space-3); color: var(--cc-muted); font-size: var(--admin-font-caption); }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section > .el-form > .el-form-item:last-child { margin-bottom: 0; }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section .el-table { --el-table-tr-bg-color: var(--admin-surface); }
|
||||
|
||||
/* Headers must keep units visible even when a saved column width is narrow. */
|
||||
.admin-density .el-table th.el-table__cell .cell,
|
||||
.admin-density-dialog .el-table th.el-table__cell .cell { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; line-height: 18px; }
|
||||
@@ -149,6 +185,7 @@
|
||||
/* The same action spacing also applies to native tables and teleported content. */
|
||||
:is(.admin-density, .admin-density-dialog) .density-actions { display: flex; align-items: center; gap: var(--cc-row-action-gap, var(--admin-space-2)); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-actions > :is(.el-button, .el-dropdown) { margin-left: 0; }
|
||||
:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--editor)) .density-actions--footer { justify-content: flex-end; flex-wrap: wrap; gap: var(--admin-space-3); }
|
||||
|
||||
.admin-density .density-detail-heading > .density-back { flex-shrink: 0; }
|
||||
|
||||
@@ -190,6 +227,7 @@
|
||||
.admin-density.density-form { --el-form-label-font-size: var(--admin-font-body); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-form .el-form-item,
|
||||
.admin-density.density-form .el-form-item { margin-bottom: var(--admin-space-5); }
|
||||
.admin-density-dialog .density-form--flush-end > .el-form-item:last-child { margin-bottom: 0; }
|
||||
|
||||
/* Opted-in FormSection consumers share form headings without page-level paint. */
|
||||
:is(.admin-density, .admin-density-dialog) .form-section.density-form-section { padding: var(--admin-space-5) 0; }
|
||||
@@ -201,12 +239,26 @@
|
||||
:is(.admin-density, .admin-density-dialog) .density-form :is(.el-select, .el-input-number, .el-date-editor) { width: 100%; }
|
||||
|
||||
/* Existing native data tables keep their business column widths and sticky cells. */
|
||||
:is(.admin-density, .admin-density-dialog) .density-table { width: 100%; border-collapse: collapse; font-size: var(--admin-font-body); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table :is(th, td) { height: var(--admin-row-height); padding: var(--admin-space-1) var(--admin-space-3); border: 1px solid var(--admin-table-border); text-align: left; vertical-align: middle; overflow-wrap: anywhere; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table th { background: var(--admin-label-bg); color: var(--cc-muted); font-weight: 600; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table td { background: var(--admin-surface); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table tbody tr:nth-child(even) td { background: var(--admin-row-stripe); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table tbody tr:hover td { background: var(--admin-row-hover); }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table { width: 100%; border-collapse: collapse; font-size: var(--admin-font-body); }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table :is(th, td) { height: var(--admin-row-height); padding: var(--admin-space-1) var(--admin-space-3); border: 1px solid var(--admin-table-border); text-align: left; vertical-align: middle; overflow-wrap: anywhere; }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table th { background: var(--admin-label-bg); color: var(--cc-muted); font-weight: 600; }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table td { background: var(--admin-surface); }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table tbody tr:nth-child(even) td { background: var(--admin-row-stripe); }
|
||||
:is(.admin-density, .admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-table tbody tr:hover td { background: var(--admin-row-hover); }
|
||||
|
||||
/* Explicit cell editors keep one continuous table; focus retains Element's primary border. */
|
||||
:is(.admin-density, .admin-density-dialog) .density-table--editable :is(.el-input, .el-textarea) {
|
||||
--el-input-border-color: transparent;
|
||||
--el-input-hover-border-color: var(--admin-table-border);
|
||||
--el-input-bg-color: transparent;
|
||||
}
|
||||
:is(.admin-density, .admin-density-dialog) .density-table-editor { display: flex; align-items: center; gap: var(--admin-space-3); min-width: 0; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table-editor .el-input-number { flex: 1; width: 100%; min-width: 0; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table--editable .el-input__inner { text-align: left; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item { margin-bottom: 0; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item.is-error { margin-bottom: var(--admin-space-5); }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table--editable :is(.el-input-number, .el-segmented, .el-date-editor) { width: 100%; min-width: 0; }
|
||||
:is(.admin-density, .admin-density-dialog) .density-table-editor > span { flex: 0 0 auto; color: var(--cc-muted); white-space: nowrap; }
|
||||
|
||||
/* Continuous description grid. Page owns column count and wide-field spans only. */
|
||||
:is(.admin-density, .admin-density-dialog) .density-fields { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; margin: 0; border-top: 1px solid var(--cc-border); border-left: 1px solid var(--cc-border); background: var(--admin-surface); }
|
||||
|
||||
@@ -13,7 +13,10 @@ const cargoConsumers = [
|
||||
'trucking/pages/TruckAppointmentDetailView.vue', 'trucking/components/TruckAppointmentForm.vue',
|
||||
'trucking/components/TruckAttachmentDialog.vue', 'container-loading/pages/ContainersView.vue',
|
||||
'container-loading/pages/ContainerDetailView.vue', 'container-loading/pages/SmartLoadingWorkbenchView.vue',
|
||||
'container-loading/components/SmartLoadingDialog.vue', 'container-loading/pages/GlobalSmartLoadingView.vue',
|
||||
'loading-lists/pages/PreloadPlanDetailView.vue',
|
||||
'container-loading/components/OceanBookingLinkDialog.vue', 'container-loading/components/ContainerOverviewPanel.vue',
|
||||
'port-schedules/components/ContainerPortSchedulePanel.vue',
|
||||
'container-loading/components/ContainerCustomsPanel.vue', 'container-loading/components/ContainerLastLegFeesPanel.vue',
|
||||
'loading-lists/components/ContainerFeesPanel.vue', 'port-schedules/components/CarrierScheduleQueryPanel.vue',
|
||||
]
|
||||
@@ -32,6 +35,8 @@ const overseasConsumers = [
|
||||
'overseas-warehouse/pages/RelabelOrderDetailView.vue',
|
||||
'overseas-warehouse/pages/PalletsView.vue',
|
||||
'overseas-warehouse/pages/PalletDetailView.vue',
|
||||
'overseas-warehouse/pages/LocationsView.vue',
|
||||
'overseas-warehouse/pages/CargoDiscardsView.vue',
|
||||
'overseas-warehouse/pages/InventoryView.vue',
|
||||
'overseas-warehouse/pages/InventoryDetailView.vue',
|
||||
'overseas-warehouse/pages/InventoryTransactionsView.vue',
|
||||
@@ -172,6 +177,139 @@ const cssRoot = (css: string) => {
|
||||
}
|
||||
|
||||
describe('approved Admin density ownership', () => {
|
||||
it('shares the measurement palette through explicit dialog sections without changing other dialog bodies', () => {
|
||||
const tokenValues: Record<string, string> = {}
|
||||
cssRoot(tokens).walkDecls(decl => { tokenValues[decl.prop] = decl.value })
|
||||
const rules = new Map<string, Record<string, string>>()
|
||||
cssRoot(shared).walkRules(rule => {
|
||||
const values: Record<string, string> = {}
|
||||
rule.walkDecls(decl => { values[decl.prop] = decl.value })
|
||||
rules.set(rule.selector, values)
|
||||
})
|
||||
for (const [tone, color] of [['summary', '#f4fbf8'], ['source', '#f7f9ff'], ['work', '#fffbf4']]) {
|
||||
expect(tokenValues[`--admin-dialog-${tone}-bg`]).toBe(color)
|
||||
expect(rules.get(`:is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section[data-tone="${tone}"]`)).toMatchObject({
|
||||
background: `var(--admin-dialog-${tone}-bg)`,
|
||||
'border-color': `var(--admin-dialog-${tone}-border)`,
|
||||
})
|
||||
}
|
||||
expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-section')).not.toHaveProperty('height')
|
||||
expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-dialog-note')).toMatchObject({ color: 'var(--cc-muted)', 'font-size': 'var(--admin-font-caption)' })
|
||||
expect(rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--editor)) .density-actions--footer')).toMatchObject({ 'justify-content': 'flex-end', 'flex-wrap': 'wrap', gap: 'var(--admin-space-3)' })
|
||||
for (const [selector, declarations] of rules) {
|
||||
if (selector.includes('density-dialog-section')) expect(Object.keys(declarations).filter(name => name.startsWith('border-left')), selector).toEqual([])
|
||||
}
|
||||
const editorRule = rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable :is(.el-input, .el-textarea)')
|
||||
expect(editorRule).toMatchObject({ '--el-input-border-color': 'transparent', '--el-input-hover-border-color': 'var(--admin-table-border)' })
|
||||
expect(editorRule).not.toHaveProperty('--el-input-focus-border-color')
|
||||
expect(rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item')).toMatchObject({ 'margin-bottom': '0' })
|
||||
expect(rules.get(':is(.admin-density, .admin-density-dialog) .density-table--editable .el-form-item.is-error')).toMatchObject({ 'margin-bottom': 'var(--admin-space-5)' })
|
||||
for (const selector of rules.keys()) if (selector.includes('density-dialog-section')) expect(selector.startsWith(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) ')).toBe(true)
|
||||
const measured = read('modules/inbounds/measurement/measurementDensity.css')
|
||||
expect(measured).not.toMatch(/border-left(?:-color|-width)?\s*:/)
|
||||
for (const color of ['#f4fbf8', '#f7f9ff', '#fffbf4']) expect(measured).not.toContain(color)
|
||||
for (const file of ['unified-orders/pages/UnifiedOrdersView.vue', 'unified-orders/components/OrderDeclarationEditorDialog.vue', 'unified-orders/components/InboundFeeDetailManager.vue', 'inbounds/measurement/InboundMeasurementPanel.vue', 'inbounds/weight/components/WeightOcrPanel.vue', 'domestic-containers/pages/DomesticContainersView.vue', 'sailing-calendar/pages/SailingCalendarView.vue', 'ocean-bookings/components/OceanBookingPrintDialog.vue', 'ocean-bookings/pages/OceanBookingDetailView.vue', 'trucking/components/TruckAttachmentDialog.vue', 'container-loading/pages/ContainersView.vue', 'container-loading/pages/ContainerDetailView.vue', 'container-loading/components/OceanBookingLinkDialog.vue']) {
|
||||
const source = read('modules/' + file)
|
||||
expect(parse(source).errors, file).toEqual([])
|
||||
const ast = parse(source).descriptor.template!.ast!
|
||||
const visit = (nodes: typeof ast.children, inDialog = false) => { for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
const inside = inDialog || ['AppDialog', 'el-dialog'].includes(node.tag)
|
||||
if (classesOf(node).some(name => ['density-dialog-section', 'density-table--editable'].includes(name))) expect(inside, file + ': sections and cell editors belong to an explicit dialog').toBe(true)
|
||||
visit(node.children, inside)
|
||||
} }
|
||||
visit(ast.children)
|
||||
}
|
||||
})
|
||||
it('opts the audit detail drawer into the same palette without styling unselected drawers or clipping snapshots', () => {
|
||||
const rules = new Map<string, Record<string, string>>()
|
||||
cssRoot(shared).walkRules(rule => {
|
||||
const declarations: Record<string, string> = {}
|
||||
rule.walkDecls(decl => { declarations[decl.prop] = decl.value })
|
||||
rules.set(rule.selector, declarations)
|
||||
})
|
||||
const snapshot = rules.get(':is(.admin-density-dialog, .admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)) .density-code-block')!
|
||||
expect(snapshot).toMatchObject({ 'white-space': 'pre-wrap', 'overflow-wrap': 'anywhere' })
|
||||
for (const key of ['height', 'min-height', 'max-height', 'overflow', 'overflow-y']) expect(snapshot).not.toHaveProperty(key)
|
||||
for (const selector of rules.keys()) {
|
||||
if (selector.includes('admin-density-drawer') && (selector.includes('density-dialog-section') || selector.includes('density-table'))) expect(selector).toContain('.admin-density-drawer:where(.admin-density-drawer--detail, .admin-density-drawer--editor)')
|
||||
if (selector.includes('.el-drawer__header') || selector.includes('.el-drawer__body')) expect(selector).not.toContain('admin-density-drawer--detail')
|
||||
}
|
||||
const audit = parse(read('modules/audit/components/CustomerAuditLogsPanel.vue')).descriptor
|
||||
const ast = audit.template!.ast!
|
||||
const visit = (nodes: typeof ast.children, inside = false) => {
|
||||
for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
const inDrawer = inside || (node.tag === 'AppDrawer' && classesOf(node).includes('admin-density-drawer--detail') && classesOf(node).includes('admin-density-drawer'))
|
||||
if (classesOf(node).some(value => ['density-dialog-section', 'density-table', 'density-code-block'].includes(value))) expect(inDrawer).toBe(true)
|
||||
visit(node.children, inDrawer)
|
||||
}
|
||||
}
|
||||
visit(audit.template!.ast!.children)
|
||||
for (const style of audit.styles) expect(style.content).not.toMatch(/snapshot[^{}]*\{[^}]*max-height|border-left:/)
|
||||
})
|
||||
|
||||
it('keeps review presentation in the shared dialog and resource editors on the shared form scale', () => {
|
||||
const review = parse(read('modules/inbounds/review/MeasurementReviewPanel.vue')).descriptor
|
||||
for (const style of review.styles) {
|
||||
expect(style.content).not.toContain('linear-gradient')
|
||||
expect(style.content).not.toMatch(/box-shadow:\s*inset/)
|
||||
}
|
||||
const host = parse(read('modules/inbounds/components/InboundTaskList.vue')).descriptor.template!.ast!
|
||||
let reviewHostFound = false
|
||||
const visit = (nodes: typeof host.children) => { for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
if (classesOf(node).includes('inbound-task-dialog')) {
|
||||
expect(node.tag).toBe('AppDialog')
|
||||
expect(classesOf(node)).toContain('admin-density-dialog')
|
||||
expect(node.props.some(prop => prop.type === 6 && prop.name === 'fullscreenable')).toBe(true)
|
||||
expect(node.children.some(child => child.type === 1 && child.props.some(prop => prop.type === 7 && prop.name === 'slot' && prop.arg?.type === 4 && prop.arg.content === 'footer'))).toBe(true)
|
||||
reviewHostFound = true
|
||||
}
|
||||
visit(node.children)
|
||||
} }
|
||||
visit(host.children)
|
||||
expect(reviewHostFound).toBe(true)
|
||||
for (const file of ['locations/pages/LocationsView.vue', 'pallets/pages/PalletsView.vue']) {
|
||||
const source = read('modules/warehouse-resources/' + file)
|
||||
expect(source).toContain('class="editor-form density-form density-form--flush-end"')
|
||||
for (const style of parse(source).descriptor.styles) {
|
||||
expect(style.content).not.toContain('.editor-form :deep(.el-form-item__label)')
|
||||
expect(style.content).not.toContain('.editor-form :deep(.el-form-item)')
|
||||
}
|
||||
}
|
||||
expect(shared).toContain('.admin-density-dialog .density-form--flush-end > .el-form-item:last-child')
|
||||
expect(shared).not.toContain('.admin-density-dialog .density-form > .el-form-item:last-child')
|
||||
})
|
||||
it('keeps batch approval overflow tooltips single-line without forcing its headers or error tags to truncate', () => {
|
||||
const view = parse(read('modules/unified-orders/pages/UnifiedOrdersView.vue')).descriptor.template!.ast!
|
||||
const tooltipColumns: string[] = []
|
||||
const visit = (nodes: typeof view.children, inBatch = false) => { for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
const inside = inBatch || classesOf(node).includes('batch-approval-table')
|
||||
if (inside && node.tag === 'el-table-column') {
|
||||
const label = node.props.find(prop => prop.type === 6 && prop.name === 'label')
|
||||
if (label?.type === 6 && ['客户', '中文品名'].includes(label.value?.content || '')) {
|
||||
expect(node.props.some(prop => prop.type === 6 && prop.name === 'show-overflow-tooltip')).toBe(true)
|
||||
tooltipColumns.push(label.value!.content)
|
||||
}
|
||||
}
|
||||
visit(node.children, inside)
|
||||
} }
|
||||
visit(view.children)
|
||||
expect(tooltipColumns).toEqual(['客户', '中文品名'])
|
||||
const rules = new Map<string, Record<string, string>>()
|
||||
cssRoot(read('modules/unified-orders/styles/orderDensity.css')).walkRules(rule => {
|
||||
const declarations: Record<string, string> = {}
|
||||
rule.walkDecls(decl => { declarations[decl.prop] = decl.value })
|
||||
rules.set(rule.selector, declarations)
|
||||
})
|
||||
expect(rules.has('.admin-density-dialog.el-dialog .batch-approval-table .cell')).toBe(false)
|
||||
expect(rules.get('.admin-density-dialog.el-dialog .batch-approval-table td .cell.el-tooltip')).toMatchObject({
|
||||
'white-space': 'nowrap', 'text-overflow': 'ellipsis', overflow: 'hidden', 'overflow-wrap': 'normal',
|
||||
})
|
||||
expect(rules.get('.admin-density-dialog.el-dialog .batch-approval-table .el-tag')?.['white-space']).toBe('normal')
|
||||
})
|
||||
it('opts transfer approval into the shared centered confirmation appearance', () => {
|
||||
const dialog = read('modules/overseas-warehouse/components/WarehouseTransferReviewDialog.vue')
|
||||
const css = read('styles/adminDensity.css')
|
||||
@@ -203,6 +341,26 @@ describe('approved Admin density ownership', () => {
|
||||
expect(panel.template!.content).toContain('v-if="canCreate"')
|
||||
expect(panel.template!.content).toContain('新增预约')
|
||||
})
|
||||
it('keeps calendar creation, its two consumers and arrival editing on shared dialog sections', () => {
|
||||
const view = parse(read('modules/appointment-calendar/AppointmentCalendarView.vue')).descriptor
|
||||
const create = parse(read('modules/appointment-calendar/AppointmentCreatePanel.vue')).descriptor
|
||||
const work = parse(read('modules/appointment-calendar/WorkCreateAppointmentForm.vue')).descriptor
|
||||
expect(view.scriptSetup!.content).toContain("import '@/styles/adminDensity.css'")
|
||||
const dialogs = view.template!.content.match(/<AppDialog\b[^>]+>/g)!
|
||||
expect(dialogs).toHaveLength(3)
|
||||
for (const dialog of dialogs) { expect(dialog).toContain('admin-density-dialog'); expect(dialog).toContain('fullscreenable') }
|
||||
expect(view.template!.content).toContain('density-table density-table--editable')
|
||||
expect(view.template!.content).toContain('label-position="left" label-width="110px"')
|
||||
expect(view.styles.map(style => style.content).join('\n')).not.toContain('arrival-field')
|
||||
expect(create.template!.content).toContain('aria-label="预约保存回执"')
|
||||
expect(create.template!.content).not.toContain('<FbaAppointmentFields')
|
||||
expect(create.template!.content).toContain('<LineClampText :content="row.customerName"')
|
||||
expect(create.template!.content).toContain('data-tone="source"')
|
||||
expect(work.template!.content).toContain('<FbaAppointmentFields continuous')
|
||||
expect(work.template!.content).toContain('density-actions density-actions--footer')
|
||||
for (const source of [create.template!.content, read('modules/appointment-calendar/WorkDetailPanel.vue')]) expect(source).toContain('<WorkCreateAppointmentForm')
|
||||
expect(create.styles).toHaveLength(0); expect(work.styles).toHaveLength(0)
|
||||
})
|
||||
it('shares list status and category tab styles across registered list consumers', () => {
|
||||
for (const file of [
|
||||
'unified-orders/pages/UnifiedOrdersView.vue', 'overseas-warehouse/pages/LastMileOrdersView.vue',
|
||||
@@ -310,11 +468,156 @@ describe('approved Admin density ownership', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps warehouse editors and import results on shared sections and cell editors', () => {
|
||||
const source = read('modules/warehouses/pages/WarehousesView.vue')
|
||||
expect(source).toContain('class="density-form density-dialog-sections"')
|
||||
expect(source).toContain('density-table density-table--editable warehouse-editor-table')
|
||||
expect(source).toContain('class="warehouse-import-dialog admin-density-dialog"')
|
||||
expect(source).toContain('class="density-table warehouse-import-summary"')
|
||||
expect(source).toContain('class="density-actions density-actions--footer"')
|
||||
expect(source).not.toContain('<FormSection')
|
||||
expect(source).not.toContain('<el-dialog')
|
||||
expect(source).not.toContain(':global(.warehouse-editor-dialog .el-dialog__body)')
|
||||
})
|
||||
|
||||
it('keeps role and permission editors on shared sections with one body scroll and fixed codes', () => {
|
||||
const role = read('modules/iam/pages/RolesView.vue')
|
||||
const permission = read('modules/iam/pages/PermissionsView.vue')
|
||||
for (const source of [role, permission]) {
|
||||
expect(source).toContain('fullscreenable :confirm-loading="saving"')
|
||||
expect(source).toContain('density-table--editable')
|
||||
expect(source).toContain('data-tone="work"')
|
||||
expect(source).not.toContain('<FormSection')
|
||||
expect(source).not.toContain('label-position="top"')
|
||||
const css = parse(source).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toContain('el-dialog__body')
|
||||
expect(css).not.toContain('border-left')
|
||||
}
|
||||
expect(role).toContain('data-tone="source"')
|
||||
expect(role).not.toContain('max-height: 380px')
|
||||
expect(role).toContain('align-items: start')
|
||||
expect(permission).not.toContain('class="form-callout"')
|
||||
})
|
||||
|
||||
it('keeps internal user forms on shared cells and native fullscreen with original geometry', () => {
|
||||
const source = read('modules/iam/pages/InternalUsersView.vue')
|
||||
expect(source).toContain('class="density-form density-dialog-sections"')
|
||||
expect(source).toContain('class="density-table density-table--editable user-editor-table"')
|
||||
expect(source).toContain('data-tone="work"')
|
||||
expect(source).toContain('fullscreenable destroy-on-close')
|
||||
expect(source).toContain('width="min(650px, calc(100vw - 28px))"')
|
||||
expect(source).not.toContain('<FormSection')
|
||||
expect(source).not.toContain('label-position="top"')
|
||||
const css = parse(source).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toMatch(/font-size|background|border|el-dialog__body|el-dialog__footer/)
|
||||
expect(css).toContain(':not(.is-fullscreen)')
|
||||
})
|
||||
|
||||
it('opts fee item editing into drawer sections and existing cell editors without changing other drawers', () => {
|
||||
const source = read('modules/fee-items/pages/FeeItemsView.vue')
|
||||
const ast = parse(source).descriptor.template!.ast!
|
||||
let editorFound = false
|
||||
const visit = (nodes: typeof ast.children, inEditor = false) => { for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
const inside = inEditor || node.tag === 'AppDrawer' && classesOf(node).includes('admin-density-drawer--editor')
|
||||
if (node.tag === 'AppDrawer') {
|
||||
editorFound = true
|
||||
for (const name of ['admin-density', 'admin-density-drawer', 'admin-density-drawer--editor']) expect(classesOf(node)).toContain(name)
|
||||
}
|
||||
if (classesOf(node).some(name => ['density-dialog-section', 'density-table--editable'].includes(name))) expect(inside).toBe(true)
|
||||
visit(node.children, inside)
|
||||
} }
|
||||
visit(ast.children)
|
||||
expect(editorFound).toBe(true)
|
||||
expect(source).not.toContain('<FormSection')
|
||||
expect(source).not.toContain('label-position="top"')
|
||||
expect(source).toContain('class="density-actions density-actions--footer"')
|
||||
expect(source).toContain('size="min(620px, 94vw)"')
|
||||
expect(shared).toContain(':where(.admin-density-drawer--detail, .admin-density-drawer--editor)')
|
||||
const css = parse(source).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toContain('direction-grid')
|
||||
expect(css).not.toContain('delete-message')
|
||||
})
|
||||
|
||||
it('keeps download task detail on shared facts, error and footer primitives', () => {
|
||||
const source = read('modules/downloads/pages/DownloadCenterView.vue')
|
||||
const dialog = source.slice(source.indexOf('<AppDialog'), source.indexOf('</AppDialog>'))
|
||||
expect(dialog).toContain('class="admin-density-dialog"')
|
||||
expect(dialog).toContain('fullscreenable')
|
||||
expect(dialog).toContain('data-tone="summary"')
|
||||
expect(dialog).toContain('class="task-detail-grid density-fields"')
|
||||
expect(dialog).toContain('<LoadErrorState v-if="detailError" compact')
|
||||
expect(dialog).toContain('class="density-actions density-actions--footer"')
|
||||
expect(dialog).not.toContain('requestedUsername || detailTask.requestedBy')
|
||||
const css = parse(source).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toMatch(/\.task-(?:detail-summary|failure)\b|\.detail-load-error\b/)
|
||||
})
|
||||
|
||||
it('keeps ship price book upload compact with shared table and confirmation styles', () => {
|
||||
const list = read('modules/fba-pricing/carrier-rates/pages/CarrierRatesView.vue')
|
||||
const detail = read('modules/fba-pricing/carrier-rates/pages/CarrierRateDetailView.vue')
|
||||
const dialog = list.slice(list.indexOf('<AppDialog'), list.indexOf('</AppDialog>'))
|
||||
expect(dialog).toContain('class="admin-density-dialog"')
|
||||
expect(dialog).toContain('width="min(560px, calc(100vw - 28px))"')
|
||||
expect(dialog).toContain(':confirm-loading="uploading"')
|
||||
expect(dialog).toContain('class="density-table density-table--editable"')
|
||||
expect(dialog).toContain('label-position="left"')
|
||||
expect(dialog).toContain('class="density-dialog-note"')
|
||||
expect(dialog).not.toContain('upload-scope-field')
|
||||
expect(list).not.toContain('.upload-file-name strong')
|
||||
for (const source of [list, detail]) expect(source).toContain("customClass: 'admin-density-confirm'")
|
||||
})
|
||||
|
||||
it('uses shared frames, continuous tables and readonly facts for fleet quote dialogs', () => {
|
||||
const editor = read('modules/overseas-master-data/components/FleetQuoteEditor.vue')
|
||||
const importer = read('modules/overseas-master-data/components/FleetQuoteImport.vue')
|
||||
for (const source of [editor, importer]) {
|
||||
expect(source).toContain('class="admin-density-dialog" fullscreenable :confirm-loading="saving"')
|
||||
expect(source).toContain('density-table--editable')
|
||||
expect(source).toContain('data-tone="work"')
|
||||
expect(source).toContain('data-tone="source"')
|
||||
expect(source).toContain('class="density-actions density-actions--footer"')
|
||||
expect(source).not.toContain('label-position="top"')
|
||||
const css = parse(source).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toMatch(/font-size|background|border|el-dialog__body|max-height/)
|
||||
}
|
||||
expect(editor).toContain('v-if="readonly"')
|
||||
expect(editor).toContain('class="density-fields fleet-quote-facts"')
|
||||
expect(read('modules/overseas-master-data/pages/FleetQuotesView.vue')).toContain("customClass: 'admin-density-confirm'")
|
||||
})
|
||||
|
||||
it('keeps pricing dialogs on shared tables and sections without local form paint', () => {
|
||||
const rate = read('modules/overseas-master-data/pages/ExchangeRatesView.vue')
|
||||
const quote = read('modules/overseas-master-data/pages/OverseasQuotesView.vue')
|
||||
for (const source of [rate, quote]) {
|
||||
const dialogs = parse(source).descriptor.template!.content.match(/<AppDialog[\s\S]*?<\/AppDialog>/g)!
|
||||
expect(dialogs.length).toBeGreaterThan(0)
|
||||
for (const dialog of dialogs) {
|
||||
expect(dialog).toContain('class="admin-density-dialog"')
|
||||
expect(dialog).not.toContain('label-position="top"')
|
||||
expect(dialog).not.toContain('#header')
|
||||
}
|
||||
expect(source).toContain('density-table--editable')
|
||||
expect(source).not.toContain('line-grid')
|
||||
expect(source).not.toContain('form-grid')
|
||||
}
|
||||
expect(quote.match(/fullscreenable/g)).toHaveLength(2)
|
||||
expect(quote).toContain('data-tone="summary"')
|
||||
expect(quote).toContain('data-tone="source"')
|
||||
expect(quote).toContain('data-tone="work"')
|
||||
const css = parse(quote).descriptor.styles.map(style => style.content).join('')
|
||||
expect(css).not.toMatch(/font-size|background|border|el-dialog__body|max-height/)
|
||||
})
|
||||
|
||||
it('keeps the supplier license dialog styled at its own teleported root', () => {
|
||||
const source = read('modules/suppliers/components/SupplierLicenseDialog.vue')
|
||||
expect(source).toContain('<AppDialog class="admin-density-dialog"')
|
||||
expect(source).toContain('width="min(560px, calc(100vw - 24px))"')
|
||||
expect(source).toContain('aria-label="营业执照信息"')
|
||||
expect(source).toContain('class="density-dialog-section" data-tone="source"')
|
||||
expect(source).toContain('class="density-table license-info-table"')
|
||||
expect(source).not.toContain('license-card')
|
||||
expect(source).not.toContain('box-shadow')
|
||||
})
|
||||
|
||||
it('uses identical authored tab paint for native domestic and Element overseas tabs', () => {
|
||||
@@ -384,6 +687,7 @@ describe('approved Admin density ownership', () => {
|
||||
}
|
||||
expect(read('modules/overseas-warehouse/components/OverseasReadOnlyDetail.vue')).toContain(':dense="dense"')
|
||||
expect(read('modules/overseas-warehouse/pages/InventoryTransactionsView.vue')).toContain('<InboundCodeRelationshipDialog dense')
|
||||
expect(read('modules/inbounds/pages/InboundOrdersView.vue')).toContain('<InboundCodeRelationshipDialog dense')
|
||||
for (const file of ['CartonDetailView', 'ProductDetailView']) expect(read('modules/overseas-warehouse/pages/' + file + '.vue')).not.toContain(' dense')
|
||||
})
|
||||
|
||||
@@ -421,21 +725,22 @@ describe('approved Admin density ownership', () => {
|
||||
it('keeps cargo native tables, row actions and detail tabs connected to shared styles', () => {
|
||||
for (const file of cargoConsumers) {
|
||||
const ast = parse(read(`modules/${file}`)).descriptor.template!.ast!
|
||||
const visit = (nodes: typeof ast.children, inCell = false, insideDensity = false) => {
|
||||
const visit = (nodes: typeof ast.children, inCell = false, insideDensity = false, insideEditor = false) => {
|
||||
for (const node of nodes) {
|
||||
if (node.type !== 1) continue
|
||||
const classes = classesOf(node)
|
||||
const enabled = insideDensity || classes.some(name => ['admin-density', 'admin-density-dialog'].includes(name))
|
||||
const editable = insideEditor || classes.includes('density-table--editable')
|
||||
if (node.tag === 'table') {
|
||||
expect(classes, `${file}: native table`).toContain('density-table')
|
||||
expect(insideDensity, `${file}: native table ancestor`).toBe(true)
|
||||
}
|
||||
if (node.tag === 'el-tabs') expect(classes).toContain('density-element-tabs')
|
||||
if (inCell && node.tag === 'el-button') {
|
||||
if (inCell && !editable && node.tag === 'el-button') {
|
||||
expect(classes, `${file}: native row action`).toContain('density-action')
|
||||
expect(node.children.some(child => child.type === 2 && child.content.trim() || child.type === 5), `${file}: empty action`).toBe(true)
|
||||
}
|
||||
visit(node.children, inCell || node.tag === 'td', enabled)
|
||||
visit(node.children, inCell || node.tag === 'td', enabled, editable)
|
||||
}
|
||||
}
|
||||
visit(ast.children)
|
||||
@@ -474,6 +779,30 @@ describe('approved Admin density ownership', () => {
|
||||
for (const file of densityPages) expect(read(`modules/${file}`), `${file}: direct entry CSS`).toContain('@/styles/adminDensity.css')
|
||||
})
|
||||
|
||||
it('keeps customer dialogs on shared sections, editors and preview geometry', () => {
|
||||
const customers = read('modules/customers/pages/CustomersView.vue')
|
||||
const detail = read('modules/customers/pages/CustomerDetailView.vue')
|
||||
expect(customers).toContain('density-table density-table--editable customer-editor-table')
|
||||
expect(customers).toContain('density-actions density-actions--footer')
|
||||
expect(customers).not.toContain('<FormSection')
|
||||
expect(customers).not.toContain('<el-dialog')
|
||||
for (const source of [customers, detail]) {
|
||||
expect(source).toContain('customer-qualification-preview')
|
||||
expect(source).toContain('data-tone="source"')
|
||||
expect(source).not.toMatch(/\.qualification-preview \{[^}]*overflow:auto/)
|
||||
}
|
||||
})
|
||||
|
||||
it('shares dense editable sections between both customer fee entries and billing rules', () => {
|
||||
for (const file of ['customers/CustomerOverseasStorageFeePanel.vue', 'customers/pages/OverseasStorageFeesView.vue', 'customers/components/CustomerBillingRulesPanel.vue']) {
|
||||
const source = read(`modules/${file}`)
|
||||
expect(source, file).toContain('density-table density-table--editable')
|
||||
expect(source, file).toContain('class="density-dialog-section" data-tone="work"')
|
||||
expect(source, file).toContain('fullscreenable')
|
||||
expect(source, file).not.toContain('class="dialog-actions"')
|
||||
}
|
||||
})
|
||||
|
||||
it('covers both customer menu entries and their shared detail primitives', () => {
|
||||
const navigation = read('core/navigation/adminNavigation.ts')
|
||||
const customers = navigation.slice(navigation.indexOf("key: 'customers'"), navigation.indexOf("key: 'domestic-warehouse'"))
|
||||
@@ -619,7 +948,7 @@ describe('approved Admin density ownership', () => {
|
||||
sectionStyles.set(rule.selector, declarations)
|
||||
})
|
||||
for (const [section, background] of [
|
||||
['summary-section', '#f4fbf8'], ['declaration', '#f7f9ff'], ['items-section', '#fffbf4'],
|
||||
['summary-section', 'var(--admin-dialog-summary-bg)'], ['declaration', 'var(--admin-dialog-source-bg)'], ['items-section', 'var(--admin-dialog-work-bg)'],
|
||||
]) {
|
||||
expect(sectionStyles.get(`.inbound-measurement-workspace-dialog.inbound-density .measurement-${section}`)).toMatchObject({
|
||||
padding: 'var(--admin-space-4, 10px) var(--admin-space-5, 12px)',
|
||||
@@ -719,6 +1048,324 @@ describe('approved Admin density ownership', () => {
|
||||
'var(--cc-row-action-gap, var(--admin-space-2))',
|
||||
])
|
||||
})
|
||||
it('keeps active last-mile order dialogs on shared sections without internal vertical caps', () => {
|
||||
const source = read('modules/overseas-warehouse/pages/LastMileOrdersView.vue')
|
||||
for (const model of ['transferOpen', 'containerConvertOpen', 'inboundPlanOpen', 'holdOpen', 'manualNodesOpen', 'reworkOpen']) {
|
||||
const frame = source.match(new RegExp('<AppDialog[^>]*v-model="' + model + '"[\\s\\S]*?(?=\\n <|\\n <|$)'))?.[0] || ''
|
||||
expect(frame, model).toContain('fullscreenable')
|
||||
}
|
||||
expect(source).toContain('data-tone="summary"')
|
||||
expect(source).toContain('data-tone="source"')
|
||||
expect(source).toContain('data-tone="work"')
|
||||
expect(source).toContain('class="frozen-pallet-count"')
|
||||
expect(source).not.toContain('max-height: 48vh')
|
||||
expect(source).not.toContain('.manual-node-table { max-height: 420px')
|
||||
expect(source).not.toContain('class="container-preview__metric')
|
||||
const detail = read('modules/overseas-warehouse/pages/LastMileOrderDetailView.vue')
|
||||
expect(detail).toContain('density-table density-table--editable fee-editor-table')
|
||||
expect(detail).toContain('label-position="left" label-width="110px" :disabled="saving"')
|
||||
expect(detail).not.toContain('.fee-form-grid')
|
||||
expect(read('modules/overseas-warehouse/components/WarehouseTransferReviewDialog.vue')).toContain('data-tone="source"')
|
||||
})
|
||||
it('keeps new dispatch settings and resources on shared continuous sections with body scrolling', () => {
|
||||
const source = read('modules/overseas-warehouse/components/SmartDispatchDialog.vue')
|
||||
const fields = read('modules/overseas-warehouse/components/DispatchResourceFields.vue')
|
||||
expect(source).toContain('fullscreenable')
|
||||
expect(source).toContain('density-table density-table--editable dispatch-create-table')
|
||||
expect(source).toContain('<DispatchResourceFields continuous')
|
||||
expect(source).toContain('label-position="left" label-width="130px"')
|
||||
for (const tone of ['summary', 'source', 'work']) expect(source).toContain(`data-tone="${tone}"`)
|
||||
expect(source).toContain('class="cargo-workspace"')
|
||||
expect(source).toContain('<LineClampText')
|
||||
expect(source).toContain('class="dispatch-source-group"')
|
||||
expect(source).not.toContain('height="420"')
|
||||
expect(source).not.toContain('class="create-grid"')
|
||||
expect(fields).toContain('continuous?: boolean')
|
||||
expect(fields).toContain('v-if="continuous"')
|
||||
expect(fields).toContain('density-table density-table--editable dispatch-resource-table')
|
||||
expect(fields).toContain('class="resource-grid"')
|
||||
})
|
||||
it('keeps dispatch workflow, delivery arrangements and owned evidence on shared compact sections', () => {
|
||||
const workflow = read('modules/overseas-warehouse/components/DispatchWorkflowDialog.vue')
|
||||
const arrangement = read('modules/overseas-warehouse/components/DispatchArrangeFields.vue')
|
||||
const evidence = read('modules/overseas-warehouse/components/DispatchEvidenceFiles.vue')
|
||||
expect(workflow).toContain(':fullscreenable="width !== \'560px\'"')
|
||||
expect(workflow).toContain('<DispatchResourceFields continuous')
|
||||
expect(workflow).toContain('density-table density-table--editable workflow-editor-table')
|
||||
expect(workflow).toContain('label-position="left" label-width="130px"')
|
||||
expect(workflow).toContain(':confirm-loading="busy || uploadingEvidence"')
|
||||
expect(workflow).toContain('<LineClampText')
|
||||
for (const tone of ['source', 'work']) {
|
||||
expect(workflow).toContain(`data-tone="${tone}"`)
|
||||
expect(arrangement).toContain(`data-tone="${tone}"`)
|
||||
}
|
||||
expect(workflow).not.toMatch(/max-height="(?:380|420)"/)
|
||||
expect(arrangement).not.toContain('max-height="240"')
|
||||
expect(arrangement).toContain('density-table density-table--editable arrange-editor-table')
|
||||
expect(arrangement).toContain('本组新增费用')
|
||||
expect(evidence).toContain('class="density-table dispatch-evidence-table"')
|
||||
expect(evidence).toContain('fullscreenable :loading="previewLoading"')
|
||||
expect(evidence).toContain('<LoadErrorState')
|
||||
expect(evidence).toContain('<LineClampText')
|
||||
for (const source of [workflow, arrangement, evidence]) {
|
||||
const styles = parse(source).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/background:|border-left:|font-size:/)
|
||||
}
|
||||
})
|
||||
it('keeps outbound creation modes compact with one body scroll and complete candidate controls', () => {
|
||||
const source = read('modules/overseas-warehouse/pages/OutboundsView.vue')
|
||||
expect(source).toContain('width="min(1760px, calc(100vw - 32px))" fullscreenable')
|
||||
expect(source).toContain('label-position="left" label-width="120px" :disabled="createLocked"')
|
||||
expect(source).toContain('density-table density-table--editable outbound-editor-table')
|
||||
expect(source).toContain('data-tone="source"')
|
||||
expect(source).toContain('data-tone="work"')
|
||||
expect(source).toContain(':confirm-loading="saving || inventoryLoading"')
|
||||
expect(source).toContain('aria-label="全选本页订单"')
|
||||
expect(source).toContain('class="return-list-card density-dialog-section"')
|
||||
expect(source).toContain(':page-sizes="[5, 10, 20, 50]"')
|
||||
expect(source).toContain('<ApiErrorMeta :details="candidateError"')
|
||||
expect(source).not.toMatch(/max-height="(?:360|380|400)"/)
|
||||
const styles = parse(source).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/background:|border-left:|font-size:|max-height:|overflow: auto/)
|
||||
})
|
||||
it('keeps delivery operations and documents on compact sections without read-only inputs', () => {
|
||||
const source = read('modules/overseas-warehouse/pages/DeliveryOrdersView.vue')
|
||||
const documents = read('modules/overseas-warehouse/components/DeliveryDocumentsDialog.vue')
|
||||
expect(source).toContain('density-table density-table--editable delivery-return-sources')
|
||||
expect(source).toContain('label-position="left" label-width="100px"')
|
||||
expect(source).toContain('label-position="left" label-width="110px"')
|
||||
expect(source).toContain(':confirm-loading="returnSaving || returnLoading"')
|
||||
expect(source).toContain('width="min(720px, calc(100vw - 28px))" fullscreenable')
|
||||
expect(documents).toContain('width="min(1040px, calc(100vw - 40px))" fullscreenable')
|
||||
expect(documents).toContain('data-tone="summary"')
|
||||
expect(documents).toContain('data-tone="source"')
|
||||
expect(documents).toContain('data-tone="work"')
|
||||
expect(documents).toContain('<LineClampText :content="file.fileName"')
|
||||
expect(documents).not.toContain(':model-value="deliveryNo" readonly')
|
||||
expect(documents).not.toContain('max-height=')
|
||||
const styles = parse(documents).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/background:|border-left:|font-size:|overflow: auto/)
|
||||
})
|
||||
it('shares relationship sections and fullscreen only through the explicit dense profile', () => {
|
||||
const content = read('shared/components/InboundCodeRelationshipDialog.vue')
|
||||
expect(content).toContain(':fullscreenable="dense"')
|
||||
expect(content).toContain("{ 'density-dialog-sections': dense }")
|
||||
expect(content.match(/'density-dialog-section': dense/g)).toHaveLength(3)
|
||||
expect(content.match(/'density-dialog-section__title': dense/g)).toHaveLength(3)
|
||||
expect(content).toContain(':data-tone="dense ? \'source\' : undefined"')
|
||||
expect(content).toContain(':data-tone="dense ? \'summary\' : undefined"')
|
||||
expect(content).not.toContain('.inbound-relationship-dialog.admin-density-dialog .relationship-section')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
cssRoot(styles).walkRules(rule => {
|
||||
rule.walkDecls(decl => {
|
||||
if (!['padding', 'background', 'border-radius', 'font-size', 'max-height'].includes(decl.prop)) return
|
||||
expect(rule.selector, 'legacy visual declarations must stay outside dense').toMatch(/:not\(\.admin-density-dialog\)|:not\(\.density-dialog-section\)|:not\(\.density-fields\)/)
|
||||
})
|
||||
})
|
||||
})
|
||||
it('keeps return packing and carton dialogs on shared sections and one body scroll', () => {
|
||||
const content = read('modules/overseas-warehouse/pages/ReturnInventoryManagementView.vue')
|
||||
const dialogs = content.slice(content.indexOf('<AppDialog'))
|
||||
expect(dialogs.match(/fullscreenable/g)).toHaveLength(3)
|
||||
expect(dialogs).toContain('1380px')
|
||||
expect(dialogs).toContain('1400px')
|
||||
expect(dialogs).toContain('1080px')
|
||||
expect(dialogs).toContain('label-position="left" label-width="110px"')
|
||||
expect(dialogs).toContain('density-table density-table--editable')
|
||||
expect(dialogs).toContain('data-tone="source"')
|
||||
expect(dialogs).toContain('data-tone="summary"')
|
||||
expect(dialogs).toContain('data-tone="work"')
|
||||
expect(dialogs).not.toContain('<el-descriptions')
|
||||
expect(dialogs).not.toContain('label-position="top"')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/max-height:|resize:|overflow: auto|border-radius:|font-size:|background:/)
|
||||
expect(content).not.toContain('.packing-form :deep(.el-input__inner)')
|
||||
})
|
||||
it('keeps discard readonly sections and confirmation compact while retaining photo geometry', () => {
|
||||
const content = read('modules/overseas-warehouse/pages/CargoDiscardsView.vue')
|
||||
const dialog = content.slice(content.indexOf('<AppDialog'))
|
||||
expect(dialog).toContain('class="admin-density-dialog"')
|
||||
expect(dialog).toContain('fullscreenable')
|
||||
expect(dialog).toContain('920px')
|
||||
expect(dialog).toContain('data-tone="summary"')
|
||||
expect(dialog).toContain('data-tone="source"')
|
||||
expect(dialog).toContain('data-tone="work"')
|
||||
expect(dialog).toContain('preview-teleported')
|
||||
expect(dialog).toContain('<LineClampText')
|
||||
expect(dialog).not.toContain('<el-timeline')
|
||||
expect(content).toContain("customClass: 'admin-density-confirm'")
|
||||
expect(content).toContain('height: 150px')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/background:|border-left:|font-size:|border-radius:/)
|
||||
})
|
||||
it('keeps overseas location editing and native confirmations on the shared dialog contract', () => {
|
||||
const content = read('modules/overseas-warehouse/pages/LocationsView.vue')
|
||||
const dialog = content.slice(content.indexOf('<AppDialog'))
|
||||
expect(dialog).toContain('class="admin-density-dialog"')
|
||||
expect(dialog).toContain('fullscreenable')
|
||||
expect(dialog).toContain('density-table density-table--editable')
|
||||
expect(dialog).toContain('label-position="left" label-width="110px" :disabled="formLocked"')
|
||||
expect(dialog).toContain('data-tone="work"')
|
||||
expect(dialog).not.toContain('label-position="top"')
|
||||
expect(content).not.toContain('editor-grid')
|
||||
expect(content.match(/customClass: 'admin-density-confirm'/g)).toHaveLength(2)
|
||||
expect(parse(content).descriptor.styles).toHaveLength(0)
|
||||
})
|
||||
it('keeps overseas pallet editing on one shared continuous table with native fullscreen', () => {
|
||||
const content = read('modules/overseas-warehouse/pages/PalletsView.vue')
|
||||
const dialog = content.slice(content.indexOf('<AppDialog'))
|
||||
expect(dialog).toContain('fullscreenable')
|
||||
expect(dialog).toContain('label-position="left" label-width="120px" :disabled="formLocked"')
|
||||
expect(dialog).toContain('density-table density-table--editable pallet-editor-table')
|
||||
expect(dialog).toContain('data-tone="work"')
|
||||
expect(dialog).not.toContain('label-position="top"')
|
||||
expect(dialog).not.toContain('<el-row')
|
||||
expect(content).not.toContain('.el-form :deep(')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
const editorRules = new Map<string, Record<string, string>>()
|
||||
cssRoot(styles).walkRules(rule => {
|
||||
if (!rule.selector.includes('pallet-editor-table')) return
|
||||
const values: Record<string, string> = {}
|
||||
rule.walkDecls(decl => { values[decl.prop] = decl.value })
|
||||
editorRules.set(rule.selector, values)
|
||||
})
|
||||
expect([...editorRules]).toEqual([
|
||||
['.pallet-editor-table', { 'table-layout': 'fixed' }],
|
||||
['.pallet-editor-table td:not([colspan])', { width: '50%' }],
|
||||
])
|
||||
})
|
||||
it('keeps relabel creation, review and PDF previews compact without replacing document sizing', () => {
|
||||
const list = read('modules/overseas-warehouse/pages/RelabelOrdersView.vue')
|
||||
const detail = read('modules/overseas-warehouse/pages/RelabelOrderDetailView.vue')
|
||||
expect(list).toContain('width="min(760px, calc(100vw - 28px))" fullscreenable')
|
||||
expect(list).toContain('density-table density-table--editable relabel-editor-table')
|
||||
expect(list).toContain('aria-label="换标审核原始资料"')
|
||||
for (const tone of ['summary', 'source', 'work']) expect(list).toContain(`data-tone="${tone}"`)
|
||||
expect(list).not.toContain('label-position="top"')
|
||||
expect(list).not.toContain('packing-plan-dialog')
|
||||
expect(detail).toContain('width="min(900px, calc(100vw - 28px))" fullscreenable')
|
||||
expect(detail).toContain(':loading="previewLoading"')
|
||||
expect(detail).toContain('height:min(72vh,760px)')
|
||||
expect(detail).toContain('<th class="relabel-pdf-actions">操作(明细PDF)</th>')
|
||||
expect(detail).not.toContain('item.destinationFbaWarehouseCode || item.destinationWarehouseId')
|
||||
expect(parse(list).descriptor.styles.map(style => style.content).join('\n')).not.toMatch(/background:|border-left:|font-size:|max-height:/)
|
||||
})
|
||||
it('keeps inbound appointment, plan and move editors on opt-in continuous forms', () => {
|
||||
const list = read('modules/overseas-warehouse/pages/InboundOrdersView.vue')
|
||||
const detail = read('modules/overseas-warehouse/pages/InboundOrderDetailView.vue')
|
||||
const fields = read('modules/overseas-warehouse/components/FbaAppointmentFields.vue')
|
||||
expect(list).toContain('<FbaAppointmentFields continuous')
|
||||
expect(list.match(/fullscreenable/g)).toHaveLength(2)
|
||||
expect(list).toContain('data-tone="summary"')
|
||||
expect(list).toContain('data-tone="source"')
|
||||
expect(list).toContain('data-tone="work"')
|
||||
expect(list).not.toContain('v-model="planEditForm.version"')
|
||||
expect(fields).toContain('continuous?: boolean')
|
||||
expect(fields).toContain('v-if="continuous"')
|
||||
expect(fields).toContain('density-table density-table--editable appointment-editor-table')
|
||||
expect(fields).toContain('label-position="left" label-width="140px"')
|
||||
expect(fields).toContain('label-position="top" class="appointment-fields density-form"')
|
||||
const move = detail.slice(detail.indexOf('<AppDialog'))
|
||||
expect(move).toContain('fullscreenable')
|
||||
expect(move).toContain('aria-label="移库来源"')
|
||||
expect(move).toContain('label-position="left" label-width="100px" :disabled="moveSaving || moveLocationsLoading"')
|
||||
expect(move).not.toContain(':model-value="moveRow?.palletNo"')
|
||||
})
|
||||
it('keeps transfer allocation and receipt popups on shared sections and the native submit footer', () => {
|
||||
const allocation = read('modules/overseas-warehouse/components/WarehouseTransferFulfillmentPanel.vue')
|
||||
const receipt = read('modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue')
|
||||
for (const source of [allocation, receipt]) {
|
||||
expect(source).toContain("import '@/styles/adminDensity.css'")
|
||||
expect(source).toContain('class="density-dialog-sections"')
|
||||
expect(source).toContain('data-tone="summary"')
|
||||
expect(source).toContain('data-tone="source"')
|
||||
expect(source).not.toMatch(/max-height:|overflow-y:/)
|
||||
}
|
||||
expect(allocation.match(/fullscreenable/g)).toHaveLength(1)
|
||||
expect(receipt.match(/fullscreenable/g)).toHaveLength(3)
|
||||
expect(allocation).toContain('row-key="inventoryPositionId" max-height=""')
|
||||
expect(receipt).toContain('row-key="cartonMark" max-height=""')
|
||||
expect(receipt).toContain('row-key="sku" max-height=""')
|
||||
const forecast = receipt.split('v-model="increaseOpen"')[1]!.split('</AppDialog>')[0]!
|
||||
expect(forecast).toContain(':confirm-loading="increasing"')
|
||||
expect(forecast).toContain('@confirm="saveIncreaseForecast"')
|
||||
expect(forecast).toContain('label-position="left" label-width="100px" :disabled="increasing"')
|
||||
expect(forecast).toContain('density-table density-table--editable')
|
||||
expect(forecast).not.toContain(':show-footer="false"')
|
||||
expect(forecast).not.toContain('<el-button')
|
||||
})
|
||||
it('keeps settlement generation and cashiers on continuous forms with native fullscreen', () => {
|
||||
const source = read('modules/finance/pages/FinanceFormalSettlementPage.vue')
|
||||
const popups = source.slice(source.indexOf('<AppDialog'))
|
||||
expect(popups.match(/fullscreenable/g)).toHaveLength(3)
|
||||
expect(popups.match(/density-table density-table--editable/g)).toHaveLength(3)
|
||||
expect(popups).toContain('label-position="left" label-width="120px" :disabled="createBusy"')
|
||||
for (const flag of ['paymentBusy', 'receiptBusy']) expect(popups).toContain(`label-position="left" label-width="140px" :disabled="${flag}"`)
|
||||
expect(popups).toContain('data-tone="summary"')
|
||||
expect(popups).toContain('data-tone="work"')
|
||||
expect(popups).toContain(':key="paymentUploadKey"')
|
||||
expect(popups).toContain(':key="receiptUploadKey"')
|
||||
expect(popups).not.toContain('label-position="top"')
|
||||
expect(source).not.toContain('.remaining-preview')
|
||||
expect(source).not.toContain('.settlement-operation-form { display: grid')
|
||||
})
|
||||
it('keeps fee and statement popup editors on the shared density and main body scroll', () => {
|
||||
const arap = read('modules/finance/pages/FinanceCustomerArapView.vue').split('<AppDialog')[1]!
|
||||
expect(arap).toContain('fullscreenable')
|
||||
expect(arap).toContain('data-tone="source"')
|
||||
expect(arap).toContain('data-tone="work"')
|
||||
expect(arap).toContain('density-table density-table--editable')
|
||||
expect(arap).toContain('label-position="left" label-width="110px" :disabled="reviewSaving"')
|
||||
const statements = read('modules/finance/pages/FinanceCustomerStatementsView.vue')
|
||||
const popups = statements.slice(statements.indexOf('<AppDialog'))
|
||||
expect(popups.match(/fullscreenable/g)).toHaveLength(2)
|
||||
expect(popups).toContain('label-position="left" label-width="90px" :disabled="createBusy"')
|
||||
expect(popups).toContain('label-width="110px" :disabled="adjustBusy"')
|
||||
expect(popups).toContain('<OperationErrorToast :details="adjustError"')
|
||||
expect(popups).toContain('data-tone="summary"')
|
||||
expect(popups).not.toContain('max-height="420"')
|
||||
expect(popups).not.toContain('label-position="top"')
|
||||
expect(statements).not.toContain('.create-period-row')
|
||||
})
|
||||
it('keeps wallet dialogs compact while preserving financial warnings and preview space', () => {
|
||||
const content = read('modules/finance/pages/FinanceCustomerAccountsView.vue')
|
||||
const popups = content.slice(content.indexOf('<AppDialog'))
|
||||
expect(popups.match(/fullscreenable/g)).toHaveLength(4)
|
||||
expect(popups.match(/class="density-table density-table--editable/g)).toHaveLength(3)
|
||||
expect(popups.match(/label-position="left" label-width="100px"/g)).toHaveLength(3)
|
||||
for (const flag of ['adjustmentSaving', 'creditLimitSaving', 'reviewSaving']) expect(popups).toContain(`:disabled="${flag}"`)
|
||||
expect(popups).toContain('data-tone="summary"')
|
||||
expect(popups).toContain('data-tone="source"')
|
||||
expect(popups).toContain('data-tone="work"')
|
||||
expect(popups).toContain('不足部分再使用可用授信')
|
||||
expect(popups).toContain('次日授信失效')
|
||||
expect(popups).not.toContain(' readonly')
|
||||
expect(content).not.toContain('.wallet-form__grid')
|
||||
expect(content).toContain('max-height: 70vh')
|
||||
expect(content).toContain('onBeforeUnmount(clearVoucherPreview)')
|
||||
})
|
||||
it('keeps notification editors and detail drawers on the shared popup palette', () => {
|
||||
for (const file of ['MessageComposerDialog', 'TemplateEditorDialog']) {
|
||||
const content = read(`modules/notifications/components/${file}.vue`)
|
||||
expect(content).toContain('fullscreenable :confirm-loading="submitting"')
|
||||
expect(content).toContain('class="density-form density-dialog-sections" label-position="left"')
|
||||
expect(content).toContain('density-table density-table--editable')
|
||||
expect(content).toContain('density-actions density-actions--footer')
|
||||
expect(content).toContain('data-tone="work"')
|
||||
expect(content).toContain('<InlineHelp')
|
||||
expect(content).not.toContain('class="dialog-actions"')
|
||||
expect(content).not.toContain('model-value="站内信" disabled')
|
||||
}
|
||||
for (const file of ['PlatformNotificationsView', 'MessageManagementView']) {
|
||||
const content = read(`modules/notifications/pages/${file}.vue`)
|
||||
expect(content).toContain('admin-density-drawer--detail')
|
||||
expect(content).toContain('data-tone="summary"')
|
||||
expect(content).toContain('data-tone="source"')
|
||||
expect(content).toContain('@retry="detailRow && openDetail(detailRow)"')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toMatch(/background:|border-left:|font-size:|max-height:/)
|
||||
}
|
||||
})
|
||||
it('keeps container evidence and customs summaries compact without duplicating field styles', () => {
|
||||
const overview = read('modules/container-loading/components/ContainerOverviewPanel.vue')
|
||||
const customs = read('modules/container-loading/components/ContainerCustomsPanel.vue')
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
--admin-table-border: #e5e7eb;
|
||||
--admin-row-hover: #f3f7fb;
|
||||
--admin-row-stripe: #fcfcfd;
|
||||
--admin-dialog-section-radius: 8px;
|
||||
--admin-dialog-summary-bg: #f4fbf8;
|
||||
--admin-dialog-summary-border: #cce8df;
|
||||
--admin-dialog-source-bg: #f7f9ff;
|
||||
--admin-dialog-source-border: #d9e2fa;
|
||||
--admin-dialog-work-bg: #fffbf4;
|
||||
--admin-dialog-work-border: #eadfc9;
|
||||
--admin-action-neutral: #1777ff;
|
||||
--admin-action-neutral-border: #dfe3ee;
|
||||
--admin-action-warning: #c76500;
|
||||
|
||||
Reference in New Issue
Block a user