Files
bj_power/bj_power_wms_client/frontend/src/pages/Semi.vue
T
SunYF c1fc6d32b2 feat: 完成多模块功能迭代与优化
本次提交包含多模块功能更新:
1. 权限系统重构:移除硬编码admin放行逻辑,新增精细化权限控制,完善权限树形结构与用户/角色保护
2. 新增物料导入导出接口,补充台账查询条件
3. 优化列表查询排序逻辑,新增区域编码大小写不敏感搜索
4. 修复帮助抽屉重复弹出问题,补充页面权限控制
5. 新增弱密码提示逻辑,优化MES/WMS用户/角色管理权限
6. 调整路由结构,新增权限校验脚本与前端权限树重构
7. 补充数据库字段与依赖包更新
2026-09-03 18:13:59 +08:00

236 lines
8.7 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
defineOptions({ name: 'Semi' })
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import request from '../utils/request'
import { fmtTime } from '../utils/format'
import { getRealName } from '../utils/auth'
import PageHelp from '../components/PageHelp.vue'
import { helpSemi } from '../help'
/* ---------- 半成品/成品入库(携带已完成工序,连续扫码) ---------- */
const materials = ref([])
onMounted(async () => {
try {
const ms = await request.get('/material/list')
materials.value = Array.isArray(ms) ? ms : ms?.list || []
} catch { /* 字典加载失败不阻塞入库 */ }
loadList()
})
const operator = getRealName()
const inForm = reactive({ materialCode: '', zoneCode: '', orderNo: '', completedProcess: '' })
const snInput = ref('')
const snList = ref([])
const inbounding = ref(false)
function addSnLines() {
for (let raw of String(snInput.value).split(/\s+/)) {
const sn = raw.trim()
if (sn && !snList.value.includes(sn)) snList.value.push(sn)
}
snInput.value = ''
}
function removeSn(i) {
snList.value.splice(i, 1)
}
async function submitInbound() {
if (!inForm.materialCode) {
ElMessage.warning('请选择物料')
return
}
if (!snList.value.length) {
ElMessage.warning('请扫码录入 SN(回车追加)')
return
}
inbounding.value = true
let ok = 0
try {
for (const sn of snList.value) {
try {
await request.post('/semi/inbound', {
sn,
materialCode: inForm.materialCode,
completedProcess: inForm.completedProcess.trim(),
zoneCode: inForm.zoneCode || undefined,
orderNo: inForm.orderNo.trim() || undefined,
operator
})
ok++
} catch { /* 单条失败不中断后续 */ }
}
ElMessage.success(`半成品入库完成:成功 ${ok} / ${snList.value.length} 条`)
snList.value = []
snInput.value = ''
} finally {
inbounding.value = false
}
}
/* ---------- 半成品出库(重上线) ---------- */
const outForm = reactive({ sn: '' })
const outbounding = ref(false)
async function submitOutbound() {
const sn = outForm.sn.trim()
if (!sn) {
ElMessage.warning('请输入 SN')
return
}
outbounding.value = true
try {
await request.post('/semi/outbound', { sn, operator })
ElMessage.success(`半成品出库成功:${sn}`)
outForm.sn = ''
await loadList()
} finally {
outbounding.value = false
}
}
/* ---------- 半成品查询 ---------- */
const qForm = reactive({ materialCode: '', status: '' })
const list = ref([])
const total = ref(0)
const loading = ref(false)
const page = reactive({ current: 1, size: 10 })
async function loadList() {
loading.value = true
try {
const data = await request.get('/semi/query', {
params: {
materialCode: qForm.materialCode.trim(),
status: qForm.status,
page: page.current,
pageSize: page.size
}
})
list.value = data?.list || []
total.value = data?.total || 0
} finally {
loading.value = false
}
}
function search() {
page.current = 1
loadList()
}
</script>
<template>
<el-card shadow="never">
<el-tabs>
<el-tab-pane label="半成品/成品入库">
<el-form label-width="110px" style="max-width:680px">
<el-form-item label="物料">
<el-select v-model="inForm.materialCode" filterable placeholder="选择物料编码" style="width:100%">
<el-option v-for="m in materials" :key="m.id" :value="m.code"
:label="`${m.code} ${m.name || ''}`" />
</el-select>
</el-form-item>
<el-form-item label="已完成工序">
<el-input v-model="inForm.completedProcess" placeholder="如:1,3,5(可空)" clearable />
</el-form-item>
<el-form-item label="工单号">
<el-input v-model="inForm.orderNo" placeholder="来源工单号(可空)" clearable />
</el-form-item>
<el-form-item label="区域">
<el-input v-model="inForm.zoneCode" placeholder="库区(可空)" clearable />
</el-form-item>
<el-form-item label="操作人">
<el-input :model-value="operator" disabled />
</el-form-item>
<el-form-item label="扫码录入">
<el-input v-model="snInput" type="textarea" :rows="3" autofocus
placeholder="连续扫描 SN,每次回车追加(可一次多条,空白分隔)"
@keydown.enter.prevent="addSnLines" />
</el-form-item>
</el-form>
<div style="margin-bottom:8px">待入库 SN<b>{{ snList.length }}</b> </div>
<el-table :data="snList.map((v, i) => ({ i, v }))" size="small" border max-height="260"
empty-text="暂无 SN" style="max-width:760px">
<el-table-column type="index" label="#" width="56" align="center" />
<el-table-column prop="v" label="SN 序列号" min-width="220" />
<el-table-column label="操作" width="80" align="center">
<template #default="{ row }">
<el-button link type="danger" size="small" @click="removeSn(row.i)">移除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top:12px">
<el-button type="primary" :loading="inbounding" @click="submitInbound">提交入库</el-button>
<el-button @click="snList = []">清空</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="半成品出库">
<el-form label-width="110px" style="max-width:560px" @submit.prevent="submitOutbound">
<el-form-item label="SN">
<el-input v-model="outForm.sn" placeholder="扫描或输入 SN,回车出库" clearable autofocus
@keyup.enter="submitOutbound" />
</el-form-item>
<el-form-item label="操作人">
<el-input :model-value="operator" disabled />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="outbounding" @click="submitOutbound">出库重上线</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="半成品/成品查询">
<el-form inline @submit.prevent="search">
<el-form-item label="物料编码">
<el-input v-model="qForm.materialCode" placeholder="物料编码" clearable style="width:200px" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="qForm.status" clearable placeholder="请选择状态" style="width:120px">
<el-option value="在库" label="在库" />
<el-option value="出库" label="出库" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="search">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="list" border size="small" v-loading="loading" empty-text="暂无记录">
<el-table-column prop="sn" label="SN" min-width="200" />
<el-table-column prop="materialCode" label="物料编码" min-width="140" />
<el-table-column prop="completedProcess" label="已完成工序" min-width="120">
<template #default="{ row }">{{ row.completedProcess || '-' }}</template>
</el-table-column>
<el-table-column prop="zoneCode" label="区域" width="90" align="center">
<template #default="{ row }">{{ row.zoneCode || '-' }}</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="90" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '在库' ? 'success' : 'info'" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderNo" label="工单号" min-width="140">
<template #default="{ row }">{{ row.orderNo || '-' }}</template>
</el-table-column>
<el-table-column label="创建时间" width="160" align="center">
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
</el-table-column>
</el-table>
<div class="pager">
<el-pagination background layout="total, prev, pager, next" :total="total"
:current-page="page.current" :page-size="page.size"
@current-change="(p) => { page.current = p; loadList() }" />
</div>
</el-tab-pane>
</el-tabs>
</el-card>
<PageHelp :data="helpSemi" page="Semi" />
</template>
<style scoped>
.pager { display: flex; justify-content: flex-end; margin-top: 10px; }
</style>