5 changed files with 232 additions and 0 deletions
-
1.gitignore
-
13src/api/haianAutoWarehouse/cacheTransport.js
-
2src/router/index.js
-
5src/views/main.vue
-
211src/views/modules/haianAutoWarehouse/haianCacheTransport.vue
@ -0,0 +1,13 @@ |
|||||
|
import { createAPI } from '@/utils/httpRequest.js' |
||||
|
|
||||
|
// 海安齐套运输:getCacheTransportAreas,POST实体必须显式传site,业务错误由页面展示。- rqrq
|
||||
|
export const getCacheTransportAreas = data => createAPI('/haiAnCacheTransport/getCacheTransportAreas', 'post', data) |
||||
|
|
||||
|
// 海安齐套运输:getCacheTransportOrders,POST实体必须显式传site,业务错误由页面展示。- rqrq
|
||||
|
export const getCacheTransportOrders = data => createAPI('/haiAnCacheTransport/getCacheTransportOrders', 'post', data) |
||||
|
|
||||
|
// 海安齐套运输:getCacheTransportLabels,POST实体必须显式传site,业务错误由页面展示。- rqrq
|
||||
|
export const getCacheTransportLabels = data => createAPI('/haiAnCacheTransport/getCacheTransportLabels', 'post', data) |
||||
|
|
||||
|
// 海安齐套运输:submitCacheTransport,POST实体必须显式传site,业务错误由页面展示。- rqrq
|
||||
|
export const submitCacheTransport = data => createAPI('/haiAnCacheTransport/submitCacheTransport', 'post', data) |
||||
@ -0,0 +1,211 @@ |
|||||
|
<template> |
||||
|
<div class="pda-container cache-page"> |
||||
|
<!-- 当前缓存区只展示calling=N的托盘;齐套色彩完全使用单据transport_flag。- rqrq --> |
||||
|
<!-- 对齐库存移库页的青绿色三栏导航,保留提交中及结果待确认时的离页保护。- rqrq --> |
||||
|
<header class="status-bar cache-header"> |
||||
|
<button type="button" class="nav-button" :disabled="submitLoading || !!submission" @click="back"><i class="el-icon-arrow-left"></i>上一页</button> |
||||
|
<strong class="header-title">{{ selected ? '订单运输' : '缓存区齐套运输' }}</strong> |
||||
|
<button type="button" class="nav-button home-button" :disabled="submitLoading || !!submission" @click="$router.push({ path: '/' })">🏠首页</button> |
||||
|
</header> |
||||
|
<div v-if="!selected" class="cache-body"> |
||||
|
<!-- 查询区使用现有PDA白色表单卡片及胶囊按钮,仅调整布局和外观。- rqrq --> |
||||
|
<div class="query-panel"> |
||||
|
<label class="field-label">缓存区域</label> |
||||
|
<el-select v-model="sourceAreaId" :disabled="queryLoading || areaLoading || openLoading" placeholder="请选择缓存区" @change="loadOrders"> |
||||
|
<el-option v-for="a in cacheAreas" :key="a.areaId" :label="a.areaDesc || a.areaId" :value="a.areaId" /> |
||||
|
</el-select> |
||||
|
<div class="query-actions"> |
||||
|
<el-checkbox v-model="showIncomplete" :disabled="queryLoading || openLoading" @change="loadOrders">显示未齐套订单</el-checkbox> |
||||
|
<button type="button" class="action-btn secondary" :disabled="queryLoading || areaLoading || openLoading" @click="refresh"><i v-if="queryLoading || areaLoading" class="el-icon-loading"></i>刷新</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<p v-if="!areaLoading && !cacheAreas.length" class="empty">尚未维护缓存区,请在PC“齐套运输任务 → 缓存区设置”维护。</p> |
||||
|
<p v-else-if="!queryLoading && !orders.length" class="empty">当前没有符合条件的订单</p> |
||||
|
<section v-for="o in orders" :key="orderKey(o)" class="order-card" @click="openOrder(o)"> |
||||
|
<div class="order-title"><strong>{{ o.orderPartNo && o.orderPartNo !== '-' ? o.soorderNo + ' - ' + o.orderPartNo : o.soorderNo }}</strong><span :class="o.transportFlag === 'Y' ? 'kit-ready' : 'kit-wait'">{{ o.transportFlag === 'Y' ? '已齐套' : '未齐套' }}</span></div> |
||||
|
<div class="info-row"><span>工厂:{{ o.subSite }}</span><span>申请单类型:{{ o.orderType || '—' }}</span></div> |
||||
|
<div>领料单号:{{ o.notifyNo }}</div> |
||||
|
<div class="card-bottom"><strong>托盘数量 {{ Number(o.palletCount) || 0 }}</strong><button type="button" class="action-btn secondary" :disabled="openLoading || queryLoading" @click.stop="openOrder(o)"><i v-if="openLoading && openingKey === orderKey(o)" class="el-icon-loading"></i>查看 / 运输</button></div> |
||||
|
</section> |
||||
|
</div> |
||||
|
<div v-else class="cache-body"> |
||||
|
<section class="order-card"> |
||||
|
<div class="order-title"><strong>{{ selected.orderPartNo && selected.orderPartNo !== '-' ? selected.soorderNo + ' - ' + selected.orderPartNo : selected.soorderNo }}</strong><span :class="selected.transportFlag === 'Y' ? 'kit-ready' : 'kit-wait'">{{ selected.transportFlag === 'Y' ? '已齐套' : '未齐套' }}</span></div> |
||||
|
<div class="info-row"><span>工厂:{{ selected.subSite }}</span><span>申请单类型:{{ selected.orderType || '—' }}</span></div> |
||||
|
<div>领料单号:{{ selected.notifyNo }}</div> |
||||
|
</section> |
||||
|
<div class="query-panel"> |
||||
|
<label class="field-label">目标区域</label> |
||||
|
<el-select v-model="targetAreaId" :disabled="submitLoading || !!submission" placeholder="请选择目标区域"> |
||||
|
<el-option v-for="a in targetAreas" :key="a.areaId" :label="a.areaDesc || a.areaId" :value="a.areaId" /> |
||||
|
</el-select> |
||||
|
<button type="button" class="action-btn primary submit-button" :disabled="submitLoading || (!submission && (!targetAreaId || !palletGroups.length || selected.transportFlag !== 'Y'))" @click="submit"><i v-if="submitLoading" class="el-icon-loading"></i>{{ submission ? '重试确认提交结果' : '运输当前托盘' }}</button> |
||||
|
<p v-if="selected.transportFlag !== 'Y'" class="kit-wait">订单尚未齐套,暂不能运输。</p> |
||||
|
<p class="hint">本次只运输当前缓存区内可调用的托盘,后到托盘到达后可再建单。</p> |
||||
|
</div> |
||||
|
<h3 class="detail-title">托盘明细({{ palletGroups.length }})· 本订单标签</h3> |
||||
|
<p v-if="!palletGroups.length" class="empty">当前没有可运输托盘,请返回刷新</p> |
||||
|
<el-collapse> |
||||
|
<el-collapse-item v-for="p in palletGroups" :key="p.palletId" :name="p.palletId" :title="p.palletId + ' · ' + p.labels.length + ' 个标签'"> |
||||
|
<div v-for="label in p.labels" :key="label.subSite + ':' + label.serialNo" class="label-card" :class="labelClass(label)"> |
||||
|
<strong>{{ label.serialNo }}</strong> |
||||
|
<div>{{ label.partNo }} · {{ label.subSite }}</div> |
||||
|
<div>数量 {{ Number(label.qty) || 0 }}<span v-if="Number(label.wcsFlag) === -1"> · 发料成功</span><span v-if="Number(label.wcsFlag) === -2"> · 移库/发料失败</span></div> |
||||
|
</div> |
||||
|
</el-collapse-item> |
||||
|
</el-collapse> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { getCacheTransportAreas, getCacheTransportOrders, getCacheTransportLabels, submitCacheTransport } from '@/api/haianAutoWarehouse/cacheTransport' |
||||
|
|
||||
|
// 海安独立PDA页面;服务重查当前托盘,页面展示不作为占用或调度依据。- rqrq |
||||
|
export default { |
||||
|
data () { |
||||
|
return { site: '', areas: [], sourceAreaId: '', showIncomplete: false, orders: [], selected: null, labels: [], targetAreaId: '', areaLoading: false, queryLoading: false, openLoading: false, openingKey: '', submitLoading: false, submission: null } |
||||
|
}, |
||||
|
computed: { |
||||
|
cacheAreas () { return this.areas.filter(a => a.cacheAreaFlag === 'Y') }, |
||||
|
targetAreas () { return this.areas.filter(a => a.chooseAble === 'Y' && a.wcsAreaCode && a.areaId !== this.sourceAreaId) }, |
||||
|
// 相同托盘只展示一个折叠块,标签使用子工厂+标签号作为唯一键。- rqrq |
||||
|
palletGroups () { |
||||
|
const groups = [] |
||||
|
this.labels.forEach(label => { |
||||
|
let pallet = groups.find(p => p.palletId === label.palletId) |
||||
|
if (!pallet) { pallet = { palletId: label.palletId, labels: [] }; groups.push(pallet) } |
||||
|
pallet.labels.push(label) |
||||
|
}) |
||||
|
return groups |
||||
|
} |
||||
|
}, |
||||
|
created () { |
||||
|
// 显式业务site来自当前工厂,缓存区初始选择第一项。- rqrq |
||||
|
this.site = this.$store.state.user.site |
||||
|
this.refresh() |
||||
|
}, |
||||
|
beforeRouteLeave (to, from, next) { |
||||
|
// 请求结果未知时先原号重试,避免页面返回后另建请求掩盖原单。- rqrq |
||||
|
if (this.submitLoading || this.submission) { |
||||
|
this.$message.warning('请先确认本次提交结果') |
||||
|
next(false) |
||||
|
} else next() |
||||
|
}, |
||||
|
methods: { |
||||
|
orderKey (o) { return [o.subSite, o.notifyNo, o.orderItemNo, o.soorderNo].join('|') }, |
||||
|
labelClass (row) { return Number(row.wcsFlag) === -1 ? 'label-issued' : Number(row.wcsFlag) === -2 ? 'label-failed' : '' }, |
||||
|
// 返回列表时重新获取当前N托盘,运输成功的盘自然不再显示。- rqrq |
||||
|
back () { |
||||
|
if (this.submitLoading || this.submission) return |
||||
|
if (this.selected) { this.selected = null; this.labels = []; this.loadOrders() } else this.$router.push('/') |
||||
|
}, |
||||
|
// 每次刷新同时读取区域配置,失败清空旧列表防止误选。- rqrq |
||||
|
async refresh () { |
||||
|
if (this.areaLoading || this.queryLoading || this.openLoading) return |
||||
|
this.site = this.$store.state.user.site |
||||
|
this.areaLoading = true |
||||
|
try { |
||||
|
const { data } = await getCacheTransportAreas({ site: this.site }) |
||||
|
if (data && data.code === 0) { |
||||
|
this.areas = data.rows || [] |
||||
|
if (!this.cacheAreas.some(a => a.areaId === this.sourceAreaId)) this.sourceAreaId = this.cacheAreas.length ? this.cacheAreas[0].areaId : '' |
||||
|
await this.loadOrders() |
||||
|
} else { this.areas = []; this.orders = []; this.$alert((data && data.msg) || '读取缓存区域失败', '提示') } |
||||
|
} catch (e) { this.areas = []; this.orders = []; this.$message.error('读取缓存区域网络异常') } finally { this.areaLoading = false } |
||||
|
}, |
||||
|
// 齐套过滤随请求显式传入,不能在页面通过托盘数量推断齐套。- rqrq |
||||
|
async loadOrders () { |
||||
|
if (this.queryLoading || this.openLoading) return |
||||
|
this.orders = [] |
||||
|
if (!this.sourceAreaId) return |
||||
|
this.queryLoading = true |
||||
|
try { |
||||
|
const { data } = await getCacheTransportOrders({ site: this.$store.state.user.site, sourceAreaId: this.sourceAreaId, showIncomplete: this.showIncomplete }) |
||||
|
if (data && data.code === 0) this.orders = data.rows || [] |
||||
|
else this.$alert((data && data.msg) || '查询订单失败', '提示') |
||||
|
} catch (e) { this.$message.error('查询订单网络异常') } finally { this.queryLoading = false } |
||||
|
}, |
||||
|
// 打开时重查当前缓存区N托盘的订单标签,防止列表查询后已被其它任务占用。- rqrq |
||||
|
async openOrder (order) { |
||||
|
if (this.openLoading || this.queryLoading) return |
||||
|
this.openLoading = true; this.openingKey = this.orderKey(order) |
||||
|
try { |
||||
|
const { data } = await getCacheTransportLabels(Object.assign({}, order, { site: this.$store.state.user.site, sourceAreaId: this.sourceAreaId })) |
||||
|
if (data && data.code === 0) { this.selected = order; this.labels = data.rows || []; this.targetAreaId = ''; this.submission = null } else this.$alert((data && data.msg) || '读取订单托盘失败', '提示') |
||||
|
} catch (e) { this.$message.error('读取订单托盘网络异常') } finally { this.openLoading = false } |
||||
|
}, |
||||
|
// 同一次提交保留请求号和完整快照;网络错误只允许原参数重试,后端幂等返回原单。- rqrq |
||||
|
async submit () { |
||||
|
if (this.submitLoading || !this.selected) return |
||||
|
if (!this.submission && (!this.targetAreaId || !this.palletGroups.length || this.selected.transportFlag !== 'Y')) return |
||||
|
if (!this.submission) { |
||||
|
this.submission = Object.assign({}, this.selected, { |
||||
|
site: this.$store.state.user.site, |
||||
|
sourceAreaId: this.sourceAreaId, |
||||
|
targetAreaId: this.targetAreaId, |
||||
|
requestNo: 'PDA-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2) + '-' + Math.random().toString(36).slice(2) |
||||
|
}) |
||||
|
} |
||||
|
this.submitLoading = true |
||||
|
try { |
||||
|
const { data } = await submitCacheTransport(this.submission) |
||||
|
if (data && data.code === 0) { |
||||
|
const row = data.row || {} |
||||
|
this.submission = null; this.selected = null; this.labels = [] |
||||
|
this.$message.success('已创建齐套运输单,共 ' + (Number(row.palletCount) || 0) + ' 盘') |
||||
|
await this.loadOrders() |
||||
|
} else { |
||||
|
this.submission = null |
||||
|
this.$alert((data && data.msg) || '提交失败,请返回刷新托盘状态', '提示') |
||||
|
} |
||||
|
} catch (e) { this.$message.error('提交结果未确认,请点击重试确认;不会重复建单') } finally { this.submitLoading = false } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 对齐库存移库的40px青绿色顶栏和灰底白卡;局部覆盖,避免改变其它PDA页面。- rqrq */ |
||||
|
.cache-page { width: 100%; height: 100vh; min-height: 0; padding-bottom: 0; display: flex; flex-direction: column; overflow: hidden; background: #f5f5f5; color: #333; } |
||||
|
.cache-header { display: grid; grid-template-columns: 72px minmax(0, 1fr) 60px; align-items: center; flex-shrink: 0; height: 40px; min-height: 40px; margin: 0; padding: 0 12px; box-sizing: border-box; background: #17b3a3; color: #fff; } |
||||
|
.header-title { text-align: center; font-size: 16px; font-weight: 500; white-space: nowrap; } |
||||
|
.nav-button { height: 40px; padding: 0; border: 0; border-radius: 0; background: transparent; color: #fff; font: inherit; font-size: 16px; text-align: left; cursor: pointer; } |
||||
|
.home-button { font-size: 14px; text-align: right; } |
||||
|
.nav-button:disabled { opacity: 0.5; cursor: not-allowed; } |
||||
|
.cache-body { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 0 20px; } |
||||
|
.query-panel, .order-card { background: #fff; margin: 4px; padding: 10px 16px; border: 1px solid #f0f0f0; border-radius: 8px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } |
||||
|
.field-label { display: block; font-size: 12px; font-weight: 500; color: #666; margin: 0 0 4px; } |
||||
|
.cache-body .el-select { width: 100%; } |
||||
|
.query-actions, .order-title, .card-bottom { display: flex; align-items: center; justify-content: space-between; gap: 10px; } |
||||
|
.query-actions { margin-top: 10px; } |
||||
|
.order-card { line-height: 1.45; overflow-wrap: anywhere; } |
||||
|
/* 覆盖全局info-row的分隔线和上下留白,订单卡片保持无横线的紧凑布局。- rqrq */ |
||||
|
.cache-page .order-card .info-row { display: flex; justify-content: space-between; gap: 12px; padding: 0; border-bottom: none; } |
||||
|
.info-row span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
||||
|
.order-title { margin-bottom: 2px; line-height: 1.35; } |
||||
|
.order-title strong { font-size: 16px; } |
||||
|
.card-bottom { margin-top: 4px; } |
||||
|
.kit-ready { color: #21823e; font-weight: bold; white-space: nowrap; } |
||||
|
.kit-wait { color: #c23838; font-weight: bold; white-space: nowrap; } |
||||
|
/* 刷新/查看使用PDA描边胶囊按钮,运输使用实心主按钮,保留加载及禁用反馈。- rqrq */ |
||||
|
.cache-page .action-btn { flex: none; min-width: 72px; min-height: 34px; padding: 8px 16px; border: 1px solid #17b3a3; border-radius: 20px; background: #fff; color: #17b3a3; font-size: 14px; line-height: 18px; cursor: pointer; } |
||||
|
.cache-page .action-btn.primary { background: #17b3a3; color: #fff; } |
||||
|
.cache-page .action-btn:hover:not(:disabled) { background: #0d8f7f; border-color: #0d8f7f; color: #fff; } |
||||
|
.cache-page .action-btn:disabled { background: #f5f7fa; border-color: #e4e7ed; color: #c0c4cc; cursor: not-allowed; transform: none; } |
||||
|
.cache-page .action-btn.primary:disabled { background: #c0c4cc; border-color: #c0c4cc; color: #fff; } |
||||
|
.cache-page .action-btn i { margin-right: 4px; } |
||||
|
.cache-page .submit-button { width: 100%; min-height: 40px; margin: 12px 0 6px; } |
||||
|
.hint { color: #999; font-size: 12px; line-height: 1.7; } |
||||
|
.empty { margin: 4px; padding: 32px 16px; border-radius: 8px; background: #fff; color: #999; line-height: 1.7; text-align: center; } |
||||
|
.detail-title { margin: 4px 4px 0; padding: 8px 16px; border-radius: 8px 8px 0 0; border-bottom: 2px solid #17b3a3; background: #fff; color: #17b3a3; font-size: 14px; font-weight: 500; } |
||||
|
.label-card { padding: 12px; border-bottom: 1px solid #e8edf3; line-height: 1.8; overflow-wrap: anywhere; } |
||||
|
.label-issued { background: #e1f3d8; color: #286317; } |
||||
|
.label-failed { background: #fff2b3; color: #775500; } |
||||
|
.cache-page /deep/ .el-input__inner { height: 32px; line-height: 32px; } |
||||
|
.cache-page /deep/ .el-input__icon { line-height: 32px; } |
||||
|
.cache-page /deep/ .el-checkbox__label { font-size: 12px; padding-left: 6px; } |
||||
|
.cache-page /deep/ .el-collapse { margin: 0 4px; } |
||||
|
.cache-page /deep/ .el-collapse-item__header { padding: 0 12px; min-height: 44px; font-size: 14px; } |
||||
|
.cache-page /deep/ .el-collapse-item__content { padding-bottom: 0; } |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue