feat(fleet): integrate trip management and fee coverage workflows

This commit is contained in:
2026-10-03 04:35:55 +08:00
parent bca08f8749
commit dc35288a1b
87 changed files with 3774 additions and 233 deletions
+20
View File
@@ -154,6 +154,26 @@
.admin-density .density-element-tabs > .el-tabs__header .el-tabs__nav-wrap::after { height: 1px; background: var(--cc-border); }
.admin-density .density-element-tabs > .el-tabs__header .el-tabs__active-bar { display: none; }
.admin-density .density-element-tabs:not(.density-contained-tabs) > .el-tabs__content { display: none; }
/* List status tabs share the approved domestic order appearance. */
.admin-density .density-status-tabs { display: flex; max-width: 100%; overflow-x: auto; margin-bottom: 0; border-bottom: 1px solid var(--admin-table-border, #e5e7eb); background: #f9fafb; scrollbar-width: thin; }
.admin-density .density-status-tabs > button,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item { position: relative; display: inline-flex; align-items: center; gap: var(--admin-space-2); flex: 0 0 auto; min-height: var(--admin-tab-height, 38px); padding: 0 var(--admin-space-6, 16px); border: 0; background: transparent; color: var(--cc-muted); font: inherit; font-size: var(--admin-font-body, 13px); cursor: pointer; white-space: nowrap; }
.admin-density .density-status-tabs > button::after,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item::after { position: absolute; right: var(--admin-space-5, 12px); bottom: 0; left: var(--admin-space-5, 12px); height: 3px; background: transparent; content: ''; }
.admin-density .density-status-tabs > button:hover,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item:hover { color: var(--cc-primary); background: #f6f9fd; }
.admin-density .density-status-tabs > button:focus-visible,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item:focus-visible { outline: 2px solid var(--cc-primary); outline-offset: -2px; }
.admin-density .density-status-tabs > button.is-active,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item.is-active { background: transparent; color: var(--cc-primary); font-weight: 600; }
.admin-density .density-status-tabs > button.is-active::after,
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item.is-active::after { background: var(--cc-primary); }
.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav-wrap::after { height: 0; }
@media (max-width: 720px) { .admin-density .density-status-tabs > button { min-height: 46px; padding: 0 14px; } }
@media (max-width: 360px) { .admin-density .density-status-tabs > button { padding: 0 10px; } }
.admin-density .density-contained-tabs > .el-tabs__content { padding: var(--admin-space-5); }
/* Explicit page descriptions share the teleported detail table scale. */
+37 -11
View File
@@ -3,6 +3,7 @@ import { dirname, resolve } from 'node:path'
import ts from 'typescript'
import { describe, expect, it } from 'vitest'
import { compileStyle, parse } from 'vue/compiler-sfc'
import { adminNavigation, getNavigationLeaves } from '@/core/navigation/adminNavigation'
// Approved entry/consumer inventory. Do not discover unrelated menus recursively.
const cargoConsumers = [
@@ -61,6 +62,13 @@ const generalCatalogConsumers = [
'overseas-master-data/pages/OverseasQuotesView.vue',
'overseas-master-data/pages/VehiclesView.vue',
'overseas-master-data/pages/VehicleDetailView.vue',
'overseas-master-data/pages/DriversView.vue',
'overseas-master-data/pages/TripsView.vue',
'overseas-master-data/pages/TripDetailView.vue',
'overseas-master-data/components/TripPlanDialog.vue',
'overseas-master-data/pages/DriverDetailView.vue',
'overseas-master-data/components/DriverEditorDialog.vue',
'overseas-master-data/components/DriverLicensePreview.vue',
'master-data/express-companies/pages/ExpressCompaniesView.vue',
'master-data/counterparties/pages/PartnersView.vue',
'fee-items/pages/FeeItemsView.vue',
@@ -151,6 +159,29 @@ const cssRoot = (css: string) => {
}
describe('approved Admin density ownership', () => {
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',
'overseas-master-data/pages/VehiclesView.vue', 'suppliers/pages/SuppliersView.vue',
'domestic-containers/pages/DomesticContainersView.vue', 'container-loading/pages/ContainersView.vue',
]) {
const page = parse(read(`modules/${file}`)).descriptor
expect(page.template!.content).toMatch(/class="[^"]*density-status-tabs[^"]*" role="tablist"/)
expect(page.styles.map((style) => style.content).join('\n')).not.toMatch(/\.(status-tabs?|supplier-tabs|container-scope-tabs)\s*[{:.]/)
}
expect(read('modules/overseas-warehouse/pages/ReturnInventoryManagementView.vue')).toContain('class="density-element-tabs density-status-tabs"')
const selectors: string[] = []
cssRoot(shared).walkRules((rule) => { selectors.push(...rule.selectors) })
expect(selectors).toContain('.admin-density .density-status-tabs > button.is-active::after')
expect(selectors).toContain('.admin-density .density-status-tabs > button:focus-visible')
const element = '.admin-density .density-element-tabs.density-status-tabs > .el-tabs__header .el-tabs__nav .el-tabs__item'
const rules: string[][] = []
cssRoot(shared).walkRules((rule) => { rules.push(rule.selectors) })
for (const suffix of ['', '::after', ':hover', ':focus-visible', '.is-active', '.is-active::after']) {
expect(rules.some((rule) => rule.includes('.admin-density .density-status-tabs > button' + suffix) && rule.includes(element + suffix))).toBe(true)
}
})
it('keeps dashboard visualizations outside detail-field table styling', () => {
const dashboard = read('modules/dashboard/pages/DashboardView.vue')
expect(dashboard).toContain('class="dashboard-metrics"')
@@ -248,11 +279,9 @@ describe('approved Admin density ownership', () => {
expect(read('modules/overseas-warehouse/pages/LastMileOrderDetailView.vue')).toContain('density-detail-overview')
})
it('covers all eleven overseas warehouse menus, details and both return inventory modes', () => {
const navigation = read('core/navigation/adminNavigation.ts')
const section = navigation.slice(navigation.indexOf("key: 'overseas-inventory'"), navigation.indexOf("key: 'customer-service'"))
for (const path of ['last-mile-orders', 'inbound-orders', 'locations', 'dispatches', 'outbounds', 'delivery-orders', 'relabel-orders', 'pallets', 'inventory', 'return-inventory', 'return-cartons']) expect(section).toContain("path: '/overseas-warehouse/" + path + "'")
expect([...section.matchAll(/path: '/g)]).toHaveLength(11)
it('covers the approved overseas pages across the latest menu groups, details and both return inventory modes', () => {
const paths = getNavigationLeaves(adminNavigation).map(node => node.path)
expect(paths).toEqual(expect.arrayContaining(['last-mile-orders', 'inbound-orders', 'locations', 'dispatches', 'outbounds', 'delivery-orders', 'relabel-orders', 'pallets', 'inventory', 'return-inventory', 'return-cartons'].map(path => '/overseas-warehouse/' + path)))
const routes = read('modules/overseas-warehouse/routes.ts')
for (const mode of ['inventory', 'cartons']) expect(routes).toContain("mode: '" + mode + "'")
for (const file of overseasConsumers) {
@@ -377,13 +406,10 @@ describe('approved Admin density ownership', () => {
}
})
it('covers all eight domestic menu entries and loads density CSS on direct entry', () => {
const navigation = read('core/navigation/adminNavigation.ts')
const domestic = navigation.slice(navigation.indexOf("key: 'domestic-warehouse'"), navigation.indexOf("key: 'overseas-delivery'"))
expect([...domestic.matchAll(/path: '/g)]).toHaveLength(domesticPages.length)
it('covers approved domestic pages across the latest menu groups and loads density CSS on direct entry', () => {
const paths = getNavigationLeaves(adminNavigation).map(node => node.path)
expect(paths).toEqual(expect.arrayContaining(domesticPages.map(page => page.path)))
for (const page of domesticPages) {
expect(domestic).toContain(`title: '${page.title}'`)
expect(domestic).toContain(`path: '${page.path}'`)
if (page.entry) expect(read(`modules/${page.entry}`)).toContain(`<${page.component} mode="${page.mode}"`)
}
for (const file of densityPages) expect(read(`modules/${file}`), `${file}: direct entry CSS`).toContain('@/styles/adminDensity.css')