feat: add dropship palletization workflow
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view v-if="user" class="page-shell dropship-page">
|
||||
<PortalHeader title="一件代发清点组托" :user="user" fallback-url="/pages/dropship-palletizing/tasks" />
|
||||
<view v-if="!online" class="network-warning" role="status">当前网络不可用,不能扫码、确认、换托或封托;联网后请刷新。</view>
|
||||
<scroll-view scroll-y class="work-scroll">
|
||||
<view v-if="error" class="message-card error" role="alert">{{ error }}</view>
|
||||
<view v-if="notice" class="message-card" role="status">{{ notice }}</view>
|
||||
<view v-if="loading && !task" class="loading-card">正在加载真实任务...</view>
|
||||
<template v-if="task">
|
||||
<view class="task-card"><view><text class="minor">入仓码</text><text class="task-code">{{ task.inboundCode || '—' }}</text><text class="minor">{{ task.customerName || '客户 —' }} · {{ task.warehouseName || '仓库 —' }}</text></view><text class="badge">{{ statusLabel(task.taskStatus) }}</text></view>
|
||||
<view class="summary-grid"><view><text>{{ task.countedCartons || 0 }}/{{ task.totalCartons || 0 }}</text><text>已清点</text></view><view><text>{{ task.unboundCartons || 0 }}</text><text>待绑托</text></view><view><text>{{ sealedPalletCount }}</text><text>已封托</text></view></view>
|
||||
<view class="panel">
|
||||
<text class="panel-title">扫描箱码或系统托盘码</text><text class="panel-copy">先扫托盘:后续箱确认后直接绑定;先扫多箱:后扫托盘一次批量绑定。</text>
|
||||
<ScanTargetInput v-model="scanValue" name="dropshipCode" label="箱码或托盘码" placeholder="扫描箱码 / DP 托盘码" :active="true" @submit="handleManualScan" />
|
||||
<view class="active-pallet"><text>当前托盘</text><text>{{ activePalletNo || '未选择(箱将进入待绑托区)' }}</text></view>
|
||||
</view>
|
||||
<view v-if="resolvedCarton" class="panel">
|
||||
<view class="heading"><view><text class="panel-title">{{ resolvedCarton.boxCode }}</text><text class="minor">入仓码 {{ resolvedCarton.inboundCode || '—' }}</text></view><text class="badge">待确认</text></view>
|
||||
<view class="sku-card"><view><text class="minor">SKU</text><text class="sku-code">{{ resolvedCarton.skuCode || '—' }}</text></view><view class="expected"><text class="minor">应收</text><text>{{ resolvedCarton.expectedSkuQuantity }}</text></view></view>
|
||||
<text class="quantity-label">SKU 实收数量</text><view class="quantity-control"><button @tap="changeQuantity(-1)">−</button><input v-model="actualSkuQuantity" type="number" aria-label="SKU 实收数量" /><button @tap="changeQuantity(1)">+</button></view>
|
||||
<view v-if="hasDifference" class="difference-box"><text>实收与应收不一致,请确认接受差异并填写处理结果。</text><label><checkbox :checked="acceptVariance" @tap="acceptVariance = !acceptVariance" />接受本次数量差异</label><textarea v-model="discrepancyRemark" maxlength="500" placeholder="填写差异原因及处理结果" /></view>
|
||||
<button class="primary-button" :disabled="busy || !online || !canConfirmCarton" @tap="confirmCarton">确认本箱实收</button>
|
||||
</view>
|
||||
<view class="panel"><view class="heading"><text class="panel-title">待绑托箱</text><text class="badge">{{ task.unboundCartons || 0 }}</text></view><view v-if="!unboundCartons.length" class="empty">暂无待绑托箱</view><view v-for="carton in unboundCartons" :key="carton.cartonId" class="carton-row"><view><text>{{ carton.boxCode }}</text><text>{{ carton.skuCode }} · 实收 {{ carton.actualSkuQuantity }}</text></view></view></view>
|
||||
<view class="panel"><view class="heading"><text class="panel-title">托盘</text><text class="minor">{{ pallets.length }} 托</text></view><view v-if="!pallets.length" class="empty">扫描 DP 托盘码开始组托</view>
|
||||
<view v-for="pallet in pallets" :key="pallet.palletId" class="pallet-card" :class="{ active: pallet.palletNo === activePalletNo }">
|
||||
<view class="heading"><view><text class="pallet-code">{{ pallet.palletNo }}</text><text class="minor">{{ pallet.cartonCount || 0 }} 箱 · {{ pallet.skuCount || 0 }} SKU</text></view><text class="badge" :class="{ sealed: pallet.status === 'SEALED' }">{{ pallet.status === 'SEALED' ? '已封托' : '组托中' }}</text></view>
|
||||
<view v-if="pallet.weightKg != null || pallet.volumeCbm != null" class="measure-row"><text>重量 {{ pallet.weightKg ?? '—' }} kg</text><text>体积 {{ pallet.volumeCbm ?? '—' }} m³</text></view>
|
||||
<view v-for="carton in palletCartons(pallet)" :key="carton.cartonId || carton.boxCode" class="carton-row"><view><text>{{ carton.boxCode }}</text><text>{{ carton.skuCode }} · 实收 {{ carton.actualSkuQuantity }}</text></view><button v-if="pallet.status !== 'SEALED'" :disabled="busy || !online" @tap="moveCarton(carton, activePalletNo && activePalletNo !== pallet.palletNo ? activePalletNo : null)">{{ activePalletNo && activePalletNo !== pallet.palletNo ? '换至当前托' : '移回待绑托' }}</button></view>
|
||||
<button v-if="pallet.status !== 'SEALED'" class="secondary-button" :disabled="busy || !online || !canSeal" @tap="sealPallet(pallet)">封托</button><text v-if="pallet.status !== 'SEALED' && !canSeal" class="guard">{{ sealBlockReason }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template><view class="bottom-space"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PortalHeader from '@/components/PortalHeader.vue'
|
||||
import ScanTargetInput from '@/components/ScanTargetInput.vue'
|
||||
import { bindPendingDropshipCartons, countDropshipCarton, getDropshipPalletizingTask, moveDropshipCarton, resolveDropshipCarton, sealDropshipPallet } from '@/api/pda-dropship.js'
|
||||
import { session, requireSession } from '@/stores/session.js'
|
||||
import { clearDropshipPalletizingDraft, restoreDropshipPalletizingDraft, saveDropshipPalletizingDraft } from '@/stores/dropship-palletizing.js'
|
||||
import { stableOperationId } from '@/utils/idempotency.js'
|
||||
import { createDropshipScanState, DROPSHIP_SCAN_INTENT, planDropshipScan, releaseDropshipScan } from '@/utils/dropship-palletizing-state.js'
|
||||
import { watchNetwork } from '@/utils/network.js'
|
||||
import { startScanBroadcast } from '@/utils/scan-broadcast.js'
|
||||
import { playErrorSound, playSuccessSound } from '@/utils/audio-feedback.js'
|
||||
import { formatApiError } from '@/utils/user-facing.js'
|
||||
export default {
|
||||
components: { PortalHeader, ScanTargetInput },
|
||||
data() { return { user: session.currentUser, taskId: '', warehouseId: '', task: null, loading: false, busy: false, online: true, error: '', notice: '', scanValue: '', scanState: createDropshipScanState(), activePalletNo: '', resolvedCarton: null, actualSkuQuantity: '', acceptVariance: false, discrepancyRemark: '', _stopNetwork: null, _stopScanner: null } },
|
||||
computed: {
|
||||
pallets() { return this.task?.pallets || [] }, unboundCartons() { return this.task?.unbound || [] }, sealedPalletCount() { return this.pallets.filter((pallet) => pallet.status === 'SEALED').length },
|
||||
hasDifference() { return this.resolvedCarton ? Number(this.actualSkuQuantity) !== Number(this.resolvedCarton.expectedSkuQuantity) : false },
|
||||
canConfirmCarton() { return Boolean(this.resolvedCarton && Number.isInteger(Number(this.actualSkuQuantity)) && Number(this.actualSkuQuantity) > 0 && (!this.hasDifference || (this.acceptVariance && this.discrepancyRemark.trim()))) },
|
||||
canSeal() { return Boolean(this.task && Number(this.task.countedCartons) === Number(this.task.totalCartons) && Number(this.task.unboundCartons) === 0) },
|
||||
sealBlockReason() { if (Number(this.task?.countedCartons) !== Number(this.task?.totalCartons)) return '仍有箱码未完成清点'; if (Number(this.task?.unboundCartons) > 0) return '仍有已清点箱未绑定托盘'; return '' },
|
||||
draftIdentity() { return { operatorId: this.user?.id, taskId: this.taskId, warehouseId: this.warehouseId } }
|
||||
},
|
||||
onLoad(options) { if (!requireSession()) return; this.user = session.currentUser; this.taskId = String(options.taskId || ''); this.warehouseId = String(options.warehouseId || ''); const draft = restoreDropshipPalletizingDraft(this.draftIdentity); if (draft?.activePalletNo) this.activePalletNo = draft.activePalletNo; this.scanState = createDropshipScanState({ activePalletNo: this.activePalletNo }); this._stopNetwork = watchNetwork((value) => { this.online = value; this.scanState.online = value }) },
|
||||
onShow() { if (!this.taskId || !this.warehouseId) { this.error = '任务参数缺失,请返回任务列表刷新'; return }; this.startScanner(); this.loadTask() }, onHide() { this.stopScanner() }, onUnload() { this.stopScanner(); if (this._stopNetwork) this._stopNetwork() },
|
||||
methods: {
|
||||
statusLabel(status) { return status === 'COMPLETED' ? '已完成' : status === 'CLAIMED' ? '清点组托中' : '待领取' }, palletCartons(pallet) { return pallet.cartons || [] },
|
||||
startScanner() { if (!this._stopScanner) this._stopScanner = startScanBroadcast({ onScan: (value) => this.processScan(value), onError: () => playErrorSound() }) }, stopScanner() { if (this._stopScanner) this._stopScanner(); this._stopScanner = null },
|
||||
async loadTask() { if (!this.online) { this.error = '当前网络不可用,请联网后刷新'; return }; this.loading = true; this.error = ''; try { this.task = await getDropshipPalletizingTask(this.taskId, this.warehouseId); if (this.task?.taskStatus === 'COMPLETED') { this.activePalletNo = ''; this.scanState = createDropshipScanState(); clearDropshipPalletizingDraft(this.draftIdentity) } } catch (error) { this.error = formatApiError(error, '清点组托任务加载失败') } finally { this.loading = false } },
|
||||
handleManualScan(value) { this.processScan(value) },
|
||||
async processScan(value) { if (this.busy) return; const plan = planDropshipScan(this.scanState, value, { unboundCartons: this.task?.unboundCartons }); this.scanState = plan.state; this.scanValue = String(value || '').trim().toUpperCase(); this.error = ''; this.notice = ''; if ([DROPSHIP_SCAN_INTENT.BLOCKED, DROPSHIP_SCAN_INTENT.INVALID, DROPSHIP_SCAN_INTENT.DUPLICATE].includes(plan.intent)) { this.error = plan.message; playErrorSound(); return }; if ([DROPSHIP_SCAN_INTENT.SELECT_PALLET, DROPSHIP_SCAN_INTENT.BIND_PENDING].includes(plan.intent)) { await this.selectPallet(plan.palletNo); this.scanState = releaseDropshipScan(this.scanState); return }; this.busy = true; try { const carton = await resolveDropshipCarton(this.taskId, this.warehouseId, plan.boxCode); if (carton.countStatus === 'BOUND') { this.notice = `箱码 ${plan.boxCode} 已绑定托盘 ${carton.palletNo || '—'},请勿重复扫描`; playErrorSound(); return this.loadTask() }; if (carton.countStatus === 'COUNTED_UNBOUND') { this.notice = `箱码 ${plan.boxCode} 已清点,当前位于待绑托区`; playErrorSound(); return this.loadTask() }; if (carton.canCount === false) { this.error = carton.blockReason || '当前箱码不可清点,请刷新任务'; playErrorSound(); return }; this.resolvedCarton = carton; this.actualSkuQuantity = String(carton.expectedSkuQuantity); this.acceptVariance = false; this.discrepancyRemark = ''; playSuccessSound() } catch (error) { this.error = formatApiError(error, '箱码识别失败'); playErrorSound() } finally { this.busy = false; this.scanState = releaseDropshipScan(this.scanState) } },
|
||||
async selectPallet(palletNo) { this.activePalletNo = palletNo; this.scanState.activePalletNo = palletNo; this.persistDraft(); if (!Number(this.task?.unboundCartons || 0)) { this.notice = `当前托盘已切换为 ${palletNo}`; playSuccessSound(); return }; this.busy = true; const operationId = stableOperationId('dropship-bind', `${this.user?.id}-${this.taskId}`, { palletNo, cartonIds: this.unboundCartons.map((carton) => carton.cartonId) }); try { this.task = await bindPendingDropshipCartons(this.taskId, { warehouseId: this.warehouseId, palletNo, operationId }); this.notice = `待绑托箱已批量绑定到 ${palletNo}`; playSuccessSound() } catch (error) { const message = formatApiError(error, '批量绑定失败,请刷新后重试'); playErrorSound(); await this.loadTask(); this.error = message } finally { this.busy = false } },
|
||||
changeQuantity(delta) { this.actualSkuQuantity = String(Math.max(1, Number(this.actualSkuQuantity || 0) + delta)) },
|
||||
async confirmCarton() { if (!this.canConfirmCarton || this.busy) return; const payload = { warehouseId: this.warehouseId, boxCode: this.resolvedCarton.boxCode, actualSkuQuantity: Number(this.actualSkuQuantity), discrepancyAction: this.hasDifference ? 'ACCEPT_VARIANCE' : 'MATCH', discrepancyRemark: this.hasDifference ? this.discrepancyRemark.trim() : null, palletNo: this.activePalletNo || null }; payload.operationId = stableOperationId('dropship-count', `${this.user?.id}-${this.taskId}`, payload); this.busy = true; this.error = ''; try { await countDropshipCarton(this.taskId, payload); this.notice = this.activePalletNo ? `本箱已清点并绑定到 ${this.activePalletNo}` : '本箱已清点,等待绑定托盘'; this.resolvedCarton = null; this.actualSkuQuantity = ''; this.scanValue = ''; await this.loadTask(); playSuccessSound() } catch (error) { const message = formatApiError(error, '实收确认失败,请刷新后重试'); playErrorSound(); await this.loadTask(); this.error = message } finally { this.busy = false } },
|
||||
async moveCarton(carton, targetPalletNo) { if (targetPalletNo === carton.palletNo) return; const payload = { warehouseId: this.warehouseId, targetPalletNo: targetPalletNo || null }; payload.operationId = stableOperationId('dropship-move', `${this.user?.id}-${this.taskId}-${carton.cartonId}`, payload); this.busy = true; this.error = ''; try { this.task = await moveDropshipCarton(this.taskId, carton.cartonId, payload); this.notice = targetPalletNo ? `箱码 ${carton.boxCode} 已换至 ${targetPalletNo}` : `箱码 ${carton.boxCode} 已移回待绑托区`; playSuccessSound() } catch (error) { const message = formatApiError(error, '换托操作失败,请刷新后重试'); playErrorSound(); await this.loadTask(); this.error = message } finally { this.busy = false } },
|
||||
async sealPallet(pallet) { if (!this.canSeal || this.busy) return; const payload = { warehouseId: this.warehouseId, expectedVersion: Number(pallet.version) }; payload.operationId = stableOperationId('dropship-seal', `${this.user?.id}-${this.taskId}-${pallet.palletId}`, payload); this.busy = true; this.error = ''; try { this.task = await sealDropshipPallet(this.taskId, pallet.palletId, payload); if (this.activePalletNo === pallet.palletNo) this.activePalletNo = ''; this.persistDraft(); if (this.task?.taskStatus === 'COMPLETED') clearDropshipPalletizingDraft(this.draftIdentity); this.notice = `托盘 ${pallet.palletNo} 已封托,后续按整托不可拆分`; playSuccessSound() } catch (error) { const message = formatApiError(error, '封托失败,请刷新后重试'); playErrorSound(); await this.loadTask(); this.error = message } finally { this.busy = false } },
|
||||
persistDraft() { saveDropshipPalletizingDraft(this.draftIdentity, { activePalletNo: this.activePalletNo }) }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dropship-page{min-width:0;overflow:hidden;background:#f5f7fa}.work-scroll{box-sizing:border-box;height:calc(100vh - 56px);padding:10px 10px 32px}.network-warning{padding:8px 10px;color:#8a2c0d;font-size:12px;line-height:18px;background:#fff1e8}.message-card,.loading-card{margin-bottom:9px;padding:10px 12px;border-radius:8px;color:#155724;font-size:12px;line-height:18px;background:#edf8f0}.message-card.error{color:#9b2c2c;background:#fff0f0}.loading-card{color:#64748b;text-align:center;background:#fff}.panel,.task-card{box-sizing:border-box;width:100%;margin-bottom:9px;padding:12px;border:1px solid #e1e7ef;border-radius:11px;background:#fff}.task-card,.heading{display:flex;min-width:0;align-items:flex-start;justify-content:space-between;gap:8px}.minor,.panel-copy,.guard{display:block;color:#667085;font-size:11px;line-height:17px}.task-code,.panel-title,.pallet-code,.sku-code{display:block;min-width:0;color:#22344a;font-size:16px;font-weight:800;overflow-wrap:anywhere}.badge{flex-shrink:0;padding:4px 7px;border-radius:999px;color:#155eef;font-size:10px;font-weight:700;background:#eff4ff}.badge.sealed{color:#137333;background:#e8f5ec}.summary-grid{display:grid;margin-bottom:9px;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px}.summary-grid view{padding:9px 4px;border-radius:9px;text-align:center;background:#fff}.summary-grid text{display:block;color:#22344a;font-size:17px;font-weight:800}.summary-grid text+text{color:#718096;font-size:10px;font-weight:500}.panel-copy{margin:3px 0 8px}.active-pallet{display:flex;margin-top:8px;padding:8px;border-radius:7px;justify-content:space-between;gap:8px;color:#64748b;font-size:11px;background:#f6f8fb}.active-pallet text:last-child{min-width:0;color:#155eef;font-weight:700;text-align:right;overflow-wrap:anywhere}.sku-card{display:flex;margin:10px 0;padding:10px;border-radius:8px;align-items:center;justify-content:space-between;gap:8px;background:#f6f8fb}.expected{text-align:right}.expected text:last-child{display:block;color:#172b45;font-size:20px;font-weight:800}.quantity-label{display:block;margin-bottom:5px;color:#475467;font-size:12px;font-weight:700}.quantity-control{display:grid;width:100%;grid-template-columns:52px minmax(0,1fr) 52px}.quantity-control button{height:48px;margin:0;padding:0;color:#155eef;font-size:22px;line-height:48px;background:#edf3ff}.quantity-control input{box-sizing:border-box;width:100%;height:48px;border:1px solid #d8e0ea;text-align:center;font-size:17px;background:#fff}.difference-box{margin-top:10px;padding:10px;border-radius:8px;color:#8a2c0d;font-size:12px;line-height:18px;background:#fff6ed}.difference-box label{display:flex;min-height:42px;align-items:center;gap:6px;font-weight:700}.difference-box textarea{box-sizing:border-box;width:100%;height:68px;padding:8px;border:1px solid #e4c7ad;border-radius:7px;font-size:12px;background:#fff}.primary-button,.secondary-button{box-sizing:border-box;width:100%;min-height:48px;margin:10px 0 0;border-radius:8px;font-size:14px;font-weight:700;line-height:48px}.primary-button{color:#fff;background:#155eef}.secondary-button{color:#155eef;background:#edf3ff}.empty{padding:13px 0 3px;color:#8793a3;font-size:12px;text-align:center}.carton-row{display:flex;min-width:0;min-height:46px;border-bottom:1px solid #edf0f4;align-items:center;justify-content:space-between;gap:7px}.carton-row view{min-width:0}.carton-row text{display:block;color:#334155;font-size:12px;font-weight:700;overflow-wrap:anywhere}.carton-row text+text{color:#667085;font-size:10px;font-weight:500}.carton-row button{flex-shrink:0;min-width:82px;height:38px;margin:0;padding:0 7px;color:#155eef;font-size:10px;line-height:38px;background:#edf3ff}.pallet-card{margin-top:9px;padding:10px;border:1px solid #dfe5ed;border-radius:9px}.pallet-card.active{border:2px solid #155eef;background:#f8fbff}.measure-row{display:flex;margin-top:7px;padding:7px;border-radius:6px;justify-content:space-between;color:#475467;font-size:10px;background:#f6f8fb}.guard{margin-top:5px;text-align:center}.bottom-space{height:20px}button::after{border:0}
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<view v-if="user" class="page-shell dropship-task-page">
|
||||
<PortalHeader title="一件代发任务" :user="user" fallback-url="/pages/home/home" />
|
||||
<view class="task-content">
|
||||
<view class="task-toolbar"><view><text class="task-title">待清点组托</text><text class="task-summary">{{ loading ? '正在同步任务' : `共 ${tasks.length} 个任务` }}</text></view><button :disabled="loading" @tap="loadTasks">{{ loading ? '刷新中' : '刷新' }}</button></view>
|
||||
<view v-if="error" class="state-card state-error" role="alert">{{ error }}</view>
|
||||
<view v-else-if="loading" class="state-card">正在加载真实任务...</view>
|
||||
<view v-else-if="!tasks.length" class="state-card">暂无待处理的一件代发清点组托任务</view>
|
||||
<button v-for="task in tasks" :key="task.taskId" class="dropship-task-card" :disabled="claimingTaskId === task.taskId || !task.taskId || !task.warehouseId" @tap="enterTask(task)">
|
||||
<view class="task-card-head"><text class="task-code">{{ task.inboundCode || '入仓码 —' }}</text><text class="status-badge">{{ statusLabel(task.taskStatus) }}</text></view>
|
||||
<text class="customer-name">{{ task.customerName || '客户 —' }}</text>
|
||||
<text class="warehouse-name">仓库 {{ task.warehouseName || '—' }}</text>
|
||||
<view class="progress-row"><text>清点 {{ task.countedCartons || 0 }}/{{ task.totalCartons || 0 }}</text><text>待绑托 {{ task.unboundCartons || 0 }}</text><text>差异 {{ task.discrepancyCartons || 0 }}</text></view>
|
||||
<view class="enter-copy">{{ claimingTaskId === task.taskId ? '领取中...' : taskAction(task) }} ›</view>
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PortalHeader from '@/components/PortalHeader.vue'
|
||||
import { claimDropshipPalletizingTask, listDropshipPalletizingTasks } from '@/api/pda-dropship.js'
|
||||
import { session, requireSession } from '@/stores/session.js'
|
||||
import { isOnline } from '@/utils/network.js'
|
||||
import { formatApiError } from '@/utils/user-facing.js'
|
||||
|
||||
export default {
|
||||
components: { PortalHeader },
|
||||
data() { return { user: session.currentUser, tasks: [], loading: false, error: '', claimingTaskId: null } },
|
||||
onShow() { if (!requireSession()) return; this.user = session.currentUser; this.loadTasks() },
|
||||
methods: {
|
||||
statusLabel(status) { return status === 'CLAIMED' ? '清点组托中' : status === 'COMPLETED' ? '已完成' : '待领取' },
|
||||
taskAction(task) { return task.taskStatus === 'CLAIMED' ? '继续作业' : '领取并开始' },
|
||||
async loadTasks() {
|
||||
if (!isOnline()) { this.error = '当前网络不可用,请联网后刷新'; return }
|
||||
this.loading = true; this.error = ''
|
||||
try { this.tasks = await listDropshipPalletizingTasks() || [] }
|
||||
catch (error) { this.error = formatApiError(error, '一件代发任务加载失败') }
|
||||
finally { this.loading = false }
|
||||
},
|
||||
async enterTask(task) {
|
||||
if (!isOnline()) { this.error = '当前网络不可用,不能开始正式作业'; return }
|
||||
this.claimingTaskId = task.taskId; this.error = ''
|
||||
try {
|
||||
if (task.taskStatus !== 'CLAIMED') await claimDropshipPalletizingTask(task.taskId, task.warehouseId)
|
||||
uni.navigateTo({ url: `/pages/dropship-palletizing/dropship-palletizing?taskId=${encodeURIComponent(task.taskId)}&warehouseId=${encodeURIComponent(task.warehouseId)}` })
|
||||
} catch (error) { this.error = formatApiError(error, '任务领取失败,请刷新后重试') }
|
||||
finally { this.claimingTaskId = null }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.task-content { padding: 12px; }
|
||||
.task-toolbar { display: flex; margin-bottom: 10px; align-items: center; justify-content: space-between; gap: 10px; }
|
||||
.task-title { display: block; color: #172b45; font-size: 18px; font-weight: 800; }
|
||||
.task-summary { display: block; margin-top: 2px; color: #667085; font-size: 11px; }
|
||||
.task-toolbar button { width: 76px; height: 44px; margin: 0; color: #fff; font-size: 14px; line-height: 44px; background: #1a73e8; }
|
||||
.state-card { padding: 24px 12px; border-radius: 10px; color: #667085; font-size: 13px; text-align: center; background: #fff; }
|
||||
.state-error { color: #b42318; background: #fff1f0; }
|
||||
.dropship-task-card { box-sizing: border-box; width: 100%; margin: 0 0 10px; padding: 13px; border: 1px solid #dce5ef; border-radius: 11px; color: #27384d; text-align: left; background: #fff; }
|
||||
.task-card-head, .progress-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
||||
.task-code { min-width: 0; font-size: 17px; font-weight: 800; overflow-wrap: anywhere; }
|
||||
.status-badge { flex-shrink: 0; padding: 4px 7px; border-radius: 999px; color: #155eef; font-size: 10px; background: #eff4ff; }
|
||||
.customer-name { display: block; margin-top: 7px; font-size: 13px; font-weight: 700; }
|
||||
.warehouse-name { display: block; margin-top: 2px; color: #667085; font-size: 11px; }
|
||||
.progress-row { margin-top: 10px; padding: 8px; border-radius: 8px; color: #475467; font-size: 11px; background: #f6f8fb; }
|
||||
.enter-copy { min-height: 42px; margin-top: 4px; color: #155eef; font-size: 13px; font-weight: 700; line-height: 42px; text-align: right; }
|
||||
button::after { border: 0; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user