feat: 新增产品标准、工艺路线及工序参数管理功能

本次提交新增了以下内容:
1. 为产品标准BO的生效日期和失效日期添加Json格式化注解
2. 新增工艺主表、工序参数明细的前后端CRUD接口与页面
3. 新增产品标准相关的全套CRUD接口
4. 新增产品标准管理页面,包含产品用途、化学成分等多类子功能模块
This commit is contained in:
2026-07-03 10:42:48 +08:00
parent c04f40ad5c
commit d7bf12d880
6 changed files with 2185 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import request from '@/utils/request'
// ==================== 工艺主表 ====================
export function listProcesse(query) {
return request({ url: '/pt/processe/list', method: 'get', params: query })
}
export function getProcesse(processId) {
return request({ url: '/pt/processe/' + processId, method: 'get' })
}
export function addProcesse(data) {
return request({ url: '/pt/processe', method: 'post', data })
}
export function updateProcesse(data) {
return request({ url: '/pt/processe', method: 'put', data })
}
export function delProcesse(processIds) {
return request({ url: '/pt/processe/' + processIds, method: 'delete' })
}
// ==================== 工序参数明细 ====================
export function listProcessStepParam(query) {
return request({ url: '/pt/processStepParam/list', method: 'get', params: query })
}
export function getProcessStepParam(paramId) {
return request({ url: '/pt/processStepParam/' + paramId, method: 'get' })
}
export function addProcessStepParam(data) {
return request({ url: '/pt/processStepParam', method: 'post', data })
}
export function updateProcessStepParam(data) {
return request({ url: '/pt/processStepParam', method: 'put', data })
}
export function delProcessStepParam(paramIds) {
return request({ url: '/pt/processStepParam/' + paramIds, method: 'delete' })
}

View File

@@ -0,0 +1,154 @@
import request from '@/utils/request'
// ==================== 产品标准主表 ====================
export function listProductStandard(query) {
return request({ url: '/pt/productStandard/list', method: 'get', params: query })
}
export function getProductStandard(standardId) {
return request({ url: '/pt/productStandard/' + standardId, method: 'get' })
}
export function addProductStandard(data) {
return request({ url: '/pt/productStandard', method: 'post', data })
}
export function updateProductStandard(data) {
return request({ url: '/pt/productStandard', method: 'put', data })
}
export function delProductStandard(standardIds) {
return request({ url: '/pt/productStandard/' + standardIds, method: 'delete' })
}
// ==================== 产品用途 ====================
export function listProductApplication(query) {
return request({ url: '/pt/productApplication/list', method: 'get', params: query })
}
export function getProductApplication(applicationId) {
return request({ url: '/pt/productApplication/' + applicationId, method: 'get' })
}
export function addProductApplication(data) {
return request({ url: '/pt/productApplication', method: 'post', data })
}
export function updateProductApplication(data) {
return request({ url: '/pt/productApplication', method: 'put', data })
}
export function delProductApplication(applicationIds) {
return request({ url: '/pt/productApplication/' + applicationIds, method: 'delete' })
}
// ==================== 化学成分 ====================
export function listProductChemicalComposition(query) {
return request({ url: '/pt/productChemicalComposition/list', method: 'get', params: query })
}
export function getProductChemicalComposition(compositionId) {
return request({ url: '/pt/productChemicalComposition/' + compositionId, method: 'get' })
}
export function addProductChemicalComposition(data) {
return request({ url: '/pt/productChemicalComposition', method: 'post', data })
}
export function updateProductChemicalComposition(data) {
return request({ url: '/pt/productChemicalComposition', method: 'put', data })
}
export function delProductChemicalComposition(compositionIds) {
return request({ url: '/pt/productChemicalComposition/' + compositionIds, method: 'delete' })
}
// ==================== 镀层规格 ====================
export function listProductCoatingSpec(query) {
return request({ url: '/pt/productCoatingSpec/list', method: 'get', params: query })
}
export function getProductCoatingSpec(coatingId) {
return request({ url: '/pt/productCoatingSpec/' + coatingId, method: 'get' })
}
export function addProductCoatingSpec(data) {
return request({ url: '/pt/productCoatingSpec', method: 'post', data })
}
export function updateProductCoatingSpec(data) {
return request({ url: '/pt/productCoatingSpec', method: 'put', data })
}
export function delProductCoatingSpec(coatingIds) {
return request({ url: '/pt/productCoatingSpec/' + coatingIds, method: 'delete' })
}
// ==================== 尺寸规格 ====================
export function listProductDimension(query) {
return request({ url: '/pt/productDimension/list', method: 'get', params: query })
}
export function getProductDimension(dimensionId) {
return request({ url: '/pt/productDimension/' + dimensionId, method: 'get' })
}
export function addProductDimension(data) {
return request({ url: '/pt/productDimension', method: 'post', data })
}
export function updateProductDimension(data) {
return request({ url: '/pt/productDimension', method: 'put', data })
}
export function delProductDimension(dimensionIds) {
return request({ url: '/pt/productDimension/' + dimensionIds, method: 'delete' })
}
// ==================== 物理性能 ====================
export function listProductPhysicalProperty(query) {
return request({ url: '/pt/productPhysicalProperty/list', method: 'get', params: query })
}
export function getProductPhysicalProperty(propertyId) {
return request({ url: '/pt/productPhysicalProperty/' + propertyId, method: 'get' })
}
export function addProductPhysicalProperty(data) {
return request({ url: '/pt/productPhysicalProperty', method: 'post', data })
}
export function updateProductPhysicalProperty(data) {
return request({ url: '/pt/productPhysicalProperty', method: 'put', data })
}
export function delProductPhysicalProperty(propertyIds) {
return request({ url: '/pt/productPhysicalProperty/' + propertyIds, method: 'delete' })
}
// ==================== 参考标准 ====================
export function listProductRefStandard(query) {
return request({ url: '/pt/productRefStandard/list', method: 'get', params: query })
}
export function getProductRefStandard(refId) {
return request({ url: '/pt/productRefStandard/' + refId, method: 'get' })
}
export function addProductRefStandard(data) {
return request({ url: '/pt/productRefStandard', method: 'post', data })
}
export function updateProductRefStandard(data) {
return request({ url: '/pt/productRefStandard', method: 'put', data })
}
export function delProductRefStandard(refIds) {
return request({ url: '/pt/productRefStandard/' + refIds, method: 'delete' })
}
// ==================== 尺寸公差 ====================
export function listProductTolerance(query) {
return request({ url: '/klp/productTolerance/list', method: 'get', params: query })
}
export function getProductTolerance(toleranceId) {
return request({ url: '/klp/productTolerance/' + toleranceId, method: 'get' })
}
export function addProductTolerance(data) {
return request({ url: '/klp/productTolerance', method: 'post', data })
}
export function updateProductTolerance(data) {
return request({ url: '/klp/productTolerance', method: 'put', data })
}
export function delProductTolerance(toleranceIds) {
return request({ url: '/klp/productTolerance/' + toleranceIds, method: 'delete' })
}
// ==================== 表面质量 ====================
export function listProductSurfaceSpec(query) {
return request({ url: '/pt/productSurfaceSpec/list', method: 'get', params: query })
}
export function getProductSurfaceSpec(surfaceId) {
return request({ url: '/pt/productSurfaceSpec/' + surfaceId, method: 'get' })
}
export function addProductSurfaceSpec(data) {
return request({ url: '/pt/productSurfaceSpec', method: 'post', data })
}
export function updateProductSurfaceSpec(data) {
return request({ url: '/pt/productSurfaceSpec', method: 'put', data })
}
export function delProductSurfaceSpec(surfaceIds) {
return request({ url: '/pt/productSurfaceSpec/' + surfaceIds, method: 'delete' })
}

View File

@@ -0,0 +1,675 @@
<template>
<div class="pt-process-page">
<DragResizePanel :initialSize="300" :minSize="240" :maxSize="550">
<!-- ==================== 左侧面板工艺列表 ==================== -->
<template #panelA>
<div class="left-panel">
<div class="left-topbar">
<el-input
v-model="processSearch"
placeholder="搜索工艺名称..."
clearable
size="small"
prefix-icon="el-icon-search"
class="left-search-input"
@keyup.enter.native="handleProcessSearch"
/>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleProcessAdd">新增</el-button>
<el-button size="mini" :icon="filterExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" @click="filterExpanded = !filterExpanded">
{{ filterExpanded ? '收起' : '筛选' }}
</el-button>
</div>
<div class="left-filter" v-show="filterExpanded">
<el-form :model="processFilters" size="small" label-width="56px">
<el-form-item label="状态">
<el-select v-model="processFilters.status" placeholder="全部" clearable style="width:100%">
<el-option label="启用" :value="1" />
<el-option label="停用" :value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" @click="handleProcessSearch">搜索</el-button>
<el-button size="mini" @click="resetProcessFilters">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="left-list" v-loading="processLoading">
<div
v-for="item in processList"
:key="item.processId"
:class="['process-item', { active: selectedProcess && selectedProcess.processId === item.processId }]"
@click="selectProcess(item)"
>
<div class="process-item-header">
<div class="process-item-name">{{ item.processName }}</div>
<div class="process-item-actions" @click.stop>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleProcessUpdate(item)" />
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleProcessDelete(item)" />
</div>
</div>
<div class="process-item-meta">
<span class="process-item-code">{{ item.processCode }}</span>
<span :class="['process-item-status', item.status === 1 ? 'on' : 'off']">
{{ item.status === 1 ? '启用' : '停用' }}
</span>
</div>
</div>
<el-empty v-if="!processLoading && processList.length === 0" description="暂无工艺路线" :image-size="50" />
</div>
<pagination
v-show="processTotal > 0"
:total="processTotal"
:page.sync="processQueryParams.pageNum"
:limit.sync="processQueryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, prev, pager, next, sizes"
:pager-count="3"
small
@pagination="getProcessList"
/>
</div>
</template>
<!-- ==================== 右侧面板工序参数 ==================== -->
<template #panelB>
<div class="right-panel">
<div v-if="!selectedProcess" class="empty-hint">
<el-empty description="请从左侧列表选择一个工艺路线,查看和编辑其工序参数" :image-size="100" />
</div>
<template v-else>
<div class="selected-process-info">
<div class="process-info-left">
<span class="process-info-name">{{ selectedProcess.processName }}</span>
<span class="process-info-code">{{ selectedProcess.processCode }}</span>
</div>
<div class="process-info-right">
<span v-if="selectedProcess.targetYield" class="process-info-tag">成材率 {{ selectedProcess.targetYield }}%</span>
<span v-if="selectedProcess.standardCapacity" class="process-info-tag">产能 {{ selectedProcess.standardCapacity }}t/h</span>
<span :class="['process-info-status', selectedProcess.status === 1 ? 'on' : 'off']">
{{ selectedProcess.status === 1 ? '启用' : '停用' }}
</span>
</div>
</div>
<div class="tab-toolbar">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增参数</el-button>
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete()">删除</el-button>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
</div>
<el-table
v-loading="loading"
:data="dataList"
size="small"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="45" align="center" />
<el-table-column v-for="col in stepColumns" :key="col.prop" v-bind="col">
<template slot-scope="scope">
<template v-if="editingId != null && editingId === scope.row.paramId">
<el-input v-if="col.editType === 'input'" v-model="editForm[col.prop]" size="small" />
<el-input-number v-else-if="col.editType === 'number'" v-model="editForm[col.prop]" size="small" :controls="false" style="width:100%" />
<el-select v-else-if="col.editType === 'select'" v-model="editForm[col.prop]" size="small" style="width:100%">
<el-option v-for="opt in col.options" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
<span v-else>{{ scope.row[col.prop] }}</span>
</template>
<span v-else>
{{ col.formatter ? col.formatter(scope.row, col, scope.row[col.prop]) : scope.row[col.prop] }}
</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="140" class-name="small-padding fixed-width">
<template slot-scope="scope">
<template v-if="editingId != null && editingId === scope.row.paramId">
<el-button size="mini" type="text" style="color:#67c23a" @click="saveEdit(scope.row)">保存</el-button>
<el-button size="mini" type="text" @click="cancelEdit()">取消</el-button>
</template>
<template v-else>
<el-button size="mini" type="text" icon="el-icon-edit" @click="startEdit(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</template>
</div>
</template>
</DragResizePanel>
<!-- ==================== 参数新增弹窗 ==================== -->
<el-dialog title="新增工序参数" :visible.sync="open" width="650px" append-to-body :close-on-click-modal="false">
<el-form ref="form" :model="form" :rules="addRules" label-width="100px" size="small">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="工序顺序" prop="stepOrder">
<el-input-number v-model="form.stepOrder" :min="1" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工序名称" prop="stepName">
<el-select v-model="form.stepName" 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-option label="拉矫" value="拉矫" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="参数编码" prop="paramCode">
<el-input v-model="form.paramCode" placeholder="TEMP/SPEED/TENSION" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="参数名称" prop="paramNameZh">
<el-input v-model="form.paramNameZh" placeholder="请输入中文名" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="参数分类" prop="paramCategory">
<el-select v-model="form.paramCategory" 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-col>
<el-col :span="12">
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="℃/m/min/MPa/A" clearable />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最小值" prop="minValue">
<el-input-number v-model="form.minValue" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最大值" prop="maxValue">
<el-input-number v-model="form.maxValue" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目标值" prop="targetValue">
<el-input-number v-model="form.targetValue" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="报警下限" prop="alarmMin">
<el-input-number v-model="form.alarmMin" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="报警上限" prop="alarmMax">
<el-input-number v-model="form.alarmMax" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否CCP" prop="isCritical">
<el-select v-model="form.isCritical" clearable style="width:100%">
<el-option label="是" :value="1" />
<el-option label="否" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sortOrder">
<el-input-number v-model="form.sortOrder" :controls="false" style="width:100%" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" size="small" @click="submitForm"> </el-button>
<el-button size="small" @click="cancelAdd"> </el-button>
</div>
</el-dialog>
<!-- ==================== 工艺新增/编辑弹窗 ==================== -->
<el-dialog :title="processDialogTitle" :visible.sync="processDialogOpen" width="550px" append-to-body :close-on-click-modal="false">
<el-form ref="processForm" :model="processForm" :rules="processRules" label-width="80px" size="small">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="工艺代号" prop="processCode">
<el-input v-model="processForm.processCode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工艺名称" prop="processName">
<el-input v-model="processForm.processName" placeholder="请输入工艺名称" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="适配产品">
<el-input v-model="processForm.adaptProductType" placeholder="逗号分隔,如 CRC,GI" clearable />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-select v-model="processForm.status" style="width:100%">
<el-option label="启用" :value="1" />
<el-option label="停用" :value="0" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="目标成材率">
<el-input-number v-model="processForm.targetYield" :controls="false" style="width:100%">
<template slot="suffix">%</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="标准产能">
<el-input-number v-model="processForm.standardCapacity" :controls="false" style="width:100%">
<template slot="suffix">t/h</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工艺流程">
<el-input v-model="processForm.flowOverview" type="textarea" :rows="3" placeholder="描述整体工艺流程" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="processForm.remark" type="textarea" :rows="2" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" size="small" @click="submitProcessForm" :loading="processSubmitting"> </el-button>
<el-button size="small" @click="cancelProcessDialog"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import DragResizePanel from '@/components/DragResizePanel/index.vue'
import {
listProcesse, getProcesse, addProcesse, updateProcesse, delProcesse,
listProcessStepParam, getProcessStepParam, addProcessStepParam, updateProcessStepParam, delProcessStepParam
} from '@/api/pt/process'
const boolFormatter = (row, col, val) => (val === 1 || val === '1') ? '是' : '否'
const STEP_COLUMNS = [
{ prop: 'stepOrder', label: '工序顺序', width: 80, editType: 'number' },
{ prop: 'stepName', label: '工序名称', width: 90, editType: 'select',
options: [{ label: '酸洗', value: '酸洗' }, { label: '冷轧', value: '冷轧' }, { label: '退火', value: '退火' }, { label: '镀锌', value: '镀锌' }, { label: '平整', value: '平整' }, { label: '拉矫', value: '拉矫' }] },
{ prop: 'paramCode', label: '参数编码', width: 100, editType: 'input' },
{ prop: 'paramNameZh', label: '参数名称', minWidth: 100, editType: 'input' },
{ prop: 'paramCategory', label: '分类', width: 70, editType: 'select',
options: [{ label: '温度', value: '温度' }, { label: '速度', value: '速度' }, { label: '压力', value: '压力' }, { label: '电流', value: '电流' }, { label: '张力', value: '张力' }] },
{ prop: 'minValue', label: '最小值', width: 80, editType: 'number' },
{ prop: 'maxValue', label: '最大值', width: 80, editType: 'number' },
{ prop: 'targetValue', label: '目标值', width: 80, editType: 'number' },
{ prop: 'unit', label: '单位', width: 60, editType: 'input' },
{ prop: 'isCritical', label: 'CCP', width: 55, editType: 'select', formatter: boolFormatter,
options: [{ label: '是', value: 1 }, { label: '否', value: 0 }] },
{ prop: 'alarmMin', label: '报警下限', width: 80, editType: 'number' },
{ prop: 'alarmMax', label: '报警上限', width: 80, editType: 'number' },
{ prop: 'sortOrder', label: '排序', width: 55, editType: 'number' }
]
export default {
name: 'PtProcesse',
components: { DragResizePanel },
data() {
return {
// ===== 工艺列表 =====
processLoading: false,
processList: [],
processTotal: 0,
processQueryParams: { pageNum: 1, pageSize: 20 },
processSearch: '',
filterExpanded: false,
processFilters: { status: null },
selectedProcess: null,
// ===== 工艺弹窗 =====
processDialogOpen: false,
processDialogTitle: '',
processForm: {},
processSubmitting: false,
processRules: {
processName: [{ required: true, message: '请输入工艺名称', trigger: 'blur' }]
},
// ===== 工序参数表格 =====
loading: false,
dataList: [],
total: 0,
queryParams: { pageNum: 1, pageSize: 20 },
showSearch: true,
ids: [],
multiple: true,
stepColumns: STEP_COLUMNS,
// ===== 行内编辑 =====
editingId: null,
editForm: {},
editFormOriginal: null,
// ===== 新增弹窗 =====
open: false,
form: {},
addRules: {
stepOrder: [{ required: true, message: '请输入工序顺序', trigger: 'blur' }],
stepName: [{ required: true, message: '请选择工序名称', trigger: 'change' }]
}
}
},
created() {
this.getProcessList()
},
methods: {
// ==================== 工艺 CRUD ====================
getProcessList() {
this.processLoading = true
const params = { ...this.processQueryParams }
if (this.processSearch) params.processName = this.processSearch
if (this.processFilters.status != null && this.processFilters.status !== '') {
params.status = this.processFilters.status
}
listProcesse(params).then(res => {
this.processList = res.rows || []
this.processTotal = res.total || 0
this.processLoading = false
}).catch(() => { this.processLoading = false })
},
handleProcessSearch() {
this.processQueryParams.pageNum = 1
this.getProcessList()
},
resetProcessFilters() {
this.processFilters = { status: null }
this.processSearch = ''
this.processQueryParams.pageNum = 1
this.getProcessList()
},
selectProcess(item) {
this.cancelEdit()
this.selectedProcess = item
this.resetQueryParams()
this.getList()
},
handleProcessAdd() {
const now = new Date()
const code = 'PRC-' +
now.getFullYear() + String(now.getMonth() + 1).padStart(2, '0') +
String(now.getDate()).padStart(2, '0') + '-' +
String(now.getHours()).padStart(2, '0') +
String(now.getMinutes()).padStart(2, '0') +
String(now.getSeconds()).padStart(2, '0')
this.processForm = {
processId: undefined, processCode: code, processName: '',
adaptProductType: '', status: 1, targetYield: undefined,
standardCapacity: undefined, flowOverview: '', remark: ''
}
this.processDialogTitle = '新增工艺路线'
this.processDialogOpen = true
this.processSubmitting = false
this.$nextTick(() => { if (this.$refs['processForm']) this.$refs['processForm'].clearValidate() })
},
handleProcessUpdate(item) {
const row = item || this.selectedProcess
if (!row) return
getProcesse(row.processId).then(res => {
const d = res.data || {}
this.processForm = {
processId: d.processId, processCode: d.processCode || '',
processName: d.processName || '', adaptProductType: d.adaptProductType || '',
status: d.status != null ? d.status : 1,
targetYield: d.targetYield, standardCapacity: d.standardCapacity,
flowOverview: d.flowOverview || '', remark: d.remark || ''
}
this.processDialogTitle = '修改工艺路线'
this.processDialogOpen = true
this.processSubmitting = false
this.$nextTick(() => { if (this.$refs['processForm']) this.$refs['processForm'].clearValidate() })
})
},
handleProcessDelete(item) {
const row = item || this.selectedProcess
if (!row) return
this.$modal.confirm('确认删除工艺「' + row.processName + '」?').then(() => {
return delProcesse(row.processId)
}).then(() => {
this.$modal.msgSuccess('删除成功')
if (this.selectedProcess && this.selectedProcess.processId === row.processId) {
this.selectedProcess = null; this.dataList = []; this.total = 0
}
this.getProcessList()
}).catch(() => {})
},
submitProcessForm() {
this.$refs['processForm'].validate(valid => {
if (!valid) return
this.processSubmitting = true
const d = this.processForm
const act = d.processId ? updateProcesse(d) : addProcesse(d)
act.then(() => {
this.$modal.msgSuccess(d.processId ? '修改成功' : '新增成功')
this.processDialogOpen = false
this.processSubmitting = false
this.getProcessList()
if (d.processId && this.selectedProcess && this.selectedProcess.processId === d.processId) {
getProcesse(d.processId).then(res => { this.selectedProcess = res.data })
}
}).catch(() => { this.processSubmitting = false })
})
},
cancelProcessDialog() {
this.processDialogOpen = false
},
// ==================== 工序参数 CRUD ====================
resetQueryParams() {
this.queryParams = { pageNum: 1, pageSize: 20, processId: this.selectedProcess ? this.selectedProcess.processId : undefined }
this.ids = []
this.multiple = true
},
getList() {
if (!this.selectedProcess) return
this.loading = true
const params = { ...this.queryParams, processId: this.selectedProcess.processId }
listProcessStepParam(params).then(res => {
this.dataList = res.rows || []
this.total = res.total || 0
this.loading = false
}).catch(() => { this.loading = false })
},
handleSelectionChange(sel) {
this.ids = sel.map(i => i.paramId)
this.multiple = !sel.length
},
handleAdd() {
this.cancelEdit()
this.form = { processId: this.selectedProcess.processId }
this.open = true
this.$nextTick(() => { if (this.$refs['form']) this.$refs['form'].clearValidate() })
},
cancelAdd() { this.open = false },
submitForm() {
this.$refs['form'].validate(valid => {
if (!valid) return
addProcessStepParam(this.form).then(() => {
this.$modal.msgSuccess('新增成功')
this.open = false
this.getList()
})
})
},
handleDelete(row) {
const ids = row ? row.paramId : this.ids.join(',')
if (!ids) { this.$modal.msgWarning('请选择要删除的记录'); return }
this.$modal.confirm('确认删除?').then(() => delProcessStepParam(ids)).then(() => {
this.getList(); this.$modal.msgSuccess('删除成功')
}).catch(() => {})
},
// ==================== 行内编辑 ====================
startEdit(row) {
const form = {}
this.stepColumns.forEach(c => { form[c.prop] = row[c.prop] })
form.paramId = row.paramId
form.processId = row.processId
this.editForm = form
this.editFormOriginal = JSON.parse(JSON.stringify(row))
this.editingId = row.paramId
},
cancelEdit() {
this.editingId = null
this.editForm = {}
this.editFormOriginal = null
},
saveEdit(row) {
const data = { ...this.editForm }
const idx = this.dataList.findIndex(r => r.paramId === row.paramId)
if (idx >= 0) this.dataList.splice(idx, 1, { ...this.dataList[idx], ...data })
this.editingId = null
this.editForm = {}
const original = this.editFormOriginal
this.editFormOriginal = null
updateProcessStepParam(data).then(() => {
this.$modal.msgSuccess('保存成功')
}).catch(() => {
if (idx >= 0 && original) this.dataList.splice(idx, 1, original)
this.$modal.msgError('保存失败,已还原')
})
}
}
}
</script>
<style lang="scss" scoped>
.pt-process-page {
height: calc(100vh - 84px);
::v-deep .left-panel .pagination-container {
padding: 4px 8px;
.el-pagination {
flex-wrap: wrap; justify-content: center;
.el-pagination__total { font-size: 11px; }
.el-pagination__sizes { margin: 0 2px; }
}
}
}
// ===== 左侧面板 =====
.left-panel {
height: 100%; display: flex; flex-direction: column;
background: #fff; border-right: 1px solid #e8e8e8;
.left-topbar {
display: flex; align-items: center; gap: 6px;
padding: 8px 10px; border-bottom: 1px solid #ebeef5;
.left-search-input { flex: 1; }
}
.left-filter {
padding: 8px 10px; border-bottom: 1px solid #ebeef5; background: #fafafa;
::v-deep .el-form-item { margin-bottom: 6px; }
::v-deep .el-form-item__label { font-size: 11px; }
}
.left-list {
flex: 1; overflow-y: auto;
.process-item {
padding: 10px 12px; cursor: pointer;
border-bottom: 1px solid #f0f2f5; transition: all 0.15s;
&:hover { background: #f5f7fa; .process-item-actions { opacity: 1; } }
&.active {
background: #ecf5ff; border-left: 3px solid #409EFF; padding-left: 9px;
.process-item-name { color: #409EFF; }
}
&:last-child { border-bottom: none; }
}
.process-item-header {
display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px;
}
.process-item-name {
font-size: 13px; font-weight: 600; color: #303133;
flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.4;
}
.process-item-actions {
opacity: 0; flex-shrink: 0; transition: opacity 0.15s;
.el-button + .el-button { margin-left: 0; }
}
.process-item-meta { display: flex; align-items: center; gap: 6px; }
.process-item-code {
font-size: 11px; color: #909399; font-family: 'SF Mono', 'Menlo', monospace;
background: #f5f7fa; padding: 1px 6px; border-radius: 3px;
}
.process-item-status {
font-size: 10px; padding: 1px 6px; border-radius: 3px;
&.on { color: #67c23a; background: #f0f9eb; }
&.off { color: #909399; background: #f4f4f5; }
}
}
}
// ===== 右侧面板 =====
.right-panel {
height: 100%; display: flex; flex-direction: column;
background: #fff; padding: 0 12px 12px; overflow: auto;
}
.selected-process-info {
display: flex; align-items: center; justify-content: space-between;
margin: 10px 0 8px; padding: 10px 14px; background: #fff;
border: 1px solid #e4e7ed; border-left: 4px solid #409EFF;
border-radius: 0 6px 6px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.04);
flex-shrink: 0; gap: 12px;
.process-info-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.process-info-name { font-size: 15px; font-weight: 700; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.process-info-code {
font-size: 11px; font-family: 'SF Mono', 'Menlo', monospace; color: #909399;
background: #f5f7fa; border: 1px solid #e4e7ed; padding: 2px 8px; border-radius: 4px; white-space: nowrap; flex-shrink: 0;
}
.process-info-right { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.process-info-tag {
font-size: 11px; padding: 2px 8px; border-radius: 4px; line-height: 18px;
color: #409EFF; background: #ecf5ff; border: 1px solid #d9ecff;
}
.process-info-status {
font-size: 11px; padding: 2px 8px; border-radius: 4px; white-space: nowrap; line-height: 18px;
&.on { color: #67c23a; background: #f0f9eb; border: 1px solid #e1f3d8; }
&.off { color: #909399; background: #f4f4f5; border: 1px solid #e9e9eb; }
}
}
.empty-hint { display: flex; justify-content: center; align-items: center; flex: 1; }
.tab-toolbar {
display: flex; align-items: center; gap: 8px;
padding: 6px 0; flex-shrink: 0;
.right-toolbar { margin-left: auto; }
}
</style>

View File

File diff suppressed because it is too large Load Diff