refactor(wms): 合并库存表为统一inventory,重构业务逻辑
1. 合并inventory_batch与serial_number为统一inventory表,用manage_mode区分结构件批次/精密件SN 2. 移除冗余的material_type字段与相关逻辑 3. 重构库存查询、检验、入库出库等业务逻辑适配新表结构 4. 调整前端路由与菜单,拆分基础数据为区域维护和物料档案 5. 优化入库校验逻辑,避免空单问题 6. 调整Vite构建配置,关闭自动清空输出目录 7. 为各模块添加详细中文注释,统一业务术语 8. 生成并更新Ent自动代码文件
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
|
||||
<!-- 既无顶层字段也无分组 -->
|
||||
<el-empty v-if="!data.fields && !(data.sections && data.sections.length)"
|
||||
description="该页面暂未编写操作说明,请先完成基础数据维护后查看效果" :image-size="70" />
|
||||
description="该页面暂未编写操作说明" :image-size="70" />
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
export const helpInbound = {
|
||||
title: '入库(来料入账)',
|
||||
overview:
|
||||
'把到货的来料记入 WMS 库存。按物料类型分两种入库方式:\n· 结构件(螺丝/机加工件) → 按【批次】入库,一批多件;\n· 精密件(芯片/器件) → 按【SN 序列号】逐件扫码,可单件追溯。\n\n物料类型由 WMS 的"物料档案"决定(精密件/结构件),入库方式随之自动给出。\n入库是整个库存链路的第一环:入库(Held) → 质量检验(合格Z02) → 之后才能被出库锁定。',
|
||||
'把到货的来料记入 WMS 库存。按管理类型分两种入库方式:\n· 结构件(螺丝/机加工件) → 按【批次】入库,一批多件;\n· 精密件(芯片/器件) → 按【SN 序列号】逐件扫码,可单件追溯。\n\n管理类型由 WMS 的"物料档案"决定(精密件/结构件),入库方式随之自动给出。\n入库是整个库存链路的第一环:入库(Held) → 质量检验(合格Z02) → 之后才能被出库锁定。',
|
||||
sections: [
|
||||
{
|
||||
title: 'Tab1 结构件入库(按批次)',
|
||||
overview: '适用于螺丝、机加工件等标准件,一次到货入一批。',
|
||||
overview: '适用于螺丝、机加工件等结构件,一次到货入一批。',
|
||||
fields: [
|
||||
{ name: '物料', source: '下拉选择。数据来自 WMS"物料档案"里类型为"结构件"的物料。', purpose: '入库的是什么物料;档案里必须存在才能选到', fill: '选择物料编码,如 SCREW-M3;选不到=还没建该物料档案' },
|
||||
{ name: '批次号', source: '留空系统自动生成(日期+序号);也可手工填。', purpose: '区别不同到货批次,实现先进先出(FIFO)', fill: '默认留空自动生成;供应商自带批号时可手填' },
|
||||
{ name: '数量', source: '手工填写整数。', purpose: '本批到货数量', fill: '整数 ≥1' },
|
||||
{ name: '区域', source: '下拉选择(必填)。数据来自"基础数据-区域维护"。', purpose: '放到哪个库区(待检/合格/不合格…)', fill: '必填;新到货一般先放"待检"区,检验合格后再转"合格"区' },
|
||||
{ name: '区域', source: '下拉选择(必填)。数据来自"区域维护"。', purpose: '放到哪个库区(待检/合格/不合格…)', fill: '必填;新到货一般先放"待检"区,检验合格后再转"合格"区' },
|
||||
{ name: '生产日期', source: '手动选择日期。', purpose: '生产时间,用于时效和批次管理', fill: '选择日期' },
|
||||
{ name: '供应商', source: '手工填写。', purpose: '来料来源', fill: '供应商名称,可空' }
|
||||
]
|
||||
@@ -24,7 +24,7 @@ export const helpInbound = {
|
||||
overview: '适用于芯片、器件等需要单件追溯的来料,一件一个 SN。',
|
||||
fields: [
|
||||
{ name: '物料', source: '下拉选择(必填)。来自"物料档案"类型为"精密件"的物料。', purpose: '入库的精密件物料', fill: '选择物料编码' },
|
||||
{ name: '区域', source: '下拉选择(必填)。来自"基础数据-区域维护"。', purpose: '放到哪个库区', fill: '必填;新到货一般先放"待检"区' },
|
||||
{ name: '区域', source: '下拉选择(必填)。来自"区域维护"。', purpose: '放到哪个库区', fill: '必填;新到货一般先放"待检"区' },
|
||||
{ name: '扫码录入', source: '扫码枪连续扫码。来自每颗器件自带的 SN 条码。', purpose: '逐件登记 SN,实现单件追溯', fill: '扫入/粘贴 SN 后回车追加,可一次连续多条' }
|
||||
]
|
||||
},
|
||||
@@ -56,7 +56,7 @@ export const helpInventory = {
|
||||
'查看当前库存:结构件按批次台账(数量/锁定/区域),精密件按 SN 序列号(状态/区域)。\n数据由入库、出库、盘点等业务实时维护,本页只读查询。',
|
||||
fields: [
|
||||
{ name: '物料编码', source: '筛选条件。', purpose: '只看某物料', fill: '输入编码,可空=全部' },
|
||||
{ name: '库存类型', source: '单选。', purpose: '看批次台账还是 SN 序列', fill: '全部 / 批次 / SN 序列' },
|
||||
{ name: '库存类型', source: '单选(批次 / SN 序列)。', purpose: '看批次台账还是 SN 序列,各自独立分页', fill: '批次 / SN 序列' },
|
||||
{ name: '关键字', source: '筛选条件。', purpose: '按批次号/物料名称/SN 模糊过滤', fill: '输入关键字,可空' }
|
||||
]
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export const helpInventory = {
|
||||
export const helpInspection = {
|
||||
title: '质量检验(来料检)',
|
||||
overview:
|
||||
'对入库的未检物料/SN 做检验:把"未检"翻转为【合格】或【不合格】。\n合格后才能被出库锁定(出库自动分配只取合格区Z02)。\n\n数据链路:入库(状态=未检) → 本页检验翻转 → 合格Z02 / 不合格隔离。',
|
||||
'对入库的未检物料/SN 做检验:把"未检"翻转为【合格】或【不合格】。\n检验只修改"质量状态"字段,与物料/SN 的存放区域无关。\n合格品才能参与出库与锁库(库存校验只取质量状态=合格的批次)。',
|
||||
fields: [
|
||||
{ name: '目标类型', source: '单选。', purpose: '检验对象是批次还是 SN', fill: '批次 BATCH / 序列号 SN' },
|
||||
{ name: '检验结论', source: '单选。', purpose: '判定结果', fill: '合格 / 不合格' },
|
||||
@@ -141,7 +141,7 @@ export const helpLedger = {
|
||||
}
|
||||
|
||||
export const helpBaseData = {
|
||||
title: '基础数据(区域 / 物料档案)',
|
||||
title: '区域维护 / 物料档案',
|
||||
overview:
|
||||
'维护库房系统的基础字典,供入库/出库/库存等所有业务下拉框选择。\n· 区域:货品存放的库区(如待检、合格Z02、不合格…);入库时要指明放到哪个区。\n· 物料档案:定义每一种物料叫什么、什么规格、是【结构件(批次)】还是【精密件(SN)】,决定后续入库方式。\n\n这两个字典是库房的第一道地基:先建好区域和物料,入库、检验、出库才能正常跑到数据。',
|
||||
sections: [
|
||||
@@ -164,7 +164,7 @@ export const helpBaseData = {
|
||||
{ name: '规格', source: '手工填写(可空)。', purpose: '规格型号', fill: '可空' },
|
||||
{ name: '单位', source: '手工填写。', purpose: '计量单位', fill: '如 个/套' },
|
||||
{ name: '类型', source: '选择。语义由库房定:结构件按批次管理,精密件按 SN 逐件管理。', purpose: '决定入库方式:结构件走批次、精密件走 SN', fill: '结构件(批次) / 精密件(SN)' },
|
||||
{ name: '物料类型', source: '选择(可空)。', purpose: '业务归类', fill: '标准件/电子件/包装件/半成品/成品' }
|
||||
{ name: '生命周期阶段', source: '物料主数据不再登记生命周期阶段(原“物料类型”字段已移除)。原料→半成品→成品的流转由库存实体各自承载:组件统一入 inventory 表(manage_mode:1=结构件批次 / 2=精密件SN),半成品入 semifinished 固定业务表(首页“半成品在库”即统计此表)。', purpose: '避免主数据预判物料命运,消除与 semifinished 业务的歧义', fill: '无需填写;半成品/成品状态以实际库存与 semifinished 表为准' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
HomeFilled, Download, Upload, Search, CircleCheck, List,
|
||||
Box, Tickets, Monitor, EditPen, Fold, Expand
|
||||
Box, Tickets, Monitor, EditPen, Fold, Expand, Location, Goods
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getUser, logout } from '../utils/auth'
|
||||
|
||||
@@ -57,7 +57,8 @@ const menus = [
|
||||
{ path: '/semi', title: '半成品/成品', icon: Box },
|
||||
{ path: '/package', title: '装箱管理', icon: Box },
|
||||
{ path: '/ledger', title: '备料台账', icon: Tickets },
|
||||
{ path: '/base-data', title: '基础数据', icon: EditPen }
|
||||
{ path: '/zone', title: '区域维护', icon: Location },
|
||||
{ path: '/material', title: '物料档案', icon: Goods }
|
||||
]
|
||||
|
||||
// 打开当前页面帮助(各页面 PageHelp 监听 wms:open-page-help 事件弹出抽屉)
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
import { helpBaseData } from '../help'
|
||||
|
||||
/* ---------- Tab1 区域维护 ---------- */
|
||||
// mode: 'zone' = 区域维护;'material' = 物料档案(同一组件拆成两个独立菜单)
|
||||
const props = defineProps({
|
||||
mode: { type: String, default: 'zone' }
|
||||
})
|
||||
|
||||
/* ---------- 区域维护 ---------- */
|
||||
const zones = ref([])
|
||||
const zoneTotal = ref(0)
|
||||
const zoneLoading = ref(false)
|
||||
@@ -70,19 +75,18 @@ async function saveZone() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Tab2 物料档案维护 ---------- */
|
||||
/* ---------- 物料档案维护 ---------- */
|
||||
const materials = ref([])
|
||||
const materialTotal = ref(0)
|
||||
const materialLoading = ref(false)
|
||||
const materialKeyword = ref('')
|
||||
const materialTypeFilter = ref('')
|
||||
const matPage = reactive({ current: 1, size: 10 })
|
||||
|
||||
const matDialog = ref(false)
|
||||
const mEditingId = ref(0)
|
||||
const mForm = reactive({
|
||||
code: '', name: '', shortName: '', spec: '', unit: '',
|
||||
manageMode: 1, materialType: '', templateCode: '', description: ''
|
||||
manageMode: 1, templateCode: '', description: ''
|
||||
})
|
||||
const mSaving = ref(false)
|
||||
|
||||
@@ -92,7 +96,6 @@ async function loadMaterials() {
|
||||
const data = await request.get('/material/query', {
|
||||
params: {
|
||||
keyword: materialKeyword.value.trim(),
|
||||
materialType: materialTypeFilter.value,
|
||||
page: matPage.current,
|
||||
pageSize: matPage.size
|
||||
}
|
||||
@@ -113,7 +116,7 @@ function resetMForm() {
|
||||
mEditingId.value = 0
|
||||
Object.assign(mForm, {
|
||||
code: '', name: '', shortName: '', spec: '', unit: '',
|
||||
manageMode: 1, materialType: '', templateCode: '', description: ''
|
||||
manageMode: 1, templateCode: '', description: ''
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,7 +130,7 @@ function openMatEdit(row) {
|
||||
Object.assign(mForm, {
|
||||
code: row.code, name: row.name, shortName: row.shortName || '',
|
||||
spec: row.spec || '', unit: row.unit || '', manageMode: row.manageMode,
|
||||
materialType: row.materialType || '', templateCode: row.templateCode || '',
|
||||
templateCode: row.templateCode || '',
|
||||
description: row.description || ''
|
||||
})
|
||||
matDialog.value = true
|
||||
@@ -159,169 +162,146 @@ async function saveMaterial() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadZones()
|
||||
loadMaterials()
|
||||
if (props.mode === 'material') loadMaterials()
|
||||
else loadZones()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-tabs>
|
||||
<el-tab-pane :label="`区域维护(${zoneTotal})`">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchZones">
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="zoneKeyword" placeholder="区域编码/名称" clearable style="width:200px"
|
||||
@keyup.enter="searchZones" />
|
||||
<!-- 区域维护 -->
|
||||
<template v-if="mode !== 'material'">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchZones">
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="zoneKeyword" placeholder="区域编码/名称" clearable style="width:200px"
|
||||
@keyup.enter="searchZones" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="zoneLoading" @click="searchZones">查询</el-button>
|
||||
<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 prop="zoneCode" label="区域编码" min-width="140" />
|
||||
<el-table-column prop="zoneName" label="区域名称" min-width="160" />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '停用' ? 'info' : 'success'" size="small">
|
||||
{{ row.status || '启用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="说明" min-width="200">
|
||||
<template #default="{ row }">{{ row.description || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openZoneEdit(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, prev, pager, next" :total="zoneTotal"
|
||||
:current-page="zonePage.current" :page-size="zonePage.size"
|
||||
@current-change="(p) => { zonePage.current = p; loadZones() }" />
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="zoneDialog" :title="zoneEditingId ? '编辑区域' : '新增区域'" width="460px"
|
||||
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 />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="zoneLoading" @click="searchZones">查询</el-button>
|
||||
<el-button type="success" @click="openZoneCreate">新增区域</el-button>
|
||||
<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.description" type="textarea" :rows="2" placeholder="备注(可空)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="zones" border size="small" v-loading="zoneLoading" empty-text="暂无区域,请点击【新增区域】">
|
||||
<el-table-column prop="zoneCode" label="区域编码" min-width="140" />
|
||||
<el-table-column prop="zoneName" label="区域名称" min-width="160" />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '停用' ? 'info' : 'success'" size="small">
|
||||
{{ row.status || '启用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="说明" min-width="200">
|
||||
<template #default="{ row }">{{ row.description || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openZoneEdit(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, prev, pager, next" :total="zoneTotal"
|
||||
:current-page="zonePage.current" :page-size="zonePage.size"
|
||||
@current-change="(p) => { zonePage.current = p; loadZones() }" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<template #footer>
|
||||
<el-button @click="zoneDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="zoneSaving" @click="saveZone">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<el-tab-pane :label="`物料档案(${materialTotal})`">
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchMaterials">
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="materialKeyword" placeholder="物料编码/名称" clearable style="width:200px"
|
||||
@keyup.enter="searchMaterials" />
|
||||
<!-- 物料档案 -->
|
||||
<template v-else>
|
||||
<el-form inline style="margin-bottom:14px" @submit.prevent="searchMaterials">
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="materialKeyword" placeholder="物料编码/名称" clearable style="width:200px"
|
||||
@keyup.enter="searchMaterials" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="materialLoading" @click="searchMaterials">查询</el-button>
|
||||
<el-button type="success" @click="openMatCreate">新增物料</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="materials" border size="small" v-loading="materialLoading"
|
||||
empty-text="暂无物料,请点击【新增物料】">
|
||||
<el-table-column prop="code" label="物料编码" min-width="120" />
|
||||
<el-table-column prop="name" label="名称" min-width="120" />
|
||||
<el-table-column prop="spec" label="规格" min-width="90">
|
||||
<template #default="{ row }">{{ row.spec || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unit" label="单位" width="70" align="center">
|
||||
<template #default="{ row }">{{ row.unit || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.manageMode === 2 ? 'warning' : 'info'" size="small">
|
||||
{{ row.manageMode === 2 ? '精密件(SN)' : '结构件(批次)' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openMatEdit(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, sizes, prev, pager, next" :total="materialTotal"
|
||||
:current-page="matPage.current" :page-size="matPage.size" :page-sizes="[10, 20, 50, 100]"
|
||||
@current-change="(p) => { matPage.current = p; loadMaterials() }"
|
||||
@size-change="(s) => { matPage.size = s; matPage.current = 1; loadMaterials() }" />
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="matDialog" :title="mEditingId ? '编辑物料' : '新增物料'" width="560px"
|
||||
append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="物料编码" required>
|
||||
<el-input v-model="mForm.code" placeholder="如 SCREW-M3" :disabled="!!mEditingId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料类型">
|
||||
<el-select v-model="materialTypeFilter" placeholder="全部" clearable style="width:130px">
|
||||
<el-option label="标准件" value="标准件" />
|
||||
<el-option label="电子件" value="电子件" />
|
||||
<el-option label="包装件" value="包装件" />
|
||||
<el-option label="半成品" value="半成品" />
|
||||
<el-option label="成品" value="成品" />
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="mForm.name" placeholder="如 螺丝 M3" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="简称">
|
||||
<el-input v-model="mForm.shortName" placeholder="简称(可空)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格">
|
||||
<el-input v-model="mForm.spec" placeholder="规格(可空)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位">
|
||||
<el-input v-model="mForm.unit" placeholder="如 个/套" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="mForm.manageMode" placeholder="请选择" style="width:100%">
|
||||
<el-option :value="1" label="结构件(批次管理)" />
|
||||
<el-option :value="2" label="精密件(SN 管理)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="materialLoading" @click="searchMaterials">查询</el-button>
|
||||
<el-button type="success" @click="openMatCreate">新增物料</el-button>
|
||||
<el-form-item label="说明">
|
||||
<el-input v-model="mForm.description" type="textarea" :rows="2" placeholder="备注(可空)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="materials" border size="small" v-loading="materialLoading"
|
||||
empty-text="暂无物料,请点击【新增物料】">
|
||||
<el-table-column prop="code" label="物料编码" min-width="120" />
|
||||
<el-table-column prop="name" label="名称" min-width="120" />
|
||||
<el-table-column prop="spec" label="规格" min-width="90">
|
||||
<template #default="{ row }">{{ row.spec || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unit" label="单位" width="70" align="center">
|
||||
<template #default="{ row }">{{ row.unit || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.manageMode === 2 ? 'warning' : 'info'" size="small">
|
||||
{{ row.manageMode === 2 ? '精密件(SN)' : '结构件(批次)' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialType" label="物料类型" width="90" align="center">
|
||||
<template #default="{ row }">{{ row.materialType || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="openMatEdit(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, sizes, prev, pager, next" :total="materialTotal"
|
||||
:current-page="matPage.current" :page-size="matPage.size" :page-sizes="[10, 20, 50, 100]"
|
||||
@current-change="(p) => { matPage.current = p; loadMaterials() }"
|
||||
@size-change="(s) => { matPage.size = s; matPage.current = 1; loadMaterials() }" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 区域弹窗 -->
|
||||
<el-dialog v-model="zoneDialog" :title="zoneEditingId ? '编辑区域' : '新增区域'" width="460px"
|
||||
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 />
|
||||
</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.description" type="textarea" :rows="2" placeholder="备注(可空)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="zoneDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="zoneSaving" @click="saveZone">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 物料弹窗 -->
|
||||
<el-dialog v-model="matDialog" :title="mEditingId ? '编辑物料' : '新增物料'" width="560px"
|
||||
append-to-body destroy-on-close>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="物料编码" required>
|
||||
<el-input v-model="mForm.code" placeholder="如 SCREW-M3" :disabled="!!mEditingId" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" required>
|
||||
<el-input v-model="mForm.name" placeholder="如 螺丝 M3" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="简称">
|
||||
<el-input v-model="mForm.shortName" placeholder="简称(可空)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格">
|
||||
<el-input v-model="mForm.spec" placeholder="规格(可空)" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位">
|
||||
<el-input v-model="mForm.unit" placeholder="如 个/套" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="mForm.manageMode" placeholder="请选择" style="width:100%">
|
||||
<el-option :value="1" label="结构件(批次管理)" />
|
||||
<el-option :value="2" label="精密件(SN 管理)" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="物料类型">
|
||||
<el-select v-model="mForm.materialType" placeholder="请选择" clearable style="width:100%">
|
||||
<el-option label="标准件" value="标准件" />
|
||||
<el-option label="电子件" value="电子件" />
|
||||
<el-option label="包装件" value="包装件" />
|
||||
<el-option label="半成品" value="半成品" />
|
||||
<el-option label="成品" value="成品" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="说明">
|
||||
<el-input v-model="mForm.description" type="textarea" :rows="2" placeholder="备注(可空)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="matDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="mSaving" @click="saveMaterial">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<template #footer>
|
||||
<el-button @click="matDialog = false">取消</el-button>
|
||||
<el-button type="primary" :loading="mSaving" @click="saveMaterial">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</el-card>
|
||||
<PageHelp :data="helpBaseData" />
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CircleCheck, Download, List, Monitor, Search, Upload, Box, Tickets, EditPen } from '@element-plus/icons-vue'
|
||||
import { CircleCheck, Download, List, Monitor, Search, Upload, Box, Tickets, EditPen, Location, Goods } from '@element-plus/icons-vue'
|
||||
import request from '../utils/request'
|
||||
import { fmtNum } from '../utils/format'
|
||||
|
||||
@@ -36,7 +36,8 @@ const entries = [
|
||||
{ path: '/semi', title: '半成品/成品', icon: Box },
|
||||
{ path: '/package', title: '装箱管理', icon: Tickets },
|
||||
{ path: '/ledger', title: '备料台账', icon: Tickets },
|
||||
{ path: '/base-data', title: '基础数据', icon: EditPen },
|
||||
{ path: '/zone', title: '区域维护', icon: Location },
|
||||
{ path: '/material', title: '物料档案', icon: Goods },
|
||||
{ path: '/display', title: '免登录大屏', icon: Monitor }
|
||||
]
|
||||
</script>
|
||||
|
||||
@@ -36,6 +36,13 @@ async function submitBatch() {
|
||||
ElMessage.warning('数量必须大于 0')
|
||||
return
|
||||
}
|
||||
// 管理粒度校验:精密件必须走 SN 入库页。
|
||||
// 否则后端因 snList 为空直接拒绝,历史上会在失败后残留一张无明细无库存的"空单"。
|
||||
const mat1 = materials.value.find(m => m.code === form1.materialCode)
|
||||
if (mat1 && Number(mat1.manageMode) === 2) {
|
||||
ElMessage.warning('该物料为精密件(按 SN 管理),请切换到「精密件 SN 入库」页扫码录入后再提交')
|
||||
return
|
||||
}
|
||||
submitting1.value = true
|
||||
try {
|
||||
const data = await request.post('/inbound/create', {
|
||||
@@ -95,6 +102,12 @@ async function submitSn() {
|
||||
ElMessage.warning('请先扫码录入 SN(输入后点击【录入到列表】)')
|
||||
return
|
||||
}
|
||||
// 管理粒度校验(反向):结构件按批次管理,不能带 SN 清单提交
|
||||
const mat2 = materials.value.find(m => m.code === form2.materialCode)
|
||||
if (mat2 && Number(mat2.manageMode) === 1) {
|
||||
ElMessage.warning('该物料为结构件(按批次管理),请切换到「结构件入库」页按数量入库')
|
||||
return
|
||||
}
|
||||
submitting2.value = true
|
||||
try {
|
||||
const data = await request.post('/inbound/create', {
|
||||
|
||||
@@ -1,31 +1,45 @@
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import request from '../utils/request'
|
||||
import { fmtDateOnly, fmtTime } from '../utils/format'
|
||||
import { fmtTime } from '../utils/format'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
import { helpInventory } from '../help'
|
||||
|
||||
const filters = reactive({ materialCode: '', keyword: '', type: '' })
|
||||
// 视图切换:库存明细 / 区域汇总
|
||||
const viewTab = ref('detail')
|
||||
// manageMode: ''=全部 1=结构件(批次) 2=精密件(SN)
|
||||
const filters = reactive({ materialCode: '', keyword: '', manageMode: '', zoneCode: '' })
|
||||
const loading = ref(false)
|
||||
const batchList = ref([])
|
||||
const serialList = ref([])
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const page = reactive({ current: 1, size: 10 })
|
||||
|
||||
// 区域下拉数据源(/zone/picker 返回 Zone 数组:zoneCode/zoneName)
|
||||
const zoneOptions = ref([])
|
||||
async function loadZones() {
|
||||
try {
|
||||
const data = await request.get('/zone/picker')
|
||||
zoneOptions.value = data || []
|
||||
} catch (e) {
|
||||
zoneOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 统一库存查询:结构件与精密件同表(inventory),一次返回 list
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await request.get('/stock/query', {
|
||||
params: {
|
||||
materialCode: filters.materialCode.trim(),
|
||||
zoneCode: filters.zoneCode,
|
||||
manageMode: filters.manageMode,
|
||||
keyword: filters.keyword.trim(),
|
||||
type: filters.type,
|
||||
page: page.current,
|
||||
pageSize: page.size
|
||||
}
|
||||
})
|
||||
batchList.value = data?.batch || []
|
||||
serialList.value = data?.serial || []
|
||||
list.value = data?.list || []
|
||||
total.value = data?.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -37,101 +51,178 @@ function search() {
|
||||
load()
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
// 可用量:结构件=数量-锁定;精密件=在库时 1-锁定 否则 0
|
||||
function avail(row) {
|
||||
if (row.manageMode === 1) return (row.quantity || 0) - (row.lockedQty || 0)
|
||||
if (row.status === '在库') return 1 - (row.lockedQty || 0)
|
||||
return 0
|
||||
}
|
||||
function typeLabel(m) {
|
||||
return m === 1 ? '结构件' : m === 2 ? '精密件' : '-'
|
||||
}
|
||||
function identifier(row) {
|
||||
return row.manageMode === 2 ? row.snCode : row.batchNo
|
||||
}
|
||||
|
||||
// 一键导出:当前查询结果(批次+SN 合并)导出为 CSV
|
||||
function exportCsv() {
|
||||
if (!list.value.length) {
|
||||
return
|
||||
}
|
||||
const header = ['类型', '标识', '物料编码', '物料名称', '数量', '锁定量', '可用量', '区域', '质量状态', '状态']
|
||||
const lines = [header.join(',')]
|
||||
for (const r of list.value) {
|
||||
const row = [
|
||||
typeLabel(r.manageMode),
|
||||
identifier(r) || '',
|
||||
r.materialCode || '',
|
||||
r.materialName || '',
|
||||
r.quantity || 0,
|
||||
r.lockedQty || 0,
|
||||
avail(r),
|
||||
r.zoneCode || '',
|
||||
r.qualityStatus || '',
|
||||
r.status || ''
|
||||
]
|
||||
lines.push(row.map((c) => `"${String(c).replace(/"/g, '""')}"`).join(','))
|
||||
}
|
||||
const blob = new Blob(['' + lines.join('\n')], { type: 'text/csv;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = '库存导出.csv'
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
// 区域汇总(各区域 × 质量状态 × 类型 的库存数量)
|
||||
const summaryLoading = ref(false)
|
||||
const summaryRows = ref([])
|
||||
async function loadSummary() {
|
||||
summaryLoading.value = true
|
||||
try {
|
||||
const data = await request.get('/stock/zone-summary')
|
||||
summaryRows.value = data?.rows || []
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleTabChange(name) {
|
||||
if (name === 'summary') loadSummary()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadZones()
|
||||
load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="never" class="mb12">
|
||||
<el-form inline @submit.prevent="search">
|
||||
<el-form-item label="物料编码">
|
||||
<el-input v-model="filters.materialCode" placeholder="物料编码" clearable style="width:200px"
|
||||
@keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item label="库存类型">
|
||||
<el-radio-group v-model="filters.type" @change="search">
|
||||
<el-radio-button value="">全部</el-radio-button>
|
||||
<el-radio-button value="batch">批次</el-radio-button>
|
||||
<el-radio-button value="serial">SN 序列</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="filters.keyword" placeholder="批次号/物料名称/SN" clearable style="width:220px"
|
||||
@keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click="search">查询</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-tabs v-model="viewTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="库存明细" name="detail">
|
||||
<el-card shadow="never" class="mb12">
|
||||
<el-form inline @submit.prevent="search">
|
||||
<el-form-item label="物料编码">
|
||||
<el-input v-model="filters.materialCode" placeholder="物料编码" clearable style="width:200px"
|
||||
@keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item label="区域">
|
||||
<el-select v-model="filters.zoneCode" placeholder="全部区域" clearable style="width:160px" @change="search">
|
||||
<el-option v-for="z in zoneOptions" :key="z.zoneCode" :label="z.zoneName" :value="z.zoneCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库存类型">
|
||||
<el-radio-group v-model="filters.manageMode" @change="search">
|
||||
<el-radio-button value="">全部</el-radio-button>
|
||||
<el-radio-button value="1">结构件</el-radio-button>
|
||||
<el-radio-button value="2">精密件</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="filters.keyword" placeholder="批次号/物料名称/SN" clearable style="width:220px"
|
||||
@keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" @click="search">查询</el-button>
|
||||
<el-button :disabled="!list.length" @click="exportCsv">导出 CSV</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card v-if="!filters.type || filters.type === 'batch'" shadow="never" class="mb12"
|
||||
header="批次库存(结构件)">
|
||||
<el-table :data="batchList" border size="small" v-loading="loading" empty-text="暂无批次数据">
|
||||
<el-table-column prop="batchNo" label="批次号" min-width="170" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="140" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="130">
|
||||
<template #default="{ row }">{{ row.materialName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="数量" width="80" align="center" />
|
||||
<el-table-column prop="lockedQty" label="锁定量" width="90" align="center" />
|
||||
<el-table-column prop="zoneCode" label="区域" width="80" align="center">
|
||||
<template #default="{ row }">{{ row.zoneCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质量状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.qualityStatus === '合格' ? 'success' : row.qualityStatus === '不合格' ? 'danger' : 'info'"
|
||||
size="small">{{ row.qualityStatus }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生产日期" width="110" align="center">
|
||||
<template #default="{ row }">{{ fmtDateOnly(row.productionDate) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="supplier" label="供应商" min-width="120">
|
||||
<template #default="{ row }">{{ row.supplier || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入库时间" width="150" align="center">
|
||||
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-card shadow="never" header="统一库存(结构件批次 + 精密件 SN)">
|
||||
<el-table :data="list" border size="small" v-loading="loading" empty-text="暂无库存数据">
|
||||
<el-table-column label="类型" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="row.manageMode === 1 ? 'primary' : 'warning'">{{ typeLabel(row.manageMode) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次号 / SN" min-width="190">
|
||||
<template #default="{ row }">{{ identifier(row) || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="140" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="130">
|
||||
<template #default="{ row }">{{ row.materialName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="数量" width="80" align="center" />
|
||||
<el-table-column prop="lockedQty" label="锁定量" width="90" align="center" />
|
||||
<el-table-column label="可用量" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: avail(row) <= 0 ? '#f56c6c' : '' }">{{ avail(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="zoneCode" label="区域" width="80" align="center">
|
||||
<template #default="{ row }">{{ row.zoneCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质量状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.qualityStatus === '合格' ? 'success' : row.qualityStatus === '不合格' ? 'danger' : 'info'"
|
||||
size="small">{{ row.qualityStatus || '未检' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '在库' ? 'success' : row.status === '锁定' ? 'warning' : 'info'"
|
||||
size="small">{{ row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入库时间" width="150" align="center">
|
||||
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-card v-if="!filters.type || filters.type === 'serial'" shadow="never" header="SN 库存(精密件)">
|
||||
<el-table :data="serialList" border size="small" v-loading="loading" empty-text="暂无 SN 数据">
|
||||
<el-table-column prop="snCode" label="SN 序列号" min-width="190" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="150" />
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === '在库' ? 'success' : row.status === '锁定' ? 'warning' : 'info'"
|
||||
size="small">{{ row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="currentZone" label="区域" width="90" align="center">
|
||||
<template #default="{ row }">{{ row.currentZone || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="质量状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.qualityStatus === '合格' ? 'success' : row.qualityStatus === '不合格' ? 'danger' : 'info'"
|
||||
size="small">{{ row.qualityStatus || '未检' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入库时间" width="160" align="center">
|
||||
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, sizes, prev, pager, next" :total="total"
|
||||
:current-page="page.current" :page-size="page.size" :page-sizes="[10, 20, 50, 100]"
|
||||
@current-change="(p) => { page.current = p; load() }"
|
||||
@size-change="(s) => { page.size = s; page.current = 1; load() }" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<div class="pager">
|
||||
<el-pagination background layout="total, sizes, prev, pager, next" :total="total"
|
||||
:current-page="page.current" :page-size="page.size" :page-sizes="[10, 20, 50, 100]"
|
||||
@current-change="(p) => { page.current = p; load() }"
|
||||
@size-change="(s) => { page.size = s; page.current = 1; load() }" />
|
||||
</div>
|
||||
<el-tab-pane label="区域汇总" name="summary">
|
||||
<el-card shadow="never" header="各区域库存数量(按 区域 + 质量状态 + 类型)">
|
||||
<el-table :data="summaryRows" border size="small" v-loading="summaryLoading" empty-text="暂无数据">
|
||||
<el-table-column prop="zoneCode" label="区域编码" width="110" />
|
||||
<el-table-column prop="zoneName" label="区域名称" min-width="140" />
|
||||
<el-table-column prop="quality" label="质量状态" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.quality === '合格' ? 'success' : row.quality === '不合格' ? 'danger' : 'info'"
|
||||
size="small">{{ row.quality }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" width="100" align="center" />
|
||||
<el-table-column prop="qty" label="数量" width="120" align="center" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<PageHelp :data="helpInventory" />
|
||||
</div>
|
||||
<PageHelp :data="helpInventory" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.mb12 { margin-bottom: 12px; }
|
||||
.pager { display: flex; justify-content: flex-end; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -18,7 +18,8 @@ const routes = [
|
||||
{ path: 'semi', name: 'Semi', component: () => import('../pages/Semi.vue'), meta: { title: '半成品/成品' } },
|
||||
{ path: 'package', name: 'Package', component: () => import('../pages/Package.vue'), meta: { title: '装箱管理' } },
|
||||
{ path: 'ledger', name: 'Ledger', component: () => import('../pages/Ledger.vue'), meta: { title: '备料台账' } },
|
||||
{ path: 'base-data', name: 'BaseData', component: () => import('../pages/BaseData.vue'), meta: { title: '基础数据' } },
|
||||
{ path: 'zone', name: 'Zone', component: () => import('../pages/BaseData.vue'), props: { mode: 'zone' }, meta: { title: '区域维护' } },
|
||||
{ path: 'material', name: 'Material', component: () => import('../pages/BaseData.vue'), props: { mode: 'material' }, meta: { title: '物料档案' } },
|
||||
{ path: 'change-password', name: 'ChangePassword', component: () => import('../pages/ChangePassword.vue'), meta: { title: '修改密码' } }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<title>库房客户端</title>
|
||||
<!-- 系统图标:库房箱体(内联 SVG,避免依赖外部文件) -->
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%231668dc'/><path d='M16 7l9 4.5v9L16 25l-9-4.5v-9L16 7z' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/><path d='M7 11.5L16 16l9-4.5M16 16v9' fill='none' stroke='%23fff' stroke-width='2' stroke-linejoin='round'/></svg>">
|
||||
<script type="module" crossorigin src="/assets/index-BWAZ_x_Q.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DncRC_8X.css">
|
||||
<script type="module" crossorigin src="/assets/index-B3vta_Fe.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B-hDjIvo.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Reference in New Issue
Block a user