diff --git a/pages/dropship-palletization/dropship-palletization.vue b/pages/dropship-palletization/dropship-palletization.vue index 7df77a2..cd7b714 100644 --- a/pages/dropship-palletization/dropship-palletization.vue +++ b/pages/dropship-palletization/dropship-palletization.vue @@ -266,7 +266,7 @@ .section-title { display: block; color: #12233f; font-size: 18px; font-weight: 700; }.section-heading { align-items: center; }.refresh-button { display: flex; align-items: center; justify-content: center; flex-shrink: 0; box-sizing: border-box; min-width: 88px; height: 44px; margin: 0; padding: 0 16px; border: 1px solid #9dc0f4; border-radius: 8px; color: #155eef; font-size: 16px; font-weight: 700; line-height: normal; background: #eaf2ff; }.refresh-button::after { border: 0; }.refresh-button[disabled] { color: #718095; border-color: #dce5f0; background: #e8edf3; } .primary-button, .secondary-button { box-sizing: border-box; width: 100%; height: 46px; margin-top: 10px; border-radius: 9px; font-size: 14px; font-weight: 700; line-height: 44px; }.primary-button { border: 0; color: #fff; background: #1a73e8; }.secondary-button { border: 1px solid #8ab8ef; color: #155eef; background: #f6f9ff; }.primary-button[disabled],.secondary-button[disabled] { opacity: .45; } .unseal-button { border-color: #fda29b; color: #b42318; background: #fff5f4; }.pallet-hint.blocked { color: #b42318; } - .unseal-dialog { box-sizing: border-box; width: min(350px, calc(100vw - 32px)); overflow: hidden; border-radius: 12px; background: #fff; }.unseal-copy { display: block; padding: 18px 16px; color: #475467; font-size: 14px; line-height: 21px; }.unseal-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 0 16px 16px; }.unseal-actions .secondary-button { margin-top: 0; }.danger-button { height: 46px; margin: 0; border: 0; border-radius: 9px; color: #fff; font-size: 14px; font-weight: 700; background: #d92d20; }.danger-button[disabled] { opacity: .45; } + .unseal-dialog { box-sizing: border-box; width: min(350px, calc(100vw - 32px)); overflow: hidden; border-radius: 12px; background: #fff; }.unseal-copy { display: block; padding: 18px 16px; color: #475467; font-size: 14px; line-height: 21px; }.unseal-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 0 16px 16px; }.unseal-actions .secondary-button { margin-top: 0; }.danger-button { display: flex; align-items: center; justify-content: center; padding: 0; line-height: 46px; height: 46px; margin: 0; border: 0; border-radius: 9px; color: #fff; font-size: 14px; font-weight: 700; background: #d92d20; }.danger-button[disabled] { opacity: .45; } .count-entry { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; padding: 5px 7px; border: 0; border-radius: 0; color: #667085; font-size: 13px; background: #f6f9ff; }.compact-action { width: 86px; height: 34px; margin: 0; border: 0; border-radius: 6px; color: #fff; font-size: 13px; line-height: 34px; background: #1a73e8; }.compact-action[disabled] { opacity: .35; }.count-unavailable { padding: 12px 8px; color: #b42318; font-size: 13px; line-height: 18px; text-align: center; background: #fff1f0; }.pallet-scan-block { margin-top: 8px; padding-top: 8px; border-top: 1px solid #e6edf5; }.field-label { display: block; margin-bottom: 5px; color: #344054; font-size: 15px; font-weight: 700; }.field-label text { margin-left: 3px; color: #98a2b3; font-size: 12px; font-weight: 400; } .carton-card, .pallet-card { margin-top: 8px; padding: 8px 0 0; border: 0; border-top: 1px solid #e3eaf2; border-radius: 0; background: transparent; }.sku-count-row { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid #edf1f5; }.sku-copy { min-width: 0; }.sku-label { display: block; color: #667085; font-size: 11px; }.sku-code { display: block; margin-top: 2px; color: #12233f; font-size: 16px; font-weight: 700; overflow-wrap: anywhere; }.pallet-meta { display: block; margin-top: 5px; color: #667085; font-size: 14px; line-height: 20px; } .quantity-stepper { display: grid; grid-template-columns: 38px 56px 38px; overflow: hidden; border: 1px solid #b8c6d8; border-radius: 8px; background: #fff; }.quantity-stepper button,.quantity-stepper input { box-sizing: border-box; height: 40px; margin: 0; border: 0; border-radius: 0; text-align: center; background: #fff; }.quantity-stepper button { color: #155eef; font-size: 20px; line-height: 40px; }.quantity-stepper input { width: 56px; border-right: 1px solid #dce4ed; border-left: 1px solid #dce4ed; color: #12233f; font-size: 15px; }.difference-field { grid-column: 1 / -1; margin-top: 0; }.pallet-hint { display: block; margin-top: 10px; color: #155eef; font-size: 11px; text-align: center; } diff --git a/pages/dropship-tasks/dropship-tasks.vue b/pages/dropship-tasks/dropship-tasks.vue index ad05511..551e386 100644 --- a/pages/dropship-tasks/dropship-tasks.vue +++ b/pages/dropship-tasks/dropship-tasks.vue @@ -34,16 +34,22 @@ import ScanTargetInput from '@/components/ScanTargetInput.vue' import { listDropshipPalletizationTasks, resolveDropshipWorkspacePallet } from '@/api/pda-dropship.js' import { session, requireSession } from '@/stores/session.js' + import { startScanBroadcast } from '@/utils/scan-broadcast.js' import { formatApiError } from '@/utils/user-facing.js' export default { components: { PortalHeader, ScanTargetInput }, data() { return { user: session.currentUser, keyword: '', tasks: [], loading: false, error: '', palletNo: '' } }, onLoad(options) { this.palletNo = String(options.palletNo || '') }, - onShow() { if (!requireSession()) return; this.user = session.currentUser; this.loadTasks() }, + onShow() { if (!requireSession()) return; this.user = session.currentUser; this.startScanner(); this.loadTasks() }, + onHide() { this.stopScanner() }, onUnload() { this.stopScanner() }, beforeUnmount() { this.stopScanner() }, methods: { + // 入仓码广播直接驱动查询,不依赖输入框焦点。 + startScanner() { this.stopScanner(); this._stopScanner = startScanBroadcast({ onScan: value => { if (this.loading) return; this.keyword = String(value).trim(); this.loadTasks() }, onError: () => { this.error = '扫码头连接失败,可输入入仓码查询' } }) }, + stopScanner() { this._stopScanner?.(); this._stopScanner = null }, statusLabel(status) { return status === 'CLAIMED' ? '作业中' : '待领取' }, - async loadTasks() { + async loadTasks(value) { + if (typeof value === 'string') this.keyword = value.trim() if (this.loading) return if (!this.user?.warehouseId) { this.error = '请返回首页选择作业仓库'; return } this.loading = true; this.error = '' diff --git a/tests/dropship-workspace.test.mjs b/tests/dropship-workspace.test.mjs index d3fb563..d745f81 100644 --- a/tests/dropship-workspace.test.mjs +++ b/tests/dropship-workspace.test.mjs @@ -130,3 +130,27 @@ test('task list stops carrying a pallet sealed while another ticket was being wo assert.equal(state.palletNo, '') assert.ok(!navigations[1].includes('DP-261019-0014')) }) + +test('task list hardware scan searches inbound code and releases receiver when hidden', async () => { + const taskSource = await readFile(new URL('../pages/dropship-tasks/dropship-tasks.vue', import.meta.url), 'utf8') + const script = taskSource.match(/