style(finance): unify list and detail layouts and receipt controls
This commit is contained in:
@@ -221,6 +221,110 @@ const cssRoot = (css: string) => {
|
||||
}
|
||||
|
||||
describe('approved Admin density ownership', () => {
|
||||
it('draws only one separator between an Element table and pagination', () => {
|
||||
const style = parse(read('shared/components/DataTable.vue')).descriptor.styles[0]!
|
||||
const compiled = compileStyle({ source: style.content, filename: 'DataTable.vue', id: 'data-v-border-test', scoped: true })
|
||||
expect(compiled.errors).toHaveLength(0)
|
||||
const selectors: string[] = []
|
||||
compiled.rawResult!.root.walkRules(rule => {
|
||||
if (rule.selector.includes('.el-table + .data-table__pagination')) {
|
||||
selectors.push(rule.selector)
|
||||
expect(rule.nodes.some(node => node.type === 'decl' && node.prop === 'border-top' && node.value === '0')).toBe(true)
|
||||
}
|
||||
})
|
||||
expect(selectors).toHaveLength(1)
|
||||
const host = document.createElement('div')
|
||||
host.innerHTML = `<div class="data-table"><div class="el-table"><div class="el-table__inner-wrapper"></div></div><footer id="standard" class="data-table__pagination" data-v-border-test></footer></div>
|
||||
<div class="data-table"><section class="custom-results"></section><footer id="custom" class="data-table__pagination" data-v-border-test></footer></div>`
|
||||
expect(host.querySelector('#standard')!.matches(selectors[0]!)).toBe(true)
|
||||
expect(host.querySelector('#custom')!.matches(selectors[0]!)).toBe(false)
|
||||
expect(style.content).not.toMatch(/inner-wrapper::before\s*\{[^}]*display:\s*none/)
|
||||
})
|
||||
|
||||
it('uses the shared compact metric cards for finance statistics and settlement totals', () => {
|
||||
expect(read('modules/finance/pages/FinanceReceivableStatisticsView.vue')).toContain('spacing="compact"')
|
||||
expect(read('modules/finance/pages/FinanceFormalSettlementPage.vue')).toContain(':spacing="detailMode ? \'normal\' : \'compact\'"')
|
||||
const financeStyles = read('modules/finance/financeDensity.css')
|
||||
expect(financeStyles).toContain('.admin-workbench.receivable-statistics-page.admin-page.is-spacing-compact,')
|
||||
expect(financeStyles).toContain('.admin-workbench.finance-settlement-page.admin-page.is-spacing-compact { gap: 0; }')
|
||||
expect(financeStyles).not.toContain('.data-table__toolbar')
|
||||
const baseStyle = parse(read('shared/components/AdminPage.vue')).descriptor.styles[0]!
|
||||
const compiled = compileStyle({ source: baseStyle.content, filename: 'AdminPage.vue', id: 'data-v-spacing-check', scoped: true })
|
||||
expect(compiled.errors).toHaveLength(0)
|
||||
const baseSelector = compiled.code.match(/([^{}]+)\{\s*gap: 12px;/)![1]!.trim()
|
||||
const specificity = (selector: string) => (selector.match(/\.[\w-]+|\[[^\]]+\]/g) ?? []).length
|
||||
for (const page of ['receivable-statistics-page', 'finance-settlement-page']) {
|
||||
const selector = `.admin-workbench.${page}.admin-page.is-spacing-compact`
|
||||
expect(specificity(selector), 'must override scoped AdminPage even when it loads later').toBeGreaterThan(specificity(baseSelector))
|
||||
}
|
||||
for (const file of ['FinanceReceivableStatisticsView', 'FinanceFormalSettlementPage']) {
|
||||
const content = read(`modules/finance/pages/${file}.vue`)
|
||||
const summary = content.match(/<section class="finance-metric-grid"[^>]*v-loading="loading"[\s\S]*?<\/section>/)![0]
|
||||
expect(content.indexOf(summary), file).toBeLessThan(content.indexOf('<template #filters>'))
|
||||
const template = parse(content).descriptor.template!.ast!
|
||||
function visit(node: { tag?: string; loc?: { source: string }; children?: unknown[] }, insideTable = false) {
|
||||
if (node.tag === 'section' && node.loc?.source === summary) expect(insideTable, file).toBe(false)
|
||||
for (const child of node.children ?? []) visit(child as typeof node, insideTable || node.tag === 'DataTable')
|
||||
}
|
||||
visit(template)
|
||||
expect(summary.match(/workbench-metric workbench-tone is-compact/g), file).toHaveLength(3)
|
||||
expect(summary).toContain('v-loading="loading" :aria-busy="loading"')
|
||||
expect(summary).not.toContain('density-detail-overview')
|
||||
for (const tone of ['blue', 'green', 'purple']) expect(summary).toContain(`data-tone="${tone}"`)
|
||||
}
|
||||
})
|
||||
it('keeps finance detail tables on one continuous surface', () => {
|
||||
const statements = read('modules/finance/pages/FinanceCustomerStatementsView.vue')
|
||||
const arap = read('modules/finance/pages/FinanceCustomerArapView.vue')
|
||||
expect(statements).not.toContain('<el-tabs')
|
||||
expect(statements).toContain('title="账单明细表"')
|
||||
expect(statements).toContain('title="账单支付历史表"')
|
||||
expect(statements.match(/class="detail-table-scroll workbench-table is-page-surface"/g)).toHaveLength(2)
|
||||
expect(statements).not.toContain('title="账单明细"')
|
||||
expect(statements).not.toContain('title="支付历史"')
|
||||
expect(arap).toContain('<div class="workbench-table is-page-surface"><DataTable :data="operationLogRows"')
|
||||
const css = read('modules/finance/financeDensity.css')
|
||||
expect(css).toContain('.finance-density-detail > .detail-page-layout__content .density-form-section')
|
||||
expect(css).toContain('padding-block: 0;')
|
||||
const accounts = read('modules/finance/pages/FinanceCustomerAccountsView.vue')
|
||||
expect(accounts).not.toContain(':deep(.detail-page-layout__content)')
|
||||
})
|
||||
it('keeps finance detail insets on the outer page without stripping card or cell padding', () => {
|
||||
for (const [file, className] of [['FinanceCustomerAccountsView', 'finance-account-detail'], ['FinanceCustomerArapView', 'finance-arap-detail']]) {
|
||||
expect(read(`modules/finance/pages/${file}.vue`)).toContain(`class="${className} admin-density admin-workbench is-embedded"`)
|
||||
}
|
||||
const css = read('modules/finance/financeDensity.css')
|
||||
const rule = css.slice(css.indexOf('/* The outer AdminPage owns'))
|
||||
expect(rule).toContain('@media screen')
|
||||
expect(rule).toContain('padding-inline: 0;')
|
||||
expect(rule).toContain('.detail-page-layout__content > .settlement-detail-content')
|
||||
expect(rule).toContain(':is(.density-detail-overview, .statement-summary-section)')
|
||||
for (const protectedClass of ['workbench-metric', 'el-descriptions__cell', 'query-toolbar', 'el-table']) expect(rule).not.toContain(protectedClass)
|
||||
const accountStyles = parse(read('modules/finance/pages/FinanceCustomerAccountsView.vue')).descriptor.styles[0]!.content
|
||||
expect(accountStyles.match(/\.account-metrics \{([^}]+)\}/)![1]).not.toMatch(/padding:|padding-inline:|padding-left:|padding-right:/)
|
||||
})
|
||||
it('keeps account detail filters in the shared table layout and long identifiers readable', () => {
|
||||
const content = read('modules/finance/pages/FinanceCustomerAccountsView.vue')
|
||||
const header = content.split('<template #header>')[1]!.split('</template>')[0]!
|
||||
expect(header).toContain('@click="exportLedger"')
|
||||
expect(header).toContain('v-if="canExport"')
|
||||
expect(header).toContain(':loading="ledgerExportBusy"')
|
||||
const ledgerSection = content.split('class="ledger-section density-form-section"')[1]!.split('</FormSection>')[0]!
|
||||
expect(ledgerSection).not.toContain('#toolbar-actions')
|
||||
expect(ledgerSection).toContain('class="detail-table-scroll workbench-table is-page-surface"')
|
||||
expect(ledgerSection).toContain('class="ledger-table" appearance="integrated"')
|
||||
const rechargeSection = content.split('class="recharge-section density-form-section"')[1]!.split('</FormSection>')[0]!
|
||||
expect(rechargeSection).toContain('class="detail-table-scroll workbench-table is-page-surface"')
|
||||
expect(content.match(/account-metric workbench-metric workbench-tone is-compact/g)).toHaveLength(6)
|
||||
expect(content).toContain('creditExpiryLabel(selected)')
|
||||
expect(content).toContain("'is-negative': Number(selected.actualBalance) < 0")
|
||||
expect(content).toContain('<template #filters><QueryToolbar v-model="ledgerFilters"')
|
||||
expect(content).not.toContain(':deep(.ledger-query.query-toolbar)')
|
||||
expect(content).toContain('class="density-descriptions account-profile"')
|
||||
const styles = parse(content).descriptor.styles.map(style => style.content).join('\n')
|
||||
expect(styles).not.toContain('text-overflow: ellipsis')
|
||||
expect(styles).toContain('overflow-wrap: anywhere')
|
||||
})
|
||||
it('keeps pallet overview spacing and metric appearance owned by shared primitives', () => {
|
||||
for (const file of ['warehouse-resources/pallets/pages/PalletsView.vue', 'overseas-warehouse/pages/PalletsView.vue']) {
|
||||
const page = parse(read(`modules/${file}`)).descriptor
|
||||
|
||||
Reference in New Issue
Block a user