From f770f1735c127574f14042aa34801ab0984f62f2 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 14 九月 2026 12:21:26 +0800
Subject: [PATCH] feat(productionOrder): 优化生产订单表格展示和交互体验

---
 src/views/productionManagement/productionOrder/index.vue |  173 +++++++++++++++++++++++++++++++++++----------------------
 public/favicon.ico                                       |    0 
 src/assets/logo/logo.png                                 |    0 
 3 files changed, 105 insertions(+), 68 deletions(-)

diff --git a/public/favicon.ico b/public/favicon.ico
index ce783dc..34f130e 100644
--- a/public/favicon.ico
+++ b/public/favicon.ico
Binary files differ
diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png
index 78b143d..61470dd 100644
--- a/src/assets/logo/logo.png
+++ b/src/assets/logo/logo.png
Binary files differ
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 55edf93..6697c09 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -78,6 +78,11 @@
                 :selectable="row => !row.endOrder"
                 @selection-change="handleSelectionChange"
                 @pagination="pagination">
+        <template #planCompleteTime="{ row }">
+          <span :title="deliveryHint(row)">
+            {{ row.planCompleteTime ? dayjs(row.planCompleteTime).format('YYYY-MM-DD') : '' }}
+          </span>
+        </template>
         <template #completionStatus="{ row }">
           <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
                        :color="progressColor(toProgressPercentage(row?.completionStatus))"
@@ -89,10 +94,11 @@
                     type="success"
                     size="small">鍏ㄩ儴瀹屾垚</el-tag>
             <div v-else
-                 class="current-process-name">
+                 class="current-process-name"
+                 :title="`${getCurrentProcess(row).name} 路 ${getCurrentProcess(row).pct}%`">
               <span class="process-color-dot"
                     :style="{ background: getCurrentProcess(row).color || '#909399' }"></span>
-              <span :style="getCurrentProcess(row).color ? { color: getCurrentProcess(row).color } : {}">{{ getCurrentProcess(row).name }}</span>
+              <span :style="readableTextColor(getCurrentProcess(row).color) ? { color: readableTextColor(getCurrentProcess(row).color) } : {}">{{ getCurrentProcess(row).name }}</span>
             </div>
           </template>
           <span v-else>-</span>
@@ -105,18 +111,13 @@
                  class="process-step">
               <div class="step-content">
                 <div class="step-circle"
-                     :class="{ 'is-completed': item.percentage >= 100 }">
-                  <span class="step-percentage"
-                        :style="{ color: item.percentage >= 70 ? item.percentage >= 100 ? '#67c23a' : '#f56c6c' : '#000' }">{{ item.percentage }}%</span>
-                </div>
-                <div class="step-name">
-                  <span v-if="item.color"
-                        :style="{ display: 'inline-block', width: '8px', height: '8px', borderRadius: '50%', marginRight: '4px', background: item.color }"></span>
-                  <span>{{ item.name }}</span>
-                </div>
+                     :title="`${item.name} ${item.percentage}%`"
+                     :style="{ background: item.color || '#409EFF', opacity: item.percentage >= 100 ? 1 : item.percentage > 0 ? 0.6 : 0.25 }"></div>
+                <div class="step-name">{{ item.name }}</div>
               </div>
               <div v-if="index < row.processRouteStatus.length - 1"
-                   class="step-line"></div>
+                   class="step-line"
+                   :style="{ background: item.percentage >= 100 ? '#67c23a' : '#dcdfe6' }"></div>
             </div>
           </div>
           <span v-else>-</span>
@@ -125,7 +126,8 @@
     </div>
     <el-dialog v-model="bindRouteDialogVisible"
                title="缁戝畾宸ヨ壓璺嚎"
-               width="500px">
+               width="500px"
+               :close-on-click-modal="false">
       <el-form label-width="90px">
         <el-form-item label="宸ヨ壓璺嚎">
           <el-select v-model="bindForm.routeId"
@@ -151,7 +153,8 @@
     <!-- 鏉ユ簮鏁版嵁寮圭獥 -->
     <el-dialog v-model="sourceDataDialogVisible"
                title="鏉ユ簮鏁版嵁"
-               width="1200px">
+               width="1200px"
+               :close-on-click-modal="false">
       <div v-if="sourceRowData"
            class="applyno-summary1">
         <div class="summary-item">
@@ -288,14 +291,15 @@
     total: 0,
   });
 
+  // 姣忔鍥哄畾鍗犲锛氬悕绉板尯96px + 杩炴帴绾�34px锛岄暱鍚嶇О鎹㈣锛屼繚璇佸渾鍦�-杩炵嚎鑺傚涓�鑷�
+  const PROCESS_STEP_WIDTH = 130;
   const processColumnWidth = computed(() => {
     if (!tableData.value || tableData.value.length === 0) return "200px";
     const maxProcesses = Math.max(
       ...tableData.value.map(row => row.processRouteStatus?.length || 0)
     );
     if (maxProcesses === 0) return "100px";
-    // 姣忎釜宸ュ簭鍦嗗湀 36px + 绾挎潯 30px = 66px锛岄澶栧姞 60px 杈硅窛鍜屾枃瀛楃┖闂�
-    return `${maxProcesses * 66 + 60}px`;
+    return `${maxProcesses * PROCESS_STEP_WIDTH + 40}px`;
   });
 
   const tableColumn = computed(() => [
@@ -309,6 +313,7 @@
       prop: "currentProcess",
       dataType: "slot",
       slot: "currentProcess",
+      align: "center",
       width: 160,
     },
     {
@@ -332,6 +337,7 @@
           : val === 5
           ? "宸茬粨鏉�"
           : "宸插彇娑�",
+      // 宸茬粨鏉熶负姝e父鍏抽棴锛堜腑鎬х伆锛夛紝宸插彇娑堟墠鏄紓甯告搷浣滐紙绾級
       formatType: val =>
         val === 1
           ? "primary"
@@ -340,8 +346,8 @@
           : val === 3
           ? "success"
           : val === 5
-          ? "danger"
-          : "info",
+          ? "info"
+          : "danger",
     },
     {
       label: "浜у搧鍚嶇О",
@@ -395,7 +401,8 @@
     {
       label: "璁″垝瀹屾垚鏃堕棿",
       prop: "planCompleteTime",
-      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
+      dataType: "slot",
+      slot: "planCompleteTime",
       width: 120,
     },
     {
@@ -403,7 +410,7 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 280,
+      width: 420,
       operation: [
         {
           name: "宸ヨ壓璺嚎",
@@ -474,7 +481,7 @@
         {
           name: "鐢熶骇杩芥函",
           type: "text",
-          color: "#409eff",
+          color: "var(--el-color-primary)",
           clickFun: row => {
             router.push({
               path: "/productionManagement/productionTraceability",
@@ -489,7 +496,7 @@
         {
           name: "缁撴潫璁㈠崟",
           type: "text",
-          color: "red",
+          color: "var(--el-color-danger)",
           showHide: row => !row.endOrder,
           clickFun: row => {
             handleEndOrder(row);
@@ -537,6 +544,27 @@
     return "#67c23a";
   };
 
+  // hex 鑹茶浆 rgba锛堢敤浜庡厜鏅�/娣″簳锛夛紝闈� hex 杩斿洖绌轰覆
+  const hexToRgba = (hex, alpha) => {
+    if (typeof hex !== "string") return "";
+    const m = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
+    if (!m) return "";
+    let n = m[1];
+    if (n.length === 3) n = n.split("").map(c => c + c).join("");
+    const v = parseInt(n, 16);
+    return `rgba(${(v >> 16) & 255}, ${(v >> 8) & 255}, ${v & 255}, ${alpha})`;
+  };
+
+  // 杩囨祬鐨勫伐搴忚壊鍦ㄧ櫧搴曚笂鏂囧瓧涓嶅彲璇伙紝鍥為��甯歌鏂囧瓧鑹�
+  const isLightColor = hex => {
+    const rgba = hexToRgba(hex, 1);
+    if (!rgba) return false;
+    const [r, g, b] = rgba.match(/\d+/g).map(Number);
+    return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.72;
+  };
+  const readableTextColor = color =>
+    !color || isLightColor(color) ? "" : color;
+
   // 褰撳墠宸ュ簭锛氱涓�閬撳畬鎴愬害涓嶈冻100%鐨勫伐搴忥紱鍏ㄩ儴瀹屾垚杩斿洖 done
   const getCurrentProcess = row => {
     const list = row.processRouteStatus;
@@ -547,7 +575,20 @@
       done: false,
       name: current.name || "鏈煡宸ュ簭",
       color: current.color || "",
+      pct: toProgressPercentage(current.percentage),
     };
+  };
+
+  // 浜ゆ湡棰勮鐨勬枃瀛楁彁绀猴紙棰滆壊涔嬪鐨勬棤闅滅鍐椾綑鎸囩ず锛�
+  const deliveryHint = row => {
+    if (!row.planCompleteTime || row.isFh) return "";
+    const diff = row.deliveryDaysDiff;
+    if (diff === 15) return "璺濅氦鏈熻繕鍓� 15 澶�";
+    if (diff === 10) return "璺濅氦鏈熻繕鍓� 10 澶�";
+    if (diff === 2) return "璺濅氦鏈熶粎鍓� 2 澶╋紝鏃堕棿绱у紶";
+    if (diff < 0) return `宸查�炬湡 ${-diff} 澶╋紝璇峰敖蹇鐞哷;
+    if (diff < 2) return `璺濅氦鏈熶粎鍓� ${diff} 澶╋紝绱ф�;
+    return "";
   };
 
   // 娣诲姞琛ㄨ绫诲悕鏂规硶
@@ -557,13 +598,13 @@
 
     const diff = row.deliveryDaysDiff;
     if (diff === 15) {
-      return "yellow";
+      return "due-15";
     } else if (diff === 10) {
-      return "pink";
+      return "due-10";
     } else if (diff === 2) {
-      return "purple";
+      return "due-2";
     } else if (diff < 2) {
-      return "red";
+      return "due-critical";
     }
   };
 
@@ -884,39 +925,44 @@
     gap: 8px;
   }
 
-  :deep(.yellow) {
-    background-color: #faf0de;
+  // 浜ゆ湡涓磋繎锛氶粍鈫掓鈫掓祬绾⑩啋绾� 鑹茬浉閫掕繘锛屽叏閮ㄤ繚鎸佹枃瀛楀彲璇�
+  :deep(.due-15) {
+    background-color: #fdf6ec;
   }
 
-  :deep(.pink) {
-    background-color: #fae1de;
+  :deep(.due-10) {
+    background-color: #faecd8;
   }
 
-  :deep(.red) {
-    background-color: #f80202;
+  :deep(.due-2) {
+    background-color: #fde2e2;
   }
 
-  :deep(.purple) {
-    background-color: #f4defa;
+  :deep(.due-critical) {
+    background-color: #fbc4c4;
+
+    td:first-child {
+      box-shadow: inset 3px 0 0 #f56c6c;
+    }
   }
   .table_list {
     margin-top: unset;
   }
 
   .current-process-name {
-    display: flex;
-    align-items: center;
-    font-size: 12px;
+    display: inline-flex;
+    flex-direction: column;
+    align-items: stretch; // 鑹叉潯瀹藉害鐢辨枃瀛楁拺寮�锛屼袱鑰呯瓑瀹�
+    padding: 4px 0;
+    font-size: 14px;
     color: #303133;
-  }
 
-  .process-color-dot {
-    display: inline-block;
-    width: 8px;
-    height: 8px;
-    border-radius: 50%;
-    margin-right: 4px;
-    flex-shrink: 0;
+    .process-color-dot {
+      display: block;
+      height: 16px;
+      border-radius: 4px;
+      margin-bottom: 6px;
+    }
   }
 
   .process-progress-container {
@@ -927,52 +973,43 @@
 
     .process-step {
       display: flex;
-      align-items: center;
+      align-items: flex-start;
       position: relative;
 
       .step-content {
         display: flex;
         flex-direction: column;
         align-items: center;
+        width: 96px;
+        flex-shrink: 0;
         z-index: 1;
 
         .step-circle {
           width: 36px;
           height: 36px;
           border-radius: 50%;
-          border: 2px solid #409eff;
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          background-color: #fff;
+          box-sizing: border-box;
           margin-bottom: 4px;
-
-          .step-percentage {
-            font-size: 11px;
-            font-weight: bold;
-          }
-
-          &.is-completed {
-            border-color: #67c23a;
-            .step-percentage {
-              color: #67c23a;
-            }
-          }
         }
 
         .step-name {
           font-size: 12px;
           color: #606266;
-          white-space: nowrap;
+          line-height: 1.4;
+          max-height: 34px; // 鏈�澶氫袱琛�
+          overflow: hidden;
+          text-align: center;
+          word-break: break-all;
         }
       }
 
       .step-line {
-        width: 30px;
-        height: 1px;
+        width: 130px; // 璐┛鐩搁偦涓ゅ渾蹇冿紝涓ょ琚渾鍦堬紙z-index鏇撮珮锛夐伄鎸�
+        height: 2px;
+        border-radius: 1px;
         background-color: #dcdfe6;
-        margin: 0 -2px;
-        margin-top: -20px; // 鍚戜笂鍋忕Щ浠ュ榻愬渾蹇�
+        flex-shrink: 0;
+        margin: 17px -48px 0; // 绾垮湪34px妲戒綅灞呬腑锛�(130-34)/2=48锛涢《杈�17px瀵瑰噯鍦嗗績
       }
     }
   }

--
Gitblit v1.9.3