Browse Source

重新报工和嘉实的生产统计报表功能完成

lucw 7 months ago
parent
commit
2915be8bc5

+ 9 - 0
src/api/producetaskrecordrulesrecord/index.js

@@ -100,3 +100,12 @@ export async function getPickAndFeed(body) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+// /mes/producetaskrulerecord/reSubmit 重新一键报工
+export async function resetSubmit(body) {
+  const res = await request.post('/mes/producetaskrulerecord/reSubmit', body);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 2 - 1
src/enum/dict.js

@@ -41,7 +41,8 @@ export default {
   流程实例的结果: 'bpm_process_instance_result',
   取样类型: 'quality_method_code',
   放行类型: 'checklist_type',
-  文档类型: 'doc_type'
+  文档类型: 'doc_type',
+  工艺参数单位: 'parameter_code'
 };
 export const numberList = ['date_method'];
 

+ 31 - 4
src/views/batchRecord/components/editModal.vue

@@ -472,7 +472,11 @@
 
         <material
           v-if="
-            form.produceTaskId && form.produceTaskInstanceId && workOrderInfo
+            form.produceTaskId &&
+            form.produceTaskInstanceId &&
+            workOrderInfo &&
+            reportWorkType == 2 &&
+            form.itemType == 2
           "
           ref="materialRef"
           :pickDetails.sync="form.pickDetails"
@@ -1572,13 +1576,36 @@
 
         console.log('materialObject 物料信息', materialObject);
         if (type == '') {
-          this.form.pickDetails = materialObject.pickDetails;
-          this.form.outputDetails = materialObject.outputDetails;
+          this.addForm.outputDetails = materialObject.outputDetails;
+          this.addForm.pickDetails = materialObject.pickDetails.map((i) => {
+            // 物料清单中如果存在则 selected 设置为 true
+            const any = this.addForm.outputDetails.find(
+              (j) => j.categoryId === i.categoryId
+            );
+            if (any) {
+              i.selected = true;
+            } else {
+              i.selected = false;
+            }
+            return i;
+          });
           this.preOutputDetails = materialObject.preOutputDetails;
         }
 
         if (type == 'output') {
-          this.form.outputDetails = materialObject.outputDetails;
+          this.addForm.outputDetails = materialObject.outputDetails;
+          this.addForm.pickDetails = this.addForm.pickDetails.map((i) => {
+            // 物料清单中如果存在则 selected 设置为 true
+            const any = this.addForm.outputDetails.find(
+              (j) => j.categoryId === i.categoryId
+            );
+            if (any) {
+              i.selected = true;
+            } else {
+              i.selected = false;
+            }
+            return i;
+          });
         }
       },
       // qualifiedQuantityMax 最大合格数量

+ 4 - 4
src/views/batchRecord/components/list.vue

@@ -242,11 +242,11 @@
             formatter: (row) => {
               switch (row.executeStatus) {
                 case 0:
-                  return '未执行';
+                  return '未报工';
                 case 1:
                   return '执行中';
                 default:
-                  return '已执行';
+                  return '已报工';
               }
             }
           },
@@ -331,7 +331,7 @@
             placeholder: '请选择',
             planList: [
               {
-                label: '未执行',
+                label: '未报工',
                 value: 0
               },
               {
@@ -339,7 +339,7 @@
                 value: 1
               },
               {
-                label: '已执行',
+                label: '已报工',
                 value: 2
               }
             ]

+ 15 - 4
src/views/produce/components/prenatalExamination/material.vue

@@ -55,7 +55,7 @@
           controls-position="right"
           :min="0"
           :max="qualifiedQuantityMax(row)"
-          @change="$emit('update:outputDetails', localOutputDetails)"
+          @change="qualifiedQuantityChange(row)"
         ></el-input-number>
       </template>
       <template v-slot:noQualifiedQuantity="{ row }">
@@ -516,13 +516,14 @@
       },
       // qualifiedQuantityMax 最大合格数量
       qualifiedQuantityMax(row) {
-        return row.reportQuantity - row.noQualifiedQuantity;
+        return row.reportQuantity;
       },
       noQualifiedQuantityMax(row) {
         return row.reportQuantity - row.qualifiedQuantity;
       },
       selectedChange(row) {
         console.log('row', row);
+        if(this.outputType != 1) return;
 
         if (row.selected) {
           // 判断是否存在
@@ -555,9 +556,14 @@
           } else {
             this.$message.warning('该物料已存在于产出清单中');
           }
-
-          this.$emit('update:outputDetails', this.localOutputDetails);
+        } else {
+          // 取消选择则删除
+          this.localOutputDetails = this.localOutputDetails.filter(
+            (item) => !(item.categoryId === row.categoryId)
+          );
         }
+
+        this.$emit('update:outputDetails', this.localOutputDetails);
       },
       // getCategoryAndLevelByCategoryId 获取产品
       async getCategoryAndLevelByCategoryId() {
@@ -745,6 +751,11 @@
         }
 
         return true;
+      },
+      // qualifiedQuantityChange 当次合格数量变化 计算 noQualifiedQuantity不合格数量
+      qualifiedQuantityChange(row) {
+        row.noQualifiedQuantity = row.reportQuantity - row.qualifiedQuantity;
+        this.$emit('update:outputDetails', this.localOutputDetails);
       }
     }
   };

+ 1 - 1
src/views/produce/components/prenatalExamination/materialModal.vue

@@ -12,7 +12,7 @@
     class="fullscreen"
   >
     <el-card shadow="never">
-      <seek-page :seekList="seekList"></seek-page>
+      <seek-page :seekList="seekList" @search="reload"></seek-page>
       <ele-pro-table
         style="min-height: 400px"
         ref="table"

+ 76 - 8
src/views/produce/components/prenatalExamination/releaseRulesDialog.vue

@@ -199,7 +199,10 @@
           </el-form-item>
         </el-col>
         <el-col :span="8">
-          <el-form-item label="产出物">
+          <el-form-item
+            v-if="addForm.produceTaskId && addForm.reportWorkType == 2"
+            label="产出物"
+          >
             <el-select
               v-model="addForm.outputType"
               placeholder="请选择产出物类型"
@@ -256,16 +259,18 @@
         </el-tab-pane>
       </el-tabs>
       <statistics
-        :details="addForm.details"
+        ref="statisticsRef"
+        :details.sync="addForm.details"
         :statisticsType="statisticsType"
         :produceTaskId="addForm.produceTaskId"
         :workOrderId="addForm.workOrderId"
         :routingId="addForm.produceRoutingId"
+        :ruleId="addForm.ruleId"
       ></statistics>
     </template>
 
     <ele-pro-table
-      v-else
+      v-if="addForm.recordRulesClassify && addForm.recordRulesClassify != 10"
       v-loading="loading"
       ref="table"
       row-key="id"
@@ -365,6 +370,19 @@
     <!-- 底部按钮 -->
     <template #footer>
       <div class="modal-footer">
+        <el-button
+          v-if="
+            $hasPermission('mes:producetaskrecordrulesrecord:res') &&
+            productionInfo &&
+            productionInfo.executeStatus == 2
+          "
+          :loading="butLoading"
+          type="primary"
+          @click="submit('submit-reset')"
+        >
+          重新一键报工
+        </el-button>
+
         <el-button
           v-if="$hasPermission('mes:producetaskrecordrulesrecord:sus')"
           :loading="butLoading"
@@ -428,7 +446,8 @@
     saveOrUpdate,
     logicDelete,
     getById,
-    queryListByWorkOrderId
+    queryListByWorkOrderId,
+    resetSubmit
   } from '@/api/producetaskrecordrulesrecord/index';
   import toolModal from '@/views/batchRecord/components/toolModal.vue';
   import { getTeam } from '@/api/produce/job.js';
@@ -451,6 +470,12 @@
     watch: {
       'addForm.details': {
         handler(details) {
+          // 排除 物料添加 和 生产统计 的情况
+          if (
+            this.addForm.recordRulesClassify == '10' ||
+            this.addForm.recordRulesClassify == '4'
+          )
+            return;
           const any = details.some((i) => i.checkResult === null);
 
           if (any || details.length === 0) return;
@@ -817,7 +842,11 @@
       // 物料信息查询
       async getListByWorkOrderId(type = '') {
         console.log('type 物料信息', type);
-        if (!this.productionInfo.produceTaskId) return;
+        if (
+          !this.productionInfo.produceTaskId ||
+          this.addForm.reportWorkType != 2
+        )
+          return;
         // 查询物料信息
         const materialObject = await queryListByWorkOrderId({
           workOrderId: this.addForm.workOrderId,
@@ -829,13 +858,36 @@
         console.log('materialList 物料信息', materialObject);
 
         if (type == '') {
-          this.addForm.pickDetails = materialObject.pickDetails;
           this.addForm.outputDetails = materialObject.outputDetails;
+          this.addForm.pickDetails = materialObject.pickDetails.map((i) => {
+            // 物料清单中如果存在则 selected 设置为 true
+            const any = this.addForm.outputDetails.find(
+              (j) => j.categoryId === i.categoryId
+            );
+            if (any) {
+              i.selected = true;
+            } else {
+              i.selected = false;
+            }
+            return i;
+          });
           this.preOutputDetails = materialObject.preOutputDetails;
         }
 
         if (type == 'output') {
           this.addForm.outputDetails = materialObject.outputDetails;
+          this.addForm.pickDetails = this.addForm.pickDetails.map((i) => {
+            // 物料清单中如果存在则 selected 设置为 true
+            const any = this.addForm.outputDetails.find(
+              (j) => j.categoryId === i.categoryId
+            );
+            if (any) {
+              i.selected = true;
+            } else {
+              i.selected = false;
+            }
+            return i;
+          });
         }
       },
       // 规则信息
@@ -879,6 +931,7 @@
       },
       handleBeforeClose() {
         this.addForm = JSON.parse(JSON.stringify(this.formDate));
+        this.statisticsType = '1';
         console.log('this.$refs.ruleFormRef', this.addForm);
         this.$nextTick(() => {
           this.$refs.ruleFormRef.clearValidate();
@@ -913,8 +966,12 @@
             return;
           }
 
-          // 报工需要验证 缓存不验证 排除 recordRulesClassify ==4 物料添加 的情况
-          if (type == 'submit' && this.addForm.recordRulesClassify != '4') {
+          // 报工需要验证 缓存不验证 排除 recordRulesClassify ==4 物料添加 == 10生产统计 的情况
+          if (
+            type.includes('submit') &&
+            this.addForm.recordRulesClassify != '4' &&
+            this.addForm.recordRulesClassify != '10'
+          ) {
             // 验证检查项目
             const detailRequired = this.addForm.details.some((i) => {
               return (
@@ -946,12 +1003,23 @@
             }
           }
 
+          if (this.$refs.statisticsRef && type != 'cache') {
+            // 验证统计项目
+            const valid = this.$refs.statisticsRef.validateStatisticsFilled();
+            if (!valid) {
+              return;
+            }
+          }
+
           // 提交
           this.butLoading = true;
           try {
             if (type == 'submit') {
               await saveOrUpdateAndSubmit(body);
               this.$message.success('报工成功!');
+            } else if (type == 'submit-reset') {
+              await resetSubmit(body);
+              this.$message.success('重新报工成功!');
             } else {
               const id = await saveOrUpdate(body);
               this.addForm.id = id;

+ 329 - 43
src/views/produce/components/prenatalExamination/statistics.vue

@@ -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>

+ 2 - 2
vue.config.js

@@ -36,8 +36,8 @@ module.exports = {
         // target: 'http://192.168.1.125:18086',
         // target: 'http://192.168.1.251:18086',
         // target: 'http://192.168.1.251:18086',
-        target: 'http://192.168.1.125:18086',
-        // target: 'http://192.168.1.116:18086', // 赵沙金
+        // target: 'http://192.168.1.125:18086',
+        target: 'http://192.168.1.116:18086', // 赵沙金
         // target: 'http://192.168.1.251:18086', // 开发环境
         // target: 'http://192.168.1.103:18086',192.168.1.116
         // target: 'http://192.168.1.144:18086',