diff --git a/src/modules/overseas-warehouse/views/WarehouseServiceView.vue b/src/modules/overseas-warehouse/views/WarehouseServiceView.vue
index 05692d0..39dd1ac 100644
--- a/src/modules/overseas-warehouse/views/WarehouseServiceView.vue
+++ b/src/modules/overseas-warehouse/views/WarehouseServiceView.vue
@@ -45,7 +45,8 @@ const props = withDefaults(defineProps<{ service: ServiceType; createPage?: bool
const auth = useAuthStore()
const router = useRouter()
const route = useRoute()
-const detailNumber = computed(() => props.detailNo || String(route.params.detailNo || ''))
+// 缓存列表不订阅其他详情页的路由参数,避免离开时清空筛选并重复请求。
+const detailNumber = computed(() => props.detailPage ? props.detailNo || String(route.params.detailNo || '') : '')
const canCreate = computed(() => auth.hasPermission(({
labeling: 'merchant:overseas-warehouse:relabel:create',
transfer: 'merchant:overseas-warehouse:transfer:create',
@@ -1856,7 +1857,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
预报箱唛({{ selectedDetail?.packageCount || 0 }} 箱)
{{ selectedDetail?.items?.find(item => String(item.id) === String(row.transferItemId))?.skuCode || '—' }}{{ row.status === 'COUNTED' ? '已清点' : row.status === 'NOT_RECEIVED' ? '未收到' : '待清点' }}关闭
- 保留原箱唛,仅补生成新增箱唛;箱内SKU和件数以PDA清点为准,SKU预报总件数保持不变。
+ 保留原箱唛,仅补生成新增箱唛;箱内SKU和件数以PDA清点为准,SKU预报总件数保持不变。
取消确认增加
diff --git a/src/modules/overseas-warehouse/views/warehouseServiceLifecycle.test.mjs b/src/modules/overseas-warehouse/views/warehouseServiceLifecycle.test.mjs
new file mode 100644
index 0000000..dc364fe
--- /dev/null
+++ b/src/modules/overseas-warehouse/views/warehouseServiceLifecycle.test.mjs
@@ -0,0 +1,49 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import { readFileSync } from 'node:fs'
+import { computed, effectScope, nextTick, reactive, ref, watch } from 'vue'
+import ts from 'typescript'
+
+const source = readFileSync(new URL('./WarehouseServiceView.vue', import.meta.url), 'utf8')
+const detailNumber = source.match(/^const detailNumber = .*$/m)[0]
+const routeWatcher = source.slice(source.indexOf('watch(() => [props.service,'), source.indexOf('watch(mode,'))
+const activation = source.slice(source.indexOf('let activatedOnce = false'), source.indexOf('onBeforeUnmount(() =>'))
+const code = ts.transpileModule([detailNumber, routeWatcher, activation].join('\n'), { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText
+function harness(detailPage = false) {
+ const scope = effectScope(), props = reactive({ service: 'transfer', createPage: false, detailPage, detailNo: '' })
+ const route = reactive({ params: { detailNo: detailPage ? 'TRANSFER-A' : undefined } })
+ const mode = ref(detailPage ? 'detail' : 'list'), page = ref(3), filters = reactive({ number: 'TRANSFER-FILTER', warehouse: 'warehouse-1', status: 'RECEIVED' })
+ const calls = [], activationCallbacks = []
+ const resetFilters = () => { calls.push('resetFilters'); Object.assign(filters, { number: '', warehouse: '', status: '' }) }
+ const loadPage = () => { calls.push({ kind: 'list', page: page.value, ...filters }) }
+ scope.run(() => new Function('computed', 'watch', 'props', 'route', 'mode', 'page', 'resetFilters', 'resetForm', 'openCreate', 'loadDetailPage', 'loadPage', 'onActivated', code)(computed, watch, props, route, mode, page, resetFilters, () => calls.push('resetForm'), () => calls.push('create'), () => calls.push('detail'), loadPage, callback => activationCallbacks.push(callback)))
+ return { scope, props, route, mode, page, filters, calls, activate: () => activationCallbacks.forEach(callback => callback()) }
+}
+
+test('cached transfer list ignores detail navigation and refreshes once on return with its original filters and page', async () => {
+ const state = harness()
+ try {
+ state.activate()
+ state.route.params.detailNo = 'TRANSFER-A'
+ await nextTick()
+ assert.deepEqual(state.calls, [])
+ assert.equal(state.page.value, 3)
+ assert.equal(state.filters.number, 'TRANSFER-FILTER')
+ state.route.params.detailNo = 'TRANSFER-B'
+ await nextTick()
+ assert.deepEqual(state.calls, [])
+ state.route.params.detailNo = undefined
+ await nextTick()
+ state.activate()
+ assert.deepEqual(state.calls, [{ kind: 'list', page: 3, number: 'TRANSFER-FILTER', warehouse: 'warehouse-1', status: 'RECEIVED' }])
+ } finally { state.scope.stop() }
+})
+
+test('real detail pages still react to another detail number without loading a background list', async () => {
+ const state = harness(true)
+ try {
+ state.route.params.detailNo = 'TRANSFER-B'
+ await nextTick()
+ assert.deepEqual(state.calls, ['resetFilters', 'resetForm', 'detail'])
+ } finally { state.scope.stop() }
+})