中航光电热表web
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

707 lines
24 KiB

<template>
<el-dialog
:title="title"
append-to-body
:modelValue="openShow"
width="70%"
@close="closeDialog"
fullscreen
>
<div style="margin-bottom: 12px" v-if="moldAddMore">
<el-button type="primary" @click="addTable">插入一行</el-button>
<el-button type="danger" @click="delTable">删除选中行</el-button>
</div>
<!-- 单个 Form 包裹整个表格 -->
<el-form ref="tableForm" :model="form" :rules="formRules" label-width="0px">
<!-- 全局错误提示 -->
<div v-if="formError" class="error-message" style="color: #f56c6c; margin-bottom: 10px">
{{ formError }}
</div>
<el-table :data="form.tableData" @select="selectChange" border :height="tableHeight">
<el-table-column type="selection" width="55"></el-table-column>
<!-- 作业中心绑定数组字段 -->
<el-table-column align="center" label="作业中心" width="150">
<template #header>
<span><i style="color: red">*</i>作业中心</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].workCenterId`"
:rules="formRules.workCenterId"
>
<el-select
v-model="scope.row.workCenterId"
placeholder="请选择"
style="width: 100%"
clearable
filterable
@change="value => onChangeData(value, scope.$index, 'workCenterId')"
>
<el-option
v-for="item in wcData"
:key="item.id"
:value="item.id"
:label="item.wcName"
></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="设备名称" width="150">
<template #header>
<span><i style="color: red">*</i>设备名称</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].equipCode`"
:rules="formRules.equipCode"
>
<el-select
v-model="scope.row.equipCode"
placeholder="请选择"
style="width: 100%"
clearable
filterable
@change="value => onChangeData(value, scope.$index, 'equipCode')"
>
<el-option
v-for="item in equipData"
:key="item.deviceCode"
:value="item.deviceCode"
:label="`${item.deviceName}/${item.deviceCode}`"
></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="设备型号" width="150">
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].equipNameType`">
{{ scope.row.equipNameType }}
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="设备分类" width="150">
<template #header>
<span><i style="color: red">*</i>设备分类</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].equipType`"
:rules="formRules.equipType"
>
<el-select
v-model="scope.row.equipType"
placeholder="请选择"
style="width: 100%"
clearable
filterable
>
<el-option
v-for="item in equipTypeData"
:key="item.value"
:value="item.value"
:label="item.label"
></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="工艺能力" width="150">
<template #header>
<span><i style="color: red">*</i>工艺能力</span>
</template>
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].craftId`" :rules="formRules.craftId">
<el-select
v-model="scope.row.craftId"
placeholder="请选择"
style="width: 100%"
clearable
filterable
@change="value => onChangeData(value, scope.$index, 'craftId')"
>
<el-option
v-for="item in craftData"
:key="item.id"
:value="item.id"
:label="item.caName"
></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="加工能力类型" width="280">
<template #header>
<span><i style="color: red">*</i>加工能力类型</span>
</template>
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].partType`" :rules="formRules.partType">
<el-radio-group v-model="scope.row.partType" @change="onPartTypeChange(scope.$index)">
<el-radio :label="0">默认</el-radio>
<el-radio :label="1">壳体外径</el-radio>
<el-radio :label="2">玻璃饼直径</el-radio>
</el-radio-group>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="额定加工能力(烧结-个/热表-面积)" width="360">
<template #header>
<span><i style="color: red">*</i>额定加工能力</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].standardProcessAbility`"
:rules="formRules.standardProcessAbility"
>
<div class="ability-input-group" v-if="scope.row.partType != 0">
<template v-for="(item, idx) in scope.row.abilityList" :key="idx">
<div class="ability-row">
<!-- 左边大于等于 -->
<el-input
v-model="item.startNum"
placeholder="最小值"
style="width: 80px"
type="number"
@change="onAbilityChange(scope.$index)"
/>
<span class="operator-label"></span>
<!-- 中间逻辑符 -->
<span class="logic-label">φ</span>
<!-- 右边小于 -->
<span class="operator-label">&lt;</span>
<el-input
v-model="item.endNum"
placeholder="最大值"
style="width: 80px"
type="number"
@change="onAbilityChange(scope.$index)"
/>
<el-input
v-model="item.standardProcessAbility"
placeholder="min/件"
style="width: 80px"
type="number"
/>
<el-button
v-if="scope.row.abilityList.length > 1"
type="danger"
icon="Minus"
circle
size="small"
@click="removeAbilityItem(scope.$index, idx)"
title="删除条件"
/>
<el-button
v-if="idx === scope.row.abilityList.length - 1"
type="primary"
icon="Plus"
circle
size="small"
@click="addAbilityItem(scope.$index)"
title="添加条件"
/>
</div>
</template>
</div>
<div v-else>
<el-input-number
v-model="scope.row.standardProcessAbility"
style="width: 100%"
:min="0"
></el-input-number>
</div>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="额定工时" width="150">
<template #header>
<span><i style="color: red">*</i>额定工时</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].standardTime`"
:rules="formRules.standardTime"
>
<el-input-number
v-model="scope.row.standardTime"
style="width: 100%"
:min="0"
></el-input-number>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="准备工时" width="150">
<template #header>
<span><i style="color: red">*</i>准备工时</span>
</template>
<template #default="scope">
<el-form-item
:prop="`tableData[${scope.$index}].prepareTime`"
:rules="formRules.prepareTime"
>
<el-input-number
v-model="scope.row.prepareTime"
style="width: 100%"
:min="0"
></el-input-number>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="轮次间隔" width="150">
<template #header>
<span><i style="color: red">*</i>轮次间隔</span>
</template>
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].interval`" :rules="formRules.interval">
<el-input-number
v-model="scope.row.interval"
style="width: 100%"
:min="0"
></el-input-number>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="指定使用" width="150">
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].assignUse`">
<el-input v-model="scope.row.assignUse" placeholder="请输入内容"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" label="备注" width="150">
<template #default="scope">
<el-form-item :prop="`tableData[${scope.$index}].remarks`">
<el-input v-model="scope.row.remarks" placeholder="请输入内容"></el-input>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeDialog" :loading="subLoading"> </el-button>
<el-button type="primary" @click="submitForm" :loading="subLoading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { getWorkCenterList } from '@/api/processManagement/addQuantity.js';
import {
getEquipment,
getCraftAbility,
addEquipAbility,
updateEquipAbility,
} from '@/api/productionSchedulingPlan/basic.js';
export default {
props: {
showDialog: { type: Boolean, default: false },
moldAddMore: { type: Boolean, default: false },
tabPosition: { type: String, default: '' },
title: { type: String, default: '' },
rowData: { type: Array, default: [] },
},
data() {
return {
subLoading: false,
tableHeight: 100,
openShow: false,
wcData: [],
formError: '', // 全局错误提示
// 单个表单模型:包含表格所有行数据
form: {
tableData: [], // 表格数据数组(直接绑定到 Form 模型)
},
// 统一校验规则:支持数组项校验
formRules: {
// 表格数据数组的整体校验(可选:如最少1行数据)
tableData: [
{
required: true,
message: '请至少添加一行数据',
trigger: 'submit',
type: 'array', // 明确类型为数组
},
{
validator: (rule, value, callback) => {
if (value.length === 0) {
callback(new Error('请至少添加一行数据'));
} else {
callback();
}
},
trigger: 'submit',
},
],
// 作业中心
workCenterId: [
{ required: true, message: '请选择作业中心', trigger: ['change', 'submit'] },
],
equipCode: [
{ required: true, message: '请选择设备名称/编码', trigger: ['change', 'submit'] },
],
equipType: [{ required: true, message: '请选择设备类型', trigger: ['change', 'submit'] }],
craftId: [{ required: true, message: '请选择工艺能力', trigger: ['change', 'submit'] }],
// standardProcessAbility: [
// { required: true, message: '请输入额定加工能力', trigger: ['change', 'submit'] },
// ],
standardTime: [
{ required: true, message: '请输入额定工时', trigger: ['change', 'submit'] },
],
prepareTime: [{ required: true, message: '请输入准备工时', trigger: ['change', 'submit'] }],
interval: [{ required: true, message: '请输入轮次间隔', trigger: ['change', 'submit'] }],
partType: [{ required: true, message: '请选择加工类型', trigger: ['change', 'submit'] }],
},
equipTypeData: [
{
label: '产线设备',
value: '0',
},
{
label: '普通设备',
value: '1',
},
],
equipData: [],
};
},
mounted() {
this.openShow = this.showDialog;
this.getWorkCenterList();
this.getEquipment();
this.getCraftAbility();
// 初始添加一行(可选)
if (this.title == '新增') {
this.addTable();
} else {
this.form.tableData = this.rowData;
}
this.calcTableHeight();
window.addEventListener('resize', this.calcTableHeight);
},
beforeUnmount() {
window.removeEventListener('resize', this.calcTableHeight);
},
methods: {
validateRange() {
const tableData = this.form.tableData || [];
for (let i = 0; i < tableData.length; i++) {
const row = tableData[i];
// 只验证 partType 不为 0 的行
if (row.partType === 0) continue;
const list = row.abilityList || [];
for (let j = 0; j < list.length; j++) {
const item = list[j];
const nextItem = list[j + 1];
// 如果填写了数据,验证区间
if (
item.startNum !== null &&
item.startNum !== '' &&
item.endNum !== null &&
item.endNum !== ''
) {
const start = parseFloat(item.startNum);
const end = parseFloat(item.endNum);
console.log(start, end, 89898989);
// 验证最小值 < 最大值
if (start >= end) {
this.$message.error(`${i + 1}行,第${j + 1}组:最小值必须小于最大值`);
return false;
}
// 验证不能为负数
if (start < 0 || end < 0) {
this.$message.error(`${i + 1}行,第${j + 1}组:值不能为负数`);
return false;
}
if (j < list.length - 1) {
const nextItem = list[j + 1];
if (item.endNum && nextItem.startNum) {
if (parseFloat(item.endNum) !== parseFloat(nextItem.startNum)) {
this.$message.error(
`${i + 1}行,第${j + 1}组的结束值 (${item.endNum}) 必须等于第${
j + 2
}组的开始值 (${nextItem.startNum})`
);
return false;
}
}
}
}
}
}
return true;
}, // 获取工艺列表
getCraftAbility() {
getCraftAbility().then(res => {
this.craftData = res.data.data;
});
},
// 获取设备列表
getEquipment() {
getEquipment().then(res => {
this.equipData = res.data.data;
console.log(res.data.data, 'data');
});
},
// 获取表格高度
calcTableHeight() {
this.$nextTick(() => {
const container = document.querySelector('.el-dialog__body');
console.log(container.offsetHeight);
this.tableHeight = container.offsetHeight - 36 - 90 - 30;
});
},
// 获取作业中心列表
getWorkCenterList() {
getWorkCenterList().then(res => {
this.wcData = res.data.data || [];
});
},
// 作业中心
onChangeData(value, index, type) {
console.log(value, index, type);
if (type == 'workCenterId') {
// this.form.workCenterName = val.wcCode;
const selectedItem = this.wcData.find(item => item.id === value);
if (selectedItem) {
this.form.tableData[index].workCenterName = selectedItem.wcName;
} else {
this.form.tableData[index].workCenterName = '';
}
}
if (type == 'equipCode') {
const selectedItem = this.equipData.find(item => item.deviceCode === value);
if (selectedItem) {
this.form.tableData[index].equipName = selectedItem.deviceName; //设备名称
this.form.tableData[index].equipNameType = selectedItem.macSpec; //设备型号
} else {
this.form.tableData[index].workCenterName = '';
}
}
if (type == 'craftId') {
const selectedItem = this.craftData.find(item => item.id === value);
if (selectedItem) {
this.form.tableData[index].craftName = selectedItem.caName;
} else {
this.form.tableData[index].craftName = '';
}
}
},
onPartTypeChange(index) {
const row = this.form.tableData[index];
// 当 partType 不为 0 时,确保 abilityList 有默认数据
if (row.partType !== 0) {
if (!row.abilityList || row.abilityList.length === 0) {
row.abilityList = [{ min: null, max: null }];
}
}
},
// 添加条件组
addAbilityItem(index) {
this.form.tableData[index].abilityList.push({
startNum: null,
endNum: null,
standardTime: null,
});
this.onAbilityChange(index);
},
// 删除条件组
removeAbilityItem(tableIndex, itemIndex) {
this.form.tableData[tableIndex].abilityList.splice(itemIndex, 1);
this.onAbilityChange(tableIndex);
},
// 输入变化时更新合并后的值
onAbilityChange(index) {
const row = this.form.tableData[index];
const list = row.abilityList || [];
// 合并为提交格式:≥min && <max || ≥min && <max
const result = list
.filter(item => item.min !== null || item.max !== null)
.map(item => {
const parts = [];
if (item.min !== null && item.min !== '') {
parts.push(`${item.min}`);
}
if (item.max !== null && item.max !== '') {
parts.push(`<${item.max}`);
}
return parts.join(' && ');
})
.filter(str => str)
.join(' || ');
row.standardProcessAbility = result;
},
selectChange(list, row) {
row._select = !row._select;
},
// 新增一行(直接push到表单模型的 tableData 中)
addTable() {
this.form.tableData.push({
_select: false, // 选择状态
partType: 0,
standardProcessAbility: null,
abilityList: [{ startNum: null, endNum: null, standardTime: null }],
});
},
// 删除选中行
delTable() {
this.form.tableData = this.form.tableData.filter(row => !row._select);
},
closeDialog() {
this.openShow = false;
this.$emit('closeDialog', true);
// 重置表单
this.form.tableData = [];
this.formError = '';
this.$refs.tableForm?.resetFields();
},
// 提交表单(单次校验所有行)
submitForm() {
this.formError = '';
// 调用单个 Form 的校验方法
this.$refs.tableForm.validate((isValid, invalidFields) => {
if (!isValid) {
// 校验失败:显示提示并滚动到第一个错误字段
this.formError = '存在未完善的字段,请检查表格中的红色提示';
this.$nextTick(() => {
// 找到第一个错误字段并滚动到视图
const firstError = document.querySelector('.el-form-item.is-error');
if (firstError) {
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
return;
}
console.log('提交数据:', this.form.tableData);
if (!this.validateRange()) return;
this.subLoading = true;
// 校验通过:准备提交数据(过滤无用字段)
const submitData = this.form.tableData.map(row => {
if (row.partType != 0) {
// 非默认类型,处理 abilityList
const list = row.abilityList || [];
// 如果最后一个值的 endNum 为空,默认赋值 1000
if (list.length > 0) {
const lastItem = list[list.length - 1];
if (
lastItem.endNum === null ||
lastItem.endNum === '' ||
lastItem.endNum === undefined
) {
lastItem.endNum = 1000;
}
}
row.standardProcessAbility = JSON.stringify(row.abilityList);
} else {
row.standardProcessAbility = row.standardProcessAbility;
}
return row;
});
if (this.title == '新增') {
addEquipAbility(submitData).then(
() => {
this.subLoading = false;
this.$message.success('提交成功');
this.closeDialog();
this.$emit('submitSuccess', submitData);
done();
},
error => {
this.subLoading = false;
window.console.log(error);
}
);
} else {
updateEquipAbility(submitData).then(
() => {
this.$message.success('提交成功');
this.closeDialog();
this.$emit('submitSuccess', submitData);
this.subLoading = false;
done();
},
error => {
window.console.log(error);
this.subLoading = false;
}
);
}
});
},
},
};
</script>
<style lang="scss" scoped>
// 优化表单字段样式
:deep(.el-table .el-form-item) {
margin-bottom: 0; // 去掉默认边距
}
// 错误提示样式优化
:deep(.el-form-item__error) {
font-size: 12px;
white-space: nowrap;
z-index: 10;
background: #fff;
padding: 2px 4px;
border: 1px solid #f56c6c;
border-radius: 4px;
}
// 表格行高适配 textarea
.el-table__row {
height: 80px !important;
}
.el-table__cell {
vertical-align: middle !important;
}
.error-message {
font-size: 14px;
line-height: 1.5;
}
:deep(.el-table .el-table__cell) {
height: 50px !important;
padding: 0 !important;
line-height: 50px !important;
}
.ability-row {
margin-bottom: 10px;
}
</style>