|
|
@@ -4,25 +4,44 @@
|
|
|
v-if="
|
|
|
statisticsType == 1 &&
|
|
|
localDetails[0] &&
|
|
|
- localDetails[0].statisticsValues
|
|
|
+ localDetails[0].statisticsValue
|
|
|
"
|
|
|
ref="table"
|
|
|
:columns="typeOneColumns"
|
|
|
- :datasource="localDetails[0].statisticsValues"
|
|
|
- cacheKey="mes-statistics-type-2511041946"
|
|
|
+ :datasource="localDetails[0].statisticsValue"
|
|
|
+ cacheKey="mes-statistics-type-1-2511041946"
|
|
|
:needPage="false"
|
|
|
>
|
|
|
<template
|
|
|
- v-for="(item, index) in localDetails[0].statisticsValues[0]"
|
|
|
+ v-for="(item, index) in localDetails[0].statisticsValue[0]"
|
|
|
v-slot:[`column_${index}`]="{ row }"
|
|
|
>
|
|
|
- <div :key="`column_${index}`">
|
|
|
- {{ `column_${index}` }}
|
|
|
-
|
|
|
+ <div :key="`column_${index}`" class="column_content">
|
|
|
+ <div v-if="item.isOnlyShow">
|
|
|
+ {{ item.value }}
|
|
|
+ </div>
|
|
|
<el-input
|
|
|
- v-model="row.value"
|
|
|
- :placeholder="`请输入${row.title}`"
|
|
|
- ></el-input>
|
|
|
+ v-else
|
|
|
+ v-model.number="item.value"
|
|
|
+ :placeholder="
|
|
|
+ item.formula
|
|
|
+ ? item.formula.replace(/[\[\]]/g, '')
|
|
|
+ : `请输入${item.title}`
|
|
|
+ "
|
|
|
+ :disabled="Boolean(item.formula)"
|
|
|
+ @input="handleDataChange(item, row)"
|
|
|
+ >
|
|
|
+ <template slot="append">
|
|
|
+ <div style="width: 80px; height: 100%">
|
|
|
+ <DictSelection
|
|
|
+ v-model="item.unit"
|
|
|
+ dictName="工艺参数单位"
|
|
|
+ placeholder="单位"
|
|
|
+ >
|
|
|
+ </DictSelection>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
</div>
|
|
|
</template>
|
|
|
</ele-pro-table>
|
|
|
@@ -30,19 +49,91 @@
|
|
|
v-if="
|
|
|
statisticsType == 2 &&
|
|
|
localDetails[1] &&
|
|
|
- localDetails[1].statisticsValues
|
|
|
+ localDetails[1].statisticsValue
|
|
|
"
|
|
|
ref="table"
|
|
|
:columns="typeTwoColumns"
|
|
|
- :datasource="localDetails[1].statisticsValues"
|
|
|
- cacheKey="mes-statistics-type-2511041946"
|
|
|
+ :datasource="localDetails[1].statisticsValue"
|
|
|
+ cacheKey="mes-statistics-type-2-2511041946"
|
|
|
+ :needPage="false"
|
|
|
+ >
|
|
|
+ <template
|
|
|
+ v-for="(item, index) in localDetails[1].statisticsValue[0]"
|
|
|
+ v-slot:[`column_${index}`]="{ row }"
|
|
|
+ >
|
|
|
+ <div :key="`column_${index}`" class="column_content">
|
|
|
+ <div v-if="row[index].isOnlyShow">
|
|
|
+ {{ row[index].value }}
|
|
|
+ </div>
|
|
|
+ <el-input
|
|
|
+ v-else
|
|
|
+ v-model.number="row[index].value"
|
|
|
+ :placeholder="
|
|
|
+ row[index].formula
|
|
|
+ ? row[index].formula.replace(/[\[\]]/g, '')
|
|
|
+ : `请输入${row[index].title}`
|
|
|
+ "
|
|
|
+ :disabled="Boolean(row[index].formula)"
|
|
|
+ @input="handleDataChange(row[index], row)"
|
|
|
+ >
|
|
|
+ <template v-if="!row[index].isOnlyShow" slot="append">
|
|
|
+ <div style="width: 80px; height: 100%">
|
|
|
+ <DictSelection
|
|
|
+ v-model="row[index].unit"
|
|
|
+ dictName="工艺参数单位"
|
|
|
+ placeholder="单位"
|
|
|
+ >
|
|
|
+ </DictSelection>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+
|
|
|
+ <ele-pro-table
|
|
|
+ v-if="
|
|
|
+ statisticsType == 3 &&
|
|
|
+ localDetails[2] &&
|
|
|
+ localDetails[2].statisticsValue
|
|
|
+ "
|
|
|
+ ref="table"
|
|
|
+ :columns="typeThreeColumns"
|
|
|
+ :datasource="localDetails[2].statisticsValue"
|
|
|
+ cacheKey="mes-statistics-type-3-2511051037"
|
|
|
:needPage="false"
|
|
|
>
|
|
|
<template
|
|
|
- v-for="(item, index) in localDetails[0].statisticsValues[0]"
|
|
|
+ v-for="(item, index) in localDetails[2].statisticsValue[0]"
|
|
|
v-slot:[`column_${index}`]="{ row }"
|
|
|
>
|
|
|
- <div :key="`column_${index}`"> {{ `column_${index}` }}</div>
|
|
|
+ <div :key="`column_${index}`" class="column_content">
|
|
|
+ <div v-if="row[index].isOnlyShow">
|
|
|
+ {{ row[index].value }}
|
|
|
+ </div>
|
|
|
+ <el-input
|
|
|
+ v-else
|
|
|
+ v-model.number="row[index].value"
|
|
|
+ :placeholder="
|
|
|
+ row[index].formula
|
|
|
+ ? row[index].formula.replace(/[\[\]]/g, '')
|
|
|
+ : `请输入${row[index].title}`
|
|
|
+ "
|
|
|
+ :disabled="Boolean(row[index].formula)"
|
|
|
+ @input="handleDataChange(row[index], row)"
|
|
|
+ >
|
|
|
+ <template v-if="!row[index].isOnlyShow" slot="append">
|
|
|
+ <div style="width: 80px; height: 100%">
|
|
|
+ <DictSelection
|
|
|
+ v-model="row[index].unit"
|
|
|
+ dictName="工艺参数单位"
|
|
|
+ placeholder="单位"
|
|
|
+ >
|
|
|
+ </DictSelection>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</ele-pro-table>
|
|
|
</div>
|
|
|
@@ -78,6 +169,11 @@
|
|
|
produceTaskId: {
|
|
|
type: [String, Number],
|
|
|
required: true
|
|
|
+ },
|
|
|
+ //ruleId
|
|
|
+ ruleId: {
|
|
|
+ type: [String, Number],
|
|
|
+ required: true
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
@@ -88,6 +184,16 @@
|
|
|
// const details = JSON.parse(JSON.stringify(val));
|
|
|
this.buildDetials(val);
|
|
|
}
|
|
|
+ },
|
|
|
+ // ruleId变化时修改
|
|
|
+ ruleId: {
|
|
|
+ handler(val) {
|
|
|
+ this.localDetails.forEach((item) => {
|
|
|
+ item.ruleId = val;
|
|
|
+ });
|
|
|
+ this.emitChange();
|
|
|
+ },
|
|
|
+ immediate: true
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
@@ -95,10 +201,10 @@
|
|
|
typeOneColumns() {
|
|
|
if (
|
|
|
this.localDetails.length > 0 &&
|
|
|
- this.localDetails[0].statisticsValues &&
|
|
|
+ this.localDetails[0].statisticsValue &&
|
|
|
this.localDetails[0].statisticsType == 1
|
|
|
) {
|
|
|
- return this.localDetails[0].statisticsValues[0].map((item, index) => {
|
|
|
+ return this.localDetails[0].statisticsValue[0].map((item, index) => {
|
|
|
return {
|
|
|
label: item.title,
|
|
|
slot: `column_${index}`,
|
|
|
@@ -113,10 +219,10 @@
|
|
|
typeTwoColumns() {
|
|
|
if (
|
|
|
this.localDetails.length > 1 &&
|
|
|
- this.localDetails[1].statisticsValues &&
|
|
|
+ this.localDetails[1].statisticsValue &&
|
|
|
this.localDetails[1].statisticsType == 2
|
|
|
) {
|
|
|
- return this.localDetails[1].statisticsValues[0].map((item, index) => {
|
|
|
+ return this.localDetails[1].statisticsValue[0].map((item, index) => {
|
|
|
return {
|
|
|
label: item.title,
|
|
|
slot: `column_${index}`,
|
|
|
@@ -131,10 +237,10 @@
|
|
|
typeThreeColumns() {
|
|
|
if (
|
|
|
this.localDetails.length > 2 &&
|
|
|
- this.localDetails[2].statisticsValues &&
|
|
|
+ this.localDetails[2].statisticsValue &&
|
|
|
this.localDetails[2].statisticsType == 3
|
|
|
) {
|
|
|
- return this.localDetails[2].statisticsValues[0].map((item, index) => {
|
|
|
+ return this.localDetails[2].statisticsValue[0].map((item, index) => {
|
|
|
return {
|
|
|
label: item.title,
|
|
|
slot: `column_${index}`,
|
|
|
@@ -155,6 +261,7 @@
|
|
|
methods: {
|
|
|
// 通知父组件数据变更
|
|
|
emitChange() {
|
|
|
+ console.log('this.localDetails', this.localDetails);
|
|
|
this.$emit('update:details', this.localDetails);
|
|
|
},
|
|
|
// 构建统计数据
|
|
|
@@ -164,60 +271,71 @@
|
|
|
return;
|
|
|
}
|
|
|
// 已构建过统计数据
|
|
|
- if (detials[0].statisticsValues) {
|
|
|
+ if (detials[0].statisticsValue) {
|
|
|
return (this.localDetails = JSON.parse(JSON.stringify(detials)));
|
|
|
}
|
|
|
// 构建统计数据
|
|
|
const list = JSON.parse(JSON.stringify(detials));
|
|
|
|
|
|
- const ruleId = list.map((i) => i.ruleId)[0];
|
|
|
-
|
|
|
- // 工序任务列表
|
|
|
- const routingTaskList = await getAllRoutingTaskList({
|
|
|
- routingId: this.routingId
|
|
|
- });
|
|
|
+ // 并发请求工序任务列表与物料数据
|
|
|
+ const [routingTaskList, pickAndFeed] = await Promise.all([
|
|
|
+ getAllRoutingTaskList({ routingId: this.routingId }),
|
|
|
+ getPickAndFeed({
|
|
|
+ workOrderId: this.workOrderId,
|
|
|
+ produceTaskId: this.produceTaskId
|
|
|
+ })
|
|
|
+ ]);
|
|
|
|
|
|
- // 物料获取
|
|
|
- const pickAndFeed = await getPickAndFeed({
|
|
|
- workOrderId: this.workOrderId,
|
|
|
- produceTaskId: this.produceTaskId
|
|
|
- });
|
|
|
console.log('routingTaskList 工序数据', routingTaskList);
|
|
|
console.log('物料数据 pickAndFeed', pickAndFeed);
|
|
|
|
|
|
this.localDetails = [
|
|
|
{
|
|
|
- ruleId,
|
|
|
+ ruleId: this.ruleId,
|
|
|
statisticsType: 1,
|
|
|
- statisticsValues: [
|
|
|
+ statisticsValue: [
|
|
|
list
|
|
|
.filter((i) => i.statisticsType == 1)
|
|
|
.map((i) => {
|
|
|
return {
|
|
|
title: i.paramValue,
|
|
|
- value: '',
|
|
|
+ value: i.defaultValue,
|
|
|
formula: i.formula,
|
|
|
- unit: ''
|
|
|
+ unit: i.unitName
|
|
|
};
|
|
|
})
|
|
|
]
|
|
|
},
|
|
|
// 物料统计
|
|
|
{
|
|
|
- ruleId,
|
|
|
+ ruleId: this.ruleId,
|
|
|
statisticsType: 2,
|
|
|
- statisticsValues: pickAndFeed.map((pick) => {
|
|
|
+ statisticsValue: pickAndFeed.map((pick) => {
|
|
|
const data = list
|
|
|
.filter((i) => i.statisticsType == 2)
|
|
|
.map((i) => {
|
|
|
return {
|
|
|
title: i.paramValue,
|
|
|
- value: '',
|
|
|
+ value: i.defaultValue,
|
|
|
formula: i.formula,
|
|
|
- unit: ''
|
|
|
+ unit: i.unitName
|
|
|
};
|
|
|
});
|
|
|
|
|
|
+ data.unshift({
|
|
|
+ title: '领用量',
|
|
|
+ value: pick.pickQuantity,
|
|
|
+ formula: '',
|
|
|
+ unit: pick.pickUnit
|
|
|
+ });
|
|
|
+
|
|
|
+ data.unshift({
|
|
|
+ title: '使用量',
|
|
|
+ value: pick.feedQuantity,
|
|
|
+ formula: '',
|
|
|
+ unit: pick.feedUnit
|
|
|
+ });
|
|
|
+
|
|
|
data.unshift({
|
|
|
title: '物料编码',
|
|
|
value: pick.categoryCode,
|
|
|
@@ -241,16 +359,184 @@
|
|
|
},
|
|
|
// 工序统计
|
|
|
{
|
|
|
- ruleId,
|
|
|
+ ruleId: this.ruleId,
|
|
|
statisticsType: 3,
|
|
|
- statisticsValues: []
|
|
|
+ statisticsValue: routingTaskList.list.map((task) => {
|
|
|
+ const data = list
|
|
|
+ .filter((i) => i.statisticsType == 3)
|
|
|
+ .map((i) => {
|
|
|
+ return {
|
|
|
+ title: i.paramValue,
|
|
|
+ value: i.defaultValue,
|
|
|
+ formula: i.formula,
|
|
|
+ unit: i.unitName
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ data.unshift({
|
|
|
+ title: '工序名称',
|
|
|
+ value: task.taskInstanceName,
|
|
|
+ formula: '',
|
|
|
+ unit: '',
|
|
|
+ // 仅展示不计算和输入
|
|
|
+ isOnlyShow: true
|
|
|
+ });
|
|
|
+
|
|
|
+ return data;
|
|
|
+ })
|
|
|
}
|
|
|
];
|
|
|
console.log('this.localDetails', this.localDetails);
|
|
|
|
|
|
// 通知父组件数据变更
|
|
|
this.emitChange();
|
|
|
+ },
|
|
|
+ // 当数据变化时计算公式
|
|
|
+ handleDataChange(item, row) {
|
|
|
+ // todo 计算公式
|
|
|
+ console.log('item', item);
|
|
|
+ console.log('row', row);
|
|
|
+ // 寻找当前行的所有可计算项,进行计算更新
|
|
|
+ const formulaItmes = row.filter((i) => i.formula);
|
|
|
+
|
|
|
+ formulaItmes.forEach((formulaItem) => {
|
|
|
+ // 简单示例 公式为 [(][合格品量][+][取样量][)][/][接收量] 其中合格品量、取样量、接收量为标题在row中寻找对应value进行计算
|
|
|
+ const tokens = formulaItem.formula
|
|
|
+ ? formulaItem.formula.match(/\[([^\]]+)\]/g) || []
|
|
|
+ : [];
|
|
|
+ let expr = '';
|
|
|
+ let invalid = false;
|
|
|
+ for (const token of tokens) {
|
|
|
+ if (invalid) break;
|
|
|
+ const content = token.slice(1, -1).trim();
|
|
|
+ if (['+', '-', '*', '/', '(', ')', '%'].includes(content)) {
|
|
|
+ expr += content;
|
|
|
+ } else {
|
|
|
+ const target = row.find((r) => r.title === content);
|
|
|
+ // 若对应的项不存在 或 value为空 则取消计算
|
|
|
+ if (
|
|
|
+ !target ||
|
|
|
+ target.value === '' ||
|
|
|
+ target.value === null ||
|
|
|
+ target.value === undefined
|
|
|
+ ) {
|
|
|
+ invalid = true;
|
|
|
+ expr = ''; // 置空表达式,后续将不计算
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ const num = Number(target.value);
|
|
|
+ if (!Number.isFinite(num)) {
|
|
|
+ invalid = true;
|
|
|
+ expr = '';
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ expr += num;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 若无效则直接跳过后续计算 (expr 已被置空)
|
|
|
+ if (/^[0-9+\-*/().\s]+$/.test(expr)) {
|
|
|
+ try {
|
|
|
+ console.log('expr', expr);
|
|
|
+ const result = Function(`"use strict";return (${expr})`)();
|
|
|
+ let value =
|
|
|
+ Number.isFinite(result) && !Number.isNaN(result) ? result : '';
|
|
|
+
|
|
|
+ if (value) {
|
|
|
+ // 如果超出4位小数 则保留4位小数
|
|
|
+ if (typeof value === 'number') {
|
|
|
+ const dec = value.toString().split('.')[1];
|
|
|
+ if (dec && dec.length > 4) {
|
|
|
+ const parts = value.toString().split('.');
|
|
|
+ if (parts[1] && parts[1].length > 4) {
|
|
|
+ value = Number(parts[0] + '.' + parts[1].slice(0, 4));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ formulaItem.value = value;
|
|
|
+ } catch (e) {
|
|
|
+ formulaItem.value = '';
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ formulaItem.value = '';
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 通知父组件数据变更
|
|
|
+ this.emitChange();
|
|
|
+ },
|
|
|
+ // 验证所有行/列(排除仅展示项),收集缺失项并提示;返回布尔值
|
|
|
+ validateStatisticsFilled() {
|
|
|
+ if (
|
|
|
+ !Array.isArray(this.localDetails) ||
|
|
|
+ this.localDetails.length === 0
|
|
|
+ ) {
|
|
|
+ this.$message.error('统计数据为空');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const typeNameMap = {
|
|
|
+ 1: '成品统计',
|
|
|
+ 2: '物料统计',
|
|
|
+ 3: '工序统计'
|
|
|
+ };
|
|
|
+
|
|
|
+ const missing = [];
|
|
|
+
|
|
|
+ this.localDetails.forEach((detail, detailIndex) => {
|
|
|
+ if (!detail || !detail.statisticsValue) return;
|
|
|
+
|
|
|
+ // 统一转成多行结构
|
|
|
+ const rows =
|
|
|
+ detail.statisticsType === 1
|
|
|
+ ? [detail.statisticsValue[0] || []]
|
|
|
+ : detail.statisticsValue || [];
|
|
|
+
|
|
|
+ rows.forEach((row, rowIndex) => {
|
|
|
+ // 记录行的标识(物料名称 / 工序名称)
|
|
|
+ let rowLabel = '';
|
|
|
+ if (detail.statisticsType === 2) {
|
|
|
+ const nameItem = row.find((i) => i && i.title === '物料名称');
|
|
|
+ if (nameItem)
|
|
|
+ rowLabel = `(${nameItem.value || '物料行' + (rowIndex + 1)})`;
|
|
|
+ } else if (detail.statisticsType === 3) {
|
|
|
+ const procItem = row.find((i) => i && i.title === '工序名称');
|
|
|
+ if (procItem)
|
|
|
+ rowLabel = `(${procItem.value || '工序行' + (rowIndex + 1)})`;
|
|
|
+ }
|
|
|
+
|
|
|
+ row.forEach((item, colIndex) => {
|
|
|
+ if (!item || item.isOnlyShow) return;
|
|
|
+ const val = item.value;
|
|
|
+ if (val === '' || val === null || val === undefined) {
|
|
|
+ missing.push(
|
|
|
+ `${typeNameMap[detail.statisticsType]}${rowLabel} - 第${
|
|
|
+ colIndex + 1
|
|
|
+ }列【${item.title}】未填写`
|
|
|
+ );
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ if (missing.length > 0) {
|
|
|
+ // 过多时截断
|
|
|
+ const displayList =
|
|
|
+ missing.length > 1
|
|
|
+ ? missing.slice(0, 1).concat(`, 共有 ${missing.length} 项未填写`)
|
|
|
+ : missing;
|
|
|
+ this.$message.warning(`请完善统计数据:\n${displayList.join('\n')}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .column_content ::v-deep .el-input-group__append {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+</style>
|