Просмотр исходного кода

班组排程和工厂排产的界面优化

695593266@qq.com 3 месяцев назад
Родитель
Сommit
670cb83af9

+ 38 - 1
src/views/productionPlan/components/gantt/components/CalendarTable.vue

@@ -164,7 +164,8 @@
   .calendar-table {
     width: max-content;
     min-width: 100%;
-    border-collapse: collapse;
+    border-collapse: separate;
+    border-spacing: 0;
     table-layout: fixed;
   }
 
@@ -176,14 +177,20 @@
   }
 
   .calendar-th {
+    position: sticky;
+    top: 0;
+    z-index: 6;
     height: 36px;
     background: #f7fafc;
     color: #263445;
     font-weight: 600;
     font-size: 14px;
+    box-shadow: 0 1px 0 #dbe7f2;
   }
 
   .calendar-th--month {
+    top: 0;
+    z-index: 6;
     height: 42px;
     font-size: 18px;
     letter-spacing: 0;
@@ -203,7 +210,32 @@
     min-width: 160px;
   }
 
+  .calendar-th--group,
+  .calendar-th--task {
+    top: 0;
+    z-index: 9;
+    background: linear-gradient(180deg, #f8fbff 0%, #eef5fb 100%);
+  }
+
+  .calendar-th--group,
+  .calendar-td--group {
+    position: sticky;
+    left: 0;
+  }
+
+  .calendar-th--task,
+  .calendar-td--task {
+    position: sticky;
+    left: 124px;
+  }
+
+  .calendar-th--task {
+    box-shadow: 4px 0 10px rgba(31, 45, 61, 0.08), 0 1px 0 #dbe7f2;
+  }
+
   .calendar-th--day {
+    top: 42px;
+    z-index: 5;
     width: 64px;
     min-width: 64px;
     font-size: 14px;
@@ -222,6 +254,7 @@
 
   .calendar-td--group,
   .calendar-td--task {
+    z-index: 3;
     padding: 8px 10px;
     background: #fff;
     color: #364152;
@@ -237,6 +270,10 @@
     font-weight: 600;
   }
 
+  .calendar-td--task {
+    box-shadow: 4px 0 10px rgba(31, 45, 61, 0.05);
+  }
+
   .calendar-td--day {
     height: 62px;
     background: #fff;

+ 47 - 13
src/views/productionPlan/components/gantt/components/GanttFilterBar.vue

@@ -89,18 +89,33 @@
 
 <style lang="scss" scoped>
   .calendar-toolbar {
-    padding: 12px 16px;
-    background: #fff;
-    border: 1px solid #e6edf5;
+    padding: 6px 10px;
+    background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
+    border: 1px solid #dbe6f1;
     border-radius: 8px;
-    box-shadow: 0 4px 14px rgba(31, 45, 61, 0.05);
+    box-shadow: 0 4px 12px rgba(31, 45, 61, 0.05);
+  }
+
+  .calendar-toolbar.gantt-filter-inline {
+    flex: 1 1 auto;
+    min-width: 0;
+    padding: 0;
+    border: 0;
+    border-radius: 0;
+    background: transparent;
+    box-shadow: none;
   }
 
   .calendar-filters {
     display: flex;
     align-items: center;
     flex-wrap: wrap;
-    gap: 10px 14px;
+    gap: 6px 10px;
+  }
+
+  .calendar-toolbar.gantt-filter-inline .calendar-filters {
+    flex-wrap: nowrap;
+    gap: 5px;
   }
 
   .calendar-filter-item {
@@ -111,38 +126,57 @@
   }
 
   .calendar-filter-label {
-    color: #4b5563;
+    color: #465568;
     font-size: 14px;
     font-weight: 600;
     white-space: nowrap;
   }
 
+  .calendar-toolbar.gantt-filter-inline .calendar-filter-label {
+    font-size: 13px;
+  }
+
   .calendar-filter-item :deep(.el-input) {
-    width: 250px;
+    width: 200px;
+  }
+
+  .calendar-toolbar.gantt-filter-inline .calendar-filter-item :deep(.el-input) {
+    width: 138px;
   }
 
   .calendar-filter-item :deep(.el-input__inner) {
-    height: 34px;
-    border-color: #e1e8f0;
-    border-radius: 4px;
+    height: 28px;
+    border-color: #dce7f2;
+    border-radius: 6px;
     background: #fbfdff;
     color: #364152;
   }
 
+  .calendar-toolbar.gantt-filter-inline
+    .calendar-filter-item
+    :deep(.el-input__inner) {
+    height: 26px;
+  }
+
   .calendar-filter-item :deep(.el-input__inner:focus) {
     border-color: #1f8ceb;
-    box-shadow: 0 0 0 2px rgba(31, 140, 235, 0.08);
+    box-shadow: 0 0 0 2px rgba(31, 140, 235, 0.1);
   }
 
   .calendar-filters > .el-button {
     margin-left: 0;
-    border-radius: 4px;
+    border-radius: 6px;
     font-weight: 600;
   }
 
+  .calendar-toolbar.gantt-filter-inline .calendar-filters > .el-button {
+    height: 26px;
+    padding: 6px 9px;
+  }
+
   @media (max-width: 1440px) {
     .calendar-filter-item :deep(.el-input) {
-      width: 210px;
+      width: 190px;
     }
   }
 

+ 35 - 35
src/views/productionPlan/components/gantt/components/ResourcePanel.vue

@@ -90,7 +90,7 @@
 
 <style lang="scss" scoped>
   .resource-panel {
-    width: 216px;
+    width: 196px;
     flex-shrink: 0;
     position: sticky;
     top: 0;
@@ -98,46 +98,46 @@
     height: 100%;
     display: flex;
     flex-direction: column;
-    background: #fff;
-    border: 1px solid #e6edf5;
-    border-radius: 8px;
+    background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
+    border: 1px solid #dbe6f1;
+    border-radius: 9px;
     overflow: hidden;
-    box-shadow: 0 4px 14px rgba(31, 45, 61, 0.06);
+    box-shadow: 0 5px 14px rgba(31, 45, 61, 0.06);
 
     &.is-fullscreen {
-      width: 224px;
+      width: 208px;
       height: 100%;
     }
   }
 
   .resource-tabs {
     display: flex;
-    gap: 6px;
-    padding: 10px;
-    background: linear-gradient(180deg, #fbfdff 0%, #f3f7fb 100%);
-    border-bottom: 1px solid #edf2f7;
+    gap: 4px;
+    padding: 6px;
+    background: linear-gradient(180deg, #f9fcff 0%, #f1f6fb 100%);
+    border-bottom: 1px solid #e6eef7;
   }
 
   .resource-tab {
     flex: 1;
-    height: 32px;
-    line-height: 32px;
+    height: 27px;
+    line-height: 27px;
     text-align: center;
     font-size: 12px;
     font-weight: 500;
     color: #5f6b7a;
     background: transparent;
     border: 1px solid transparent;
-    border-radius: 5px;
+    border-radius: 6px;
     cursor: pointer;
     user-select: none;
     transition: all 0.2s ease;
 
     &.is-active {
       color: #fff;
-      background: #1f8ceb;
+      background: linear-gradient(180deg, #2a9af0 0%, #1684df 100%);
       border-color: #1f8ceb;
-      box-shadow: 0 3px 8px rgba(31, 140, 235, 0.22);
+      box-shadow: 0 2px 6px rgba(31, 140, 235, 0.2);
     }
 
     &:not(.is-active):hover {
@@ -148,8 +148,8 @@
   }
 
   .resource-search {
-    padding: 10px;
-    border-bottom: 1px solid #edf2f7;
+    padding: 6px;
+    border-bottom: 1px solid #e6eef7;
     display: flex;
     align-items: center;
     gap: 6px;
@@ -159,9 +159,9 @@
     }
 
     :deep(.el-input__inner) {
-      height: 30px;
+      height: 26px;
       border-color: #e1e8f0;
-      border-radius: 4px;
+      border-radius: 6px;
       background: #fbfdff;
       color: #364152;
     }
@@ -173,7 +173,7 @@
 
     :deep(.el-button) {
       margin-left: 0;
-      border-radius: 4px;
+      border-radius: 6px;
       font-weight: 600;
     }
   }
@@ -181,7 +181,7 @@
   .resource-list {
     flex: 1;
     overflow-y: auto;
-    padding: 8px 8px 10px;
+    padding: 5px 6px 7px;
     background: #fff;
   }
 
@@ -192,7 +192,7 @@
   .resource-list::-webkit-scrollbar-thumb {
     border: 2px solid #fff;
     border-radius: 999px;
-    background: #c9d4e2;
+    background: #bdcad8;
   }
 
   .resource-list::-webkit-scrollbar-thumb:hover {
@@ -201,33 +201,33 @@
 
   .resource-item,
   .resource-empty {
-    min-height: 34px;
-    padding: 8px 10px;
-    line-height: 20px;
-    font-size: 13px;
+    min-height: 27px;
+    padding: 5px 8px;
+    line-height: 17px;
+    font-size: 12px;
     color: #364152;
     word-break: break-all;
   }
 
   .resource-item {
     position: relative;
-    margin-bottom: 5px;
+    margin-bottom: 3px;
     border: 1px solid transparent;
-    border-radius: 5px;
+    border-radius: 7px;
     cursor: pointer;
     transition: all 0.2s ease;
 
     &.is-selected {
-      color: #1683d8;
-      background: #edf7ff;
-      border-color: #d7ebff;
+      color: #0f75bd;
+      background: linear-gradient(90deg, #edf7ff 0%, #f9fcff 100%);
+      border-color: #c4e1f8;
       font-weight: 600;
     }
 
     &.is-selected::before {
       position: absolute;
-      top: 8px;
-      bottom: 8px;
+      top: 7px;
+      bottom: 7px;
       left: 0;
       width: 3px;
       border-radius: 0 3px 3px 0;
@@ -236,8 +236,8 @@
     }
 
     &:hover {
-      background: #f6f9fc;
-      border-color: #e7edf5;
+      background: #f4f9fe;
+      border-color: #dcebf8;
     }
   }
 

+ 156 - 80
src/views/productionPlan/components/gantt/project-gantt.vue

@@ -17,6 +17,32 @@
           工作日历
         </el-button>
         <slot name="toolbar"></slot>
+        <gantt-filter-bar
+          v-show="viewMode === 'gantt'"
+          class="gantt-filter-inline"
+          :query="ganttQuery"
+          :plan-code-label="
+            resourceLabelMode === 'order'
+              ? '\u751f\u4ea7\u8ba2\u5355\u53f7'
+              : '\u8ba1\u5212\u7f16\u53f7'
+          "
+          @update:query="ganttQuery = $event"
+          @apply="applyGanttFilters"
+          @reset="resetGanttFilters"
+        />
+        <gantt-filter-bar
+          v-show="viewMode === 'calendar'"
+          class="gantt-filter-inline"
+          :query="calendarQuery"
+          :plan-code-label="
+            resourceLabelMode === 'order'
+              ? '\u751f\u4ea7\u8ba2\u5355\u53f7'
+              : '\u8ba1\u5212\u7f16\u53f7'
+          "
+          @update:query="calendarQuery = $event"
+          @apply="applyCalendarFilters"
+          @reset="resetCalendarFilters"
+        />
       </div>
       <el-button
         size="mini"
@@ -44,7 +70,9 @@
           v-show="!isFullscreen"
           :query="ganttQuery"
           :plan-code-label="
-            resourceLabelMode === 'order' ? '生产订单号' : '计划编号'
+            resourceLabelMode === 'order'
+              ? '\u751f\u4ea7\u8ba2\u5355\u53f7'
+              : '\u8ba1\u5212\u7f16\u53f7'
           "
           @update:query="ganttQuery = $event"
           @apply="applyGanttFilters"
@@ -59,7 +87,9 @@
           v-show="!isFullscreen"
           :query="calendarQuery"
           :plan-code-label="
-            resourceLabelMode === 'order' ? '生产订单号' : '计划编号'
+            resourceLabelMode === 'order'
+              ? '\u751f\u4ea7\u8ba2\u5355\u53f7'
+              : '\u8ba1\u5212\u7f16\u53f7'
           "
           @update:query="calendarQuery = $event"
           @apply="applyCalendarFilters"
@@ -413,7 +443,7 @@
                 this.getActiveResourceName(item) ||
                 item.teamName ||
                 planMeta.teamName ||
-                '未分组',
+                '\u672a\u5206\u7ec4',
               productionLineName:
                 item.productionLineName || planMeta.productionLineName || '',
               factoryName: planMeta.factoryName || '',
@@ -427,7 +457,7 @@
               barLabel:
                 [item.title, item.name || item.text || item.code]
                   .filter(Boolean)
-                  .join(':') || `任务${index + 1}`,
+                  .join(' / ') || `\u4efb\u52a1${index + 1}`,
               color: item.color || resolveTaskColor(item.type),
               startDate,
               endDate
@@ -533,7 +563,9 @@
           } else {
             result.push({
               key,
-              label: `${day.date.getFullYear()}年${day.date.getMonth() + 1}月`,
+              label: `${day.date.getFullYear()}\u5e74${
+                day.date.getMonth() + 1
+              }\u6708`,
               span: 1
             });
           }
@@ -546,7 +578,7 @@
         }
         const rowMap = new Map();
         this.calendarFilteredTasks.forEach((item) => {
-          const groupKey = item.groupLabel || '未分组';
+          const groupKey = item.groupLabel || '\u672a\u5206\u7ec4';
           if (!rowMap.has(groupKey)) {
             rowMap.set(groupKey, []);
           }
@@ -671,16 +703,16 @@
             item.resourceName ||
             item.name ||
             item.teamName ||
-            '未分组'
+            '\u672a\u5206\u7ec4'
           );
         }
-        return item.name || item.teamName || '未分组';
+        return item.name || item.teamName || '\u672a\u5206\u7ec4';
       },
       getCalendarPersonGroupName(item) {
         if (this.resourceLabelMode === 'order') {
-          return this.getTaskPersonName(item) || '未分组';
+          return this.getTaskPersonName(item) || '\u672a\u5206\u7ec4';
         }
-        return this.getTaskPersonName(item) || '未分组';
+        return this.getTaskPersonName(item) || '\u672a\u5206\u7ec4';
       },
       getCalendarProductionLineName(item, resourceList = []) {
         if (this.resourceLabelMode === 'order') {
@@ -755,7 +787,7 @@
           item.leaderUserName ||
           '';
         return String(name || '')
-          .replace(/^人员[::]/, '')
+          .replace(/^\u4eba\u5458[:\uff1a]/, '')
           .trim();
       },
       getActiveResourceName(item) {
@@ -1183,7 +1215,7 @@
             css: daysStyle
           }
         ];
-        //这里在时间轴表格内的cell上添加class
+        // 这里在时间轴表格内的cell上添加class
         gantt.templates.timeline_cell_class = function (item, date) {
           if (date.getDay() == 0 || date.getDay() == 6) {
             return 'weekend';
@@ -1201,10 +1233,10 @@
           return ['task-222', task.customClass].filter(Boolean).join(' ');
         };
 
-        gantt.config.scale_height = 64;
-        gantt.config.task_height = 26;
-        gantt.config.row_height = 34;
-        gantt.config.min_column_width = 42;
+        gantt.config.scale_height = 50;
+        gantt.config.task_height = 22;
+        gantt.config.row_height = 27;
+        gantt.config.min_column_width = 40;
         gantt.config.show_task_cells = false;
         gantt.plugins({
           marker: true
@@ -1232,24 +1264,28 @@
           if (box) {
             box.parentNode.removeChild(box);
           }
-          let classList = e.target.className.split(' ');
+          let classList = String(e?.target?.className || '').split(' ');
           // 这里自定义了展开/收起树形的点击事件
           if (classList.includes('gantt_open')) {
-            gantt.open(id); // 展开
+            gantt.open(id); // 灞曞紑
             return true;
           }
           if (classList.includes('gantt_close')) {
-            gantt.close(id); // 收起
+            gantt.close(id); // 鏀惰捣
             return true;
           }
           // 这里是其他的点击事件
           let task = _this.ganttData.find((item) => {
             return item.id == id;
           });
+          if (!task) {
+            return true;
+          }
           if (task.taskType === 'stage') {
             // toDo()
             return true;
           }
+          return true;
         });
         gantt.i18n.setLocale('cn');
 
@@ -1402,17 +1438,25 @@
 </script>
 <style lang="scss" scoped>
   .project-gantt {
+    position: relative;
     display: flex;
     flex-direction: column;
     width: 100%;
     height: 100%;
-    min-height: 280px;
-    gap: 10px;
-    padding: 10px;
+    min-height: 260px;
+    gap: 6px;
+    padding: 6px;
     box-sizing: border-box;
-    border: 1px solid #edf2f7;
-    border-radius: 8px;
-    background: #f7f9fc;
+    border: 1px solid #d8e3ef;
+    border-radius: 10px;
+    background: linear-gradient(
+        135deg,
+        rgba(31, 140, 235, 0.06) 0%,
+        transparent 32%
+      ),
+      linear-gradient(180deg, #ffffff 0%, #f7fafc 100%);
+    box-shadow: 0 10px 26px rgba(26, 39, 55, 0.08);
+    overflow: hidden;
 
     &.is-fullscreen {
       position: fixed;
@@ -1423,33 +1467,62 @@
       width: 100vw;
       height: 100vh;
       z-index: 9999;
-      background: #f6f8fb;
-      padding: 16px;
+      background: #eef4fa;
+      padding: 10px;
       box-sizing: border-box;
     }
   }
+  .project-gantt::before {
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    height: 2px;
+    background: linear-gradient(90deg, #2489e8 0%, #14b8a6 60%, #7aa7ff 100%);
+    content: '';
+    pointer-events: none;
+  }
   .view-switch {
     display: flex;
     align-items: center;
     justify-content: space-between;
-    gap: 8px;
-    min-height: 46px;
-    padding: 7px 10px;
+    gap: 6px;
+    min-height: 34px;
+    padding: 3px 6px;
     box-sizing: border-box;
-    border: 1px solid #e7edf5;
-    border-radius: 7px;
-    background: #fff;
-    box-shadow: 0 2px 8px rgba(31, 45, 61, 0.04);
+    border: 1px solid #dfe9f4;
+    border-radius: 8px;
+    background: rgba(255, 255, 255, 0.94);
+    box-shadow: 0 3px 12px rgba(31, 45, 61, 0.05);
   }
   .view-switch-left {
     display: flex;
     align-items: center;
-    gap: 10px;
+    gap: 6px;
+    flex: 1 1 auto;
     min-width: 0;
-    flex-wrap: wrap;
+    overflow-x: auto;
+    flex-wrap: nowrap;
+    padding-bottom: 1px;
+  }
+  .view-switch-left::-webkit-scrollbar {
+    height: 4px;
+  }
+  .view-switch-left::-webkit-scrollbar-thumb {
+    border-radius: 999px;
+    background: #c9d4e2;
+  }
+  .view-switch-left ::v-deep .el-date-editor--daterange.el-input__inner {
+    width: 330px;
+    flex: 0 0 auto;
+  }
+  .gantt-view ::v-deep > .calendar-toolbar,
+  .calendar-view ::v-deep > .calendar-toolbar {
+    display: none;
   }
   .view-switch ::v-deep .el-button {
-    border-radius: 4px;
+    flex: 0 0 auto;
+    border-radius: 7px;
     font-weight: 600;
     transition: all 0.18s ease;
   }
@@ -1460,8 +1533,8 @@
   }
   .view-switch ::v-deep .el-button--primary {
     border-color: #1f8ceb;
-    background: #1f8ceb;
-    box-shadow: 0 3px 8px rgba(31, 140, 235, 0.22);
+    background: linear-gradient(180deg, #2a9af0 0%, #1684df 100%);
+    box-shadow: 0 3px 8px rgba(31, 140, 235, 0.2);
   }
   .calendar-view {
     flex: 1;
@@ -1469,7 +1542,7 @@
     min-height: 0;
     display: flex;
     flex-direction: column;
-    gap: 12px;
+    gap: 6px;
   }
   .gantt-view {
     flex: 1;
@@ -1477,14 +1550,14 @@
     min-height: 0;
     display: flex;
     flex-direction: column;
-    gap: 12px;
+    gap: 6px;
   }
   .project-gantt__body {
     flex: 1;
     min-height: 0;
     display: flex;
     align-items: stretch;
-    gap: 10px;
+    gap: 6px;
   }
   .gantt-box {
     flex: 1;
@@ -1492,10 +1565,10 @@
     width: calc(100%);
     height: calc(100%);
     background: #fff;
-    border: 1px solid #e6edf5;
-    border-radius: 8px;
+    border: 1px solid #d8e3ef;
+    border-radius: 9px;
     overflow: hidden;
-    box-shadow: 0 4px 14px rgba(31, 45, 61, 0.06);
+    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.74);
   }
   .gantt-hover-tooltip {
     position: fixed;
@@ -1528,7 +1601,7 @@
 
   :deep(.gantt_ver_scroll),
   :deep(.gantt_hor_scroll) {
-    background: #f4f7fb;
+    background: #eef4fa;
   }
 
   :deep(.gantt_ver_scroll::-webkit-scrollbar),
@@ -1543,7 +1616,7 @@
   :deep(.gantt_data_area::-webkit-scrollbar-thumb) {
     border: 2px solid #f8fafc;
     border-radius: 999px;
-    background: #c9d1dc;
+    background: #bcc8d6;
   }
 
   :deep(.gantt_ver_scroll::-webkit-scrollbar-thumb:hover),
@@ -1553,34 +1626,34 @@
   }
 
   :deep(.gantt_task_scale) {
-    border-bottom: 1px solid #dde7f0;
+    border-bottom: 1px solid #d9e4ef;
 
     .gantt_scale_line > .gantt_scale_cell {
-      color: #3f4a56;
+      color: #344054;
       font-size: 11px;
-      border-right: 1px solid #e6edf5;
+      border-right: 1px solid #e7edf5;
       background: #fbfdff;
 
       &.today {
-        background: #e5f2ff;
-        color: #1683d8 !important;
-        box-shadow: inset 0 -2px 0 #1f8ceb;
+        background: #e8f4ff;
+        color: #0f75bd !important;
+        box-shadow: inset 0 -2px 0 #14b8a6;
       }
     }
   }
   :deep(.gantt_scale_line) {
-    border-bottom: 1px solid #dde7f0;
+    border-bottom: 1px solid #d9e4ef;
   }
   :deep(.gantt_scale_line:first-child .gantt_scale_cell) {
-    height: 30px;
-    line-height: 30px;
-    font-size: 15px;
+    height: 24px;
+    line-height: 24px;
+    font-size: 14px;
     font-weight: 600;
-    color: #2f3b4a;
-    background: linear-gradient(180deg, #f8fbff 0%, #eef5fb 100%);
+    color: #253244;
+    background: linear-gradient(180deg, #f9fcff 0%, #eef5fb 100%);
   }
   :deep(.gantt_scale_line:last-child .gantt_scale_cell) {
-    height: 38px;
+    height: 26px;
     padding: 0;
     box-sizing: border-box;
   }
@@ -1594,14 +1667,14 @@
     text-align: center;
   }
   :deep(.gantt-day-scale__date) {
-    font-size: 12px;
-    line-height: 16px;
+    font-size: 11px;
+    line-height: 13px;
     color: #606266;
   }
   :deep(.gantt-day-scale__week) {
-    margin-top: 2px;
-    font-size: 14px;
-    line-height: 18px;
+    margin-top: 0;
+    font-size: 12px;
+    line-height: 14px;
     font-weight: 500;
     color: #303133;
   }
@@ -1656,28 +1729,31 @@
   }
   :deep(.gantt_task_bg .gantt_task_row),
   :deep(.gantt_task_row) {
-    border-bottom: 1px solid #f1f5f9;
+    border-bottom: 1px solid #edf2f7;
   }
   :deep(.gantt_task_bg .gantt_task_row:nth-child(odd)) {
-    background: #fcfdff;
+    background: #fbfdff;
+  }
+
+  :deep(.gantt_task_bg .gantt_task_row:nth-child(even)) {
+    background: #ffffff;
   }
   :deep(.gantt_task_bg .gantt_task_row:hover) {
-    background: #f7fbff;
+    background: #f5fbff;
   }
   :deep(.task-222) {
-    height: 28px !important;
-    line-height: 28px !important;
+    height: 22px !important;
+    line-height: 22px !important;
     border: none !important;
-    border-radius: 5px !important;
-    box-shadow: 0 2px 5px rgba(15, 23, 42, 0.14) !important;
-    filter: saturate(0.88) brightness(0.99);
+    border-radius: 7px !important;
+    box-shadow: 0 3px 8px rgba(15, 23, 42, 0.16) !important;
+    filter: saturate(0.92) brightness(1);
     overflow: hidden;
     transition: box-shadow 0.18s ease, filter 0.18s ease;
-    backdrop-filter: saturate(1.05);
   }
   :deep(.task-222:hover) {
-    filter: saturate(0.94) brightness(1);
-    box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18) !important;
+    filter: saturate(1) brightness(1.02);
+    box-shadow: 0 5px 14px rgba(15, 23, 42, 0.2) !important;
   }
   :deep(.gantt_task_line.task-parent-red),
   :deep(.gantt_task_line.task-parent-red .gantt_task_progress) {
@@ -1685,18 +1761,18 @@
   }
   :deep(.gantt_task_line.task-parent-blue),
   :deep(.gantt_task_line.task-parent-blue .gantt_task_progress) {
-    background: #18a0ea !important;
+    background: linear-gradient(90deg, #1688dc 0%, #26a9ee 100%) !important;
   }
   :deep(.gantt_task_line.task-child-plan) {
     border: none !important;
   }
   :deep(.gantt_task_line.task-station-green),
   :deep(.gantt_task_line.task-station-green .gantt_task_progress) {
-    background: #62ddd4 !important;
+    background: linear-gradient(90deg, #24a865 0%, #53c988 100%) !important;
   }
   :deep(.gantt_task_line.task-node-purple),
   :deep(.gantt_task_line.task-node-purple .gantt_task_progress) {
-    background: #b985ff !important;
+    background: linear-gradient(90deg, #8d62e5 0%, #b985ff 100%) !important;
   }
   :deep(.gantt_split_parent) {
     opacity: 1;
@@ -1714,7 +1790,7 @@
     color: #fff;
     font-size: 12px;
     font-weight: 600;
-    line-height: 28px;
+    line-height: 22px;
     white-space: nowrap;
     text-overflow: ellipsis;
     text-align: center;

+ 274 - 72
src/views/productionPlan/components/newFactoryProductionScheduling.vue

@@ -4,7 +4,7 @@
     width="100%"
     :visible.sync="visible"
     :close-on-click-modal="false"
-    custom-class="ele-dialog-form"
+    custom-class="ele-dialog-form scheduling-dialog"
     :title="schedulingTitle"
     :maxable="true"
   >
@@ -391,10 +391,10 @@
       },
       splitPaneWidth() {
         if (this.isOrderScheduling) {
-          if (this.viewportWidth <= 1280) return '34%';
-          if (this.viewportWidth <= 1440) return '38%';
+          if (this.viewportWidth <= 1280) return '36%';
+          if (this.viewportWidth <= 1440) return '39%';
           if (this.viewportWidth <= 1680) return '42%';
-          return '46%';
+          return '44%';
         }
         if (this.viewportWidth <= 1280) return '44%';
         if (this.viewportWidth <= 1440) return '50%';
@@ -413,18 +413,73 @@
         if (this.viewportWidth <= 1440) return 320;
         return 360;
       },
+      schedulingLayoutAvailableHeightValue() {
+        const headerHeight = this.viewportHeight <= 760 ? 38 : 42;
+        const footerHeight = this.viewportHeight <= 760 ? 34 : 38;
+        const bodyVerticalPadding = this.viewportHeight <= 760 ? 8 : 12;
+        const contentGap = this.viewportHeight <= 760 ? 5 : 6;
+        return (
+          this.viewportHeight -
+          10 -
+          headerHeight -
+          footerHeight -
+          bodyVerticalPadding -
+          contentGap
+        );
+      },
+      ganttPanelHeightValue() {
+        const minGanttHeight =
+          this.viewportHeight <= 720
+            ? 230
+            : this.viewportHeight <= 820
+            ? 300
+            : this.viewportHeight <= 960
+            ? 340
+            : 380;
+        const maxGanttHeight =
+          this.viewportHeight <= 820
+            ? 390
+            : this.viewportHeight <= 1080
+            ? 500
+            : 560;
+        const balancedHeight = Math.floor(
+          this.schedulingLayoutAvailableHeightValue / 2
+        );
+        return Math.max(
+          232,
+          Math.min(maxGanttHeight, Math.max(minGanttHeight, balancedHeight))
+        );
+      },
+      schedulingContentHeightValue() {
+        const remainingHeight =
+          this.schedulingLayoutAvailableHeightValue -
+          this.ganttPanelHeightValue;
+        const minHeight =
+          this.viewportHeight <= 720
+            ? 230
+            : this.viewportHeight <= 820
+            ? 300
+            : this.viewportHeight <= 900
+            ? 340
+            : 380;
+        return Math.max(minHeight, remainingHeight);
+      },
       ganttHeight() {
-        if (this.viewportHeight <= 760) return '360px';
-        if (this.viewportHeight <= 900) return '430px';
-        return '500px';
+        return `${this.ganttPanelHeightValue}px`;
       },
       schedulingContentHeight() {
-        if (this.viewportHeight <= 760) return '480px';
-        if (this.viewportHeight <= 900) return '540px';
-        return '585px';
+        return `${this.schedulingContentHeightValue}px`;
       },
       planTableHeight() {
-        const tabHeight = 52;
+        const tabHeight = 42;
+        const paginationHeight = 44;
+        const tableHeight = Math.max(
+          220,
+          this.schedulingContentHeightValue - tabHeight - paginationHeight
+        );
+        if (tableHeight >= 0) {
+          return `${tableHeight}px`;
+        }
         if (this.isOrderScheduling) {
           // ele-pro-table 的 height 只作用在表格主体,工具栏和分页器会继续占用额外高度。
           // 班组排产右侧是自定义 flex 分页区,左侧需要略收表格主体高度,保证两个分页器垂直位置一致。
@@ -443,13 +498,9 @@
       },
       taskConfigTableMaxHeight() {
         if (this.isOrderScheduling) {
-          if (this.viewportHeight <= 760) return 270;
-          if (this.viewportHeight <= 900) return 330;
-          return 390;
+          return Math.max(260, this.schedulingContentHeightValue - 132);
         }
-        if (this.viewportHeight <= 760) return 288;
-        if (this.viewportHeight <= 900) return 328;
-        return 390;
+        return Math.max(280, this.schedulingContentHeightValue - 132);
       }
     },
     created() {
@@ -902,11 +953,17 @@
           item.leaderUserId ||
           item.executionTeamLeaderId ||
           '';
+        const executionStartTime = formatDotLineDateTime(
+          item.executionStartTime
+        );
+        const executionEndTime = formatDotLineDateTime(item.executionEndTime);
         return {
           ...item,
           _taskKey: item.id ?? `detail-${item.taskId ?? index}`,
-          executionStartTime: formatDotLineDateTime(item.executionStartTime),
-          executionEndTime: formatDotLineDateTime(item.executionEndTime),
+          executionStartTime,
+          executionEndTime,
+          dispatchDefaultStartTime: executionStartTime,
+          dispatchDefaultEndTime: executionEndTime,
           executionType: execType,
           executionFactoryId: entrust
             ? item.executionFactoryId ?? item.executionTeamId ?? ''
@@ -987,6 +1044,8 @@
           executionTeamName: '',
           executionStartTime: '',
           executionEndTime: '',
+          dispatchDefaultStartTime: '',
+          dispatchDefaultEndTime: '',
           executionFactoryId: '',
           executionFactoryName: ''
         };
@@ -2593,35 +2652,47 @@
   }
 
   .scheduling-content {
-    margin-top: 10px;
+    position: relative;
+    margin-top: 6px;
     width: 100%;
+    min-height: 0;
+    padding: 0;
+    box-sizing: border-box;
   }
 
   .form-wrapper {
+    position: relative;
+    display: flex;
+    flex-direction: column;
     width: 100%;
     height: 100%;
     min-width: 0;
-    overflow: visible;
-    border: 1px solid #e6edf5;
-    border-radius: 8px;
-    background: #fff;
-    box-shadow: 0 4px 14px rgba(31, 45, 61, 0.05);
-  }
-
-  .form-wrapper.is-order-scheduling {
     overflow: hidden;
+    border: 1px solid #d8e3ef;
+    border-radius: 10px;
+    background: linear-gradient(180deg, #ffffff 0%, #fafdff 100%);
+    box-shadow: 0 10px 26px rgba(26, 39, 55, 0.08);
   }
 
-  .form-wrapper.is-plan-scheduling {
-    overflow: hidden;
+  .form-wrapper::before,
+  .plan-dot-line-detail::before {
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    height: 2px;
+    background: linear-gradient(90deg, #2489e8 0%, #14b8a6 60%, #7aa7ff 100%);
+    content: '';
+    pointer-events: none;
   }
 
   .plan-table-status-tabs {
+    flex: 0 0 42px;
     height: 42px;
-    padding: 0 12px;
+    padding: 3px 12px 0;
     box-sizing: border-box;
-    border-bottom: 1px solid #edf2f7;
-    background: #fff;
+    border-bottom: 1px solid #e5edf6;
+    background: linear-gradient(180deg, #f9fcff 0%, #f3f7fb 100%);
   }
 
   .plan-table-status-tabs ::v-deep {
@@ -2634,30 +2705,41 @@
     }
 
     .el-tabs__item {
-      height: 42px;
-      line-height: 42px;
-      padding: 0 18px;
-      color: #606266;
+      height: 36px;
+      line-height: 36px;
+      margin-right: 8px;
+      padding: 0 16px;
+      border-radius: 7px 7px 0 0;
+      color: #667085;
       font-size: 14px;
       font-weight: 600;
+      transition: all 0.18s ease;
+    }
+
+    .el-tabs__item:not(.is-active):hover {
+      color: #1683d8;
+      background: rgba(31, 140, 235, 0.06);
     }
 
     .el-tabs__item.is-active {
-      color: #1890ff;
+      color: #0f75bd;
+      background: #fff;
+      box-shadow: 0 -1px 0 #dce8f4, 1px 0 0 #e5edf6, -1px 0 0 #e5edf6;
     }
 
     .el-tabs__active-bar {
-      height: 3px;
-      border-radius: 3px 3px 0 0;
-      background: #1890ff;
+      height: 2px;
+      border-radius: 2px 2px 0 0;
+      background: linear-gradient(90deg, #1f8ceb 0%, #16b8a6 100%);
     }
   }
 
   .form-wrapper ::v-deep .ele-pro-table {
+    flex: 1 1 auto;
     height: calc(100% - 42px);
     min-height: 0;
     overflow: hidden;
-    background: #fff;
+    background: transparent;
   }
 
   .form-wrapper.is-order-scheduling ::v-deep .ele-pro-table,
@@ -2667,7 +2749,9 @@
   }
 
   .form-wrapper.is-order-scheduling ::v-deep .ele-pro-table > .el-table {
-    flex: 0 0 auto;
+    flex: 1 1 auto;
+    height: calc(100% - 44px) !important;
+    min-height: 0;
   }
 
   .form-wrapper.is-order-scheduling ::v-deep .ele-pro-table > .el-pagination {
@@ -2676,27 +2760,38 @@
 
   .form-wrapper ::v-deep .el-table {
     color: #364152;
-    border-color: #edf2f7;
+    border-color: #e5edf6;
     font-size: 14px;
   }
 
   .form-wrapper ::v-deep .el-table__body-wrapper {
-    overflow-y: auto;
+    overflow-y: auto !important;
+    overflow-x: auto;
+    padding-bottom: 1px;
+    box-sizing: border-box;
   }
 
   .form-wrapper ::v-deep .el-table th.el-table__cell {
-    background: #f7fafc;
-    color: #263445;
+    background: linear-gradient(180deg, #f8fbff 0%, #eef5fb 100%);
+    color: #1f2d3d;
     font-weight: 600;
+    position: sticky;
+    top: 0;
+    z-index: 2;
   }
 
   .form-wrapper ::v-deep .el-table td.el-table__cell,
   .form-wrapper ::v-deep .el-table th.el-table__cell {
-    border-bottom-color: #edf2f7;
+    border-bottom-color: #e9eff6;
+  }
+
+  .form-wrapper ::v-deep .el-table td.el-table__cell {
+    padding-top: 5px;
+    padding-bottom: 5px;
   }
 
   .form-wrapper ::v-deep .el-table__row:hover > td.el-table__cell {
-    background: #f7fbff;
+    background: #f5faff;
   }
 
   .form-wrapper ::v-deep .el-table__row.current-row > td.el-table__cell,
@@ -2704,7 +2799,7 @@
     ::v-deep
     .el-table__row.el-table__row--striped.current-row
     > td.el-table__cell {
-    background: #eef7ff;
+    background: #eaf5ff;
   }
 
   .form-wrapper ::v-deep .el-link {
@@ -2712,19 +2807,21 @@
     align-items: center;
     justify-content: center;
     min-width: 42px;
-    height: 24px;
-    padding: 0 8px;
-    border-radius: 999px;
-    background: #eef7ff;
-    color: #1683d8;
+    height: 26px;
+    padding: 0 10px;
+    border: 1px solid #c8e0f4;
+    border-radius: 6px;
+    background: linear-gradient(180deg, #f6fbff 0%, #eaf5ff 100%);
+    color: #0f75bd;
     font-weight: 600;
     line-height: 24px;
     transition: all 0.18s ease;
   }
 
   .form-wrapper ::v-deep .el-link:hover {
-    background: #dff0ff;
-    color: #0f75bd;
+    border-color: #8ec6f4;
+    background: linear-gradient(180deg, #eef8ff 0%, #dff0ff 100%);
+    color: #0964a3;
   }
 
   .form-wrapper ::v-deep .el-checkbox__inner {
@@ -2760,7 +2857,9 @@
   }
 
   .form-wrapper.is-plan-scheduling ::v-deep .ele-pro-table > .el-table {
-    flex: 0 0 auto;
+    flex: 1 1 auto;
+    height: calc(100% - 44px) !important;
+    min-height: 0;
   }
 
   .form-wrapper.is-plan-scheduling ::v-deep .ele-pro-table > .el-pagination {
@@ -2771,11 +2870,14 @@
     display: flex;
     align-items: center;
     justify-content: center;
-    height: 64px;
-    padding: 0 12px;
+    flex: 0 0 44px;
+    height: 44px;
+    min-height: 44px;
+    padding: 0 10px;
     box-sizing: border-box;
-    border-top: 1px solid #edf2f7;
-    background: #f8fbff;
+    border-top: 1px solid #e5edf6;
+    background: linear-gradient(180deg, #ffffff 0%, #f1f6fb 100%);
+    box-shadow: 0 -3px 10px rgba(31, 45, 61, 0.04);
   }
 
   .form-wrapper.is-plan-scheduling
@@ -2790,12 +2892,15 @@
     display: flex;
     align-items: center;
     justify-content: center;
-    height: 64px;
+    flex: 0 0 44px;
+    height: 44px;
+    min-height: 44px;
     margin-top: 0 !important;
-    padding: 0 12px;
+    padding: 0 10px;
     box-sizing: border-box;
-    border-top: 1px solid #edf2f7;
-    background: #f8fbff;
+    border-top: 1px solid #e5edf6;
+    background: linear-gradient(180deg, #ffffff 0%, #f1f6fb 100%);
+    box-shadow: 0 -3px 10px rgba(31, 45, 61, 0.04);
   }
 
   .form-wrapper.is-order-scheduling ::v-deep .order-plan-pagination > * {
@@ -2806,7 +2911,7 @@
     width: 100%;
     height: 100%;
     overflow: hidden;
-    padding-left: 12px;
+    padding-left: 10px;
     box-sizing: border-box;
     display: flex;
     flex-direction: column;
@@ -2828,21 +2933,22 @@
   }
 
   .plan-dot-line-detail {
+    position: relative;
     display: flex;
     flex-direction: column;
     height: 100%;
     min-width: 0;
     overflow: hidden;
-    border: 1px solid #e6edf5;
-    border-radius: 8px;
-    background: #fff;
-    box-shadow: 0 4px 14px rgba(31, 45, 61, 0.05);
+    border: 1px solid #d8e3ef;
+    border-radius: 10px;
+    background: linear-gradient(180deg, #ffffff 0%, #fafdff 100%);
+    box-shadow: 0 10px 26px rgba(26, 39, 55, 0.08);
   }
 
   .plan-dot-line-scrollable {
     flex: 1;
     overflow: hidden;
-    padding-bottom: 20px;
+    padding-bottom: 14px;
     box-sizing: border-box;
     min-height: 0;
   }
@@ -2910,3 +3016,99 @@
     }
   }
 </style>
+
+<style lang="scss">
+  .scheduling-dialog.el-dialog {
+    width: calc(100vw - 10px) !important;
+    max-width: none;
+    height: calc(100vh - 10px);
+    max-height: calc(100vh - 10px);
+    margin-top: 5px !important;
+    margin-bottom: 5px !important;
+    border: 1px solid rgba(190, 205, 222, 0.85);
+    border-radius: 12px;
+    background: #eef4fa;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    box-shadow: 0 22px 64px rgba(15, 23, 42, 0.22);
+
+    .el-dialog__header {
+      flex: 0 0 auto;
+      padding: 8px 16px;
+      border-bottom: 1px solid #dce7f2;
+      background: linear-gradient(180deg, #ffffff 0%, #f7fafc 100%);
+    }
+
+    .el-dialog__title {
+      color: #1d2939;
+      font-size: 16px;
+      font-weight: 700;
+      letter-spacing: 0;
+    }
+
+    .el-dialog__body {
+      flex: 1 1 auto;
+      display: flex;
+      flex-direction: column;
+      min-height: 0;
+      padding: 6px 8px 6px;
+      background: radial-gradient(
+          circle at 12% 0,
+          rgba(31, 140, 235, 0.08) 0,
+          transparent 28%
+        ),
+        linear-gradient(90deg, rgba(31, 140, 235, 0.035) 1px, transparent 1px),
+        linear-gradient(180deg, rgba(22, 184, 166, 0.025) 1px, transparent 1px),
+        #eef4fa;
+      background-size: auto, 24px 24px, 24px 24px;
+      overflow: hidden;
+    }
+
+    .project-gantt {
+      flex: 0 0 auto;
+    }
+
+    .scheduling-content {
+      flex: 1 1 auto;
+      height: auto !important;
+      min-height: 0;
+    }
+
+    .el-dialog__footer {
+      flex: 0 0 auto;
+      padding: 5px 16px 6px;
+      border-top: 1px solid #dce7f2;
+      background: linear-gradient(180deg, #f9fbfd 0%, #f3f7fb 100%);
+    }
+  }
+
+  @media (max-height: 760px) {
+    .scheduling-dialog.el-dialog {
+      .el-dialog__header {
+        padding: 8px 14px;
+      }
+
+      .el-dialog__body {
+        padding: 6px 8px;
+      }
+
+      .el-dialog__footer {
+        padding: 4px 14px 5px;
+      }
+    }
+  }
+
+  @media (max-width: 1180px) {
+    .scheduling-dialog.el-dialog {
+      .el-dialog__body {
+        overflow: auto;
+      }
+
+      .scheduling-content {
+        flex: 0 0 auto;
+        min-height: 720px;
+      }
+    }
+  }
+</style>

+ 35 - 23
src/views/productionPlan/components/newFactoryProductionScheduling/PlanRoutePanel.vue

@@ -86,10 +86,10 @@
     max-width: 100%;
     min-width: 0;
     box-sizing: border-box;
-    border: 1px solid #ebeef5;
-    border-radius: 4px;
-    padding: 12px;
-    background: #fff;
+    border: 1px solid #dbe6f1;
+    border-radius: 9px;
+    padding: 7px 10px 5px;
+    background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
     overflow: visible;
   }
 
@@ -118,16 +118,17 @@
   }
 
   .panel-title {
-    font-size: 14px;
+    font-size: 13px;
     font-weight: 600;
-    margin-bottom: 10px;
+    margin-bottom: 3px;
+    color: #2f3b4a;
   }
 
   .route-steps {
     width: 100%;
     overflow-x: auto;
     overflow-y: hidden;
-    padding-bottom: 8px;
+    padding-bottom: 0;
     box-sizing: content-box;
   }
 
@@ -135,13 +136,14 @@
     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;
+    justify-content: flex-end;
+    gap: 10px;
+    margin-bottom: 6px;
+    padding: 5px 10px;
+    border: 1px solid #c8e8cf;
+    border-radius: 9px;
+    background: linear-gradient(180deg, #ffffff 0%, #f4fcf6 100%);
+    font-size: 13px;
   }
 
   .route-panel-wrap.compact {
@@ -169,16 +171,25 @@
     display: inline-flex;
     align-items: baseline;
     gap: 4px;
+    min-width: 0;
+    padding: 3px 8px;
+    border: 1px solid #c8e8cf;
+    border-radius: 7px;
+    background: linear-gradient(180deg, #f4fcf6 0%, #edf9f0 100%);
+    white-space: nowrap;
   }
 
   .plan-time-label {
-    color: #606266;
+    color: #008000;
+    font-weight: 600;
     white-space: nowrap;
   }
 
   .plan-time-value {
-    color: #303133;
-    font-weight: 500;
+    overflow: hidden;
+    color: #008000;
+    font-weight: 600;
+    text-overflow: ellipsis;
   }
 
   .route-steps ::v-deep {
@@ -193,8 +204,8 @@
     }
 
     .el-step__title {
-      font-size: 15px;
-      line-height: 1.35;
+      font-size: 13px;
+      line-height: 1.25;
       max-width: 8em;
       margin-left: auto;
       margin-right: auto;
@@ -208,7 +219,7 @@
     }
 
     .el-step__line {
-      top: 12px;
+      top: 10px;
     }
 
     .el-step__head.is-success {
@@ -233,12 +244,13 @@
     }
 
     .el-step__icon {
-      width: 26px;
-      height: 26px;
+      width: 22px;
+      height: 22px;
+      font-size: 13px;
     }
 
     .el-step__main {
-      padding-top: 5px;
+      padding-top: 2px;
       margin-top: 0;
     }
 

+ 211 - 65
src/views/productionPlan/components/newFactoryProductionScheduling/TaskConfigPanel.vue

@@ -802,8 +802,12 @@
             );
             const rowKey = this.dispatchObjectRowKey(item);
             const task = this.getDispatchRowTask(item);
-            const draft = this.dispatchRowDraftMap[rowKey] || {};
+            const draft =
+              this.dispatchRowDraftMap[this.dispatchRowDraftKey(item)] || {};
             const assignment = this.getDispatchAssignment(item);
+            const defaultStartTime =
+              this.activeTask.dispatchDefaultStartTime || '';
+            const defaultEndTime = this.activeTask.dispatchDefaultEndTime || '';
             return {
               ...item,
               id: rowKey,
@@ -821,13 +825,13 @@
                   ? draft.executionStartTime
                   : task?.executionStartTime ||
                     assignment?.executionStartTime ||
-                    this.getDispatchTaskDefaultStartTime(),
+                    defaultStartTime,
               executionEndTime:
                 draft.executionEndTime !== undefined
                   ? draft.executionEndTime
                   : task?.executionEndTime ||
                     assignment?.executionEndTime ||
-                    this.getDispatchTaskDefaultEndTime(),
+                    defaultEndTime,
               status: task?.status || assignment?.status,
               changeId: task?.changeId || assignment?.changeId,
               teamTimeIds: task?.teamTimeIds || assignment?.teamTimeIds,
@@ -897,7 +901,9 @@
         immediate: true,
         handler() {
           this.dispatchReportType = this.getDefaultDispatchReportType();
+          this.dispatchRowDraftMap = {};
           this.dispatchAssignmentMap = {};
+          this.dispatchPage = 1;
           this.loadDispatchAssignData();
         }
       },
@@ -915,6 +921,8 @@
         handler(list) {
           if (!Array.isArray(list) || list.length === 0) {
             this.activeTaskKey = '';
+            this.dispatchRowDraftMap = {};
+            this.dispatchPage = 1;
             return;
           }
           const hasActive = list.some(
@@ -1401,12 +1409,19 @@
         return this.isEmptyDispatchQuantity(value) ? '' : value;
       },
       updateDispatchRowDraft(row, patch) {
-        const key = this.dispatchObjectRowKey(row);
+        const key = this.dispatchRowDraftKey(row);
         this.$set(this.dispatchRowDraftMap, key, {
           ...(this.dispatchRowDraftMap[key] || {}),
           ...patch
         });
       },
+      dispatchRowDraftKey(row, task = this.activeTask) {
+        return [
+          this.getDispatchWorkOrderId() || this.currentPlan?.id || '',
+          this.dispatchTaskGroupKey(task) || '',
+          this.dispatchObjectRowKey(row)
+        ].join('::');
+      },
       getDispatchTaskQuantityTotal(row) {
         const groupKey = this.dispatchTaskGroupKey(row);
         if (
@@ -1517,8 +1532,8 @@
           id: undefined,
           _dispatchDraft: true,
           quantity: '',
-          executionStartTime: this.getDispatchTaskDefaultStartTime(source),
-          executionEndTime: this.getDispatchTaskDefaultEndTime(source),
+          executionStartTime: row.executionStartTime || '',
+          executionEndTime: row.executionEndTime || '',
           _taskKey: `${this.dispatchTaskGroupKey(
             source
           )}-${this.dispatchObjectRowKey(row)}`
@@ -1527,7 +1542,7 @@
         return next;
       },
       clearDispatchObject(row, task = this.getDispatchRowTask(row)) {
-        this.$delete(this.dispatchRowDraftMap, this.dispatchObjectRowKey(row));
+        this.$delete(this.dispatchRowDraftMap, this.dispatchRowDraftKey(row));
         if (!task) {
           row.quantity = '';
           row.executionStartTime = '';
@@ -1692,6 +1707,20 @@
           }
           return false;
         }
+        const planEndTime = this.parseDispatchTime(
+          this.getDispatchPlanEndTime()
+        );
+        if (
+          startTime !== null &&
+          planEndTime !== null &&
+          startTime > planEndTime
+        ) {
+          this.clearDispatchStartTime(row);
+          if (showMessage) {
+            this.$message.warning('开始时间不能大于计划结束时间');
+          }
+          return false;
+        }
         return true;
       },
       validateDispatchEndTime(row, showMessage = true) {
@@ -2084,19 +2113,24 @@
     max-width: 100%;
     min-width: 0;
     box-sizing: border-box;
-    border: 1px solid #e6edf5;
-    border-radius: 6px;
+    border: 1px solid #d8e3ef;
+    border-radius: 10px;
     padding: 8px 10px 10px;
-    background: #fff;
+    background: linear-gradient(180deg, #ffffff 0%, #fafdff 100%);
     margin-top: 10px;
   }
 
+  .config-panel.factory-task-config-panel {
+    margin-top: 0;
+    padding: 6px 8px 8px;
+  }
+
   .dispatch-config-panel {
     height: 100%;
     margin-top: 0;
     padding: 0;
     border: 0;
-    border-radius: 8px;
+    border-radius: 10px;
     overflow: hidden;
   }
 
@@ -2107,7 +2141,24 @@
     width: 100%;
     min-width: 0;
     overflow: hidden;
-    background: #fff;
+    background: transparent;
+  }
+
+  .dispatch-config-panel ::v-deep .el-empty {
+    height: 100%;
+    min-height: 240px;
+    padding: 24px 0;
+    box-sizing: border-box;
+    border-radius: 10px;
+    background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
+  }
+
+  .dispatch-config-panel ::v-deep .el-empty__image {
+    width: 118px;
+  }
+
+  .dispatch-config-panel ::v-deep .el-empty__description {
+    margin-top: 10px;
   }
 
   .dispatch-report-type {
@@ -2115,11 +2166,13 @@
     flex: 0 0 auto;
     align-items: center;
     gap: 8px;
-    padding: 12px 18px;
+    min-height: 38px;
+    padding: 5px 12px;
+    box-sizing: border-box;
     color: #606266;
     font-size: 14px;
-    background: linear-gradient(180deg, #fbfdff 0%, #f6f9fc 100%);
-    border-bottom: 1px solid #edf2f7;
+    background: linear-gradient(180deg, #ffffff 0%, #f4f9fd 100%);
+    border-bottom: 1px solid #e5edf6;
   }
 
   .dispatch-required {
@@ -2148,9 +2201,9 @@
     gap: 4px;
     min-width: 0;
     padding: 3px 8px;
-    border: 1px solid #d8efd9;
-    border-radius: 999px;
-    background: #f2fbf2;
+    border: 1px solid #c8e8cf;
+    border-radius: 7px;
+    background: linear-gradient(180deg, #f4fcf6 0%, #edf9f0 100%);
     white-space: nowrap;
   }
 
@@ -2174,19 +2227,21 @@
 
     .el-tabs__header {
       margin: 0;
-      background: #f7fafc;
-      border-bottom-color: #edf2f7;
+      background: #f8fbff;
+      border-bottom-color: #e5edf6;
     }
 
     .el-tabs__nav-scroll {
-      padding: 0 12px;
+      padding: 0 10px;
     }
 
     .el-tabs__item {
-      height: 46px;
-      line-height: 46px;
+      height: 34px;
+      line-height: 34px;
       min-width: 82px;
-      padding: 0 22px;
+      margin-right: 6px;
+      padding: 0 14px;
+      border-radius: 8px 8px 0 0;
       font-size: 14px;
       font-weight: 500;
       color: #909399;
@@ -2196,14 +2251,14 @@
 
     .el-tabs__item:not(.is-active):hover {
       color: #1683d8;
-      background: #f4f9ff;
+      background: rgba(31, 140, 235, 0.06);
     }
 
     .el-tabs__item.is-active {
-      color: #1890ff;
+      color: #0f75bd;
       background: #fff;
       font-weight: 600;
-      box-shadow: inset 0 -2px 0 #1890ff;
+      box-shadow: inset 0 -2px 0 #16b8a6;
     }
 
     .el-tabs__content {
@@ -2214,20 +2269,20 @@
   .dispatch-process-meta {
     flex: 0 0 auto;
     position: relative;
-    margin: 10px 18px;
-    padding: 10px 12px 10px 16px;
-    border: 1px solid #d8efd9;
-    border-radius: 6px;
+    margin: 6px 12px;
+    padding: 6px 10px 6px 14px;
+    border: 1px solid #c8e8cf;
+    border-radius: 9px;
     color: #008000;
     font-size: 14px;
     font-weight: 600;
-    background: #f6fff6;
+    background: linear-gradient(90deg, #f2fbf4 0%, #fbfffb 100%);
   }
 
   .dispatch-process-meta::before {
     position: absolute;
-    top: 10px;
-    bottom: 10px;
+    top: 8px;
+    bottom: 8px;
     left: 0;
     width: 3px;
     border-radius: 0 3px 3px 0;
@@ -2239,10 +2294,13 @@
     flex: 0 0 auto;
     display: flex;
     align-items: center;
-    gap: 12px;
-    padding: 10px 18px;
-    border-bottom: 1px solid #edf2f7;
-    background: #f8fbff;
+    gap: 8px;
+    min-height: 40px;
+    padding: 5px 12px;
+    box-sizing: border-box;
+    border-top: 1px solid #eef3f8;
+    border-bottom: 1px solid #e5edf6;
+    background: linear-gradient(180deg, #ffffff 0%, #f6faff 100%);
   }
 
   .dispatch-toolbar-left,
@@ -2250,12 +2308,12 @@
   .dispatch-assign-mode {
     display: inline-flex;
     align-items: center;
-    gap: 10px;
+    gap: 8px;
   }
 
   .dispatch-toolbar-left {
     flex: 0 0 auto;
-    gap: 6px;
+    gap: 7px;
   }
 
   .dispatch-toolbar-left ::v-deep .el-button + .el-button {
@@ -2263,7 +2321,8 @@
   }
 
   .dispatch-toolbar ::v-deep .el-button {
-    border-radius: 4px;
+    min-height: 28px;
+    border-radius: 7px;
     font-weight: 600;
     transition: all 0.18s ease;
   }
@@ -2275,7 +2334,15 @@
   }
 
   .dispatch-toolbar-left ::v-deep .el-button--primary {
-    box-shadow: 0 2px 6px rgba(31, 140, 235, 0.2);
+    border-color: #1f8ceb;
+    background: linear-gradient(180deg, #2a9af0 0%, #1684df 100%);
+    box-shadow: 0 3px 8px rgba(31, 140, 235, 0.2);
+  }
+
+  .dispatch-toolbar-left ::v-deep .el-button--success {
+    border-color: #58c234;
+    background: linear-gradient(180deg, #8edc66 0%, #5fc238 100%);
+    box-shadow: 0 3px 8px rgba(95, 194, 56, 0.2);
   }
 
   .dispatch-assign-mode {
@@ -2300,13 +2367,16 @@
   .dispatch-table-wrap {
     flex: 1 1 auto;
     min-height: 0;
+    padding: 0 8px;
+    box-sizing: border-box;
     overflow: hidden;
+    background: #fff;
   }
 
   .dispatch-table {
     height: 100%;
-    border-left: 0;
-    border-right: 0;
+    border-color: #e5edf6;
+    border-radius: 9px;
   }
 
   .dispatch-status-badge {
@@ -2317,7 +2387,7 @@
     height: 24px;
     padding: 0 10px;
     border: 1px solid #d8e1eb;
-    border-radius: 12px;
+    border-radius: 999px;
     color: #606b78;
     background: #f8fafc;
     font-size: 13px;
@@ -2351,10 +2421,11 @@
     flex: 0 0 auto;
     align-items: center;
     justify-content: center;
-    min-height: 64px;
-    padding: 8px 12px;
-    border-top: 1px solid #edf2f7;
-    background: #f8fbff;
+    min-height: 42px;
+    padding: 4px 10px;
+    border-top: 1px solid #e5edf6;
+    background: linear-gradient(180deg, #ffffff 0%, #f1f6fb 100%);
+    box-shadow: 0 -3px 10px rgba(31, 45, 61, 0.04);
   }
 
   .panel-title {
@@ -2367,11 +2438,16 @@
     display: flex;
     align-items: center;
     justify-content: space-between;
-    gap: 12px;
-    margin-bottom: 8px;
+    gap: 8px;
+    margin-bottom: 5px;
     flex-wrap: wrap;
   }
 
+  .factory-task-config-panel .panel-header {
+    min-height: 28px;
+    margin-bottom: 4px;
+  }
+
   .panel-header .panel-title {
     margin-bottom: 0;
   }
@@ -2383,6 +2459,11 @@
     flex: 0 0 auto;
   }
 
+  .factory-task-config-panel .panel-actions ::v-deep .el-button {
+    min-height: 26px;
+    padding: 6px 10px;
+  }
+
   .readonly-tip {
     color: #e6a23c;
     font-size: 12px;
@@ -2395,9 +2476,15 @@
   }
 
   .task-config-table-wrap {
+    min-height: 0;
     overflow: hidden;
   }
 
+  .factory-task-config-panel .task-config-table-wrap {
+    flex: 1 1 auto;
+    height: 100%;
+  }
+
   .last-end-over-plan-wrap ::v-deep .el-input__inner,
   .end-over-delivery-wrap ::v-deep .el-input__inner {
     color: #f56c6c;
@@ -2436,10 +2523,39 @@
     }
 
     .config-table-control .el-input__inner {
-      height: 30px;
+      height: 28px;
       padding-left: 10px;
       padding-right: 10px;
       font-size: 13px;
+      line-height: 28px;
+    }
+
+    .config-table-control .el-input__prefix,
+    .config-table-control .el-input__suffix {
+      top: 0;
+      height: 100%;
+      display: inline-flex;
+      align-items: center;
+    }
+
+    .config-table-control .el-input__prefix {
+      left: 8px;
+    }
+
+    .config-table-control .el-input__suffix {
+      right: 6px;
+    }
+
+    .config-table-control .el-input__icon {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      height: 100%;
+      line-height: 1;
+    }
+
+    .config-table-control .el-select__caret {
+      line-height: 1;
     }
 
     .el-table td,
@@ -2449,7 +2565,7 @@
     }
 
     .el-table__row {
-      height: 42px;
+      height: 34px;
     }
 
     .el-table__body-wrapper {
@@ -2484,37 +2600,57 @@
     }
   }
 
+  .factory-task-config-panel .config-table ::v-deep {
+    .el-table td,
+    .el-table th {
+      padding-top: 3px;
+      padding-bottom: 3px;
+    }
+
+    .el-table__row {
+      height: 32px;
+    }
+
+    .config-table-control .el-input__inner {
+      height: 26px;
+      line-height: 26px;
+    }
+  }
+
   .dispatch-table ::v-deep {
     font-size: 14px;
 
     .el-table th {
-      background: #f7fafc;
-      color: #263445;
+      background: linear-gradient(180deg, #f8fbff 0%, #eef5fb 100%);
+      color: #253244;
       font-size: 14px;
       font-weight: 600;
+      position: sticky;
+      top: 0;
+      z-index: 2;
     }
 
     .el-table td,
     .el-table th {
-      border-bottom-color: #edf2f7;
+      border-bottom-color: #e9eff6;
     }
 
     .el-table__row:hover > td.el-table__cell {
-      background: #f7fbff;
+      background: #f5faff;
     }
 
     .el-table__body tr.current-row > td.el-table__cell {
-      background: #eef7ff;
+      background: #eaf5ff;
     }
 
     .el-table__body tr.dispatch-row-assigned > td.el-table__cell {
       position: relative;
       color: #52606d;
-      background: #f3fbf4;
+      background: #f4fbf5;
     }
 
     .el-table__body tr.dispatch-row-assigned:hover > td.el-table__cell {
-      background: #eaf7ec;
+      background: #e9f7ed;
     }
 
     .el-table__body tr.dispatch-row-assigned > td:first-child::before {
@@ -2536,7 +2672,7 @@
     .el-table__body-wrapper::-webkit-scrollbar-thumb {
       border: 2px solid #fff;
       border-radius: 999px;
-      background: #c9d4e2;
+      background: #bdcad8;
     }
 
     .el-table__body-wrapper::-webkit-scrollbar-thumb:hover {
@@ -2545,20 +2681,30 @@
 
     .el-table td,
     .el-table th {
-      padding-top: 6px;
-      padding-bottom: 6px;
+      padding-top: 4px;
+      padding-bottom: 4px;
     }
 
     .el-table__row {
-      height: 44px;
+      height: 36px;
     }
 
     .el-table__fixed-right {
       box-shadow: -4px 0 8px rgba(0, 0, 0, 0.04);
     }
 
+    .el-table__body-wrapper {
+      background: linear-gradient(
+          90deg,
+          rgba(31, 140, 235, 0.025) 1px,
+          transparent 1px
+        ),
+        #fff;
+      background-size: 48px 48px;
+    }
+
     .config-table-control .el-input__inner {
-      border-color: #e1e8f0;
+      border-color: #dce7f2;
       background: #fbfdff;
     }
 

+ 2 - 2
vue.config.js

@@ -35,14 +35,14 @@ module.exports = {
         // target: 'http://192.168.1.103:18086',
         // target: 'http://192.168.1.158:18086',
         // target: 'http://192.168.158:18086',
-        // target: 'http://192.168.1.251:18086',
+        target: 'http://192.168.1.251:18086',
         // target: 'http://192.168.1.144:18086',
         // target: 'http://192.168.1.251:18186',
         // 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:51001/api',
-        target: 'http://192.168.1.125:18086',
+        // target: 'http://192.168.1.125:18086',
 
         // target: 'http://192.168.1.116:18086',