Ver código fonte

修改界面的样式

695593266@qq.com 3 meses atrás
pai
commit
b57622fb62

+ 12 - 0
src/api/productionPlan/index.js

@@ -400,6 +400,18 @@ export async function taskReleaseWork(data) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+//提醒材料员领料
+export async function remindMaterialPicking(data) {
+  const res = await request.post(
+    `/aps/productionplan/remindMaterialPicking`,
+    data
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
 //任务齐套性检查
 export async function updatePreRelease(data) {
   const res = await request.post(`/aps/productionplan/updatePreRelease`, data);

+ 50 - 1
src/views/productionPlan/components/checkProductionPreparations.vue

@@ -59,6 +59,19 @@
           齐套检查
         </el-link>
 
+        <el-link
+          v-if="canRemindMaterialPicking(row)"
+          type="primary"
+          :underline="false"
+          :disabled="materialPickingRemindLoadingId === row.id"
+          style="margin-left: 8px"
+          @click="remindMaterialPicking(row)"
+        >
+          {{
+            materialPickingRemindLoadingId === row.id ? '发送中...' : '提醒领料'
+          }}
+        </el-link>
+
         <!-- <el-link
           type="primary"
           :underline="false"
@@ -258,6 +271,7 @@
   import {
     completenessCheck,
     taskReleaseWork,
+    remindMaterialPicking,
     updatePreRelease,
     updatePreReleaseOrder,
     listBomTaskFile,
@@ -439,7 +453,8 @@
         ncSelection: [],
         productionData: {},
         routeVisible: false,
-        fileList: []
+        fileList: [],
+        materialPickingRemindLoadingId: ''
       };
     },
 
@@ -492,6 +507,7 @@
           {
             columnKey: 'action',
             label: '操作',
+            width: 160,
             align: 'center',
             resizable: false,
             slot: 'action',
@@ -788,6 +804,39 @@
         );
       },
 
+      canRemindMaterialPicking(row) {
+        return row && row.preType == 1 && row.statusStr == '缺料';
+      },
+
+      async remindMaterialPicking(row) {
+        if (this.materialPickingRemindLoadingId) return;
+
+        const productionPlanId =
+          this.itemData.productionPlanId || this.itemData.id;
+        const params = {
+          ...row,
+          productionPlanId,
+          planId: productionPlanId,
+          preReleaseId: row.id,
+          preType: row.preType
+        };
+        if (this.type == 'order') {
+          params.apsWorkOrderId = this.itemData.id;
+        }
+
+        this.materialPickingRemindLoadingId = row.id;
+        await remindMaterialPicking(params)
+          .then(() => {
+            this.$message.success('已提醒材料员领料');
+          })
+          .catch((err) => {
+            this.$message.error(err.message || '提醒发送失败');
+          })
+          .finally(() => {
+            this.materialPickingRemindLoadingId = '';
+          });
+      },
+
       async getUserPage() {
         if (!this.form.executeGroupId) return (this.leaderList = []);
         let par = {

+ 140 - 45
src/views/productionPlan/components/gantt/project-gantt.vue

@@ -107,6 +107,7 @@
       </div>
     </div>
     <div
+      ref="hoverTooltip"
       v-show="hoverTooltip.visible"
       class="gantt-hover-tooltip"
       :style="hoverTooltip.style"
@@ -864,21 +865,18 @@
           this.hideHoverTooltip();
           return;
         }
-        this.hoverTooltip = {
-          visible: true,
-          content: buildTooltipHtml(
+        this.setHoverTooltip(
+          buildTooltipHtml(
             task,
             this.planMetaByCode,
             this.activeTab,
             this.resourceLabelMode
           ),
-          style: {
-            left: `${event.clientX + 16}px`,
-            top: `${event.clientY + 16}px`
-          }
-        };
+          event
+        );
       },
       hideHoverTooltip() {
+        this.removeNativeGanttTooltips();
         if (!this.hoverTooltip.visible) {
           return;
         }
@@ -887,6 +885,76 @@
           visible: false
         };
       },
+      removeNativeGanttTooltips() {
+        document.querySelectorAll('.gantt_tooltip').forEach((item) => {
+          item.parentNode && item.parentNode.removeChild(item);
+        });
+      },
+      getHoverTooltipBaseStyle(event) {
+        const viewportWidth =
+          window.innerWidth || document.documentElement.clientWidth || 1200;
+        const viewportHeight =
+          window.innerHeight || document.documentElement.clientHeight || 800;
+        const margin = 12;
+        const maxWidth = Math.max(260, Math.min(420, viewportWidth - 24));
+        const maxHeight = Math.max(180, viewportHeight - 24);
+        return {
+          left: `${Math.min(
+            event.clientX + 14,
+            viewportWidth - maxWidth - margin
+          )}px`,
+          top: `${Math.min(
+            event.clientY + 14,
+            viewportHeight - 180 - margin
+          )}px`,
+          maxWidth: `${maxWidth}px`,
+          maxHeight: `${maxHeight}px`
+        };
+      },
+      setHoverTooltip(content, event) {
+        this.removeNativeGanttTooltips();
+        this.hoverTooltip = {
+          visible: true,
+          content,
+          style: this.getHoverTooltipBaseStyle(event)
+        };
+        this.$nextTick(() => {
+          this.adjustHoverTooltipPosition(event);
+        });
+      },
+      adjustHoverTooltipPosition(event) {
+        const tooltip = this.$refs.hoverTooltip;
+        if (!tooltip || !event) {
+          return;
+        }
+        const viewportWidth =
+          window.innerWidth || document.documentElement.clientWidth || 1200;
+        const viewportHeight =
+          window.innerHeight || document.documentElement.clientHeight || 800;
+        const margin = 12;
+        const offset = 14;
+        const rect = tooltip.getBoundingClientRect();
+        const width = rect.width || 360;
+        const height = rect.height || 220;
+        let left = event.clientX + offset;
+        let top = event.clientY + offset;
+        if (left + width + margin > viewportWidth) {
+          left = event.clientX - width - offset;
+        }
+        if (top + height + margin > viewportHeight) {
+          top = event.clientY - height - offset;
+        }
+        left = Math.max(margin, Math.min(left, viewportWidth - width - margin));
+        top = Math.max(margin, Math.min(top, viewportHeight - height - margin));
+        this.hoverTooltip = {
+          ...this.hoverTooltip,
+          style: {
+            ...this.hoverTooltip.style,
+            left: `${left}px`,
+            top: `${top}px`
+          }
+        };
+      },
       syncGanttVisibility() {
         this.$nextTick(() => {
           const ganttRoot =
@@ -1059,26 +1127,14 @@
         return resourceMatchedTasks.filter((item) => matchedIds.has(item.id));
       },
       showCalendarTooltip(event, entries) {
-        this.hoverTooltip = {
-          visible: true,
-          content: buildCalendarTooltipHtml(entries),
-          style: {
-            left: `${event.clientX + 16}px`,
-            top: `${event.clientY + 16}px`
-          }
-        };
+        this.removeNativeGanttTooltips();
+        this.setHoverTooltip(buildCalendarTooltipHtml(entries), event);
       },
       moveHoverTooltip(event) {
         if (!this.hoverTooltip.visible) {
           return;
         }
-        this.hoverTooltip = {
-          ...this.hoverTooltip,
-          style: {
-            left: `${event.clientX + 16}px`,
-            top: `${event.clientY + 16}px`
-          }
-        };
+        this.adjustHoverTooltipPosition(event);
       },
       getChildRangeMap() {
         return this.ganttTasks.reduce((result, item) => {
@@ -1110,6 +1166,8 @@
         if (this.viewMode === mode) {
           return;
         }
+        this.hideHoverTooltip();
+        this.removeNativeGanttTooltips();
         this.viewMode = mode;
       },
       async toggleFullscreen() {
@@ -1433,6 +1491,7 @@
         'MSFullscreenChange',
         this.handleFullscreenChange
       );
+      this.removeNativeGanttTooltips();
     }
   };
 </script>
@@ -1574,6 +1633,8 @@
     position: fixed;
     z-index: 9999;
     pointer-events: none;
+    max-width: calc(100vw - 24px);
+    max-height: calc(100vh - 24px);
   }
   .gantt-container {
     width: calc(100%);
@@ -1802,61 +1863,95 @@
 </style>
 <style lang="scss">
   .gantt_tooltip {
+    display: none !important;
     padding: 0 !important;
     border: none !important;
     background: transparent !important;
     box-shadow: none !important;
   }
   .gantt-tooltip-card {
-    min-width: 320px;
-    max-width: 420px;
-    padding: 12px 14px;
-    border: 1px solid rgba(226, 232, 240, 0.9);
-    border-radius: 6px;
-    background: rgba(255, 255, 255, 0.96);
+    position: relative;
+    width: min(420px, calc(100vw - 24px));
+    min-width: min(300px, calc(100vw - 24px));
+    max-height: inherit;
+    padding: 12px 14px 14px;
+    box-sizing: border-box;
+    overflow-y: auto;
+    border: 1px solid rgba(198, 210, 224, 0.92);
+    border-radius: 8px;
+    background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
     color: #1f2937;
-    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18);
-    backdrop-filter: blur(8px);
+    box-shadow: 0 16px 38px rgba(15, 23, 42, 0.18),
+      0 0 0 1px rgba(255, 255, 255, 0.72) inset;
+    backdrop-filter: blur(10px);
+  }
+  .gantt-tooltip-card::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    height: 3px;
+    background: linear-gradient(90deg, #15b8a6 0%, #2587d8 100%);
+  }
+  .gantt-tooltip-card::-webkit-scrollbar {
+    width: 8px;
+  }
+  .gantt-tooltip-card::-webkit-scrollbar-thumb {
+    border: 2px solid #f8fbff;
+    border-radius: 999px;
+    background: #b8c7d8;
   }
   .gantt-tooltip-card__title {
-    margin-bottom: 8px;
+    margin-bottom: 10px;
+    padding-right: 4px;
+    padding-bottom: 8px;
+    border-bottom: 1px solid #e8eef6;
     font-size: 13px;
-    font-weight: 600;
+    font-weight: 700;
     line-height: 20px;
     color: #0f172a;
     word-break: break-all;
   }
   .gantt-tooltip-card__row {
     display: flex;
-    align-items: center;
+    align-items: flex-start;
+    gap: 8px;
     font-size: 12px;
-    line-height: 20px;
+    line-height: 19px;
   }
   .gantt-tooltip-card__section + .gantt-tooltip-card__section {
-    margin-top: 8px;
-    padding-top: 8px;
-    border-top: 1px solid #edf2f7;
+    margin-top: 10px;
+    padding-top: 10px;
+    border-top: 1px dashed #d9e4ef;
   }
   .gantt-tooltip-card__section-title {
-    margin-bottom: 4px;
+    display: inline-flex;
+    align-items: center;
+    height: 20px;
+    margin-bottom: 6px;
+    padding: 0 8px;
+    border-radius: 999px;
+    background: #edf8f6;
     font-size: 12px;
     font-weight: 600;
-    color: #475569;
+    color: #147b72;
   }
   .gantt-tooltip-card__row + .gantt-tooltip-card__row {
-    margin-top: 2px;
+    margin-top: 5px;
   }
   .gantt-tooltip-card__label {
-    flex: 0 0 72px;
+    flex: 0 0 78px;
     color: #64748b;
+    text-align: right;
     white-space: nowrap;
   }
   .gantt-tooltip-card__value {
     flex: 1;
     min-width: 0;
     color: #111827;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
+    font-weight: 500;
+    white-space: normal;
+    word-break: break-all;
   }
 </style>

+ 237 - 11
src/views/productionPlan/components/newFactoryProductionScheduling.vue

@@ -84,6 +84,7 @@
           :height="planTableHeight"
           :page-size="20"
           :pagination-class="planPaginationClass"
+          @reload="handlePlanTableReload"
           @update:selection="handlePlanSelectionChange"
         >
           <template v-slot:action="{ row }">
@@ -139,9 +140,11 @@
               ]"
             >
               <task-config-panel
+                :key="taskConfigPanelRenderKey"
                 :class="{ 'factory-task-config-panel': !isOrderScheduling }"
                 :dot-line-fullscreen="dotLineFullscreen"
                 :is-current-plan-readonly="isCurrentPlanReadonly"
+                :order-detail-readonly="isOrderDetailReadonly"
                 :current-plan="currentPlan"
                 :dot-line-loading="dotLineLoading"
                 :task-list="taskList"
@@ -151,6 +154,9 @@
                 :homemade-object-label="homemadeObjectLabel"
                 :show-quantity-column="isOrderScheduling"
                 :order-dispatch-style="isOrderScheduling"
+                :show-approve-button="!isOrderScheduling"
+                :approve-button-disabled="!canApproveCurrentPlan"
+                :approve-loading="approvalLoading"
                 :table-max-height="taskConfigTableMaxHeight"
                 :fill-table-height="!isOrderScheduling"
                 :factory-list="factoryList"
@@ -169,6 +175,7 @@
                 @add-dispatch-stations="addDispatchStations"
                 @dispatch-action-success="handleDispatchActionSuccess"
                 @row-submit="submitDotLineRow"
+                @approve="handleApproveCurrentPlan"
               />
             </div>
           </div>
@@ -212,9 +219,14 @@
     getPlansByIds
   } from '@/api/productionPlan/index.js';
   import { getTaskListById } from '@/api/materialPlan';
-  import { listByFirstTaskId, teamPage } from '@/api/mainData';
+  import {
+    listByFirstTaskId,
+    parameterGetByCode,
+    teamPage
+  } from '@/api/mainData';
   import { getFactoryarea } from '@/api/saleOrder';
   import { getUserPage } from '@/api/system/organization';
+  import { release, releaseByApproval } from '@/api/productionPlan/order.js';
   import {
     getPlanDotLine,
     savePlanDotLine
@@ -286,6 +298,10 @@
         dialogRenderKey: 0,
         dotLineLoading: false,
         dotLineFullscreen: false,
+        approvalLoading: false,
+        isReview: false,
+        orderDetailReadonly: false,
+        orderDetailActionMode: '',
         teamOptions: [],
         workstationOptionsMap: {},
         workstationOptionsLoadingMap: {},
@@ -334,7 +350,18 @@
         return text || '—';
       },
       isCurrentPlanReadonly() {
-        return this.entryReadonly || this.isReadonlyPlan(this.currentPlan);
+        return (
+          (!this.isOrderScheduling && this.entryReadonly) ||
+          this.isOrderDetailReadonly ||
+          (!this.isOrderScheduling && this.isReadonlyPlan(this.currentPlan))
+        );
+      },
+      isOrderDetailReadonly() {
+        return (
+          this.isOrderScheduling &&
+          !!this.currentPlan &&
+          this.orderDetailActionMode === 'view'
+        );
       },
       // 当前弹窗同时承载两条业务链路:
       // 1. 工厂排程:从生产计划页面进入,不传 mode,保持默认 plan 模式。
@@ -362,6 +389,17 @@
       schedulingTitle() {
         return this.isOrderScheduling ? '班组排程' : '工厂排产';
       },
+      taskConfigPanelRenderKey() {
+        const planKey =
+          this.currentPlan?.workOrderId || this.currentPlan?.id || 'empty';
+        return [
+          this.schedulingMode,
+          this.planTableStatusTab,
+          planKey,
+          this.orderDetailActionMode || 'none',
+          this.isCurrentPlanReadonly ? 'readonly' : 'edit'
+        ].join('-');
+      },
       planTableStatusTabs() {
         if (this.isOrderScheduling) {
           return [
@@ -501,11 +539,29 @@
           return Math.max(260, this.schedulingContentHeightValue - 132);
         }
         return Math.max(280, this.schedulingContentHeightValue - 132);
+      },
+      canApproveCurrentPlan() {
+        const row = this.currentPlan;
+        const hasPermission =
+          typeof this.$hasPermission !== 'function' ||
+          this.$hasPermission('aps:productionplan:release');
+        return (
+          !this.isOrderScheduling &&
+          !!row?.id &&
+          this.planTableStatusTab === 'unpublished' &&
+          String(row.status) === '2' &&
+          String(row.dotLineStatus) === '1' &&
+          row.splitBatch != 1 &&
+          row.approvalStatus != 1 &&
+          row.approvalStatus != 2 &&
+          hasPermission
+        );
       }
     },
     created() {
       this.loadTeamOptions();
       this.loadFactoryOptions();
+      this.loadApprovalReviewConfig();
     },
     mounted() {
       this.updateViewportSize();
@@ -546,7 +602,26 @@
       isReadonlyPlan(row) {
         return READONLY_LAYOUT_STATUSES.includes(String(row?.status ?? ''));
       },
+      getOrderStatusCode(row) {
+        const status = row?.status;
+        if (status && typeof status === 'object') {
+          return String(status.code ?? status.value ?? status.id ?? '');
+        }
+        return String(status ?? '');
+      },
+      isOrderSchedulingReadonlyRow(row) {
+        if (!this.isOrderScheduling) {
+          return false;
+        }
+        return (
+          this.planTableStatusTab === 'dispatched' ||
+          ['4', '5', '6', '7'].includes(this.getOrderStatusCode(row))
+        );
+      },
       getPlanActionText(row) {
+        if (this.isOrderSchedulingReadonlyRow(row)) {
+          return '查看';
+        }
         return !this.isOrderScheduling && this.isReadonlyPlan(row)
           ? '查看'
           : '布点';
@@ -568,6 +643,13 @@
         return [this.getPlanTableStatusRequestWhere(tabName)];
       },
       async handlePlanTableStatusTabClick() {
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
+        this.currentPlan = null;
+        this.planRoutingPayload = null;
+        this.taskList = [];
+        this.selection = [];
+        this.syncTableSelection('planTable', []);
         await this.reloadPlanTableByStatus();
       },
       async reloadPlanTableByStatus() {
@@ -577,11 +659,18 @@
           await this.reloadPlanSchedulingPlansByStatus();
         }
       },
-      async reloadPlanSchedulingPlansByStatus() {
+      async reloadPlanSchedulingPlansByStatus(options = {}) {
+        const preserveCurrent = !!options.preserveCurrent;
+        const currentPlanKey = preserveCurrent
+          ? this.getPlanRowKey(this.currentPlan)
+          : '';
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
         const loading = this.$loading({
           lock: true,
           text: '加载计划中...'
         });
+        let matchedCurrentPlan = null;
         try {
           const requestWhereList = this.getPlanTableStatusRequestWhereList();
           const resultList = await Promise.all(
@@ -600,18 +689,44 @@
           );
           this.allPlanDataList = deepClone(rows);
           this.planDataList = this.buildPlanTableRows(rows, rows);
+          if (preserveCurrent && currentPlanKey) {
+            matchedCurrentPlan =
+              this.planDataList.find(
+                (item) => this.getPlanRowKey(item) === currentPlanKey
+              ) || null;
+          }
           this.planTableRenderKey += 1;
         } catch (e) {
           this.$message.error(e.message || '获取计划数据失败');
         } finally {
           loading.close();
         }
+        if (preserveCurrent && matchedCurrentPlan) {
+          this.currentPlan = {
+            ...(this.currentPlan || {}),
+            ...matchedCurrentPlan
+          };
+          this.selection = [matchedCurrentPlan];
+          this.syncTableSelection('planTable', [matchedCurrentPlan]);
+          return;
+        }
         this.selection = [];
         this.currentPlan = null;
         this.planRoutingPayload = null;
         this.taskList = [];
         this.syncTableSelection('planTable', []);
       },
+      async handlePlanTableReload() {
+        if (this.isOrderScheduling) {
+          await this.reloadOrderSchedulingOrdersByDateRange(this.dateRange);
+          return;
+        }
+        await this.refreshSchedulingView();
+        await this.reloadPlanSchedulingPlansByStatus({ preserveCurrent: true });
+        if (this.currentPlan?.id) {
+          await this.loadPlanDotLineDetail(this.currentPlan);
+        }
+      },
       getSchedulingDateRangeParams() {
         if (!Array.isArray(this.dateRange) || this.dateRange.length !== 2) {
           return {};
@@ -806,6 +921,8 @@
         );
         this.planPickerVisible = false;
         this.planDataList = nextPlanDataList;
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
         this.currentPlan = null;
         this.planRoutingPayload = null;
         this.taskList = [];
@@ -856,7 +973,12 @@
           this.$message.warning('该计划没有配置BOM请先配置BOM');
           return;
         }
+        const readonly = this.isOrderSchedulingReadonlyRow(row);
+        this.orderDetailReadonly = readonly;
+        this.orderDetailActionMode = readonly ? 'view' : 'edit';
         const currentPlan = this.resolveCurrentPlan(row);
+        currentPlan._orderDetailReadonly = readonly;
+        currentPlan.orderDetailReadonly = readonly;
         this.currentPlan = currentPlan;
         this.selection = [row];
         this.syncTableSelection('planTable', [row]);
@@ -1127,6 +1249,16 @@
           this.teamOptions = [];
         }
       },
+      async loadApprovalReviewConfig() {
+        try {
+          const res = await parameterGetByCode({
+            code: 'planned_release_require'
+          });
+          this.isReview = res?.value == '1';
+        } catch (e) {
+          this.isReview = false;
+        }
+      },
       async loadWorkOrderExecutionOptions() {
         // 班组排程自制执行对象分两类:
         // - 人员:打开弹窗时一次性加载,所有工序共用。
@@ -1598,10 +1730,14 @@
         });
         this.$set(this.workstationOptionsMap, key, nextList);
       },
-      async handleDispatchActionSuccess() {
+      async handleDispatchActionSuccess(event = {}) {
         if (!this.isOrderScheduling) {
           return;
         }
+        if (event.type === 'confirm') {
+          await this.reloadOrderSchedulingOrdersByDateRange(this.dateRange);
+          return;
+        }
         await this.refreshSchedulingView();
       },
       buildSavePayload(isCache = 0) {
@@ -1734,6 +1870,8 @@
       },
 
       async reloadOrderSchedulingOrdersByDateRange(dateRange) {
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
         if (typeof this.orderListLoader !== 'function') {
           await this.refreshSchedulingView();
           return;
@@ -1759,11 +1897,20 @@
             : null;
           if (matchedCurrentOrder) {
             const currentPlan = this.resolveCurrentPlan(matchedCurrentOrder);
+            this.orderDetailReadonly =
+              this.isOrderSchedulingReadonlyRow(matchedCurrentOrder);
+            this.orderDetailActionMode = this.orderDetailReadonly
+              ? 'view'
+              : 'edit';
+            currentPlan._orderDetailReadonly = this.orderDetailReadonly;
+            currentPlan.orderDetailReadonly = this.orderDetailReadonly;
             this.currentPlan = currentPlan;
             this.selection = [matchedCurrentOrder];
             this.syncTableSelection('planTable', [matchedCurrentOrder]);
             await this.loadPlanDotLineDetail(currentPlan);
           } else {
+            this.orderDetailReadonly = false;
+            this.orderDetailActionMode = '';
             this.currentPlan = null;
             this.planRoutingPayload = null;
             this.taskList = [];
@@ -2200,6 +2347,8 @@
           allPlans = selectedPlans;
         }
         this.entryReadonly = readonly;
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
         this.allPlanDataList = deepClone(allPlans || []);
         this.planDataList =
           mode === 'order'
@@ -2548,6 +2697,61 @@
       //   return arr;
       // },
 
+      async handleApproveCurrentPlan() {
+        const row = this.currentPlan;
+        if (!row?.id) {
+          this.$message.warning('请先勾选一条计划后再审批');
+          return;
+        }
+        if (!row.produceRoutingName && !row.routingName) {
+          this.$message.error('请先选择工艺路线!');
+          return;
+        }
+        if (String(row.dotLineStatus) !== '1') {
+          this.$message.error('请先完成布点!');
+          return;
+        }
+        if (!this.canApproveCurrentPlan) {
+          this.$message.warning('当前计划不满足审批条件');
+          return;
+        }
+
+        const titleText = this.isReview
+          ? '是否发起流程'
+          : '发布工单后不可撤回,确定发布吗?';
+        const title = this.isReview ? '流程确认' : '发布确认';
+        const text = this.isReview ? '流程发布中...' : '工单发布中...';
+
+        try {
+          await this.$confirm(titleText, title);
+        } catch (e) {
+          return;
+        }
+
+        const loading = this.$loading({
+          lock: true,
+          fullscreen: true,
+          text
+        });
+        this.approvalLoading = true;
+        try {
+          const api = this.isReview ? releaseByApproval : release;
+          const data = await api([row.id]);
+          if (data || data === 0) {
+            this.$message.success('发布成功!');
+          } else {
+            this.$message.error('发布失败,请重新发布!');
+          }
+          await this.refreshSchedulingView();
+          await this.reloadPlanSchedulingPlansByStatus();
+        } catch (e) {
+          this.$message.error(e.message || '发布失败,请重新发布!');
+        } finally {
+          this.approvalLoading = false;
+          loading.close();
+        }
+      },
+
       async submitDotLineConfig() {
         if (!this.currentPlan?.id) {
           this.$message.warning('请先勾选一条计划后再确定');
@@ -2568,7 +2772,14 @@
         try {
           await savePlanDotLine(payload);
           await this.refreshSchedulingView();
-          await this.loadPlanDotLineDetail(this.currentPlan);
+          if (!this.isOrderScheduling) {
+            await this.reloadPlanSchedulingPlansByStatus({
+              preserveCurrent: true
+            });
+          }
+          if (this.currentPlan?.id) {
+            await this.loadPlanDotLineDetail(this.currentPlan);
+          }
           this.dotLineFullscreen = false;
           this.$message.success('保存成功');
         } catch (e) {
@@ -2592,15 +2803,25 @@
         }
         if (!this.validateTaskRow(row, { checkSequence: false })) return;
 
-        const payload = this.buildSavePayload(0);
-        const loading = this.$loading({ lock: true, text: '保存中...' });
+        const payload = this.buildSavePayload(this.isOrderScheduling ? 0 : 1);
+        const loadingText = this.isOrderScheduling ? '保存中...' : '缓存中...';
+        const successText = this.isOrderScheduling ? '保存成功' : '缓存成功';
+        const errorText = this.isOrderScheduling ? '保存失败' : '缓存失败';
+        const loading = this.$loading({ lock: true, text: loadingText });
         try {
           await savePlanDotLine(payload);
           await this.refreshSchedulingView();
-          await this.loadPlanDotLineDetail(this.currentPlan);
-          this.$message.success('保存成功');
+          if (!this.isOrderScheduling) {
+            await this.reloadPlanSchedulingPlansByStatus({
+              preserveCurrent: true
+            });
+          }
+          if (this.currentPlan?.id) {
+            await this.loadPlanDotLineDetail(this.currentPlan);
+          }
+          this.$message.success(successText);
         } catch (e) {
-          this.$message.error(e.message || '保存失败');
+          this.$message.error(e.message || errorText);
         } finally {
           loading.close();
         }
@@ -2614,6 +2835,8 @@
         }
         this.form = {};
         this.entryReadonly = false;
+        this.orderDetailReadonly = false;
+        this.orderDetailActionMode = '';
         this.currentPlan = null;
         this.planRoutingPayload = null;
         this.taskList = [];
@@ -2705,8 +2928,11 @@
     }
 
     .el-tabs__item {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
       height: 36px;
-      line-height: 36px;
+      line-height: 1;
       margin-right: 8px;
       padding: 0 16px;
       border-radius: 7px 7px 0 0;

+ 276 - 34
src/views/productionPlan/components/newFactoryProductionScheduling/TaskConfigPanel.vue

@@ -49,19 +49,29 @@
         <div class="dispatch-toolbar">
           <div class="dispatch-toolbar-left">
             <el-button
+              v-if="showFirstTaskConfirmButton"
               type="primary"
               size="mini"
               :loading="dispatchToolbarLoading"
-              :disabled="isDispatchToolbarDisabled(1)"
+              :disabled="readonlyMode || isDispatchToolbarDisabled('confirm')"
+              @click="handleFirstTaskConfirm"
+            >
+              确定
+            </el-button>
+            <el-button
+              type="primary"
+              size="mini"
+              :loading="dispatchToolbarLoading"
+              :disabled="readonlyMode || isDispatchToolbarDisabled(1)"
               @click="handleDispatchToolbarAction(1)"
             >
-              派单
+              任务派单
             </el-button>
             <el-button
               type="primary"
               size="mini"
               :loading="dispatchToolbarLoading"
-              :disabled="isDispatchToolbarDisabled(2)"
+              :disabled="readonlyMode || isDispatchToolbarDisabled(2)"
               @click="handleDispatchToolbarAction(2)"
             >
               撤回
@@ -70,7 +80,7 @@
               type="primary"
               size="mini"
               :loading="dispatchToolbarLoading"
-              :disabled="isDispatchToolbarDisabled(3)"
+              :disabled="readonlyMode || isDispatchToolbarDisabled(3)"
               @click="handleDispatchToolbarAction(3)"
             >
               保存
@@ -79,7 +89,9 @@
               type="success"
               size="mini"
               :loading="dispatchToolbarLoading"
-              :disabled="isDispatchToolbarDisabled('addStation')"
+              :disabled="
+                readonlyMode || isDispatchToolbarDisabled('addStation')
+              "
               @click="handleDispatchAddStation"
             >
               添加工位
@@ -169,6 +181,7 @@
                   class="dispatch-state-checkbox"
                   :value="isCurrentPageDispatchChecked"
                   :indeterminate="isCurrentPageDispatchIndeterminate"
+                  :disabled="readonlyMode"
                   @change="handleCurrentPageDispatchCheckChange"
                 />
               </template>
@@ -176,6 +189,7 @@
                 <el-checkbox
                   class="dispatch-state-checkbox"
                   :value="isSelectedDispatchObject(row)"
+                  :disabled="readonlyMode"
                   @change="handleDispatchObjectCheckChange(row, $event)"
                 />
               </template>
@@ -258,7 +272,7 @@
                   :value="row.quantity"
                   placeholder="请输入数量"
                   class="config-table-control"
-                  :disabled="isDispatchRowAssigned(row)"
+                  :disabled="isDispatchRowControlDisabled(row)"
                   @input="handleDispatchRowQuantityInput(row, $event)"
                 />
               </template>
@@ -277,7 +291,7 @@
                   placeholder="开始时间"
                   clearable
                   class="config-table-control"
-                  :disabled="isDispatchRowAssigned(row)"
+                  :disabled="isDispatchRowControlDisabled(row)"
                   @change="handleDispatchRowStartTimeChange(row)"
                 />
               </template>
@@ -306,7 +320,7 @@
                     placeholder="完成时间"
                     clearable
                     class="config-table-control"
-                    :disabled="isDispatchRowAssigned(row)"
+                    :disabled="isDispatchRowControlDisabled(row)"
                     @change="handleDispatchRowEndTimeChange(row)"
                   />
                 </div>
@@ -351,7 +365,7 @@
       <div class="panel-header">
         <div class="panel-title">工艺配置</div>
         <div class="panel-actions">
-          <span v-if="isCurrentPlanReadonly" class="readonly-tip">
+          <span v-if="readonlyMode" class="readonly-tip">
             当前状态仅支持查看
           </span>
           <el-tooltip
@@ -368,10 +382,20 @@
               ></i>
             </el-button>
           </el-tooltip>
+          <el-button
+            v-if="showApproveButton"
+            type="primary"
+            size="mini"
+            :loading="approveLoading"
+            :disabled="approveButtonDisabled"
+            @click="$emit('approve')"
+          >
+            审批
+          </el-button>
           <el-button
             type="primary"
             size="mini"
-            :disabled="!currentPlan || dotLineLoading || isCurrentPlanReadonly"
+            :disabled="!currentPlan || dotLineLoading || readonlyMode"
             @click="$emit('submit')"
           >
             保存
@@ -413,7 +437,7 @@
                 v-model="row.executionType"
                 placeholder="执行模式"
                 clearable
-                :disabled="isCurrentPlanReadonly"
+                :disabled="readonlyMode"
                 class="config-table-control"
                 @change="$emit('execution-type-change', row)"
               >
@@ -438,7 +462,7 @@
                 placeholder="请选择"
                 clearable
                 filterable
-                :disabled="isCurrentPlanReadonly"
+                :disabled="readonlyMode"
                 class="config-table-control"
                 @change="$emit('execution-object-change', row)"
               >
@@ -456,7 +480,7 @@
                 placeholder="请选择"
                 clearable
                 filterable
-                :disabled="isCurrentPlanReadonly"
+                :disabled="readonlyMode"
                 class="config-table-control"
                 @change="$emit('execution-object-change', row)"
               >
@@ -497,12 +521,12 @@
                 :controls="false"
                 :precision="3"
                 placeholder="请输入数量"
-                :disabled="isCurrentPlanReadonly"
+                :disabled="readonlyMode"
                 class="config-table-control"
               />
             </template>
           </el-table-column>
-          <el-table-column label="执行开始时间" min-width="136" align="center">
+          <el-table-column label="执行开始时间" min-width="164" align="center">
             <template slot-scope="{ row }">
               <el-date-picker
                 v-model="row.executionStartTime"
@@ -510,13 +534,13 @@
                 value-format="yyyy-MM-dd HH:mm:ss"
                 placeholder="执行开始时间"
                 clearable
-                :disabled="isCurrentPlanReadonly"
+                :disabled="readonlyMode"
                 class="config-table-control"
                 @change="$emit('time-change', row, 'executionStartTime')"
               />
             </template>
           </el-table-column>
-          <el-table-column label="执行结束时间" min-width="136" align="center">
+          <el-table-column label="执行结束时间" min-width="164" align="center">
             <template slot-scope="{ row }">
               <div
                 :class="{
@@ -530,7 +554,7 @@
                   value-format="yyyy-MM-dd HH:mm:ss"
                   placeholder="执行结束时间"
                   clearable
-                  :disabled="isCurrentPlanReadonly"
+                  :disabled="readonlyMode"
                   class="config-table-control"
                   @change="$emit('time-change', row, 'executionEndTime')"
                 />
@@ -542,12 +566,10 @@
               <el-button
                 type="text"
                 size="small"
-                :disabled="
-                  !currentPlan || dotLineLoading || isCurrentPlanReadonly
-                "
+                :disabled="!currentPlan || dotLineLoading || readonlyMode"
                 @click="$emit('row-submit', row)"
               >
-                保存
+                缓存
               </el-button>
             </template>
           </el-table-column>
@@ -566,12 +588,18 @@
 <script>
   import chooseStation from '@/views/workOrder/components/chooseStation.vue';
   import {
+    checkAssignConfirm,
     listAssign,
+    parameterGetByCode,
     resetAssignee,
     taskAssignment,
     taskRevoked,
     taskSave
   } from '@/api/mainData/index.js';
+  import {
+    getcheckLoginUserIsTeamLeader,
+    releaseWorkOrder
+  } from '@/api/workOrder/index.js';
 
   const DISPATCH_TEAM_ID_KEYS = [
     'dispatchTeamId',
@@ -604,6 +632,10 @@
         type: Boolean,
         default: false
       },
+      orderDetailReadonly: {
+        type: Boolean,
+        default: false
+      },
       currentPlan: {
         type: Object,
         default: null
@@ -640,6 +672,18 @@
         type: Boolean,
         default: false
       },
+      showApproveButton: {
+        type: Boolean,
+        default: false
+      },
+      approveButtonDisabled: {
+        type: Boolean,
+        default: true
+      },
+      approveLoading: {
+        type: Boolean,
+        default: false
+      },
       tableMaxHeight: {
         type: Number,
         default: 390
@@ -713,6 +757,9 @@
       };
     },
     computed: {
+      readonlyMode() {
+        return this.orderDetailReadonly || this.isCurrentPlanReadonly;
+      },
       dispatchVisibleColumns: {
         get() {
           return this.dispatchColumnOptions
@@ -749,6 +796,13 @@
           null
         );
       },
+      showFirstTaskConfirmButton() {
+        return (
+          this.orderDispatchStyle &&
+          !this.readonlyMode &&
+          this.isFirstDispatchTask(this.activeTask)
+        );
+      },
       reportTypeValue() {
         return this.normalizeDispatchReportType(this.dispatchReportType);
       },
@@ -781,7 +835,11 @@
         const start =
           plan.startTime || plan.planStartTime || plan.executionStartTime || '';
         const end =
-          plan.endTime || plan.planCompleteTime || plan.executionEndTime || '';
+          plan.endTime ||
+          plan.planEndTime ||
+          plan.planCompleteTime ||
+          plan.executionEndTime ||
+          '';
         const startText = this.formatDispatchDateTime(start);
         const endText = this.formatDispatchDateTime(end);
         if (startText && endText) {
@@ -832,8 +890,8 @@
                   : task?.executionEndTime ||
                     assignment?.executionEndTime ||
                     defaultEndTime,
-              status: task?.status || assignment?.status,
-              changeId: task?.changeId || assignment?.changeId,
+              status: assignment?.status || '',
+              changeId: assignment?.changeId || '',
               teamTimeIds: task?.teamTimeIds || assignment?.teamTimeIds,
               weight: task?.weight || assignment?.weight || item.weight || '',
               code: item.code || item.workstationCode || '',
@@ -996,7 +1054,11 @@
       getDispatchPlanEndTime() {
         const plan = this.currentPlan || {};
         return (
-          plan.endTime || plan.planCompleteTime || plan.executionEndTime || ''
+          plan.endTime ||
+          plan.planEndTime ||
+          plan.planCompleteTime ||
+          plan.executionEndTime ||
+          ''
         );
       },
       getDispatchTaskDefaultStartTime(row = this.activeTask) {
@@ -1032,6 +1094,32 @@
             ''
         );
       },
+      isFirstDispatchTask(row) {
+        if (!row) {
+          return false;
+        }
+        const firstTaskId =
+          this.currentPlan?.firstTaskId ??
+          this.currentPlan?.taskId ??
+          this.currentPlan?.sourceTaskId ??
+          '';
+        if (
+          firstTaskId !== null &&
+          firstTaskId !== undefined &&
+          firstTaskId !== ''
+        ) {
+          return (
+            String(
+              row.sourceTaskId ?? row.firstTaskId ?? row.taskId ?? row.id
+            ) === String(firstTaskId)
+          );
+        }
+        const index = this.dispatchTaskTabs.findIndex(
+          (item) =>
+            this.dispatchTaskGroupKey(item) === this.dispatchTaskGroupKey(row)
+        );
+        return index === 0;
+      },
       dispatchObjectRowKey(row) {
         const rawId =
           row?.rawId ??
@@ -1214,6 +1302,9 @@
         return ids;
       },
       getDispatchToolbarPermissionState(action) {
+        if (this.readonlyMode) {
+          return { allowed: false, message: '当前状态仅支持查看' };
+        }
         if (!this.activeTask) {
           return { allowed: false, message: '请选择工序' };
         }
@@ -1317,8 +1408,13 @@
         const statusText = this.getDispatchStatusText(data?.status);
         return statusText === '已派单' || statusText === '派单';
       },
+      isDispatchRowControlDisabled() {
+        return this.readonlyMode;
+      },
       getDispatchTableRowClassName({ row }) {
-        return this.isDispatchRowAssigned(row) ? 'dispatch-row-assigned' : '';
+        return this.readonlyMode && this.isDispatchRowAssigned(row)
+          ? 'dispatch-row-assigned'
+          : '';
       },
       normalizeDispatchAssignment(item) {
         const assigneeId =
@@ -1608,7 +1704,7 @@
         return task;
       },
       handleDispatchRowQuantityInput(row, value) {
-        if (this.isDispatchRowAssigned(row)) {
+        if (this.isDispatchRowControlDisabled(row)) {
           return;
         }
         this.$set(row, 'quantity', value);
@@ -1642,6 +1738,9 @@
         row.quantity = this.formatDispatchRowQuantity(task.quantity);
       },
       handleDispatchObjectCheckChange(row, checked) {
+        if (this.readonlyMode) {
+          return;
+        }
         if (checked) {
           const task = this.ensureDispatchObjectSelected(row);
           if (task && row.quantity) {
@@ -1771,7 +1870,7 @@
         this.$emit('time-change', row, 'executionEndTime');
       },
       handleDispatchRowStartTimeChange(row) {
-        if (this.isDispatchRowAssigned(row)) {
+        if (this.isDispatchRowControlDisabled(row)) {
           return;
         }
         const task = this.ensureDispatchObjectSelected(row);
@@ -1789,7 +1888,7 @@
         });
       },
       handleDispatchRowEndTimeChange(row) {
-        if (this.isDispatchRowAssigned(row)) {
+        if (this.isDispatchRowControlDisabled(row)) {
           return;
         }
         const task = this.ensureDispatchObjectSelected(row);
@@ -1805,20 +1904,24 @@
         });
       },
       handleCurrentPageDispatchCheckChange(checked) {
+        if (this.readonlyMode) {
+          return;
+        }
         this.pagedDispatchObjectRows.forEach((row) => {
           this.handleDispatchObjectCheckChange(row, checked);
         });
       },
       getDispatchActionRowData(row) {
         const task = this.getDispatchRowTask(row);
+        const assignment = this.getDispatchAssignment(row);
         return {
           ...row,
           quantity: task?.quantity ?? row.quantity,
           executionStartTime:
             task?.executionStartTime ?? row.executionStartTime,
           executionEndTime: task?.executionEndTime ?? row.executionEndTime,
-          changeId: task?.changeId ?? row.changeId,
-          status: task?.status ?? row.status,
+          changeId: assignment?.changeId ?? row.changeId,
+          status: assignment?.status ?? row.status,
           teamTimeIds: task?.teamTimeIds ?? row.teamTimeIds,
           weight: task?.weight ?? row.weight
         };
@@ -1949,6 +2052,123 @@
           assignees: this.buildDispatchAssignees(rows)
         };
       },
+      validateFirstTaskConfirmReportType() {
+        if (this.reportTypeValue === 0 || this.reportTypeValue === 1) {
+          return true;
+        }
+        this.$message.warning('请先选择报工类型');
+        return false;
+      },
+      async validateFirstTaskConfirmLeader(teamId) {
+        try {
+          const permissionRes = await parameterGetByCode({
+            code: 'production_order_dispatch_permission'
+          });
+          if (String(permissionRes?.value || 0) !== '1') {
+            return true;
+          }
+          const isTeamLeader = await getcheckLoginUserIsTeamLeader(teamId);
+          if (!isTeamLeader) {
+            this.$message.warning('当前班组不是对应组长');
+            return false;
+          }
+          return true;
+        } catch (e) {
+          this.$message.error(e.message || '校验组长身份失败');
+          return false;
+        }
+      },
+      getFirstTaskAssignedRows() {
+        return this.dispatchObjectRows.filter((row) =>
+          this.isDispatchRowAssigned(row)
+        );
+      },
+      buildFirstTaskConfirmPayload(assignedRows) {
+        const row = this.activeTask || {};
+        const assignType = Number(row.assignType ?? 1);
+        const dispatchIds = assignedRows.map((item) =>
+          this.getDispatchAssigneeId(item)
+        );
+        const payload = {
+          assignType,
+          singleReport: this.reportTypeValue,
+          teamId: this.getDispatchTeamId(),
+          dispatchingMethod:
+            this.currentPlan?.dispatchingMethod ??
+            this.currentPlan?.taskAss ??
+            0,
+          crewIds: [],
+          workstationIds: [],
+          factoryLineIds: [],
+          id: this.getDispatchWorkOrderId() || this.currentPlan?.id || '',
+          planStartTime: this.getDispatchPlanStartTime(),
+          planEndTime: this.getDispatchPlanEndTime()
+        };
+        if (assignType === 2) {
+          payload.crewIds = dispatchIds;
+        } else if (assignType === 3) {
+          payload.factoryLineIds = dispatchIds;
+        } else {
+          payload.workstationIds = dispatchIds;
+        }
+        return payload;
+      },
+      async handleFirstTaskConfirm() {
+        if (!this.validateDispatchToolbarPermission('confirm')) {
+          return;
+        }
+        if (!this.validateFirstTaskConfirmReportType()) {
+          return;
+        }
+        const teamId = this.getDispatchTeamId();
+        if (!teamId) {
+          this.$message.warning('请先选择班组');
+          return;
+        }
+        const workCenterId = this.getDispatchWorkCenterId(this.activeTask);
+        const workOrderId =
+          this.getDispatchWorkOrderId() || this.currentPlan?.id;
+        if (!workCenterId || !workOrderId) {
+          this.$message.warning('当前订单或工序信息不完整,无法操作');
+          return;
+        }
+        if (!(await this.validateFirstTaskConfirmLeader(teamId))) {
+          return;
+        }
+        const assignedRows = this.getFirstTaskAssignedRows();
+        if (!assignedRows.length) {
+          const taskName =
+            this.activeTask?.taskName || this.activeTask?.name || '';
+          this.$message.warning(`请您先进行首工序任务(${taskName}工序)派单`);
+          return;
+        }
+        this.dispatchToolbarLoading = true;
+        try {
+          const checked = await checkAssignConfirm({
+            teamId,
+            workCenterId,
+            workOrderId
+          });
+          if (!checked) {
+            return;
+          }
+          const response = await releaseWorkOrder(
+            this.buildFirstTaskConfirmPayload(assignedRows)
+          );
+          if (response) {
+            this.$message.success('操作成功');
+            await this.loadDispatchAssignData();
+            this.$emit('dispatch-action-success', {
+              type: 'confirm',
+              task: this.activeTask
+            });
+          }
+        } catch (e) {
+          this.$message.error(e.message || '操作失败');
+        } finally {
+          this.dispatchToolbarLoading = false;
+        }
+      },
       async handleDispatchToolbarAction(type) {
         if (!this.validateDispatchToolbarPermission(type)) {
           return;
@@ -2236,8 +2456,11 @@
     }
 
     .el-tabs__item {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
       height: 34px;
-      line-height: 34px;
+      line-height: 1;
       min-width: 82px;
       margin-right: 6px;
       padding: 0 14px;
@@ -2345,6 +2568,17 @@
     box-shadow: 0 3px 8px rgba(95, 194, 56, 0.2);
   }
 
+  .dispatch-toolbar-left ::v-deep .el-button.is-disabled,
+  .dispatch-toolbar-left ::v-deep .el-button.is-disabled:hover,
+  .dispatch-toolbar-left ::v-deep .el-button.is-disabled:focus,
+  .dispatch-toolbar-left ::v-deep .el-button.is-disabled:active {
+    color: #9aa6b2;
+    border-color: #d8e1eb;
+    background: #edf2f7;
+    box-shadow: none;
+    cursor: not-allowed;
+  }
+
   .dispatch-assign-mode {
     flex: 1 1 auto;
     justify-content: center;
@@ -2592,7 +2826,7 @@
 
     .config-table-control.el-date-editor .el-input__inner {
       padding-left: 28px;
-      padding-right: 6px;
+      padding-right: 20px;
     }
 
     .el-input-number .el-input__inner {
@@ -2615,6 +2849,14 @@
       height: 26px;
       line-height: 26px;
     }
+
+    .config-table-control.el-select .el-input__inner {
+      padding-right: 30px;
+    }
+
+    .config-table-control .el-input--prefix .el-input__inner {
+      padding-left: 28px;
+    }
   }
 
   .dispatch-table ::v-deep {

+ 19 - 3
src/views/productionPlan/index.vue

@@ -420,9 +420,7 @@
           <el-link
             type="primary"
             :underline="false"
-            v-if="
-              row.approvalStatus != 1 && (row.status == 3 || row.status == 2)
-            "
+            v-if="canProductionPreparations(row)"
             @click="productionPreparations(row)"
           >
             生产准备
@@ -703,6 +701,21 @@
 
         return canRelease;
       },
+      canProductionPreparations(row) {
+        if (row.approvalStatus == 1) {
+          return false;
+        }
+
+        const clientEnvironmentId =
+          this.clientEnvironmentId ||
+          this.$store.state.user.info.clientEnvironmentId;
+
+        if (clientEnvironmentId == 9) {
+          return row.status == 4;
+        }
+
+        return row.status == 3 || row.status == 2;
+      },
       setColumns() {
         let clientEnvironmentId =
           this.$store.state.user.info.clientEnvironmentId;
@@ -1537,6 +1550,9 @@
 
       //生产前准备
       productionPreparations(item) {
+        if (!this.canProductionPreparations(item)) {
+          return this.$message.warning('计划发布完成后才能进行生产准备!');
+        }
         this.$refs.checkProductionPreparationsRef.open(item);
       },
 

+ 1 - 1
src/views/workOrder/index.vue

@@ -998,7 +998,7 @@
         // 订单表格不再取勾选缓存,统一使用订单列表接口返回的当月订单数据。
         this.$refs.newFactoryProductionSchedulingRef.open({
           ids,
-          readonly: true,
+          readonly: false,
           mode: 'order',
           selectedWorkOrders: list,
           orderListLoader: (dateRange, extraWhere) =>

+ 2 - 2
vue.config.js

@@ -41,8 +41,8 @@ module.exports = {
         // target: 'http://192.168.1.211:18086',
         // target: 'http://192.168.1.251:18186',
         // target: 'http://192.168.1.102:18086',
-        target: 'http://aiot.zoomwin.com.cn:51005/api',
-        // target: 'http://192.168.1.125:18086',
+        // target: 'http://aiot.zoomwin.com.cn:51005/api',
+        target: 'http://192.168.1.125:18086',
 
         // target: 'http://192.168.1.116:18086',