refactor: 重构工艺工序相关命名与数据模型

1.  全局替换"工序"为"工艺"统一术语,包括页面文案、枚举、注释
2.  重构工单与工件工艺数据模型:
    - 新增工艺组合表flow_material作为备料/齐套唯一源头
    - 新增工位状态表station_state支持自主停单/恢复接单
    - 移除station_process派工表,改用工单工艺组合作为路线唯一源头
    - 替换processCode为flowId作为工艺关联标识
    - 重构工件当前进度字段为currentStationNo
3.  删除冗余的静态备份资源文件
4.  调整物料选择组件默认启用仅显示激活物料
5.  优化工单创建/编辑逻辑,新增工艺组合校验与保存
This commit is contained in:
SunYF
2026-09-22 11:32:29 +08:00
parent 932700ca65
commit d609ff8a9e
185 changed files with 8623 additions and 4501 deletions
+114 -18
View File
@@ -1,20 +1,20 @@
<template>
<el-card>
<el-tabs v-model="activeTab">
<!-- ============ 流程列表 ============ -->
<el-tab-pane label="流程列表" name="list">
<!-- ============ 工艺列表 ============ -->
<el-tab-pane label="工艺列表" name="list">
<el-form inline>
<el-form-item>
<el-button type="primary" @click="loadFlows">查询</el-button>
</el-form-item>
<el-form-item>
<el-button v-if="can('produce.processflow:add')" type="success" @click="openFlow()">新增流程</el-button>
<el-button v-if="can('produce.processflow:add')" type="success" @click="openFlow()">新增工艺</el-button>
</el-form-item>
</el-form>
<el-table :data="flows" border size="small">
<el-table-column prop="id" label="ID" width="60" />
<el-table-column prop="name" label="流程名" min-width="130" show-overflow-tooltip />
<el-table-column prop="name" label="工艺名称" min-width="130" show-overflow-tooltip />
<el-table-column label="PDF" width="90" align="center">
<template #default="{ row }">
<el-button v-if="row.pdfFile" link type="primary" @click="previewPdf(row.pdfFile)">预览</el-button>
@@ -57,17 +57,17 @@
<el-pagination style="margin-top:12px;justify-content:flex-end" background layout="total, prev, pager, next" :total="flowTotal" :page-size="flowPageSize" v-model:current-page="flowPage" @current-change="loadFlows" />
</el-tab-pane>
<!-- ============ 流程配置(新增/编辑共用,替代原弹窗套弹窗) ============ -->
<el-tab-pane label="流程配置" name="config">
<!-- ============ 工艺配置(新增/编辑共用,替代原弹窗套弹窗) ============ -->
<el-tab-pane label="工艺配置" name="config">
<el-alert
v-if="!flowDlg.id && !flowDlg.name"
type="info" :closable="false" show-icon
title="从「流程列表」点「新增流程」或某行的「编辑」进入此处;填写后保存即生效。" />
title="从「工艺列表」点「新增工艺」或某行的「编辑」进入此处;填写后保存即生效。" />
<el-form :model="flowDlg" label-width="90px" style="margin-top:12px">
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="流程名" required>
<el-input v-model="flowDlg.name" placeholder="如 3号工位装配流程" />
<el-form-item label="工艺名称" required>
<el-input v-model="flowDlg.name" placeholder="如 3号工位装配工艺" />
</el-form-item>
</el-col>
</el-row>
@@ -99,8 +99,8 @@
<el-input v-model="flowDlg.remark" placeholder="备注(可空)" />
</el-form-item>
<!-- 工序步骤子表 -->
<el-form-item label="工序步骤">
<!-- 工艺步骤子表 -->
<el-form-item label="工艺步骤">
<div style="width:100%">
<el-table :data="flowDlg.steps" border size="small">
<el-table-column label="步骤" width="80" align="center">
@@ -160,6 +160,56 @@
<el-button size="small" style="margin-top:10px" @click="addStep">新增步骤</el-button>
</div>
</el-form-item>
<!-- 物料清单子表(整表覆盖保存) -->
<el-form-item label="物料清单">
<div style="width:100%">
<el-alert v-if="!flowDlg.id" type="info" :closable="false" style="margin-bottom:8px"
title="新工艺请先保存,保存后即可维护物料清单。" />
<el-table v-else :data="flowDlg.materials" border size="small">
<el-table-column label="物料编码" min-width="130">
<template #default="{ row }"><el-input v-model="row.materialCode" placeholder="手填" /></template>
</el-table-column>
<el-table-column label="物料名称" min-width="120">
<template #default="{ row }"><el-input v-model="row.materialName" placeholder="手填" /></template>
</el-table-column>
<el-table-column label="规格" min-width="110">
<template #default="{ row }"><el-input v-model="row.spec" /></template>
</el-table-column>
<el-table-column label="单位" width="80">
<template #default="{ row }"><el-input v-model="row.unit" /></template>
</el-table-column>
<el-table-column label="管控方式" width="150">
<template #default="{ row }">
<el-select v-model="row.manageMode" placeholder="请选择">
<el-option value="1" label="批次·结构件" />
<el-option value="2" label="序列号·电气件" />
</el-select>
</template>
</el-table-column>
<el-table-column label="单台用量" width="120">
<template #default="{ row }">
<el-input-number v-model="row.unitQty" :min="0" size="small" controls-position="right" style="width:100%" />
</template>
</el-table-column>
<el-table-column label="损耗率%" width="110">
<template #default="{ row }">
<el-input-number v-model="row.lossRate" :min="0" size="small" controls-position="right" style="width:100%" />
</template>
</el-table-column>
<el-table-column label="操作" width="70" align="center">
<template #default="{ row }">
<el-button link type="danger" size="small" @click="removeMaterial(row)">删</el-button>
</template>
</el-table-column>
</el-table>
<div style="display:flex;gap:10px;margin-top:10px;align-items:center">
<el-button v-if="flowDlg.id" size="small" @click="addMaterial">新增物料行</el-button>
<el-button v-if="flowDlg.id" size="small" type="primary" :loading="matSaving" @click="saveMaterials">保存物料清单</el-button>
<span style="color:#909399;font-size:12px">物料编码/名称/规格/单位手填,须属于产品物料清单范围,工单保存时校验;备料需求 = 各工位分产量 × 单台用量 ×(1+损耗率%)。</span>
</div>
</div>
</el-form-item>
</el-form>
<div style="text-align:right;margin-top:8px">
<el-button @click="activeTab = 'list'">取消</el-button>
@@ -254,7 +304,7 @@ function statusText(s) {
return s === 'ACTIVE' ? '启用' : '停用'
}
/* ---------- 工艺流程 ---------- */
/* ---------- 工艺 ---------- */
function blankStep(seq) {
return {
_key: Date.now() + Math.random(),
@@ -311,8 +361,53 @@ async function onPdfChange(e) {
const flowDlg = reactive({
id: 0, name: '', stations: [], pdfFile: '', status: 'ACTIVE',
remark: '', uploading: false, steps: [blankStep(1)]
remark: '', uploading: false, steps: [blankStep(1)], materials: []
})
const matSaving = ref(false)
function blankMaterial() {
return { materialCode: '', materialName: '', spec: '', unit: '', manageMode: '2', unitQty: 1, lossRate: 0 }
}
// 加载该工艺的物料清单(整表编辑,保存时整表覆盖)
async function loadMaterials(flowId) {
flowDlg.materials = []
if (!flowId) return
try {
const list = (await request.get(`/process-flows/${flowId}/materials`)) || []
flowDlg.materials = list.map((m) => ({
materialCode: m.materialCode || '', materialName: m.materialName || '', spec: m.spec || '',
unit: m.unit || '', manageMode: m.manageMode || '2', unitQty: m.unitQty ?? 1, lossRate: m.lossRate ?? 0
}))
} catch { /* 拦截器已提示 */ }
}
function addMaterial() {
flowDlg.materials.push(blankMaterial())
}
function removeMaterial(row) {
const i = flowDlg.materials.indexOf(row)
if (i >= 0) flowDlg.materials.splice(i, 1)
}
async function saveMaterials() {
if (!flowDlg.id) return ElMessage.warning('请先保存工艺,再维护物料清单')
const items = flowDlg.materials.map((m) => ({
materialCode: (m.materialCode || '').trim(),
materialName: (m.materialName || '').trim(),
spec: m.spec || '', unit: m.unit || '',
manageMode: m.manageMode || '2',
unitQty: Number(m.unitQty) || 0,
lossRate: Number(m.lossRate) || 0
}))
if (items.some((m) => !m.materialCode)) return ElMessage.warning('存在未填写物料编码的行,请补全或删除')
matSaving.value = true
try {
await request.post(`/process-flows/${flowDlg.id}/materials`, { items })
ElMessage.success('物料清单已保存')
} catch { /* 拦截器已提示 */ } finally {
matSaving.value = false
}
}
function openFlow(row) {
if (row) {
@@ -333,9 +428,10 @@ function openFlow(row) {
attachment: (s.attachment || []).map((a) => ({ ...a })),
criteria: (s.criteria || []).map((c) => ({ ...c }))
}))
loadMaterials(row.id)
} else {
Object.assign(flowDlg, {
id: 0, name: '', stations: [], pdfFile: '', status: 'ACTIVE', remark: '', steps: []
id: 0, name: '', stations: [], pdfFile: '', status: 'ACTIVE', remark: '', steps: [], materials: []
})
}
if (!flowDlg.steps.length) flowDlg.steps.push(blankStep(1))
@@ -403,7 +499,7 @@ function saveCriteria() {
}
async function saveFlow() {
if (!flowDlg.name) return ElMessage.warning('请填写流程名')
if (!flowDlg.name) return ElMessage.warning('请填写工艺名称')
// 绑定关系由「关联工位」页维护:此处只保存流程本身(不收集/不改动工位绑定)
const body = {
id: flowDlg.id || undefined,
@@ -430,7 +526,7 @@ async function saveFlow() {
}
async function delFlow(row) {
await ElMessageBox.confirm('确认删除该工艺流程?删除将同时解除其绑定的工位。', '提示', { type: 'warning' })
await ElMessageBox.confirm('确认删除该工艺?删除将同时解除其绑定的工位。', '提示', { type: 'warning' })
await request.delete(`/process-flows/${row.id}`)
ElMessage.success('删除成功')
loadFlows()
@@ -441,11 +537,11 @@ async function toggleStatus(row) {
const action = toStatus === 'INACTIVE' ? '停用' : '启用'
if (toStatus === 'INACTIVE') {
await ElMessageBox.confirm(
`确认停用「${row.name}」?停用后绑定该流程的工位将不再加载其作业步骤(绑定关系保留,启用后自动恢复)。`,
`确认停用「${row.name}」?停用后绑定该工艺的工位将不再加载其作业步骤(绑定关系保留,启用后自动恢复)。`,
'提示', { type: 'warning' })
} else {
await ElMessageBox.confirm(
`确认启用「${row.name}」?启用后其绑定工位(${(row.stations || []).map(s => '工位' + s).join('、') || '-'})将恢复使用该流程。`,
`确认启用「${row.name}」?启用后其绑定工位(${(row.stations || []).map(s => '工位' + s).join('、') || '-'})将恢复使用该工艺。`,
'提示', { type: 'warning' }
)
}