Bladeren bron

修改布点的bug

695593266@qq.com 2 maanden geleden
bovenliggende
commit
a7ab43e239

+ 20 - 7
src/views/productionPlan/components/detail/planDotLineDetail.vue

@@ -25,7 +25,10 @@
       </div>
       <div class="config-panel">
         <div class="panel-title">工艺配置</div>
-        <el-empty v-if="taskList.length === 0" description="暂无工艺"></el-empty>
+        <el-empty
+          v-if="taskList.length === 0"
+          description="暂无工艺"
+        ></el-empty>
         <div v-else class="task-config-table-wrap">
           <el-table
             :data="taskList"
@@ -52,9 +55,7 @@
               align="center"
             >
               <template slot-scope="{ row, $index }">
-                <span class="task-name-text">{{
-                  taskName(row, $index)
-                }}</span>
+                <span class="task-name-text">{{ taskName(row, $index) }}</span>
               </template>
             </el-table-column>
             <el-table-column
@@ -69,13 +70,16 @@
               </template>
             </el-table-column>
             <el-table-column
-              label="执行班组"
+              label="执行对象"
               min-width="130"
               align="center"
-              prop="executionTeamName"
               label-class-name="config-execution-team-header"
               class-name="config-meta-cell"
-            />
+            >
+              <template slot-scope="{ row }">
+                {{ executionObjectDisplay(row) }}
+              </template>
+            </el-table-column>
             <el-table-column
               label="执行开始时间"
               min-width="168"
@@ -103,6 +107,8 @@
   import { getPlanDotLine } from '@/api/productionPlan/planDotLine';
 
   const EXEC_TYPE_MAP = { 0: '自制', 1: '请托', 2: '委外' };
+  /** 与 planDotLine.vue 一致:0 自制、1 请托、2 委外 */
+  const EXEC_TYPE = Object.freeze({ HOMEMADE: 0, ENTRUST: 1, OUTSOURCE: 2 });
 
   export default {
     name: 'PlanDotLineDetail',
@@ -192,6 +198,13 @@
       executionTypeText(val) {
         return EXEC_TYPE_MAP[val] ?? '';
       },
+      executionObjectDisplay(row) {
+        const t =
+          row.executionType != null ? Number(row.executionType) : undefined;
+        if (t === EXEC_TYPE.HOMEMADE) return row.executionTeamName ?? '';
+        if (t === EXEC_TYPE.ENTRUST) return row.executionFactoryName ?? '';
+        return '';
+      },
       formatTime(val) {
         if (val == null || val === '') return '';
         const str = String(val).trim();

+ 275 - 40
src/views/productionPlan/components/planDotLine.vue

@@ -15,6 +15,16 @@
     @opened="onModalOpened"
   >
     <div class="plan-dot-line">
+      <div v-if="currentPlan" class="plan-time-bar">
+        <span class="plan-time-item">
+          <span class="plan-time-label">计划开始时间:</span>
+          <span class="plan-time-value">{{ planStartDisplay }}</span>
+        </span>
+        <span class="plan-time-item">
+          <span class="plan-time-label">计划结束时间:</span>
+          <span class="plan-time-value">{{ planEndDisplay }}</span>
+        </span>
+      </div>
       <div class="top-route">
         <div class="panel-title">工艺路线</div>
         <el-empty
@@ -80,7 +90,6 @@
                 <el-select
                   v-model="row.executionType"
                   placeholder="执行模式"
-                  clearable
                   class="config-table-control"
                   @change="onExecutionTypeChange(row)"
                 >
@@ -93,16 +102,16 @@
                 </el-select>
               </template>
             </el-table-column>
-            <el-table-column label="执行班组" min-width="130" align="center">
+            <el-table-column label="执行对象" min-width="130" align="center">
               <template slot-scope="{ row }">
                 <el-select
+                  v-if="execTypeNum(row) === EXEC_TYPE.HOMEMADE"
                   v-model="row.executionTeamId"
-                  placeholder="请选择班组"
+                  placeholder="请选择"
                   clearable
                   filterable
-                  :disabled="isTeamSelectDisabled(row)"
                   class="config-table-control"
-                  @change="onExecutionTeamChange(row)"
+                  @change="onExecutionObjectChange(row)"
                 >
                   <el-option
                     v-for="team in teamOptions"
@@ -111,6 +120,29 @@
                     :value="team.id"
                   />
                 </el-select>
+                <el-select
+                  v-else-if="execTypeNum(row) === EXEC_TYPE.ENTRUST"
+                  v-model="row.executionFactoryId"
+                  placeholder="请选择"
+                  clearable
+                  filterable
+                  class="config-table-control"
+                  @change="onExecutionObjectChange(row)"
+                >
+                  <el-option
+                    v-for="f in factoryList"
+                    :key="f.value"
+                    :label="f.label"
+                    :value="f.value"
+                  />
+                </el-select>
+                <el-select
+                  v-else
+                  :value="''"
+                  placeholder="委外无需选择"
+                  disabled
+                  class="config-table-control"
+                />
               </template>
             </el-table-column>
             <el-table-column
@@ -134,15 +166,21 @@
               min-width="168"
               align="center"
             >
-              <template slot-scope="{ row }">
-                <el-date-picker
-                  v-model="row.executionEndTime"
-                  type="datetime"
-                  value-format="yyyy-MM-dd HH:mm:ss"
-                  placeholder="执行结束时间"
-                  class="config-table-control"
-                  @change="handleTimeChange(row, 'executionEndTime')"
-                />
+              <template slot-scope="{ row, $index }">
+                <div
+                  :class="{
+                    'last-end-over-plan-wrap': isLastRowEndAfterPlanEnd($index)
+                  }"
+                >
+                  <el-date-picker
+                    v-model="row.executionEndTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="执行结束时间"
+                    class="config-table-control"
+                    @change="handleTimeChange(row, 'executionEndTime')"
+                  />
+                </div>
               </template>
             </el-table-column>
           </el-table>
@@ -169,6 +207,7 @@
   } from '@/api/productionPlan/planDotLine';
   import { getTaskListById } from '@/api/materialPlan';
   import { teamPage } from '@/api/mainData';
+  import { getFactoryarea } from '@/api/saleOrder';
 
   const EXEC_TYPE = Object.freeze({ HOMEMADE: 0, ENTRUST: 1, OUTSOURCE: 2 });
 
@@ -204,6 +243,14 @@
     return !Number.isNaN(s) && !Number.isNaN(e) && e < s;
   }
 
+  /** 工序结束时间是否晚于计划结束时间(用于最后一道工序标红与提醒) */
+  function isEndAfterPlanEnd(executionEndStr, planEndStr) {
+    if (!executionEndStr || !planEndStr) return false;
+    const e = new Date(executionEndStr).getTime();
+    const p = new Date(planEndStr).getTime();
+    return !Number.isNaN(e) && !Number.isNaN(p) && e > p;
+  }
+
   export default {
     data() {
       return {
@@ -220,7 +267,10 @@
         /** 普通弹窗下动态限制表格最大高度,避免写死 420 */
         tableMaxHeight: 420,
         /** 工艺路线步骤 error 状态(-1 表示无) */
-        routeErrorIndex: -1
+        routeErrorIndex: -1,
+        factoryList: [],
+        /** 模板中比对执行模式用 */
+        EXEC_TYPE
       };
     },
     computed: {
@@ -239,6 +289,24 @@
         if (!list.length) return -1;
         const a = this.routeStepsActive;
         return a >= list.length ? list.length - 1 : a;
+      },
+      /** 顶部展示:与列表行字段 startTime / endTime 一致(见 productionPlan/index.vue 列配置) */
+      planStartDisplay() {
+        const t = this.currentPlan?.startTime;
+        return t ? formatDateTime(t) || String(t) : '—';
+      },
+      planEndDisplay() {
+        const t = this.currentPlan?.endTime;
+        return t ? formatDateTime(t) || String(t) : '—';
+      },
+      /** 最后一道工序执行结束时间是否晚于计划结束时间 */
+      lastProcessEndAfterPlanEnd() {
+        const list = this.taskList;
+        const planEnd = this.currentPlan?.endTime;
+        if (!list.length || !planEnd) return false;
+        const last = list[list.length - 1];
+        if (!last?.executionEndTime) return false;
+        return isEndAfterPlanEnd(last.executionEndTime, planEnd);
       }
     },
     watch: {
@@ -297,11 +365,32 @@
       async open(data) {
         this.currentPlan = data || null;
         this.planRoutingPayload = null;
-        await Promise.all([this.loadTaskList(), this.loadTeamOptions()]);
+        await Promise.all([
+          this.loadTaskList(),
+          this.loadTeamOptions(),
+          this.loadFactoryList()
+        ]);
         this.visible = true;
         this.dialogVisible = true;
       },
 
+      async loadFactoryList() {
+        const par = { type: 1, size: 9999 };
+        try {
+          const res = await getFactoryarea(par);
+          if (res.list?.length) {
+            this.factoryList = res.list.map((el) => ({
+              value: el.id,
+              label: el.name
+            }));
+          } else {
+            this.factoryList = [];
+          }
+        } catch {
+          this.factoryList = [];
+        }
+      },
+
       async loadTaskList() {
         this.taskList = [];
         this.planRoutingPayload = null;
@@ -341,18 +430,34 @@
 
       normalizeDetailRow(item, index) {
         const execType = toSafeNumber(item.executionType) ?? EXEC_TYPE.HOMEMADE;
-        const noTeam =
-          execType === EXEC_TYPE.ENTRUST || execType === EXEC_TYPE.OUTSOURCE;
+        const outsource = execType === EXEC_TYPE.OUTSOURCE;
+        const entrust = execType === EXEC_TYPE.ENTRUST;
+        const homemade = execType === EXEC_TYPE.HOMEMADE;
         return {
           ...item,
           _taskKey: item.id ?? `detail-${item.taskId ?? index}`,
           executionStartTime: formatDateTime(item.executionStartTime),
           executionEndTime: formatDateTime(item.executionEndTime),
           executionType: execType,
-          executionTeamId: noTeam ? '' : item.executionTeamId ?? '',
-          executionTeamLeader: noTeam ? '' : item.executionTeamLeader ?? '',
-          executionTeamLeaderId: noTeam ? '' : item.executionTeamLeaderId ?? '',
-          executionTeamName: noTeam ? '' : item.executionTeamName ?? ''
+          executionFactoryId:
+            entrust
+              ? item.executionFactoryId ?? item.executionTeamId ?? ''
+              : '',
+          executionFactoryName:
+            entrust
+              ? item.executionFactoryName ??
+                item.executionTeamName ??
+                ''
+              : '',
+          executionTeamId: homemade ? item.executionTeamId ?? '' : '',
+          executionTeamLeader:
+            outsource || entrust ? '' : item.executionTeamLeader ?? '',
+          executionTeamLeaderId:
+            outsource || entrust ? '' : item.executionTeamLeaderId ?? '',
+          executionTeamName:
+            outsource || entrust
+              ? ''
+              : item.executionTeamName ?? ''
         };
       },
 
@@ -379,13 +484,14 @@
           executionTeamLeaderId: '',
           executionTeamName: '',
           executionStartTime: '',
-          executionEndTime: ''
+          executionEndTime: '',
+          executionFactoryId: '',
+          executionFactoryName: ''
         };
       },
 
-      isTeamSelectDisabled(row) {
-        const t = Number(row.executionType);
-        return t === EXEC_TYPE.ENTRUST || t === EXEC_TYPE.OUTSOURCE;
+      execTypeNum(row) {
+        return toSafeNumber(row.executionType) ?? EXEC_TYPE.HOMEMADE;
       },
 
       clearExecutionTeamFields(row) {
@@ -393,19 +499,35 @@
         this.$set(row, 'executionTeamName', '');
         this.$set(row, 'executionTeamLeader', '');
         this.$set(row, 'executionTeamLeaderId', '');
+        this.$set(row, 'executionFactoryId', '');
+        this.$set(row, 'executionFactoryName', '');
       },
 
       onExecutionTypeChange(row) {
-        if (this.isTeamSelectDisabled(row)) {
-          this.clearExecutionTeamFields(row);
-        }
+        this.clearExecutionTeamFields(row);
       },
 
-      onExecutionTeamChange(row) {
-        const team = this.teamOptions.find((t) => t.id === row.executionTeamId);
-        row.executionTeamName = team?.name ?? '';
-        row.executionTeamLeader = team?.leaderUserName ?? '';
-        row.executionTeamLeaderId = team?.leaderUserId ?? '';
+      onExecutionObjectChange(row) {
+        const t = this.execTypeNum(row);
+        if (t === EXEC_TYPE.HOMEMADE) {
+          const team = this.teamOptions.find(
+            (x) => x.id === row.executionTeamId
+          );
+          row.executionTeamName = team?.name ?? '';
+          row.executionTeamLeader = team?.leaderUserName ?? '';
+          row.executionTeamLeaderId = team?.leaderUserId ?? '';
+          row.executionFactoryId = '';
+          row.executionFactoryName = '';
+        } else if (t === EXEC_TYPE.ENTRUST) {
+          const f = this.factoryList.find(
+            (x) => x.value === row.executionFactoryId
+          );
+          row.executionFactoryName = f?.label ?? '';
+          row.executionTeamId = '';
+          row.executionTeamName = '';
+          row.executionTeamLeader = '';
+          row.executionTeamLeaderId = '';
+        }
       },
 
       handleClearAll() {
@@ -458,18 +580,45 @@
           if (!Number.isNaN(d) && !Number.isNaN(v) && v > d) {
             this.$message.warning('时间不能大于计划交付时间');
             this.$set(row, changeKey, '');
+            return;
           }
         }
+        const planEnd = this.currentPlan?.endTime;
+        if (
+          changeKey === 'executionEndTime' &&
+          planEnd &&
+          row.executionEndTime &&
+          isEndAfterPlanEnd(row.executionEndTime, planEnd)
+        ) {
+          this.$message.warning('执行结束时间不能超过计划结束时间');
+          this.$set(row, 'executionEndTime', '');
+          return;
+        }
+      },
+
+      /** 最后一行「执行结束时间」单元格是否标红(晚于计划结束时间) */
+      isLastRowEndAfterPlanEnd(index) {
+        const list = this.taskList;
+        const planEnd = this.currentPlan?.endTime;
+        if (!list.length || index !== list.length - 1 || !planEnd) {
+          return false;
+        }
+        const row = list[index];
+        return (
+          !!row?.executionEndTime &&
+          isEndAfterPlanEnd(row.executionEndTime, planEnd)
+        );
       },
 
       validateTaskList() {
         const label = (t) => t.taskName || t.name || '当前工艺';
 
         for (const item of this.taskList) {
-          if (item.executionType == null || item.executionType === '') {
-            this.$set(item, 'executionType', EXEC_TYPE.HOMEMADE);
+          const execType = toSafeNumber(item.executionType);
+          if (execType === undefined) {
+            this.$message.warning(`${label(item)}请选择执行模式`);
+            return false;
           }
-          const execType = Number(item.executionType);
 
           if (execType === EXEC_TYPE.HOMEMADE) {
             if (
@@ -480,7 +629,31 @@
               this.$message.warning(
                 `${label(
                   item
-                )}执行模式为自制时,需填写执行班组、执行开始和结束时间`
+                )}执行模式为自制时,需选择执行班组并填写执行开始和结束时间`
+              );
+              return false;
+            }
+          }
+
+          if (execType === EXEC_TYPE.ENTRUST) {
+            if (
+              !item.executionFactoryId ||
+              !item.executionStartTime ||
+              !item.executionEndTime
+            ) {
+              this.$message.warning(
+                `${label(
+                  item
+                )}执行模式为请托时,需选择工厂并填写执行开始和结束时间`
+              );
+              return false;
+            }
+          }
+
+          if (execType === EXEC_TYPE.OUTSOURCE) {
+            if (!item.executionStartTime || !item.executionEndTime) {
+              this.$message.warning(
+                `${label(item)}执行模式为委外时,需填写执行开始和结束时间`
               );
               return false;
             }
@@ -494,6 +667,17 @@
             );
             return false;
           }
+          const planEnd = this.currentPlan?.endTime;
+          if (
+            planEnd &&
+            item.executionEndTime &&
+            isEndAfterPlanEnd(item.executionEndTime, planEnd)
+          ) {
+            this.$message.warning(
+              `${label(item)}执行结束时间不能超过计划结束时间`
+            );
+            return false;
+          }
 
           const deadline = this.currentPlan?.planDeliveryTime;
           if (deadline) {
@@ -522,14 +706,18 @@
         const head = this.planRoutingPayload || {};
 
         const detailList = this.taskList.map((row) => {
+          const execType = toSafeNumber(row.executionType) ?? 0;
           const detail = {
             executionEndTime: row.executionEndTime || undefined,
             executionStartTime: row.executionStartTime || undefined,
-            executionTeamId: row.executionTeamId || undefined,
+            executionTeamId:
+              execType === EXEC_TYPE.HOMEMADE
+                ? row.executionTeamId || undefined
+                : undefined,
             executionTeamLeader: row.executionTeamLeader || '',
             executionTeamLeaderId: row.executionTeamLeaderId || '',
             executionTeamName: row.executionTeamName || '',
-            executionType: toSafeNumber(row.executionType) ?? 0,
+            executionType: execType,
             planCode: row.planCode || plan?.code,
             planId: row.planId ?? plan?.id,
             routingId:
@@ -543,6 +731,10 @@
             taskSort: row.taskSort,
             type: toSafeNumber(row.type)
           };
+          if (execType === EXEC_TYPE.ENTRUST) {
+            detail.executionFactoryId = row.executionFactoryId || undefined;
+            detail.executionFactoryName = row.executionFactoryName || '';
+          }
           if (row.id != null) detail.id = row.id;
           return detail;
         });
@@ -565,6 +757,7 @@
       },
 
       async handleCache() {
+        if (!this.validateTaskList()) return;
         const payload = this.buildSavePayload(1);
         const loading = this.$loading({ lock: true, text: '缓存中...' });
         try {
@@ -633,6 +826,48 @@
     min-height: 360px;
   }
 
+  .plan-time-bar {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 12px 28px;
+    margin-bottom: 12px;
+    padding: 10px 12px;
+    border: 1px solid #ebeef5;
+    border-radius: 4px;
+    background: #fafafa;
+    font-size: 14px;
+  }
+
+  .plan-time-item {
+    display: inline-flex;
+    align-items: baseline;
+    gap: 4px;
+  }
+
+  .plan-time-label {
+    color: #606266;
+    white-space: nowrap;
+  }
+
+  .plan-time-value {
+    color: #303133;
+    font-weight: 500;
+  }
+
+  .last-end-over-plan-wrap {
+    width: 100%;
+  }
+
+  .last-end-over-plan-wrap ::v-deep .el-input__inner {
+    color: #f56c6c;
+    border-color: #f56c6c;
+  }
+
+  .last-end-over-plan-wrap ::v-deep .el-input__inner:focus {
+    border-color: #f56c6c;
+  }
+
   .top-route,
   .config-panel {
     border: 1px solid #ebeef5;

+ 6 - 1
src/views/productionPlan/index.vue

@@ -374,7 +374,11 @@
           <el-link
             type="primary"
             :underline="false"
-            v-if="planDotLineEnabled && (row.status == 3 || row.status == 2)"
+            v-if="
+              planDotLineEnabled &&
+              (row.status == 3 || row.status == 2) &&
+              row.approvalStatus != 1
+            "
             @click="planDotLine(row)"
           >
             布点
@@ -383,6 +387,7 @@
           <el-link
             type="primary"
             :underline="false"
+            v-if="row.approvalStatus != 1"
             @click="productionPreparations(row)"
           >
             生产准备

+ 19 - 3
src/views/workOrder/components/details.vue

@@ -451,13 +451,16 @@
                     </template>
                   </el-table-column>
                   <el-table-column
-                    label="执行班组"
+                    label="执行对象"
                     min-width="130"
                     align="center"
-                    prop="executionTeamName"
                     label-class-name="config-execution-team-header"
                     class-name="config-meta-cell"
-                  />
+                  >
+                    <template slot-scope="{ row }">
+                      {{ dotLineExecutionObjectDisplay(row) }}
+                    </template>
+                  </el-table-column>
                   <el-table-column
                     label="执行开始时间"
                     min-width="168"
@@ -494,6 +497,11 @@
   import { getPlanDotLine } from '@/api/productionPlan/planDotLine';
 
   const EXEC_TYPE_MAP = { 0: '自制', 1: '请托', 2: '委外' };
+  const EXEC_TYPE = Object.freeze({
+    HOMEMADE: 0,
+    ENTRUST: 1,
+    OUTSOURCE: 2
+  });
   export default {
     components: {},
     props: {
@@ -840,6 +848,14 @@
         return item.taskName || item.name || `工艺${index + 1}`;
       },
 
+      dotLineExecutionObjectDisplay(row) {
+        const t =
+          row.executionType != null ? Number(row.executionType) : undefined;
+        if (t === EXEC_TYPE.HOMEMADE) return row.executionTeamName ?? '';
+        if (t === EXEC_TYPE.ENTRUST) return row.executionFactoryName ?? '';
+        return '';
+      },
+
       dotLineRouteStepTitle(item, index) {
         return (
           item.taskTypeName || item.taskName || item.name || `工艺${index + 1}`

+ 6 - 2
src/views/workOrder/index.vue

@@ -62,7 +62,10 @@
             icon="el-icon-truck"
             size="small"
             type="primary"
-            v-if="$hasPermission('aps:batchReleaseWorkOrder:release')"
+            v-if="
+              $hasPermission('aps:batchReleaseWorkOrder:release') &&
+              !planDotLine
+            "
             @click="batchDispatch"
             >批量派单
             <!--  -->
@@ -167,7 +170,8 @@
             v-if="
               unpackShow(row) &&
               row.splitResidue !== 0 &&
-              $hasPermission('aps:workorder:ordersplitting')
+              $hasPermission('aps:workorder:ordersplitting') &&
+              !planDotLine
             "
             :underline="false"
             type="primary"

+ 9 - 2
src/views/workOrder/mixins/planDotLineRelease.js

@@ -183,6 +183,7 @@ export default {
               id: item.sourceTaskId, //工序 id
               name: item.taskName, //工序 名称
               executionTeamName: item.executionTeamName || '', // 工序执行班组
+              executionTeamId: item.executionTeamId || '',
               assignType: 1, // 默认的指派数据
               assignName: '工位', // 默认指派数据名称
               list: [], // 当前工序下面的指派 绑定的表格
@@ -205,6 +206,7 @@ export default {
               endDate: '', // 结束日期 (当前工序中最晚的计划结束时间)
               workCenterId: item.workCenterId,
               teamName: item.executionTeamName,
+              teamId: item.executionTeamId,
               executionType: item.executionType
             };
             list.push(obj);
@@ -234,8 +236,9 @@ export default {
     // 初始化查询 查询全部工序操作过的数据
     async initializeQuery() {
       try {
-        // 不存在 班组数据的话 就不调用这个方法
+        // 不存在 班组数据的话 就不调用接口,但仍需初始化首工序数据
         if (!this.form.teamId) {
+          this.handleClick({ name: this.processId });
           return;
         }
         let params = {
@@ -755,6 +758,9 @@ export default {
         return;
       }
       this.form.teamName = data.executionTeamName || '';
+      console.log(this.form.teamNam, ' this.form.teamName');
+      this.form.teamId = data.executionTeamId || '';
+      console.log(this.form.teamId, 'this.form.teamId');
       await this.changeRadio(data.assignType, data.index);
     },
     // 指派选择
@@ -780,8 +786,9 @@ export default {
       let dataRow = this.processList[index];
       list = this.applyExecutionTimeToList(list, dataRow);
 
-      // 不存在 班组数据的话 就不调用这个方法
+      // 不存在 班组数据的话 就不调用接口,但仍需将基础列表赋值
       if (!this.form.teamId) {
+        this.$set(dataRow, 'list', list);
         return;
       }
       let params = {