优化
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<view v-if="user" class="page-shell portal-page fulfillment-page">
|
||||
<PortalHeader title="排车作业" :user="user" />
|
||||
<scroll-view class="fulfillment-scroll" scroll-y><view class="portal-content fulfillment-content">
|
||||
<view class="fulfillment-heading"><text class="fulfillment-title">整车拣货、装车与发车</text><text class="fulfillment-subtitle">一张排车单代表一趟车,按实物托盘码或来源箱唛逐项作业。</text></view>
|
||||
<view class="fulfillment-heading"><text class="fulfillment-title">整车拣货、装车与发车</text></view>
|
||||
<view v-if="pendingCommand" class="fulfillment-card gate-card"><text>排车 {{ pendingCommand.dispatchNo }} 的原操作结果待核对,请保留原请求。</text><button :disabled="busy || loading" @tap="recoverCommand">核对并重试原请求</button><button v-if="pendingCommand.rejected" :disabled="busy" @tap="discardRejected">按明确拒绝结果重新核对</button></view>
|
||||
<view v-if="current" class="detail-toolbar"><text class="detail-title">{{ current.dispatchNo }}</text><button :disabled="busy" @tap="closeTask">‹ 返回列表</button></view>
|
||||
<view class="fulfillment-card"><ScanTargetInput name="dispatch" :model-value="scanCode" label="排车单号/实物托盘码/来源箱唛" placeholder="扫描业务单号或实物码" :active="true" @update:modelValue="scanCode = $event" @submit="handleScan" /><button :disabled="busy || loading || !scanCode || Boolean(pendingCommand)" @tap="handleScan(scanCode)">匹配扫描</button></view>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PortalHeader title="业务模块" :user="user" />
|
||||
<view class="portal-content">
|
||||
<view class="module-hero" :class="`module-hero-${currentModule.key}`"><view class="module-hero-icon">{{ currentModule.icon }}</view><view class="module-hero-copy"><text class="module-hero-title">{{ currentModule.name }}</text><text class="module-hero-desc">{{ currentModule.description }}</text></view><text class="module-hero-count">{{ availableCount }}/{{ currentModule.operations.length }} 可用</text></view>
|
||||
<view class="section-heading"><text class="group-title">业务功能</text><text class="section-heading-hint">点击可用功能开始作业</text></view>
|
||||
<view class="section-heading"><text class="group-title">业务功能</text></view>
|
||||
<view class="operation-list"><button v-for="operation in currentModule.operations" :key="operation.name" class="operation-item" :class="{ 'operation-item-disabled': !operation.available }" :disabled="!operation.available" @tap="openOperation(operation)"><view class="operation-icon">{{ operation.icon }}</view><view class="operation-info"><text class="operation-name">{{ operation.name }}</text><text class="operation-desc">{{ operation.description }}</text></view><text v-if="operation.available" class="operation-ready">可处理</text><text v-else class="operation-locked">未开放</text><text v-if="operation.available" class="operation-arrow">›</text></button></view>
|
||||
<view v-if="availableCount < currentModule.operations.length" class="module-empty-note">灰色功能尚未开放,仅可进入已接入真实接口的作业。</view>
|
||||
</view>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view v-if="user" class="page-shell portal-page work-page">
|
||||
<PortalHeader title="商品移库" :user="user" fallback-url="/pages/module/module?key=inventory" />
|
||||
<scroll-view class="work-scroll" scroll-y><view class="portal-content work-content">
|
||||
<view class="work-heading"><text class="work-title">整托移库</text><text class="work-subtitle">扫描来源托盘和同仓目标库位,按服务端资源版本一次移动整托库存</text></view>
|
||||
<view class="work-heading"><text class="work-title">整托移库</text></view>
|
||||
<view v-if="restoring" class="empty-box"><uni-load-more status="loading" /><text class="empty-copy">正在恢复未完成的移库草稿…</text></view>
|
||||
<view v-else-if="!source && !sourceCode" class="empty-box"><text class="empty-copy">暂无已读取托盘,请先扫描或输入来源托盘码</text></view>
|
||||
<view class="work-card">
|
||||
@@ -17,7 +17,7 @@
|
||||
<button class="secondary" :disabled="!targetCode.trim() || busy" @tap="resolveTarget(targetCode)">{{ resolving === 'target' ? '校验中…' : '校验目标库位' }}</button>
|
||||
<view v-if="target" class="task-card section-gap"><view class="row-head"><view><text class="business-code">{{ target.locationCode }}</text><text class="meta-line">{{ locationTypeLabel(target.locationType) }}</text></view><text class="status-pill">{{ statusLabel(target.status) }}</text></view><view class="metric-row"><view class="metric"><strong>{{ target.occupiedPackages }}</strong><text>已占箱数</text></view><view class="metric"><strong>{{ target.inventoryPositionCount }}</strong><text>库存位置</text></view></view></view>
|
||||
</view>
|
||||
<view class="work-card"><text class="card-title">3. 移库原因</text><textarea v-model="reason" class="reason-input" maxlength="500" placeholder="必填,例如:库位容量调整" /><button class="primary" :disabled="!canMove || busy" @tap="submitMove">{{ busyAction === 'move' ? '移库中…' : '确认整托移库' }}</button><text class="hint">来源和目标必须属于 Token 绑定的同一海外仓;冲突后必须重新扫描。</text></view>
|
||||
<view class="work-card"><text class="card-title">3. 移库原因(必填)</text><textarea v-model="reason" class="reason-input" maxlength="500" aria-label="移库原因(必填)" /><button class="primary" :disabled="!canMove || busy" @tap="submitMove">{{ busyAction === 'move' ? '移库中…' : '确认整托移库' }}</button><text class="hint">发生冲突后请重新扫描。</text></view>
|
||||
<view v-if="result" class="success-box" role="status"><text class="success-title">移库完成</text><text>{{ result.moveNo || '移库单号待生成' }}</text><text>{{ result.palletCode }}:{{ result.sourceLocationCode || '未绑定库位' }} → {{ result.targetLocationCode || '—' }}</text><text>{{ result.packageCount }} 箱 · {{ result.replayed ? '幂等恢复结果' : '本次提交成功' }}</text><button class="secondary" @tap="resetPage">继续移库</button></view>
|
||||
<view v-if="error" class="error-box" role="alert"><text class="error-title">{{ error.message }}</text><text v-if="error.code">错误码:{{ error.code }}</text><text v-if="error.requestId">requestId:{{ error.requestId }}</text><text v-if="error.traceId">traceId:{{ error.traceId }}</text><button class="secondary" @tap="refreshFacts">重新读取托盘和库位</button></view>
|
||||
</view></scroll-view>
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
<view class="matched-inbound-progress"><text>总计 {{ activeInbound.totalPackages }} 箱</text><text>已收 {{ activeInbound.receivedPackages }} 箱</text><text>待收 {{ activeInbound.pendingPackages }} 箱</text></view>
|
||||
</view>
|
||||
<text class="section-label">2. 扫描预留实物托盘码</text>
|
||||
<text class="reservation-hint">扫码确认服务端已预留的托盘及库位;合托提示不改变当前分配。</text>
|
||||
|
||||
<ScanTargetInput name="pallet" v-model="palletInput" label="托盘码" placeholder="扫描或人工输入托盘码" :active="activeTarget === 'pallet'" @activate="activeTarget = 'pallet'" @submit="resolvePallet" />
|
||||
<button class="secondary-button" :disabled="busy || !palletInput.trim()" @tap="resolvePallet(palletInput)">校验托盘</button>
|
||||
<view v-if="resolvedPallet" class="resolved-card"><text>{{ resolvedPallet.palletCode }}</text><text>{{ resolvedPallet.compatible ? '预留托盘校验通过,可收货' : '托盘不匹配' }}</text><text v-if="resolvedPallet.locationCode">库位:{{ resolvedPallet.locationCode }}{{ resolvedPallet.locationType === 'SHARED_OVERFLOW' ? '(A0共享溢出位)' : '' }}</text><text v-else>库位:待现场确认</text></view>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<view class="portal-content pallet-content">
|
||||
<view class="pallet-heading">
|
||||
<text class="pallet-title">托盘管理</text>
|
||||
<text class="pallet-subtitle">扫描实物托盘,按 Token 绑定的当前海外仓执行作业</text>
|
||||
|
||||
</view>
|
||||
|
||||
<scroll-view class="pallet-mode-scroll" scroll-x :show-scrollbar="false">
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
<view v-if="mode !== 'query' && sources.length" class="pallet-card">
|
||||
<text class="pallet-card-title">作业备注</text>
|
||||
<textarea v-model="reason" class="pallet-reason" maxlength="500" placeholder="选填,异常或调整原因" />
|
||||
<textarea v-model="reason" class="pallet-reason" maxlength="500" aria-label="作业备注(选填)" />
|
||||
<text class="pallet-confirm-tip">{{ confirmationTip }}</text>
|
||||
<button class="pallet-primary" :disabled="!canSubmit || submitting" @tap="submitOperation">{{ submitting ? '提交中…' : actionText }}</button>
|
||||
</view>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<template v-if="taskTab === 'sku' && selectedItem && action !== 'RELABEL' && remaining(selectedItem) > 0">
|
||||
<view class="mode-row section-gap"><button v-for="mode in actionModes" :key="mode.key" class="mode-button" :class="{ active: action === mode.key }" @tap="changeAction(mode.key)">{{ mode.name }}</button></view>
|
||||
<ScanTargetInput name="oldLabel" v-model="oldLabel" label="仓库唛头" placeholder="扫描或输入仓库唛头" :active="activeTarget === 'old'" @activate="activeTarget = 'old'" @submit="handleOldLabel" />
|
||||
<template v-if="action !== 'RELABEL'"><textarea v-model="reason" class="reason-input section-gap" maxlength="1000" placeholder="必填:跳过或异常原因" /><view v-if="action === 'EXCEPTION'" class="mode-row section-gap"><button v-for="item in exceptionCodes" :key="item.key" class="mode-button" :class="{ active: exceptionCode === item.key }" @tap="exceptionCode = item.key">{{ item.name }}</button></view></template>
|
||||
<template v-if="action !== 'RELABEL'"><text class="field-label section-gap">跳过或异常原因(必填)</text><textarea v-model="reason" class="reason-input" maxlength="1000" aria-label="跳过或异常原因(必填)" /><view v-if="action === 'EXCEPTION'" class="mode-row section-gap"><button v-for="item in exceptionCodes" :key="item.key" class="mode-button" :class="{ active: exceptionCode === item.key }" @tap="exceptionCode = item.key">{{ item.name }}</button></view></template>
|
||||
<button class="primary" :disabled="busy || !canSubmitUnit" @tap="submitUnit">{{ busyAction === 'unit' ? '提交中…' : unitActionText }}</button>
|
||||
</template>
|
||||
<view class="section-gap">
|
||||
@@ -56,13 +56,13 @@
|
||||
<text class="photo-name">{{ photoNames[index] || '换标现场照片' }}</text>
|
||||
<view class="photo-actions"><button class="link-button" :disabled="busy" @tap="retakePhoto(index)">重拍</button><button class="link-button" :disabled="busy" @tap="removePhoto(index)">移除</button></view>
|
||||
</view></view>
|
||||
<textarea v-model="remark" class="reason-input section-gap" maxlength="900" :placeholder="allProcessed ? '完成备注(选填)' : '必填:未全部扫描完成的仓库异常作业备注'" />
|
||||
<text class="field-label section-gap">{{ allProcessed ? '完成备注(选填)' : '异常作业备注(必填)' }}</text><textarea v-model="remark" class="reason-input" maxlength="900" :aria-label="allProcessed ? '完成备注(选填)' : '异常作业备注(必填)'" />
|
||||
<view v-if="completionBlockReason" class="error-box section-gap"><text class="error-title">还不能完成换标</text><text>{{ completionBlockReason }}</text></view>
|
||||
</view>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="stage === 'review'">
|
||||
<view class="section-gap"><text class="card-title">待客户确认</text><text class="meta-line">现场换标已经完成。请客户在客户工作台核对完工结果并确认;需要退回重做时由平台处理。</text></view>
|
||||
<view class="section-gap"><text class="card-title">待客户确认</text><text class="meta-line">现场换标已完成,等待客户确认。</text></view>
|
||||
</template>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<scroll-view class="work-scroll" scroll-y><view class="portal-content work-content">
|
||||
<button v-if="!result && (activePlan || mode === 'BOX')" class="link-button" :disabled="busy" @tap="leavePlan">返回扫码 / 计划列表</button>
|
||||
<ReturnBoxMergeWork v-if="mode === 'BOX' && !result" ref="boxWork" :key="activePlan?.planNo || initialSource?.cartonMark || 'direct'" :plan="activePlan" :initial-source="initialSource" @busy-change="busy = $event" @completed="packingCompleted" @error="setError($event, '箱合并失败')" />
|
||||
<view v-if="!result && !activePlan && mode !== 'BOX'" class="work-card direct-merge-card"><text class="card-title">仓库现场直接合箱</text><text class="hint">扫描 SKU 或箱号开始;系统会自动锁定客户和当前仓,跨客户货物不能合箱。</text><ScanTargetInput name="stock" v-model="searchCode" label="SKU 或箱号" placeholder="扫描 SKU 或箱号,查询可合箱库存" :active="true" @submit="search" /><button class="primary" :disabled="!searchCode.trim() || busy" @tap="search(searchCode)">{{ busy ? '查询中…' : '查询可合箱库存' }}</button></view>
|
||||
<view v-if="!result && !activePlan && plans.length" class="work-card"><view class="row-head"><text class="card-title">平台合箱计划</text><text class="meta-line">审核通过后推送</text></view><text class="hint">按计划锁定的来源箱和数量作业,后续合箱规则不变。</text><button v-for="plan in plans" :key="plan.planNo" class="task-card candidate-card" @tap="startPlan(plan)"><view class="row-head"><text class="business-code">{{ plan.planNo }}</text><text class="status-pill">{{ plan.status === 'READY' ? '待合箱' : plan.status }}</text></view><text class="meta-line">退货单:{{ plan.returnNos.join('、') }}</text><text class="select-link">开始作业 ›</text></button></view>
|
||||
<view v-if="!result && !activePlan && mode !== 'BOX'" class="work-card direct-merge-card"><text class="card-title">仓库现场直接合箱</text><ScanTargetInput name="stock" v-model="searchCode" label="SKU 或箱号" placeholder="扫描 SKU 或箱号,查询可合箱库存" :active="true" @submit="search" /><button class="primary" :disabled="!searchCode.trim() || busy" @tap="search(searchCode)">{{ busy ? '查询中…' : '查询可合箱库存' }}</button></view>
|
||||
<view v-if="!result && !activePlan && plans.length" class="work-card"><view class="row-head"><text class="card-title">平台合箱计划</text><text class="meta-line">审核通过后推送</text></view><button v-for="plan in plans" :key="plan.planNo" class="task-card candidate-card" @tap="startPlan(plan)"><view class="row-head"><text class="business-code">{{ plan.planNo }}</text><text class="status-pill">{{ plan.status === 'READY' ? '待合箱' : plan.status }}</text></view><text class="meta-line">退货单:{{ plan.returnNos.join('、') }}</text><text class="select-link">开始作业 ›</text></button></view>
|
||||
|
||||
<view v-if="customerCandidates.length && !selectedCustomer && !result" class="section-gap"><view class="work-card"><text class="card-title">选择客户</text><text class="hint">该 SKU 在多个客户有可用退货库存,请先选择客户</text><view class="task-list"><button v-for="(customer, index) in customerCandidates" :key="index" class="task-card candidate-card" @tap="selectCustomer(customer)"><view class="row-head"><view><text class="business-code">{{ customerLabel(customer, index) }}</text><text class="meta-line">可用 SKU {{ customer.skus.length }} 个 · 退货单 {{ customer.orders.length }} 个</text></view><text class="select-link">选择 ›</text></view></button></view></view></view>
|
||||
<view v-if="candidates !== null && !customerCandidates.length && !result" class="section-gap"><view v-if="!candidates.length" class="empty-box"><text class="empty-copy">没有匹配的已完成退货单或可用库存</text></view><view v-else class="task-list"><button v-for="item in candidates" :key="item.returnNo" class="task-card candidate-card" @tap="addOrder(item)"><view class="row-head"><view><text class="business-code">{{ item.returnNo }}</text><text class="meta-line">客户参考号:{{ item.customerReferenceNo || '—' }}</text></view><text class="select-link">添加 ›</text></view></button></view></view>
|
||||
|
||||
<template v-if="selectedOrders.length && !result && mode === 'SKU'">
|
||||
<view class="work-card"><text class="card-title">{{ activePlan ? `合箱计划 ${activePlan.planNo}` : '客户散件库存' }}</text><text class="hint">{{ activePlan ? '数量已由计划锁定,请分配到目标箱。' : '已锁定一个客户;可用数量来自该客户同仓全部散件库存,后台自动追溯来源退货单。' }}</text></view>
|
||||
<view class="work-card"><text class="card-title">{{ activePlan ? `合箱计划 ${activePlan.planNo}` : '客户散件库存' }}</text></view>
|
||||
<view class="work-card"><text class="card-title">新箱 SKU 数量</text><view class="item-list"><view v-for="item in availableSkus" :key="item.skuCode" class="item-card"><view class="row-head"><view><text class="business-code">{{ item.skuCode }}</text><text class="meta-line">{{ item.productNameZh || '商品名称未填写' }}</text></view><text class="remaining-copy">可用 {{ item.availableQuantity }}</text></view><view class="quantity-row"><text>装入数量</text><input class="number-input" type="number" :disabled="!!activePlan" :value="quantity(item.skuCode)" placeholder="0" @input="setQuantity(item, $event.detail.value)" /></view></view></view></view>
|
||||
<button class="secondary" :disabled="busy || !mergeQuantity" @tap="distributeBoxes">按单 SKU 一箱分配</button>
|
||||
<view v-for="(box,index) in targetBoxes" :key="index" class="work-card">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<view v-if="!current && !result && !receiptBatch" class="work-card">
|
||||
<text class="card-title">扫描 SKU 69 码</text>
|
||||
<ScanTargetInput name="lookup" v-model="lookupCode" label="SKU 69 码" placeholder="扫描商品 69 码,自动带出退货单号" :active="activeTarget === 'lookup'" @activate="activeTarget = 'lookup'" @submit="lookup" />
|
||||
<text class="scan-hint">扫描完成后自动匹配对应退货单</text>
|
||||
|
||||
</view>
|
||||
|
||||
<view v-if="!current && candidates !== null && !result && !receiptBatch" class="section-gap">
|
||||
@@ -47,7 +47,7 @@
|
||||
</template>
|
||||
<view v-if="unitType === 'PALLET'" class="item-list receipt-sku-list"><view v-for="item in selectedItems" :key="item.skuCode" class="item-card"><view class="row-head"><view><text class="business-code">{{ item.skuCode }}</text><text class="meta-line">{{ item.productNameZh || '商品名称未填写' }}</text></view><text class="remaining-copy">剩余 {{ remaining(item) }}</text></view><view class="sku-quantity-fields"><view class="quantity-row"><text>数量</text><input class="number-input" type="number" :value="quantity(item.skuCode)" placeholder="0" @input="setQuantity(item, $event.detail.value)" /></view><view v-if="unitType === 'PALLET'" class="quantity-row"><text>SKU 箱数</text><input class="number-input" type="number" :value="boxCount(item.skuCode)" placeholder="0" @input="setBoxCount(item, $event.detail.value)" /></view></view></view></view></view>
|
||||
<view v-if="unitType === 'PALLET'" class="summary-bar"><view><text class="summary-label">{{ unitType === 'PARCEL' ? '本次合计' : '本托合计' }}</text><text class="summary-value">{{ receiptQuantity }}</text></view><button class="primary submit-button" :disabled="!canConfirm || busy" @tap="confirmReceipt">{{ busy ? '提交中…' : unitType === 'PARCEL' ? '确认包裹签收' : '确认签收并入托' }}</button></view>
|
||||
<view class="work-card"><view class="row-head"><view><text class="card-title">退货打托照片</text><text class="hint">{{ unitType !== 'PALLET' ? '支持多张照片,上传后点击确认签收入库' : '签收过程中可随时拍照' }}</text></view><text class="status-pill">{{ photoFileIds.length }} / 20 张</text></view><button class="secondary" :disabled="busy || photoFileIds.length >= 20" @tap="choosePhoto()">拍照或从相册选择</button><view v-if="photoFileIds.length" class="photo-grid"><view v-for="(fileId,index) in photoFileIds" :key="fileId" class="photo-tile"><image class="photo-thumbnail" :src="photoUrls[fileId]" mode="aspectFill" @tap="previewPhoto(fileId)" /><text class="photo-name">{{ photoNames[index] || '退货打托照片' }}</text><button class="link-button" :disabled="busy" @tap="removePhoto(index)">移除</button></view></view></view>
|
||||
<view class="work-card"><view class="row-head"><view><text class="card-title">退货打托照片</text></view><text class="status-pill">{{ photoFileIds.length }} / 20 张</text></view><button class="secondary" :disabled="busy || photoFileIds.length >= 20" @tap="choosePhoto()">拍照或从相册选择</button><view v-if="photoFileIds.length" class="photo-grid"><view v-for="(fileId,index) in photoFileIds" :key="fileId" class="photo-tile"><image class="photo-thumbnail" :src="photoUrls[fileId]" mode="aspectFill" @tap="previewPhoto(fileId)" /><text class="photo-name">{{ photoNames[index] || '退货打托照片' }}</text><button class="link-button" :disabled="busy" @tap="removePhoto(index)">移除</button></view></view></view>
|
||||
<view v-if="unitType !== 'PALLET'" class="summary-bar"><view><text class="summary-label">本次合计</text><text class="summary-value">{{ receiptQuantity }}</text></view><button class="primary submit-button" :disabled="busy || !canConfirm || !allBoxRowsConfirmed || !photoFileIds.length" @tap="confirmReceipt">{{ busy ? '提交中…' : '确认签收入库' }}</button></view>
|
||||
<button v-if="current.status === 'PARTIALLY_RECEIVED'" class="secondary manual-complete" :disabled="busy" @tap="manualComplete">缺货手动完结</button>
|
||||
<view v-if="current.completedPallets?.length" class="work-card"><text class="card-title">本单已完成打托</text><view class="item-list"><view v-for="pallet in current.completedPallets" :key="pallet.palletCode" class="item-card"><text class="business-code">{{ pallet.palletCode }}</text><text class="meta-line">{{ pallet.skuCode }} · {{ pallet.quantity }} 件</text><text class="meta-line">签收批次:{{ pallet.receiptBatchNo }}</text></view></view></view>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<view v-if="user" class="page-shell portal-page work-page">
|
||||
<PortalHeader title="库存盘点" :user="user" fallback-url="/pages/module/module?key=inventory" />
|
||||
<scroll-view class="work-scroll" scroll-y><view class="portal-content work-content">
|
||||
<view class="work-heading"><text class="work-title">库存盘点</text><text class="work-subtitle">按 Token 绑定的海外仓冻结库位或托盘库存,逐位置实盘后提交差异</text></view>
|
||||
<view class="work-heading"><text class="work-title">库存盘点</text></view>
|
||||
<view class="work-card">
|
||||
<text class="card-title">新建盘点任务</text>
|
||||
<view class="mode-row"><button class="mode-button" :class="{ active: scopeType === 'LOCATION' }" @tap="changeScope('LOCATION')">按库位</button><button class="mode-button" :class="{ active: scopeType === 'PALLET' }" @tap="changeScope('PALLET')">按托盘</button></view>
|
||||
@@ -24,7 +24,7 @@
|
||||
<input v-model.number="item.inputPackages" class="number-input section-gap" type="number" inputmode="numeric" placeholder="输入实盘箱数(可为 0)" />
|
||||
<button class="secondary" :disabled="busy || !validCount(item) || current.task.status !== 'IN_PROGRESS'" @tap="saveCount(item)">{{ isSaving(item) ? '保存中…' : '保存本位置实盘数' }}</button>
|
||||
</view></view>
|
||||
<template v-if="current.task.status === 'IN_PROGRESS'"><text class="card-title section-gap">提交原因</text><textarea v-model="reason" class="reason-input" maxlength="500" placeholder="必填,例如:月度循环盘点" /><button class="primary" :disabled="busy || !allCounted || !reason.trim()" @tap="submitTask">{{ busyAction === 'submit' ? '提交中…' : '提交盘点并调整差异' }}</button><text class="hint">所有位置完成实盘后才能提交;版本变化时页面会自动刷新。</text></template>
|
||||
<template v-if="current.task.status === 'IN_PROGRESS'"><text class="card-title section-gap">提交原因(必填)</text><textarea v-model="reason" class="reason-input" maxlength="500" aria-label="提交原因(必填)" /><button class="primary" :disabled="busy || !allCounted || !reason.trim()" @tap="submitTask">{{ busyAction === 'submit' ? '提交中…' : '提交盘点并调整差异' }}</button><text class="hint">所有位置完成实盘后才能提交。</text></template>
|
||||
</view>
|
||||
|
||||
<view v-if="error" class="error-box" role="alert"><text class="error-title">{{ error.message }}</text><text v-if="error.code">错误码:{{ error.code }}</text><text v-if="error.requestId">requestId:{{ error.requestId }}</text><text v-if="error.traceId">traceId:{{ error.traceId }}</text><button class="secondary" @tap="retry">重新读取</button></view>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</view>
|
||||
<scroll-view scroll-y :scroll-into-view="cartonScrollTarget" scroll-with-animation class="transfer-scroll"><view class="portal-content transfer-content">
|
||||
<view v-if="entryMode === 'list'" class="transfer-heading">
|
||||
<view><text class="transfer-title">仓储转运测量列表</text><text class="transfer-subtitle">选择待测转运单</text></view>
|
||||
<view><text class="transfer-title">仓储转运测量列表</text></view>
|
||||
<button class="refresh-button" :disabled="loading" aria-label="刷新待测转运单" @tap="loadCandidates"><uni-icons type="refresh" color="#135ee8" size="18" /><text>{{ loading ? '加载中' : '刷新' }}</text></button>
|
||||
</view>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<view v-if="pallet.generated" class="pallet-stats"><view><strong>{{ palletSummary(pallet).cartonCount }}<text> 箱</text></strong><text>实收箱数</text></view><view><strong>{{ palletVolume(pallet) }}<text> m³</text></strong><text>体积</text></view><view><strong>{{ palletSummary(pallet).weight.toFixed(2) }}<text> kg</text></strong><text>重量</text></view></view>
|
||||
<view v-if="pallet.generated" class="pallet-brief"><text class="pallet-business-code" selectable>{{ pallet.palletNo }}</text><text v-if="String(receivingPalletId) === String(pallet.draftId)" class="receiving-badge">当前签收</text><button v-else :disabled="cartonsCounted || countingCartons" @tap="useReceivingPallet(pallet)">设为签收托盘</button></view>
|
||||
<view v-show="!pallet.collapsed">
|
||||
<view class="measure-section-head"><view><text class="measure-title">本托箱子汇总</text><text class="measure-hint">由分配到本托的实收箱自动汇总,无需填写托盘尺寸和重量</text></view></view>
|
||||
<view class="measure-section-head"><view><text class="measure-title">本托箱子汇总</text></view></view>
|
||||
<view class="summary-metrics"><view><strong>{{ palletSummary(pallet).cartonCount }}</strong><text>实收箱数</text></view><view><strong>{{ palletVolume(pallet) }}</strong><text>箱子体积 m³</text></view><view><strong>{{ palletSummary(pallet).weight.toFixed(2) }}</strong><text>箱子重量 kg</text></view></view>
|
||||
<button v-if="!pallet.generated" class="generate-button" :disabled="pallet.busy || cartonsCounted || countingCartons" @tap="generatePallet(pallet)">{{ pallet.busy ? '生成中…' : '生成托盘号' }}</button>
|
||||
<view v-else class="generated-card"><view><text class="print-code">{{ pallet.palletNo }}</text><text>{{ destinationLabel(pallet, selected) }}</text></view><button :disabled="pallet.busy" @tap="printDraftPallet(pallet)">打印标签</button></view>
|
||||
|
||||
@@ -18,18 +18,18 @@ async function vueFiles(directory) {
|
||||
return files
|
||||
}
|
||||
|
||||
test('every editable PDA input and textarea provides an input hint', async () => {
|
||||
test('every editable PDA input and textarea provides a label or input hint', async () => {
|
||||
const missing = []
|
||||
for (const file of await vueFiles(sourceRoot)) {
|
||||
const source = await readFile(file, 'utf8')
|
||||
for (const match of source.matchAll(/<(input|textarea)\b[^>]*>/gs)) {
|
||||
if (!/\s:?placeholder\s*=/.test(match[0])) {
|
||||
if (!/\s:?(?:placeholder|aria-label)\s*=/.test(match[0])) {
|
||||
const line = source.slice(0, match.index).split('\n').length
|
||||
missing.push(`${relative(sourceRoot, file)}:${line}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.deepEqual(missing, [], `缺少 placeholder:${missing.join(', ')}`)
|
||||
assert.deepEqual(missing, [], `缺少输入标签或提示:${missing.join(', ')}`)
|
||||
})
|
||||
|
||||
test('quantity inputs explain the valid package range', async () => {
|
||||
|
||||
@@ -130,7 +130,7 @@ test('relabel review opens a pending task as customer-confirmation read-only det
|
||||
assert.match(page, /<view v-else class="task-list">[\s\S]*@tap="openReview\(item\)"/)
|
||||
assert.match(page, /查看并复核/)
|
||||
assert.match(page, /async openReview\(item\)/)
|
||||
assert.match(page, /现场换标已经完成。请客户在客户工作台核对完工结果并确认/)
|
||||
assert.match(page, /现场换标已完成,等待客户确认/)
|
||||
assert.doesNotMatch(page, /@tap="review\('APPROVE'\)"/)
|
||||
assert.match(page, /<button v-if="stage !== 'review'" class="link-button"[^>]*@tap="bootstrap"/)
|
||||
assert.match(page, /if \(this\.stage === 'execute'\) this\.startScanner\(\)/)
|
||||
|
||||
Reference in New Issue
Block a user