| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- <template>
- <div>
- <ele-pro-table
- v-if="
- statisticsType == 1 &&
- localDetails[0] &&
- localDetails[0].statisticsValue
- "
- ref="table"
- :columns="typeOneColumns"
- :datasource="localDetails[0].statisticsValue"
- cacheKey="mes-statistics-type-1-2511041946"
- :needPage="false"
- >
- <template
- v-for="(item, index) in localDetails[0].statisticsValue[0]"
- v-slot:[`column_${index}`]="{ row }"
- >
- <div :key="`column_${index}`" class="column_content">
- <div v-if="item.isOnlyShow">
- {{ item.value }}
- </div>
- <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>
- <ele-pro-table
- v-if="
- statisticsType == 2 &&
- localDetails[1] &&
- localDetails[1].statisticsValue
- "
- ref="table"
- :columns="typeTwoColumns"
- :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[2].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>
- </div>
- </template>
- <script>
- import {
- getAllRoutingTaskList,
- getPickAndFeed
- } from '@/api/producetaskrecordrulesrecord/index';
- export default {
- props: {
- details: {
- type: Array,
- default: () => []
- },
- statisticsType: {
- type: String,
- default: '1' // 1-成品统计,2-物料统计,3-工序统计
- },
- // 工艺路线id 必填
- routingId: {
- type: [String, Number],
- required: true
- },
- // workOrderId 工单id 必填
- workOrderId: {
- type: [String, Number],
- required: true
- },
- // produceTaskId 工序id
- produceTaskId: {
- type: [String, Number],
- required: true
- },
- //ruleId
- ruleId: {
- type: [String, Number],
- required: true
- }
- },
- watch: {
- details: {
- deep: true,
- immediate: true,
- handler(val) {
- // 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: {
- // 成品统计表头
- typeOneColumns() {
- if (
- this.localDetails.length > 0 &&
- this.localDetails[0].statisticsValue &&
- this.localDetails[0].statisticsType == 1
- ) {
- return this.localDetails[0].statisticsValue[0].map((item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center'
- };
- });
- } else {
- return [];
- }
- },
- // 物料统计表头
- typeTwoColumns() {
- if (
- this.localDetails.length > 1 &&
- this.localDetails[1].statisticsValue &&
- this.localDetails[1].statisticsType == 2
- ) {
- return this.localDetails[1].statisticsValue[0].map((item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center'
- };
- });
- } else {
- return [];
- }
- },
- // 工序统计表头
- typeThreeColumns() {
- if (
- this.localDetails.length > 2 &&
- this.localDetails[2].statisticsValue &&
- this.localDetails[2].statisticsType == 3
- ) {
- return this.localDetails[2].statisticsValue[0].map((item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center'
- };
- });
- } else {
- return [];
- }
- }
- },
- data() {
- return {
- localDetails: []
- };
- },
- methods: {
- // 通知父组件数据变更
- emitChange() {
- console.log('this.localDetails', this.localDetails);
- this.$emit('update:details', this.localDetails);
- },
- // 构建统计数据
- async buildDetials(detials) {
- if (detials.length === 0) {
- this.localDetails = [];
- return;
- }
- // 已构建过统计数据
- if (detials[0].statisticsValue) {
- return (this.localDetails = JSON.parse(JSON.stringify(detials)));
- }
- // 构建统计数据
- const list = JSON.parse(JSON.stringify(detials));
- // 并发请求工序任务列表与物料数据
- const [routingTaskList, pickAndFeed] = await Promise.all([
- getAllRoutingTaskList({ routingId: this.routingId }),
- getPickAndFeed({
- workOrderId: this.workOrderId,
- produceTaskId: this.produceTaskId
- })
- ]);
- console.log('routingTaskList 工序数据', routingTaskList);
- console.log('物料数据 pickAndFeed', pickAndFeed);
- this.localDetails = [
- {
- ruleId: this.ruleId,
- statisticsType: 1,
- statisticsValue: [
- list
- .filter((i) => i.statisticsType == 1)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- unit: i.unitName
- };
- })
- ]
- },
- // 物料统计
- {
- ruleId: this.ruleId,
- statisticsType: 2,
- statisticsValue: pickAndFeed.map((pick) => {
- const data = list
- .filter((i) => i.statisticsType == 2)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- 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,
- formula: '',
- unit: '',
- // 仅展示不计算和输入
- isOnlyShow: true
- });
- data.unshift({
- title: '物料名称',
- value: pick.categoryName,
- formula: '',
- unit: '',
- // 仅展示不计算和输入
- isOnlyShow: true
- });
- return data;
- })
- },
- // 工序统计
- {
- ruleId: this.ruleId,
- statisticsType: 3,
- 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>
|