feat: 完成WMS系统权限体系与业务功能迭代
本提交完成了WMS系统的多维度优化升级: 1. 新增RBAC权限系统,支持角色/菜单/按钮三级权限管控 2. 重构库存盘点、物料管理、区域维护等模块的查询筛选与展示逻辑 3. 优化入库/出库/质检等业务流程,完善数据冗余与业务闭环 4. 移除旧装箱表,将装箱逻辑合并到出库主表 5. 新增前端权限判断工具、导出工具与端到端测试用例 6. 补充完善各类注释与数据库字段说明
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { getRealName } from '../utils/auth'
|
||||
@@ -9,81 +9,111 @@ import { helpInspection } from '../help'
|
||||
const targetType = ref('BATCH')
|
||||
const status = ref('合格')
|
||||
const inspector = ref(getRealName())
|
||||
const submitting = ref(false)
|
||||
|
||||
/* ---------- 单个录入(扫码) ---------- */
|
||||
const scanRef = ref()
|
||||
const targetId = ref('')
|
||||
const submitting1 = ref(false)
|
||||
// 检验量化字段
|
||||
const inspectQty = ref(0)
|
||||
const passQty = ref(0)
|
||||
const checkDesc = ref('')
|
||||
const failReason = ref('')
|
||||
|
||||
onMounted(() => nextTick(() => scanRef.value?.focus()))
|
||||
// 待检编号列表(合并单/批量,统一一次提交)
|
||||
const targetList = ref([])
|
||||
const scanInput = ref('')
|
||||
const pickerValue = ref('')
|
||||
|
||||
async function submitSingle() {
|
||||
const id = targetId.value.trim()
|
||||
if (!id) {
|
||||
ElMessage.warning('请扫入或输入批次号或 SN 序列号')
|
||||
return
|
||||
}
|
||||
if (!inspector.value.trim()) {
|
||||
ElMessage.warning('请填写检验员')
|
||||
return
|
||||
}
|
||||
submitting1.value = true
|
||||
// 输入框标题/占位随目标类型变化(而非仅一行小字提示)
|
||||
const inputLabel = computed(() => (targetType.value === 'BATCH' ? '批次号' : 'SN 序列号'))
|
||||
const inputPlaceholder = computed(() =>
|
||||
targetType.value === 'BATCH'
|
||||
? '扫入或粘贴批次号,可用 逗号/分号/空格/回车 分隔,一次可录多条'
|
||||
: '扫入或粘贴 SN 序列号,可用 逗号/分号/空格/回车 分隔,一次可录多条'
|
||||
)
|
||||
|
||||
// 库存拾取器:直接选已存在的批次号/SN,避免手输(垃圾设计)
|
||||
const pickerOptions = ref([])
|
||||
async function loadPicker() {
|
||||
try {
|
||||
await request.post('/inspection/create', {
|
||||
targetType: targetType.value,
|
||||
targetId: id,
|
||||
status: status.value,
|
||||
inspector: inspector.value.trim()
|
||||
const mm = targetType.value === 'BATCH' ? 1 : 2
|
||||
const data = await request.get('/stock/query', {
|
||||
params: { manageMode: mm, page: 1, pageSize: 500 }
|
||||
})
|
||||
ElMessage.success(`检验已记录:${targetType.value} ${id} → ${status.value}`)
|
||||
targetId.value = ''
|
||||
nextTick(() => scanRef.value?.focus())
|
||||
} finally {
|
||||
submitting1.value = false
|
||||
pickerOptions.value = data?.list || []
|
||||
} catch (e) {
|
||||
pickerOptions.value = []
|
||||
}
|
||||
}
|
||||
onMounted(loadPicker)
|
||||
|
||||
/* ---------- 批量翻转 ---------- */
|
||||
const batchText = ref('')
|
||||
const submitting2 = ref(false)
|
||||
|
||||
function parseTargets() {
|
||||
return [...new Set(String(batchText.value).split(/\s+/).map(s => s.trim()).filter(Boolean))]
|
||||
function pickerLabel(opt) {
|
||||
const id = targetType.value === 'BATCH' ? opt.batchNo : opt.snCode
|
||||
return `${opt.materialCode || ''}|${id || ''}|质量:${opt.qualityStatus || '未检'}`
|
||||
}
|
||||
|
||||
async function submitBatch() {
|
||||
const ids = parseTargets()
|
||||
if (!ids.length) {
|
||||
ElMessage.warning('请录入待检编号(每行一个)')
|
||||
function addFromInput() {
|
||||
const arr = String(scanInput.value)
|
||||
.split(/[,,.。;;、\s]+/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
for (const v of arr) {
|
||||
if (!targetList.value.includes(v)) targetList.value.push(v)
|
||||
}
|
||||
scanInput.value = ''
|
||||
}
|
||||
function addFromPicker() {
|
||||
if (!pickerValue.value) return
|
||||
if (!targetList.value.includes(pickerValue.value)) targetList.value.push(pickerValue.value)
|
||||
pickerValue.value = ''
|
||||
}
|
||||
function removeAt(i) {
|
||||
targetList.value.splice(i, 1)
|
||||
}
|
||||
function clearAll() {
|
||||
targetList.value = []
|
||||
}
|
||||
|
||||
async function submitInspection() {
|
||||
if (!targetList.value.length) {
|
||||
ElMessage.warning('请先录入或选择待检编号')
|
||||
return
|
||||
}
|
||||
if (!inspector.value.trim()) {
|
||||
ElMessage.warning('请填写检验员')
|
||||
return
|
||||
}
|
||||
submitting2.value = true
|
||||
submitting.value = true
|
||||
try {
|
||||
const data = await request.post('/inspection/batch-flip', {
|
||||
targetType: targetType.value,
|
||||
targetIds: ids,
|
||||
targetIds: [...targetList.value],
|
||||
status: status.value,
|
||||
inspectQty: Number(inspectQty.value) || 0,
|
||||
passQty: Number(passQty.value) || 0,
|
||||
checkDesc: checkDesc.value.trim(),
|
||||
failReason: failReason.value.trim(),
|
||||
inspector: inspector.value.trim()
|
||||
})
|
||||
ElMessage.success(`批量检验完成:共处理 ${data?.flipped ?? ids.length} 条`)
|
||||
batchText.value = ''
|
||||
const n = data?.flipped ?? targetList.value.length
|
||||
ElMessage.success(`检验已记录:共 ${n} 条 → ${status.value}`)
|
||||
clearAll()
|
||||
inspectQty.value = 0
|
||||
passQty.value = 0
|
||||
checkDesc.value = ''
|
||||
failReason.value = ''
|
||||
} finally {
|
||||
submitting2.value = false
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never" style="max-width:760px;margin:0 auto">
|
||||
<el-form label-width="110px">
|
||||
<el-card shadow="never" style="max-width:860px;margin:0 auto">
|
||||
<el-form label-width="96px">
|
||||
<!-- 目标类型直接改变输入框标题与占位,直观体现当前录入对象 -->
|
||||
<el-form-item label="目标类型">
|
||||
<el-radio-group v-model="targetType">
|
||||
<el-radio-button value="BATCH">批次 BATCH</el-radio-button>
|
||||
<el-radio-button value="SN">序列号 SN</el-radio-button>
|
||||
<el-radio-group v-model="targetType" @change="loadPicker">
|
||||
<el-radio-button value="BATCH">批次 (结构件)</el-radio-button>
|
||||
<el-radio-button value="SN">序列号 SN (精密件)</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="检验结论">
|
||||
@@ -96,26 +126,61 @@ async function submitBatch() {
|
||||
<el-input v-model="inspector" placeholder="检验员姓名" clearable style="width:260px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">单个录入(扫码)</el-divider>
|
||||
<el-form-item label="扫码输入">
|
||||
<el-input ref="scanRef" v-model="targetId" autofocus clearable
|
||||
placeholder="扫入批次号或 SN,回车提交"
|
||||
style="width:420px"
|
||||
@keyup.enter="submitSingle" />
|
||||
<span class="tip">当前目标:{{ targetType === 'BATCH' ? '批次号' : 'SN 序列号' }}</span>
|
||||
<el-divider content-position="left">检验量化(可空)</el-divider>
|
||||
<el-form-item label="检验数量">
|
||||
<el-input-number v-model="inspectQty" :min="0" :max="999999" placeholder="抽检件数" style="width:160px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="submitting1" @click="submitSingle">提交检验</el-button>
|
||||
<el-form-item label="合格数量">
|
||||
<el-input-number v-model="passQty" :min="0" :max="999999" placeholder="合格件数(≤检验数量)" style="width:160px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="检测说明">
|
||||
<el-input v-model="checkDesc" placeholder="检验项目/方法/标准(可空)" clearable style="width:420px" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="status === '不合格'" label="不合格原因">
|
||||
<el-input v-model="failReason" type="textarea" :rows="2" placeholder="不合格原因(可空)" style="width:420px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">批量翻转</el-divider>
|
||||
<el-form-item label="批量编号">
|
||||
<el-input v-model="batchText" type="textarea" :rows="6"
|
||||
placeholder="每行一个编号,支持扫码枪连续录入" />
|
||||
<el-divider content-position="left">待检编号(可扫/选多条,统一提交)</el-divider>
|
||||
|
||||
<!-- 从库存直接拾取,避免手输批次号 -->
|
||||
<el-form-item :label="`从库存选${inputLabel}`">
|
||||
<el-select
|
||||
v-model="pickerValue"
|
||||
filterable
|
||||
clearable
|
||||
:placeholder="`选择已存在的${inputLabel}(来自库存)`"
|
||||
style="width:100%"
|
||||
@change="addFromPicker"
|
||||
>
|
||||
<el-option v-for="opt in pickerOptions" :key="targetType === 'BATCH' ? opt.batchNo : opt.snCode"
|
||||
:value="targetType === 'BATCH' ? opt.batchNo : opt.snCode"
|
||||
:label="pickerLabel(opt)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 批量/扫码录入:单条也走这里,无需单独的"单个录入" -->
|
||||
<el-form-item :label="`录入${inputLabel}`">
|
||||
<div style="width:100%">
|
||||
<el-input v-model="scanInput" type="textarea" :rows="4"
|
||||
:placeholder="inputPlaceholder" />
|
||||
<el-button type="primary" plain style="margin-top:6px" :disabled="!scanInput.trim()"
|
||||
@click="addFromInput">录入到列表</el-button>
|
||||
<span style="margin-left:8px;color:#909399;font-size:12px">支持扫码枪/粘贴,多编号用逗号、分号、空格或回车分隔</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 已选清单 -->
|
||||
<el-form-item label="已选清单">
|
||||
<div style="width:100%">
|
||||
<el-tag v-for="(v, i) in targetList" :key="v + i" closable type="info"
|
||||
style="margin:0 8px 8px 0" @close="removeAt(i)">{{ v }}</el-tag>
|
||||
<span v-if="!targetList.length" style="color:#909399;font-size:12px">尚未选择任何待检编号</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="submitting2" @click="submitBatch">批量提交</el-button>
|
||||
<el-button @click="batchText = ''">清空</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitInspection">提交检验</el-button>
|
||||
<el-button :disabled="!targetList.length" @click="clearAll">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@@ -124,4 +189,4 @@ async function submitBatch() {
|
||||
|
||||
<style scoped>
|
||||
.tip { margin-left: 10px; color: #909399; font-size: 12px; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user