diff --git a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs index d032e76..fdd0d6e 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.test.mjs +++ b/src/modules/first-leg/views/FirstLegOrdersView.test.mjs @@ -109,6 +109,14 @@ test('uses only fields present on C02 rows before loading full detail', () => { assert.match(source, /createdFrom: range\[0\] \? `\$\{range\[0\]\}T00:00:00`/) }) +test('shows and searches the inbound code after a draft is submitted', () => { + assert.match(source, /placeholder: '订单号、入仓码、渠道或仓库'/) + assert.match(source, /order\.businessSummary\.inboundCode/) + assert.match(source, /v-if="\(row as FirstLegOrder\)\.businessSummary\.inboundCode"[^>]*>入仓码/) + assert.match(source, /v-if="order\.businessSummary\.inboundCode"[^>]*>入仓码/) + assert.match(source, /v-if="detailOrder\.businessSummary\.inboundCode">
入仓码<\/dt>/) +}) + test('keeps the seven-stage progress and business summary usable on mobile', () => { assert.match(progressSource, /grid-template-columns: repeat\(7, 82px\)/) assert.match(progressSource, /overflow-x: auto/) diff --git a/src/modules/first-leg/views/FirstLegOrdersView.vue b/src/modules/first-leg/views/FirstLegOrdersView.vue index 92d7ede..333eecd 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.vue +++ b/src/modules/first-leg/views/FirstLegOrdersView.vue @@ -44,7 +44,7 @@ const availableDestinationWarehouses = computed(() => store.warehouses.filter((w const filters = ref({ keyword: '', status: '', createdRange: [] }) const appliedFilters = ref({ ...filters.value }) const queryFields: QueryField[] = [ - { key: 'keyword', type: 'input', placeholder: '订单号、渠道或仓库', props: { 'aria-label': '订单关键词' } }, + { key: 'keyword', type: 'input', placeholder: '订单号、入仓码、渠道或仓库', props: { 'aria-label': '订单关键词' } }, { key: 'status', type: 'select', placeholder: '全部状态', options: FIRST_LEG_ORDER_STATUSES.map((item) => ({ label: item.label, value: item.code })), props: { 'aria-label': '订单状态' } }, { key: 'createdRange', type: 'daterange', placeholder: '订单创建日期', props: { 'aria-label': '订单创建日期' } }, ] @@ -264,7 +264,7 @@ const filteredOrders = computed(() => store.orders.filter((order) => { const keyword = typeof keywordValue === 'string' ? keywordValue.trim().toLowerCase() : '' const status = typeof statusValue === 'string' ? statusValue as FirstLegOrderStatus | '' : '' const createdRange = Array.isArray(createdRangeValue) ? createdRangeValue.filter((value): value is string => typeof value === 'string') : [] - const matchesKeyword = !keyword || [order.orderNo, order.referenceNo, order.origin, order.destination, order.items.map((item) => item.cargoNameCn).join(' ')] + const matchesKeyword = !keyword || [order.orderNo, order.businessSummary.inboundCode, order.referenceNo, order.origin, order.destination, order.items.map((item) => item.cargoNameCn).join(' ')] .filter(Boolean) .some((value) => String(value).toLowerCase().includes(keyword)) const matchesStatus = !status || order.status === status @@ -874,6 +874,7 @@ async function searchOrders(model: QueryModel) { @@ -891,6 +892,7 @@ async function searchOrders(model: QueryModel) {
{{ getFirstLegStatusLabel(order.status) }}
+ 入仓码 {{ order.businessSummary.inboundCode }} {{ order.referenceNo || '无客户参考号' }}
目的地
{{ order.destination || '待更新' }}
收货仓
{{ order.receivingWarehouseName || '待更新' }}
货物摘要
{{ getOrderCargoSummary(order) }}
运输渠道
{{ order.transportMode ? getFirstLegTransportModeLabel(order.transportMode) : order.channelName || '待更新' }}
更新于 {{ formatDateTime(order.updatedAt) }}
详情编辑
@@ -1008,6 +1010,7 @@ async function searchOrders(model: QueryModel) {

订单与运输

+
入仓码
{{ detailOrder.businessSummary.inboundCode }}
客户参考号
{{ displayValue(detailOrder.referenceNo) }}
创建时间
{{ formatDateTime(detailOrder.createdAt) }}
运输方式
{{ getFirstLegTransportModeLabel(detailOrder.transportMode) }}
diff --git a/src/navigation/workspace-tabs/unsavedWork.ts b/src/navigation/workspace-tabs/unsavedWork.ts index a334770..d801722 100644 --- a/src/navigation/workspace-tabs/unsavedWork.ts +++ b/src/navigation/workspace-tabs/unsavedWork.ts @@ -1,4 +1,4 @@ -import { computed, onBeforeUnmount, ref, watchEffect, type Ref } from 'vue' +import { computed, onBeforeUnmount, ref, watch, type Ref } from 'vue' import { defineStore } from 'pinia' export interface UnsavedWorkEntry { @@ -23,7 +23,11 @@ export const useUnsavedWorkStore = defineStore('client-unsaved-work', () => { if (owner && owners.get(entry.key) !== owner) return const index = entries.value.findIndex((item) => item.key === entry.key) if (index === -1) entries.value.push(entry) - else entries.value[index] = entry + else { + const current = entries.value[index] + if (current.routeKey === entry.routeKey && current.label === entry.label && current.dirty === entry.dirty) return + entries.value[index] = entry + } } function remove(key: string, owner?: symbol) { @@ -62,13 +66,13 @@ export function useUnsavedWorkRegistration(options: { const store = useUnsavedWorkStore() const owner = Symbol(options.key) store.claim(options.key, owner) - watchEffect(() => { + watch(options.dirty, (dirty) => { store.upsert({ key: options.key, routeKey: options.routeKey, label: options.label, - dirty: options.dirty.value, + dirty, }, owner) - }) + }, { immediate: true }) onBeforeUnmount(() => store.remove(options.key, owner)) } diff --git a/src/navigation/workspaceTabs.test.mjs b/src/navigation/workspaceTabs.test.mjs index f6acb5a..b666043 100644 --- a/src/navigation/workspaceTabs.test.mjs +++ b/src/navigation/workspaceTabs.test.mjs @@ -65,6 +65,13 @@ test('unsaved registrations use instance ownership so stale cached views cannot assert.match(unsavedSource, /store\.remove\(options\.key, owner\)/) }) +test('unsaved registration only tracks the dirty ref and cannot subscribe to its own store writes', () => { + assert.match(unsavedSource, /watch\(options\.dirty, \(dirty\) =>/) + assert.match(unsavedSource, /\}, \{ immediate: true \}\)/) + assert.doesNotMatch(unsavedSource, /watchEffect/) + assert.match(unsavedSource, /current\.routeKey === entry\.routeKey[\s\S]*current\.dirty === entry\.dirty\) return/) +}) + test('the tab strip keeps touch targets and horizontal navigation usable on narrow screens', () => { assert.match(tabStyles, /overflow-x: auto/) assert.match(tabStyles, /scrollbar-width: none/)