feat:多项功能优化

This commit is contained in:
zhoujianjin
2026-09-18 18:54:54 +08:00
parent 672cd23329
commit 543655d441
48 changed files with 1289 additions and 447 deletions
+177 -31
View File
@@ -40,15 +40,34 @@ const zoneFilter = reactive({
const zonePage = reactive({ current: 1, size: 10 })
const zoneDialog = ref(false)
const zoneEditingId = ref(0)
// 区域维护按「区域编码」分组:编辑键是 zoneCode(不是单行 id),一个区域可挂多个货位
const zoneEditingCode = ref('')
// 区域库位:区域 → 货架号 → 第几层 → 位置号(客户诉求 问题记录 L32/L169,手填)
const zoneForm = reactive({ zoneCode: '', zoneName: '', description: '', shelfNo: '', layerNo: '', positionNo: '' })
// 一个区域下可有多个货位,故 locations 为数组;新增时默认给一行
const zoneForm = reactive({ zoneCode: '', zoneName: '', description: '', status: '启用', locations: [] })
const zoneSaving = ref(false)
// 批量生成货位(货架号 × 层区间 × 位置区间),解决「一个货架不止一层、每层不止一个位置号」的逐条录入
const genForm = reactive({ shelfNo: '', layerFrom: 1, layerTo: 1, posFrom: 1, posTo: 1 })
function newZoneLocation() {
return { id: 0, shelfNo: '', layerNo: '', positionNo: '' }
}
// 货位标签(区域-货架-层-位置),仅用于分组子表展示,口径与后端 locLabel 一致
function zoneLocLabel(zoneCode, loc) {
const parts = []
if (zoneCode) parts.push(zoneCode)
if (loc.shelfNo) parts.push(loc.shelfNo + '架')
if (loc.layerNo) parts.push(loc.layerNo + '层')
if (loc.positionNo) parts.push(loc.positionNo + '位')
return parts.length ? parts.join('-') : '(未填写货位)'
}
async function loadZones() {
zoneLoading.value = true
try {
const data = await request.get('/zone/list', {
const data = await request.get('/zone/grouped', {
params: {
zoneCode: zoneFilter.zoneCode.trim().toUpperCase(),
zoneName: zoneFilter.zoneName.trim(),
@@ -70,25 +89,75 @@ function searchZones() {
}
function openZoneCreate() {
zoneEditingId.value = 0
Object.assign(zoneForm, { zoneCode: '', zoneName: '', description: '', shelfNo: '', layerNo: '', positionNo: '' })
zoneEditingCode.value = ''
Object.assign(zoneForm, {
zoneCode: '', zoneName: '', description: '', status: '启用',
locations: [newZoneLocation()]
})
Object.assign(genForm, { shelfNo: '', layerFrom: 1, layerTo: 1, posFrom: 1, posTo: 1 })
zoneDialog.value = true
}
function openZoneEdit(row) {
zoneEditingId.value = row.id
zoneEditingCode.value = row.zoneCode
Object.assign(zoneForm, {
zoneCode: row.zoneCode, zoneName: row.zoneName, description: row.description || '',
shelfNo: row.shelfNo || '', layerNo: row.layerNo || '', positionNo: row.positionNo || ''
zoneCode: row.zoneCode,
zoneName: row.zoneName,
description: row.description || '',
status: row.status || '启用',
locations: (row.locations || []).map((l) => ({
id: l.id, shelfNo: l.shelfNo || '', layerNo: l.layerNo || '', positionNo: l.positionNo || ''
}))
})
if (!zoneForm.locations.length) zoneForm.locations.push(newZoneLocation())
Object.assign(genForm, { shelfNo: '', layerFrom: 1, layerTo: 1, posFrom: 1, posTo: 1 })
zoneDialog.value = true
}
function addZoneLocation() {
zoneForm.locations.push(newZoneLocation())
}
function removeZoneLocation(idx) {
if (zoneForm.locations.length <= 1) {
ElMessage.warning('至少保留一个货位')
return
}
zoneForm.locations.splice(idx, 1)
}
// 按 货架号 × [层区间] × [位置区间] 笛卡尔积批量追加货位行
function genZoneLocations() {
const shelf = (genForm.shelfNo || '').trim()
const lf = Number(genForm.layerFrom), lt = Number(genForm.layerTo)
const pf = Number(genForm.posFrom), pt = Number(genForm.posTo)
if (!Number.isInteger(lf) || !Number.isInteger(lt) || !Number.isInteger(pf) || !Number.isInteger(pt) ||
lf < 0 || lt < lf || pf < 0 || pt < pf) {
ElMessage.warning('层/位置的起止值不合法(起始需 ≤ 结束)')
return
}
const count = (lt - lf + 1) * (pt - pf + 1)
if (count > 200) {
ElMessage.warning('一次最多生成 200 个货位,请缩小范围')
return
}
for (let l = lf; l <= lt; l++) {
for (let p = pf; p <= pt; p++) {
zoneForm.locations.push({ id: 0, shelfNo: shelf, layerNo: String(l), positionNo: String(p) })
}
}
ElMessage.success(`已生成 ${count} 个货位行,可再手动微调`)
}
async function saveZone() {
if (!zoneForm.zoneCode || !zoneForm.zoneName) {
ElMessage.warning('区域编码和区域名称必填')
return
}
if (!zoneForm.locations.length) {
ElMessage.warning('至少填写一个货位')
return
}
try {
await ElMessageBox.confirm('确认保存该区域信息吗?保存后将写入数据库。', '操作确认', {
confirmButtonText: '确认保存',
@@ -100,11 +169,23 @@ async function saveZone() {
}
zoneSaving.value = true
try {
if (zoneEditingId.value) {
await request.post('/zone/update', { id: zoneEditingId.value, ...zoneForm })
const payload = {
zoneCode: zoneForm.zoneCode,
zoneName: zoneForm.zoneName,
description: zoneForm.description,
status: zoneForm.status,
locations: zoneForm.locations.map((l) => ({
id: l.id || 0,
shelfNo: (l.shelfNo || '').trim(),
layerNo: (l.layerNo || '').trim(),
positionNo: (l.positionNo || '').trim()
}))
}
if (zoneEditingCode.value) {
await request.post('/zone/update', payload)
ElMessage.success('区域修改成功')
} else {
await request.post('/zone/create', { ...zoneForm })
await request.post('/zone/create', payload)
ElMessage.success('区域新增成功')
}
zoneDialog.value = false
@@ -346,18 +427,36 @@ async function onMaterialFile(e) {
<el-button type="success" @click="openZoneCreate">新增区域</el-button>
</el-form-item>
</el-form>
<el-table :data="zones" border size="small" v-loading="zoneLoading" empty-text="暂无区域,请点击【新增区域】">
<el-table-column label="ID" prop="id" width="60" align="center" />
<el-table :data="zones" border size="small" v-loading="zoneLoading" row-key="zoneCode"
empty-text="暂无区域请点击新增区域">
<!-- 分组展示一个区域一行展开后是该区域下的货位明细不再重复区域编码/名称 -->
<el-table-column type="expand">
<template #default="{ row }">
<div style="padding:4px 16px 10px">
<el-table :data="row.locations" border size="small" empty-text="该区域暂无货位">
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="货架号" width="120" align="center">
<template #default="{ row: loc }">{{ loc.shelfNo || '-' }}</template>
</el-table-column>
<el-table-column label="第几层" width="100" align="center">
<template #default="{ row: loc }">{{ loc.layerNo || '-' }}</template>
</el-table-column>
<el-table-column label="位置号" width="110" align="center">
<template #default="{ row: loc }">{{ loc.positionNo || '-' }}</template>
</el-table-column>
<el-table-column label="货位标签" min-width="180">
<template #default="{ row: loc }">{{ zoneLocLabel(row.zoneCode, loc) }}</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column prop="zoneCode" label="区域编码" min-width="120" />
<el-table-column prop="zoneName" label="区域名称" min-width="150" />
<el-table-column prop="shelfNo" label="货架号" width="90" align="center">
<template #default="{ row }">{{ row.shelfNo || '-' }}</template>
</el-table-column>
<el-table-column prop="layerNo" label="层" width="70" align="center">
<template #default="{ row }">{{ row.layerNo || '-' }}</template>
</el-table-column>
<el-table-column prop="positionNo" label="位置号" width="90" align="center">
<template #default="{ row }">{{ row.positionNo || '-' }}</template>
<el-table-column label="货位数" width="90" align="center">
<template #default="{ row }">
<el-tag size="small" type="info">{{ row.locationCount || 0 }}</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" width="90" align="center">
<template #default="{ row }">
@@ -385,27 +484,74 @@ async function onMaterialFile(e) {
@size-change="(s) => { zonePage.size = s; zonePage.current = 1; loadZones() }" />
</div>
<el-dialog v-model="zoneDialog" :title="zoneEditingId ? '编辑区域' : '新增区域'" width="460px"
<el-dialog v-model="zoneDialog" :title="zoneEditingCode ? '编辑区域' : '新增区域'" width="740px"
append-to-body destroy-on-close>
<el-form label-width="90px">
<el-form-item label="区域编码" required>
<el-input v-model="zoneForm.zoneCode" placeholder=" Z05" :disabled="!!zoneEditingId" clearable @input="zoneForm.zoneCode = (zoneForm.zoneCode || '').toUpperCase()" />
<el-input v-model="zoneForm.zoneCode" placeholder=" Z05" :disabled="!!zoneEditingCode" clearable
@input="zoneForm.zoneCode = (zoneForm.zoneCode || '').toUpperCase()" />
</el-form-item>
<el-form-item label="区域名称" required>
<el-input v-model="zoneForm.zoneName" placeholder=" 待检区" clearable />
</el-form-item>
<el-form-item label="货架号">
<el-input v-model="zoneForm.shelfNo" placeholder=" A / 03可空手填" clearable />
</el-form-item>
<el-form-item label="第几层">
<el-input v-model="zoneForm.layerNo" placeholder=" 3可空手填" clearable />
</el-form-item>
<el-form-item label="位置号">
<el-input v-model="zoneForm.positionNo" placeholder=" 12可空手填" clearable />
<el-form-item v-if="zoneEditingCode" label="状态">
<el-select v-model="zoneForm.status" style="width:130px">
<el-option label="启用" value="启用" />
<el-option label="停用" value="停用" />
</el-select>
</el-form-item>
<el-form-item label="说明">
<el-input v-model="zoneForm.description" type="textarea" :rows="2" placeholder="备注可空" />
</el-form-item>
<el-form-item label="货位">
<div style="width:100%">
<el-table :data="zoneForm.locations" border size="small" max-height="240">
<el-table-column label="货架号" width="150">
<template #default="{ row }">
<el-input v-model="row.shelfNo" placeholder=" A" size="small" clearable />
</template>
</el-table-column>
<el-table-column label="第几层" width="140">
<template #default="{ row }">
<el-input v-model="row.layerNo" placeholder=" 3" size="small" clearable />
</template>
</el-table-column>
<el-table-column label="位置号" width="140">
<template #default="{ row }">
<el-input v-model="row.positionNo" placeholder=" 12" size="small" clearable />
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ $index }">
<el-button link type="danger" size="small" @click="removeZoneLocation($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top:8px">
<el-button size="small" @click="addZoneLocation">添加一行</el-button>
</div>
<el-divider content-position="left" style="margin:12px 0" />
<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap">
<el-input v-model="genForm.shelfNo" placeholder="货架号" size="small" style="width:90px" clearable />
<span style="font-size:12px;color:#606266">层</span>
<el-input-number v-model="genForm.layerFrom" :min="0" :step="1" step-strictly size="small"
controls-position="right" style="width:92px" />
<span style="font-size:12px;color:#606266">至</span>
<el-input-number v-model="genForm.layerTo" :min="0" :step="1" step-strictly size="small"
controls-position="right" style="width:92px" />
<span style="font-size:12px;color:#606266">位置</span>
<el-input-number v-model="genForm.posFrom" :min="0" :step="1" step-strictly size="small"
controls-position="right" style="width:92px" />
<span style="font-size:12px;color:#606266">至</span>
<el-input-number v-model="genForm.posTo" :min="0" :step="1" step-strictly size="small"
controls-position="right" style="width:92px" />
<el-button size="small" type="primary" plain @click="genZoneLocations">生成货位</el-button>
</div>
<div style="color:#909399;font-size:12px;margin-top:6px">
例:货架号 A、层 1 至 3、位置 1 至 4 → 一次生成 12 个货位(A架1层1位 … A架3层4位)
</div>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="zoneDialog = false">取消</el-button>