| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128 |
- <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"
- show-summary
- :summary-method="getSummaries"
- >
- <template v-slot:toolkit>
- <el-button
- v-if="localDetails[0].statisticsValue[0]"
- type="primary"
- @click="addRow"
- >
- 添加
- </el-button>
- </template>
- <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="row[index].paramType == 7 || row[index].isOnlyShow">
- {{ row[index].value }}
- </div>
- <!-- 数值 -->
- <template v-else-if="row[index].paramType == 1">
- <div style="display: flex">
- <el-input-number
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- :disabled="Boolean(row[index].formula)"
- :controls="false"
- @change="handleDataChange(row)"
- ></el-input-number>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 公式 -->
- <template v-else-if="row[index].paramType == 9">
- <div style="display: flex">
- <el-input
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- disabled
- @input="handleDataChange(row)"
- ></el-input>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 时间 -->
- <el-date-picker
- v-else-if="row[index].paramType == 5"
- v-model="row[index].value"
- type="datetime"
- placeholder="选择日期时间"
- style="width: 100%"
- >
- </el-date-picker>
- <!-- 其他 -->
- <el-input
- v-else
- v-model="row[index].value"
- placeholder="请输入内容"
- ></el-input>
- </div>
- </template>
- <template v-slot:operation="{ row }">
- <el-button type="text" size="small" @click="deleteRow(row)"
- >删除</el-button
- >
- </template>
- </ele-pro-table>
- <div
- v-if="
- statisticsType == 1 &&
- localDetails[0] &&
- localDetails[0].statisticsValue
- "
- class=""
- >
- </div>
- <ele-pro-table
- v-if="
- statisticsType == 2 &&
- localDetails[1] &&
- localDetails[1].statisticsValue
- "
- ref="table"
- :columns="typeTwoColumns"
- :datasource="typeTwoDataSource"
- cacheKey="mes-statistics-type-2-2511041946"
- :needPage="false"
- >
- <template v-slot:toolkit>
- <div>
- <el-input
- v-model="keyword"
- placeholder="请输入物料名称或编码"
- ></el-input>
- </div>
- </template>
- <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].paramType == 7 || row[index].isOnlyShow">
- {{ row[index].value }}
- </div>
- <!-- 数值 -->
- <template v-else-if="row[index].paramType == 1">
- <div style="display: flex">
- <el-input-number
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- :disabled="Boolean(row[index].formula)"
- :controls="false"
- @change="handleDataChange(row)"
- ></el-input-number>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 公式 -->
- <template v-else-if="row[index].paramType == 9">
- <div style="display: flex">
- <el-input
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- disabled
- @input="handleDataChange(row)"
- ></el-input>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 时间 -->
- <el-date-picker
- v-else-if="row[index].paramType == 5"
- v-model="row[index].value"
- type="datetime"
- placeholder="选择日期时间"
- style="width: 100%"
- >
- </el-date-picker>
- <!-- 其他 -->
- <el-input
- v-else
- v-model="row[index].value"
- placeholder="请输入内容"
- ></el-input>
- </div>
- </template>
- </ele-pro-table>
- <ele-pro-table
- v-if="
- statisticsType == 3 &&
- localDetails[2] &&
- localDetails[2].statisticsValue
- "
- ref="table"
- :columns="typeThreeColumns"
- :datasource="typeThreeDataSource"
- cacheKey="mes-statistics-type-3-2511051037"
- :needPage="false"
- >
- <template v-slot:toolkit>
- <el-select
- v-model="activeProductTaskName"
- placeholder="请选择"
- style="margin-right: 20px"
- >
- <el-option
- v-for="item in productTaskNameList"
- :key="item"
- :label="item"
- :value="item"
- >
- </el-option>
- </el-select>
- </template>
- <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].paramType == 7 || row[index].isOnlyShow">
- {{ row[index].value }}
- </div>
- <!-- 数值 -->
- <template v-else-if="row[index].paramType == 1">
- <div style="display: flex">
- <el-input
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- :disabled="Boolean(row[index].formula)"
- :controls="false"
- @change="handleDataChange(row)"
- ></el-input>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 公式 -->
- <template v-else-if="row[index].paramType == 9">
- <div style="display: flex">
- <el-input
- v-model="row[index].value"
- :placeholder="
- row[index].formula
- ? row[index].formula.replace(/[\[\]]/g, '')
- : `请输入${row[index].title}`
- "
- disabled
- @input="handleDataChange(row)"
- ></el-input>
- <div style="width: 80px; height: 100%; flex-shrink: 0">
- <DictSelection
- v-model="row[index].unit"
- dictName="工艺参数单位"
- placeholder="单位"
- >
- </DictSelection
- ></div>
- </div>
- </template>
- <!-- 时间 -->
- <el-date-picker
- v-else-if="row[index].paramType == 5"
- v-model="row[index].value"
- type="datetime"
- placeholder="选择日期时间"
- style="width: 100%"
- >
- </el-date-picker>
- <!-- 其他 -->
- <el-input
- v-else
- v-model="row[index].value"
- placeholder="请输入内容"
- ></el-input>
- </div>
- </template>
- </ele-pro-table>
- </div>
- </template>
- <script>
- import {
- listProduceTaskAndFormNum,
- getPickAndFeed,
- inStoreageRecord
- } from '@/api/producetaskrecordrulesrecord/index';
- export default {
- props: {
- details: {
- type: Array,
- default: () => []
- },
- // 规则详情列表
- rulesDetailList: {
- type: Array,
- default: () => []
- },
- statisticsType: {
- type: String,
- default: '1' // 1-成品统计,2-物料统计,3-工序统计
- },
- // 工艺路线id
- routingId: {
- type: [String, Number],
- default: ''
- },
- // workOrderId 工单id
- workOrderId: {
- type: [String, Number],
- default: ''
- },
- // bomCategoryId
- bomCategoryId: {
- type: [String, Number],
- default: ''
- },
- //ruleId
- ruleId: {
- type: [String, Number],
- default: ''
- },
- // 单位
- unit: {
- type: String,
- default: ''
- },
- // 要求生产数量
- formingNum: {
- type: [Number],
- default: 0
- }
- },
- watch: {
- details: {
- deep: true,
- immediate: true,
- handler(val) {
- // 已构建过统计数据
- if (
- val[0] &&
- val[0].statisticsValue &&
- val[0].statisticsValue.length > 0
- ) {
- this.localDetails = JSON.parse(JSON.stringify(val));
- } else {
- this.buildDetials(val);
- }
- }
- }
- },
- computed: {
- // 成品统计表头
- typeOneColumns() {
- if (
- this.localDetails.length > 0 &&
- this.localDetails[0].statisticsValue &&
- this.localDetails[0].statisticsType == 1 &&
- this.localDetails[0].statisticsValue[0]
- ) {
- const cList = this.localDetails[0].statisticsValue[0].map(
- (item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center',
- minWidth: 200
- };
- }
- );
- cList.unshift({
- width: 50,
- type: 'index',
- columnKey: 'index',
- align: 'center'
- });
- cList.push({
- width: 70,
- label: '操作',
- slot: 'operation',
- fixed: 'right'
- });
- return cList;
- } else {
- return [];
- }
- },
- // 物料统计表头
- typeTwoColumns() {
- if (
- this.localDetails.length > 1 &&
- this.localDetails[1].statisticsValue &&
- this.localDetails[1].statisticsType == 2 &&
- this.localDetails[1].statisticsValue[0]
- ) {
- const cList = this.localDetails[1].statisticsValue[0].map(
- (item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center',
- minWidth: 200
- };
- }
- );
- cList.unshift({
- width: 45,
- type: 'index',
- columnKey: 'index',
- align: 'center'
- });
- return cList;
- } else {
- return [];
- }
- },
- // 工序统计表头
- typeThreeColumns() {
- if (
- this.localDetails.length > 2 &&
- this.localDetails[2].statisticsValue &&
- this.localDetails[2].statisticsType == 3 &&
- this.localDetails[2].statisticsValue[0]
- ) {
- const cList = this.localDetails[2].statisticsValue[0].map(
- (item, index) => {
- return {
- label: item.title,
- slot: `column_${index}`,
- align: 'center',
- minWidth: 200
- };
- }
- );
- cList.unshift({
- width: 45,
- type: 'index',
- columnKey: 'index',
- align: 'center'
- });
- return cList;
- } else {
- return [];
- }
- },
- // 工序名称列表
- productTaskNameList() {
- if (this.localDetails[2]?.statisticsValue) {
- const list = this.localDetails[2].statisticsValue.map((rows) => {
- return rows.find((i) => i.title === '工序名称')?.value || '';
- });
- list.unshift('全部'); // 全部选项
- return list;
- } else {
- return [];
- }
- },
- typeTwoDataSource() {
- if (this.keyword.trim() === '') {
- return this.localDetails[1].statisticsValue;
- } else {
- return this.localDetails[1].statisticsValue.filter((rows) => {
- const nameItem = rows.find((i) => i.title === '物料名称');
- const codeItem = rows.find((i) => i.title === '物料编码');
- return (
- (nameItem && nameItem.value.includes(this.keyword)) ||
- (codeItem && codeItem.value.includes(this.keyword))
- );
- });
- }
- },
- typeThreeDataSource() {
- if (this.activeProductTaskName === '全部') {
- return this.localDetails[2].statisticsValue;
- } else {
- return this.localDetails[2].statisticsValue.filter((rows) => {
- const nameItem = rows.find((i) => i.title === '工序名称');
- return nameItem && nameItem.value === this.activeProductTaskName;
- });
- }
- }
- },
- data() {
- return {
- localDetails: [
- {
- ruleId: this.ruleId,
- statisticsType: 1,
- statisticsValue: []
- },
- {
- ruleId: this.ruleId,
- statisticsType: 2,
- statisticsValue: []
- },
- {
- ruleId: this.ruleId,
- statisticsType: 3,
- statisticsValue: []
- }
- ],
- activeProductTaskName: '全部',
- keyword: '',
- _emitChangeTimer: null
- };
- },
- methods: {
- // 通知父组件数据变更(防抖 500ms)
- emitChange() {
- if (this._emitChangeTimer) clearTimeout(this._emitChangeTimer);
- this._emitChangeTimer = setTimeout(() => {
- this.$emit('update:details', this.localDetails);
- }, 500);
- },
- // 构建统计数据
- async buildDetials(detials) {
- if (detials.length === 0) {
- console.log('重置了');
- this.localDetails = [
- {
- ruleId: this.ruleId,
- statisticsType: 1,
- statisticsValue: []
- },
- {
- ruleId: this.ruleId,
- statisticsType: 2,
- statisticsValue: []
- },
- {
- ruleId: this.ruleId,
- statisticsType: 3,
- statisticsValue: []
- }
- ];
- return;
- }
- console.log('this.localDetails -- 开始构建', this.localDetails);
- if (
- (this.localDetails[0].statisticsValue &&
- this.localDetails[0].statisticsValue.length == 0) ||
- this.localDetails[0].ruleId != this.ruleId
- ) {
- const list = this.rulesDetailList
- .filter((i) => i.statisticsType == 1)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- unit: i.unitName || this.unit,
- paramType: i.paramType
- };
- });
- // 已构建过统计数据
- this.localDetails[0] = {
- ...this.localDetails[0],
- ruleId: this.ruleId,
- statisticsType: 1,
- statisticsValue: [list]
- };
- }
- // 构建物料和工序数据
- this.rebuildMaterialAndProcessData();
- },
- // 构建物料和工序数据
- async rebuildMaterialAndProcessData() {
- if (this.unit) {
- // 存在单位则 更新statisticsType为1的单位
- this.localDetails[0].statisticsValue.forEach((rows) => {
- rows.forEach((item) => {
- if (!item.unit) {
- item.unit = this.unit;
- }
- });
- });
- }
- let routingTaskList = [];
- let pickAndFeed = [];
- if (this.workOrderId) {
- // 并发获取工序任务列表和物料数据
- let [routingTaskReslut, pickAndFeedList, inStoreageRecordList] =
- await Promise.all([
- listProduceTaskAndFormNum({ workOrderId: this.workOrderId }),
- getPickAndFeed({
- workOrderId: this.workOrderId,
- bomCategoryId: this.bomCategoryId
- }),
- inStoreageRecord({
- workOrderId: this.workOrderId
- })
- ]);
- pickAndFeed = pickAndFeedList || [];
- routingTaskList = routingTaskReslut || [];
- console.log('inStoreageRecordList', inStoreageRecordList);
- if (inStoreageRecordList && inStoreageRecordList.length > 0) {
- // 成品入库一行的记录
- const rows = this.rulesDetailList
- .filter((i) => i.statisticsType == 1)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- unit: i.unitName || this.unit,
- paramType: i.paramType
- };
- });
- if (this.rulesDetailList.length > 0) {
- const list = inStoreageRecordList.map((record) => {
- const rowsCopy = JSON.parse(JSON.stringify(rows));
- rowsCopy.forEach((item) => {
- if (item.title.includes('成品入库数')) {
- item.value = record.totalCount;
- } else if (
- item.title === '成品入库时间' ||
- item.title === '入库时间' ||
- item.title === '生产日期'
- ) {
- item.value = record.storageTime;
- }
- item.unit = record.measuringUnit || this.unit;
- });
- return rowsCopy;
- });
- // 赋值
- this.localDetails[0].statisticsValue = list;
- console.log(
- 'this.localDetails[0].statisticsValue 重新',
- this.localDetails[0].statisticsValue
- );
- }
- }
- }
- console.log('routingTaskList 工序数据', routingTaskList);
- console.log('物料数据 pickAndFeed', pickAndFeed);
- this.localDetails[1] = {
- ...this.localDetails[1],
- ruleId: this.ruleId,
- statisticsType: 2,
- statisticsValue: pickAndFeed.map((pick) => {
- const data = this.rulesDetailList
- .filter((i) => i.statisticsType == 2)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- unit: i.unitName || this.unit,
- paramType: i.paramType
- };
- });
- data.unshift({
- title: '领用量',
- value: pick.pickQuantity,
- formula: '',
- unit: pick.pickUnit,
- // 1数值类型
- paramType: 1
- });
- data.unshift({
- title: '使用量',
- value: pick.feedQuantity,
- formula: '',
- unit: pick.feedUnit,
- // 1数值类型
- paramType: 1
- });
- data.unshift({
- title: '物料编码',
- value: pick.categoryCode,
- formula: '',
- unit: '',
- // 仅展示不计算和输入
- isOnlyShow: true,
- // 7文本类型
- paramType: 7
- });
- data.unshift({
- title: '物料名称',
- value: pick.categoryName,
- formula: '',
- unit: '',
- // 仅展示不计算和输入
- isOnlyShow: true,
- // produceTaskName 工序名称
- produceTaskName: pick.produceTaskName,
- // 7文本类型
- paramType: 7
- });
- return data;
- })
- };
- this.localDetails[2] = {
- ...this.localDetails[2],
- ruleId: this.ruleId,
- statisticsType: 3,
- statisticsValue: routingTaskList.map((task) => {
- const data = this.rulesDetailList
- .filter((i) => i.statisticsType == 3)
- .map((i) => {
- return {
- title: i.paramValue,
- value: i.defaultValue,
- formula: i.formula,
- unit: i.unitName || this.unit,
- paramType: i.paramType
- };
- });
- data.unshift({
- title: '工序名称',
- value: task.name || task.produceTaskName,
- formula: '',
- unit: '',
- // 仅展示不计算和输入
- isOnlyShow: true,
- // 7文本类型
- paramType: 7
- });
- // 接收
- if (!isNaN(task.formedNum)) {
- const item = data.find(
- (i) => i.title === '接收量' || i.title === '接收数量'
- );
- if (item) {
- item.value = task.formedNum;
- }
- }
- // 不合格品
- if (!isNaN(task.noQualifiedSum)) {
- const item = data.find(
- (i) => i.title == '不合格品量' || i.title == '不合格品数量'
- );
- if (item) {
- item.value = task.noQualifiedSum;
- }
- }
- // 合格品量
- if (!isNaN(task.qualified)) {
- const item = data.find(
- (i) => i.title == '合格品量' || i.title == '合格品数量'
- );
- if (item) {
- item.value = task.qualified;
- }
- }
- return data;
- })
- };
- console.log('this.localDetails', this.localDetails);
- // 通知父组件数据变更
- this.emitChange();
- },
- // 当数据变化时计算公式
- handleDataChange(row) {
- console.log('row 触发', row);
- // todo 计算公式
- // 寻找当前行的所有可计算项,进行计算更新
- const formulaItmes = row.filter((i) => i.formula);
- formulaItmes.forEach((formulaItem) => {
- // 简单示例 公式为 [(][合格品量][+][取样量][)][/][接收量]
- 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);
- // 特殊处理:当标题为“要求生产数量”且当前行未找到对应项时,用 this.formingNum 参与计算
- if (!target && content === '要求生产数量') {
- const formingVal = Number(this.formingNum);
- if (!Number.isFinite(formingVal)) {
- invalid = true;
- expr = '';
- break;
- }
- expr += formingVal;
- continue;
- }
- // 若对应的项不存在 或 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 (expr && /^[0-9+\-*/().\s]+$/.test(expr)) {
- try {
- const result = Function(`"use strict";return (${expr})`)();
- let value =
- Number.isFinite(result) && !Number.isNaN(result) ? result : '';
- if (value !== '' && typeof value === 'number') {
- const dec = value.toString().split('.')[1];
- if (dec && dec.length > 4) {
- const intPart = Math.trunc(value);
- const truncated = dec.slice(0, 4);
- value = Number(intPart + '.' + truncated);
- }
- }
- formulaItem.value = value;
- } catch (e) {
- formulaItem.value = '';
- }
- } else {
- formulaItem.value = '';
- }
- });
- // 通知父组件数据变更
- this.emitChange();
- },
- // 验证所有行/列(排除仅展示项),收集缺失项并提示;返回布尔值
- validateStatisticsFilled() {
- return true; // 暂时不验证
- 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;
- },
- getSummaries() {
- const sums = [];
- // 优化:缓存公式解析、统一数值获取,减少重复正则与遍历
- if (
- !this.localDetails[0] ||
- !this.localDetails[0].statisticsValue ||
- this.localDetails[0].statisticsValue.length === 0
- ) {
- return sums;
- }
- const rows = this.localDetails[0].statisticsValue;
- const firstRow = rows[0] || [];
- sums.push('合计');
- // 缓存结构:{ formulaString: [{type:'op'|'num'|'ref', value:'+'|number|'列标题'}] }
- if (!this._formulaCache) this._formulaCache = Object.create(null);
- const parseFormula = (formula) => {
- if (!formula) return [];
- if (this._formulaCache[formula]) return this._formulaCache[formula];
- // 提取 [xxx] 片段
- const rawTokens = formula.match(/\[([^\]]+)\]/g) || [];
- const tokens = [];
- rawTokens.forEach((token) => {
- const content = token.slice(1, -1).trim();
- // 运算符/括号
- if (['+', '-', '*', '/', '(', ')', '%'].includes(content)) {
- tokens.push({ type: 'op', value: content });
- } else {
- // 引用列标题或特殊内置
- tokens.push({ type: 'ref', value: content });
- }
- });
- this._formulaCache[formula] = tokens;
- return tokens;
- };
- // 辅助:安全数值
- const toNum = (v) => {
- const n = Number(v);
- return Number.isFinite(n) ? n : 0;
- };
- // 先累加所有“非公式数值列”
- rows.forEach((row) => {
- row.forEach((cell, colIndex) => {
- const targetIndex = colIndex + 1;
- if (sums[targetIndex] === undefined) sums[targetIndex] = '';
- if (cell.isOnlyShow || cell.paramType !== 1) {
- return;
- }
- if (cell.formula) {
- // 公式列占位为 0,后续再算
- if (sums[targetIndex] === '') sums[targetIndex] = 0;
- return;
- }
- const num = Number(cell.value);
- if (!Number.isFinite(num)) return;
- if (typeof sums[targetIndex] !== 'number') sums[targetIndex] = 0;
- sums[targetIndex] += num;
- });
- });
- // 计算公式列(使用上面得到的合计值作为引用值)
- firstRow.forEach((meta, colIndex) => {
- if (!meta.formula) return;
- const targetIndex = colIndex + 1;
- const tokens = parseFormula(meta.formula);
- if (!tokens.length) {
- sums[targetIndex] = '';
- return;
- }
- let expr = '';
- let invalid = false;
- for (const tk of tokens) {
- if (invalid) break;
- if (tk.type === 'op') {
- expr += tk.value;
- continue;
- }
- // 引用列/特殊引用
- if (tk.value === '要求生产数量') {
- const formingVal = Number(this.formingNum);
- if (!Number.isFinite(formingVal)) {
- invalid = true;
- } else {
- expr += formingVal;
- }
- continue;
- }
- const refColIdx = firstRow.findIndex((i) => i.title === tk.value);
- if (refColIdx === -1) {
- invalid = true;
- break;
- }
- const refVal = sums[refColIdx + 1];
- if (!Number.isFinite(Number(refVal))) {
- invalid = true;
- break;
- }
- expr += toNum(refVal);
- }
- if (invalid || !expr || !/^[0-9+\-*/().\s%]+$/.test(expr)) {
- sums[targetIndex] = '';
- return;
- }
- try {
- let result = Function('"use strict";return (' + expr + ')')();
- if (!Number.isFinite(result) || Number.isNaN(result)) {
- sums[targetIndex] = '';
- } else {
- const dec = result.toString().split('.')[1];
- if (dec && dec.length > 4) {
- const intPart = Math.trunc(result);
- const truncated = dec.slice(0, 4);
- result = Number(intPart + '.' + truncated);
- }
- sums[targetIndex] = result;
- }
- } catch (e) {
- sums[targetIndex] = '';
- }
- });
- return sums;
- },
- // 添加行
- addRow() {
- const row = JSON.parse(
- JSON.stringify(this.localDetails[0].statisticsValue[0])
- );
- row.forEach((item) => {
- if (!item.isOnlyShow) {
- item.value = '';
- }
- });
- this.localDetails[0].statisticsValue.push(row);
- },
- deleteRow(row) {
- // 最后一条不允许删除
- if (this.localDetails[0].statisticsValue.length <= 1) {
- this.$message.warning('至少保留一条统计数据');
- return;
- }
- const index = this.localDetails[0].statisticsValue.indexOf(row);
- this.localDetails[0].statisticsValue.splice(index, 1);
- // 通知父组件数据变更
- this.emitChange();
- }
- }
- };
- </script>
- <style scoped lang="scss">
- .column_content ::v-deep .el-input-group__append {
- padding: 0;
- }
- </style>
|