From 265d03ef2304f9461ba4926d729cda520c1e959c Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期一, 16 三月 2026 13:28:46 +0800
Subject: [PATCH] 一些修改

---
 src/views/productionManagement/processRoute/index.vue | 2148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 1,977 insertions(+), 171 deletions(-)

diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
index 7d5ab5d..228ccb2 100644
--- a/src/views/productionManagement/processRoute/index.vue
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -1,194 +1,2000 @@
 <template>
   <div class="app-container">
-    <div class="search_form">
-      <el-form :model="searchForm" :inline="true">
-        <el-form-item label="瑙勬牸鍚嶇О:">
-          <el-input v-model="searchForm.model" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-                    style="width: 200px;"
-                    @change="handleQuery" />
+    <div class="route-header">
+      <div class="add-route-btn"
+           @click="handleAddRoute">
+        <el-icon>
+          <Plus />
+        </el-icon>
+        <span>鏂板宸ヨ壓璺嚎</span>
+      </div>
+    </div>
+    <div class="route-card-list">
+      <div v-for="route in routeList"
+           :key="route.id"
+           class="route-card">
+        <div class="card-header">
+          <div class="route-info">
+            <span class="route-name"><el-icon style="margin-right: 8px;line-height: 30px;">
+                <ScaleToOriginal />
+              </el-icon>{{route.routeCode }}<el-tag style="margin-left: 8px"
+                      :type="!route.status ? 'warning' : 'success'">{{ !route.status ? '鑽夌' : '鎵瑰噯' }}</el-tag></span>
+            <!-- <span class="route-code">{{ route.routeCode }}</span> -->
+          </div>
+          <div class="route-actions">
+            <el-button v-if="!route.status"
+                       link
+                       type="success"
+                       @click="handleApproveRoute(route)">
+              <el-icon>
+                <Check />
+              </el-icon>
+              鎵瑰噯
+            </el-button>
+            <el-button v-if="route.status"
+                       link
+                       type="warning"
+                       @click="handleRevokeApproveRoute(route)">
+              <el-icon>
+                <Close />
+              </el-icon>
+              鎾ら攢鎵瑰噯
+            </el-button>
+            <el-button link
+                       type="primary"
+                       @click="handleEditRoute(route)">
+              <el-icon>
+                <Edit />
+              </el-icon>
+              缂栬緫
+            </el-button>
+            <el-button link
+                       type="danger"
+                       @click="handleDeleteRoute(route)">
+              <el-icon>
+                <Delete />
+              </el-icon>
+              鍒犻櫎
+            </el-button>
+          </div>
+        </div>
+        <div class="card-body">
+          <div class="route-meta">
+            <span class="meta-item">
+              <el-icon>
+                <Box />
+              </el-icon>
+              <span class="meta-label">浜у搧:</span>
+              <span class="meta-value">{{ route.productName }} - {{ route.productModelName }}</span>
+            </span>
+            <span class="meta-item">
+              <el-icon>
+                <Document />
+              </el-icon>
+              <span class="meta-label">BOM:</span>
+              <span class="meta-value">{{ route.bomNo || '-' }}</span>
+            </span>
+            <span class="meta-item">
+              <el-icon>
+                <Document />
+              </el-icon>
+              <span class="meta-label">璺嚎鎻忚堪:</span>
+              <span class="meta-value">{{ route.description || '鏆傛棤鎻忚堪' }}</span>
+            </span>
+          </div>
+          <div class="expand-btn-wrapper">
+            <el-button class="expand-btn"
+                       :class="{ expanded: route.expanded }"
+                       type="primary"
+                       text
+                       @click="toggleExpand(route)">
+              <span class="btn-text">{{ route.expanded ? '鏀惰捣宸ュ簭璺嚎' : '灞曞紑宸ュ簭璺嚎' }}</span>
+              <el-icon class="expand-icon">
+                <component :is="route.expanded ? 'ArrowUp' : 'ArrowDown'" />
+              </el-icon>
+            </el-button>
+          </div>
+        </div>
+        <div v-if="route.expanded"
+             class="process-route">
+          <div class="process-flow">
+            <div v-for="(process, index) in route.processList"
+                 :key="process.id"
+                 class="process-flow-item"
+                 draggable="true"
+                 @dragstart="handleDragStart($event, index, route.id)"
+                 @dragover="handleDragOver($event)"
+                 @drop="handleDrop($event, index, route.id)"
+                 @dragend="handleDragEnd">
+              <div class="process-node"
+                   :class="{ expanded: process.expanded }">
+                <div class="process-node-header">
+                  <div class="process-number">{{ index + 1 }}</div>
+                  <div class="process-actions">
+                    <el-button link
+                               type="danger"
+                               @click="handleDeleteProcess(route.id, process)">
+                      <el-icon>
+                        <Delete />
+                      </el-icon>
+                    </el-button>
+                  </div>
+                </div>
+                <div class="process-node-body">
+                  <div class="process-code">{{ process.no }}</div>
+                  <div class="process-name">{{ process.name }}</div>
+                  <div class="process-desc">{{ process.remark || '鏆傛棤鎻忚堪' }}</div>
+                </div>
+                <div class="process-node-footer">
+                  <!-- <el-tag size="small"
+                          :type="process.status === '1' ? 'success' : 'info'">
+                    {{ process.status === '1' ? '鍚敤' : '鍋滅敤' }}
+                  </el-tag> -->
+                  <el-button type="primary"
+                             link
+                             size="small"
+                             @click="toggleProcessParams(process)">
+                    {{ process.expanded ? '鏀惰捣鍙傛暟' : '灞曞紑鍙傛暟' }}
+                    ({{ process.paramList?.length || 0 }})
+                  </el-button>
+                </div>
+                <div v-if="process.expanded"
+                     class="process-params-section">
+                  <div class="params-header">
+                    <span>鍙傛暟鍒楄〃</span>
+                    <el-button type="primary"
+                               link
+                               size="small"
+                               @click="handleAddParam(route.id, process)">
+                      <el-icon>
+                        <Plus />
+                      </el-icon>鏂板
+                    </el-button>
+                  </div>
+                  <div class="params-list">
+                    <div v-for="param in process.paramList"
+                         :key="param.id"
+                         class="param-item">
+                      <div class="param-info">
+                        <span class="param-code">{{ param.parameterCode }}</span>
+                        <span class="param-name">{{ param.parameterName }}</span>
+                        <!-- <el-tag size="small"
+                                style="margin-right: 20px;"
+                                :type="getParamTypeTag(param.parameterType)">
+                          {{ param.parameterType }}
+                        </el-tag> -->
+                        <span class="param-value">鏍囧噯鍊硷細{{ param.standardValue }} {{ param.unit }}</span>
+                      </div>
+                      <div class="param-actions">
+                        <el-button link
+                                   type="primary"
+                                   size="small"
+                                   @click="handleEditParam(route.id, process, param)">
+                          缂栬緫
+                        </el-button>
+                        <el-button link
+                                   type="danger"
+                                   size="small"
+                                   @click="handleDeleteParam(route.id, process, param)">
+                          鍒犻櫎
+                        </el-button>
+                      </div>
+                    </div>
+                    <el-empty v-if="!process.paramList || process.paramList.length === 0"
+                              description="鏆傛棤鍙傛暟"
+                              :image-size="50" />
+                  </div>
+                </div>
+              </div>
+              <div v-if="index < route.processList.length - 1"
+                   class="flow-arrow">
+                <el-icon>
+                  <Right />
+                </el-icon>
+              </div>
+            </div>
+            <div class="add-process-node"
+                 @click="handleSelectProcess(route, index)">
+              <el-icon>
+                <Plus />
+              </el-icon>
+              <span>鏂板宸ュ簭</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 宸ヨ壓璺嚎鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="routeDialogVisible"
+               :title="isRouteEdit ? '缂栬緫宸ヨ壓璺嚎' : '鏂板宸ヨ壓璺嚎'"
+               width="500px">
+      <el-form :model="routeForm"
+               :rules="routeRules"
+               ref="routeFormRef"
+               label-width="120px">
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productModelId">
+          <el-button type="primary"
+                     @click="showProductSelectDialog = true">
+            {{ routeForm.productName && routeForm.productModelName 
+              ? `${routeForm.productName} - ${routeForm.productModelName}` 
+              : '閫夋嫨浜у搧' }}
+          </el-button>
         </el-form-item>
-        <el-form-item>
-          <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+        <el-form-item label="BOM"
+                      prop="bomId">
+          <el-select v-model="routeForm.bomId"
+                     placeholder="璇烽�夋嫨BOM"
+                     clearable
+                     :disabled="!routeForm.productModelId || bomOptions.length === 0"
+                     style="width: 100%">
+            <el-option v-for="item in bomOptions"
+                       :key="item.id"
+                       :label="item.bomNo || `BOM-${item.id}`"
+                       :value="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="璺嚎缂栫爜"
+                      prop="routeCode">
+          <el-input v-model="routeForm.routeCode"
+                    disabled
+                    placeholder="鑷姩鐢熸垚" />
+        </el-form-item>
+        <el-form-item label="璺嚎鎻忚堪"
+                      prop="description">
+          <el-input v-model="routeForm.description"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ヨ矾绾挎弿杩�" />
+        </el-form-item>
+        <!-- <el-form-item label="鐘舵��"
+                      prop="status">
+          <el-radio-group v-model="routeForm.status">
+            <el-radio label="1">鍚敤</el-radio>
+            <el-radio label="0">鍋滅敤</el-radio>
+          </el-radio-group>
+        </el-form-item> -->
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="routeDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleRouteSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 浜у搧閫夋嫨寮圭獥 -->
+    <ProductSelectDialog v-model="showProductSelectDialog"
+                         @confirm="handleProductSelect"
+                         single />
+    <!-- 宸ュ簭鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="processDialogVisible"
+               :title="isProcessEdit ? '缂栬緫宸ュ簭' : '鏂板宸ュ簭'"
+               width="500px">
+      <el-form :model="processForm"
+               :rules="processRules"
+               ref="processFormRef"
+               label-width="120px">
+        <el-form-item label="宸ュ簭缂栫爜"
+                      prop="no">
+          <el-input v-model="processForm.no"
+                    placeholder="璇疯緭鍏ュ伐搴忕紪鐮�" />
+        </el-form-item>
+        <el-form-item label="宸ュ簭鍚嶇О"
+                      prop="name">
+          <el-input v-model="processForm.name"
+                    placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
+        </el-form-item>
+        <el-form-item label="宸ュ簭鎻忚堪"
+                      prop="remark">
+          <el-input v-model="processForm.remark"
+                    type="textarea"
+                    :rows="3"
+                    placeholder="璇疯緭鍏ュ伐搴忔弿杩�" />
+        </el-form-item>
+        <el-form-item label="鐘舵��"
+                      prop="status">
+          <el-radio-group v-model="processForm.status">
+            <el-radio :label="true">鍚敤</el-radio>
+            <el-radio :label="false">鍋滅敤</el-radio>
+          </el-radio-group>
         </el-form-item>
       </el-form>
-    </div>
-    <div class="table_list">
-      <div style="text-align: right" class="mb10">
-        <el-button type="primary" @click="showNewModal">鏂板宸ヨ壓璺嚎</el-button>
-        <el-button type="danger" @click="handleDelete" :disabled="selectedRows.length === 0" plain>鍒犻櫎宸ヨ壓璺嚎</el-button>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="processDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleProcessSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 閫夋嫨宸ュ簭瀵硅瘽妗� -->
+    <el-dialog v-model="selectProcessDialogVisible"
+               title="閫夋嫨宸ュ簭"
+               width="1000px">
+      <div class="process-select-container">
+        <!-- 宸︿晶宸ュ簭鍒楄〃 -->
+        <div class="process-list-area">
+          <div class="area-title">鍙�夊伐搴�</div>
+          <div class="search-box">
+            <el-input v-model="processSearchKeyword"
+                      placeholder="璇疯緭鍏ュ伐搴忓悕绉版悳绱�"
+                      clearable
+                      size="small"
+                      @input="handleProcessSearch">
+              <template #prefix>
+                <el-icon>
+                  <Search />
+                </el-icon>
+              </template>
+            </el-input>
+          </div>
+          <el-table :data="filteredProcessList"
+                    height="360"
+                    border
+                    highlight-current-row
+                    @current-change="handleProcessSelect">
+            <el-table-column prop="no"
+                             label="宸ュ簭缂栧彿"
+                             width="100" />
+            <el-table-column prop="name"
+                             label="宸ュ簭鍚嶇О" />
+            <el-table-column prop="remark"
+                             label="宸ュ簭鎻忚堪" />
+            <el-table-column prop="status"
+                             label="鐘舵��"
+                             width="80">
+              <template #default="scope">
+                <el-tag size="small"
+                        :type="scope.row.status ? 'success' : 'info'">
+                  {{ scope.row.status ? '鍚敤' : '鍋滅敤' }}
+                </el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <!-- 鍙充晶宸ュ簭璇︽儏 -->
+        <div class="process-detail-area">
+          <div class="area-title">宸ュ簭璇︽儏</div>
+          <el-form v-if="selectedProcessItem"
+                   :model="selectedProcessItem"
+                   label-width="100px"
+                   class="process-detail-form">
+            <el-form-item label="宸ュ簭缂栧彿">
+              <span class="detail-text">{{ selectedProcessItem.no }}</span>
+            </el-form-item>
+            <el-form-item label="宸ュ簭鍚嶇О">
+              <span class="detail-text">{{ selectedProcessItem.name }}</span>
+            </el-form-item>
+            <el-form-item label="宸ュ簭鎻忚堪">
+              <span class="detail-text">{{ selectedProcessItem.remark || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鐘舵��">
+              <el-tag size="small"
+                      :type="selectedProcessItem.status ? 'success' : 'info'">
+                {{ selectedProcessItem.status ? '鍚敤' : '鍋滅敤' }}
+              </el-tag>
+            </el-form-item>
+            <el-form-item label="鏄惁璐ㄦ">
+              <el-tag size="small"
+                      :type="selectedProcessItem.isQuality ? 'success' : 'info'">
+                {{ selectedProcessItem.isQuality ? '璐ㄦ' : '闈炶川妫�' }}
+              </el-tag>
+            </el-form-item>
+            <!-- <el-form-item label="鍙傛暟鏁伴噺">
+              <span class="detail-text">{{ selectedProcessItem.paramCount || 0 }}涓�</span>
+            </el-form-item> -->
+          </el-form>
+          <el-empty v-else
+                    description="璇蜂粠宸︿晶閫夋嫨宸ュ簭" />
+        </div>
       </div>
-      <PIMTable
-          rowKey="id"
-          :column="tableColumn"
-          :tableData="tableData"
-          :page="page"
-          :isSelection="true"
-          @selection-change="handleSelectionChange"
-          :tableLoading="tableLoading"
-          @pagination="pagination"
-          :total="page.total"
-      />
-    </div>
-    <new-process
-        v-if="isShowNewModal"
-        v-model:visible="isShowNewModal"
-        @completed="getList"
-    />
-
-    <edit-process
-        v-if="isShowEditModal"
-        v-model:visible="isShowEditModal"
-        :record="record"
-        @completed="getList"
-    />
-
-    <route-item-form
-        v-if="isShowItemModal"
-        v-model:visible="isShowItemModal"
-        :record="record"
-        @completed="getList"
-    />
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="selectProcessDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     :disabled="!selectedProcessItem"
+                     @click="handleProcessSelectSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 鍙傛暟鏂板/缂栬緫瀵硅瘽妗� -->
+    <el-dialog v-model="paramDialogVisible"
+               :title="isParamEdit ? '缂栬緫鍙傛暟' : '鏂板鍙傛暟'"
+               width="500px">
+      <el-form :model="paramForm"
+               :rules="paramRules"
+               ref="paramFormRef"
+               label-width="120px">
+        <el-form-item label="鍙傛暟缂栧彿"
+                      prop="parameterCode">
+          <el-input v-model="paramForm.parameterCode"
+                    placeholder="璇疯緭鍏ュ弬鏁扮紪鍙�" />
+        </el-form-item>
+        <el-form-item label="鍙傛暟鍚嶇О"
+                      prop="parameterName">
+          <el-input v-model="paramForm.parameterName"
+                    placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍙傛暟妯″紡"
+                      prop="parameterType2">
+          <el-select v-model="paramForm.parameterType2"
+                     placeholder="璇烽�夋嫨鍙傛暟妯″紡">
+            <el-option label="鍗曞��"
+                       value="1" />
+            <el-option label="鍖洪棿"
+                       value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鍙傛暟绫诲瀷"
+                      prop="parameterType">
+          <el-select v-model="paramForm.parameterType"
+                     @change="handleParamTypeChange"
+                     placeholder="璇烽�夋嫨鍙傛暟绫诲瀷">
+            <el-option label="鏁板�兼牸寮�"
+                       value="鏁板�兼牸寮�" />
+            <el-option label="鏂囨湰鏍煎紡"
+                       value="鏂囨湰鏍煎紡" />
+            <el-option label="涓嬫媺閫夐」"
+                       value="涓嬫媺閫夐」" />
+            <el-option label="鏃堕棿鏍煎紡"
+                       value="鏃堕棿鏍煎紡" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="paramForm.parameterType === '涓嬫媺閫夐」'"
+                      label="鏁版嵁瀛楀吀"
+                      prop="parameterFormat">
+          <el-select v-model="paramForm.parameterFormat"
+                     placeholder="璇烽�夋嫨鏁版嵁瀛楀吀">
+            <el-option v-for="item in dictTypes"
+                       :key="item.dictType"
+                       :label="item.dictName"
+                       :value="item.dictType" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-else-if="paramForm.parameterType === '鏃堕棿鏍煎紡'"
+                      label="鏃堕棿鏍煎紡"
+                      prop="parameterFormat">
+          <el-select v-model="paramForm.parameterFormat"
+                     placeholder="璇烽�夋嫨鏃堕棿鏍煎紡">
+            <el-option label="YYYY-MM-DD HH:mm:ss"
+                       value="YYYY-MM-DD HH:mm:ss" />
+            <el-option label="YYYY-MM-DD"
+                       value="YYYY-MM-DD" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-else
+                      label="鍙傛暟鏍煎紡"
+                      prop="parameterFormat">
+          <el-input v-model="paramForm.parameterFormat"
+                    placeholder="璇疯緭鍏ュ弬鏁版牸寮�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯鍊�"
+                      prop="standardValue">
+          <el-input v-model="paramForm.standardValue"
+                    placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+        </el-form-item>
+        <el-form-item label="鍗曚綅"
+                      prop="unit">
+          <el-input v-model="paramForm.unit"
+                    placeholder="璇疯緭鍏ュ崟浣�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="paramDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleParamSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 閫夋嫨鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="selectParamDialogVisible"
+               title="閫夋嫨鍙傛暟"
+               width="1000px">
+      <div class="param-select-container">
+        <!-- 宸︿晶鍙傛暟鍒楄〃 -->
+        <div class="param-list-area">
+          <div class="area-title">鍙�夊弬鏁�</div>
+          <div class="search-box">
+            <el-input v-model="paramSearchKeyword"
+                      placeholder="璇疯緭鍏ュ弬鏁板悕绉版悳绱�"
+                      clearable
+                      size="small"
+                      @input="handleParamSearch">
+              <template #prefix>
+                <el-icon>
+                  <Search />
+                </el-icon>
+              </template>
+            </el-input>
+          </div>
+          <el-table :data="filteredParamList"
+                    height="300"
+                    border
+                    highlight-current-row
+                    @current-change="handleParamSelect">
+            <el-table-column prop="paramName"
+                             label="鍙傛暟鍚嶇О" />
+            <el-table-column prop="paramType"
+                             label="鍙傛暟绫诲瀷">
+              <template #default="scope">
+                <el-tag size="small"
+                        :type="getParamTypeTag(scope.row.paramType)">
+                  {{ getParamTypeText(scope.row.paramType) }}
+                </el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+          <!-- 鍒嗛〉鎺т欢 -->
+          <div class="pagination-container"
+               style="margin-top: 10px;">
+            <el-pagination v-model:current-page="paramPage.current"
+                           v-model:page-size="paramPage.size"
+                           :page-sizes="[10, 20, 50, 100]"
+                           layout="total, sizes, prev, pager, next, jumper"
+                           :total="paramPage.total"
+                           @size-change="handleParamSizeChange"
+                           @current-change="handleParamCurrentChange"
+                           size="small" />
+          </div>
+        </div>
+        <!-- 鍙充晶鍙傛暟璇︽儏 -->
+        <div class="param-detail-area">
+          <div class="area-title">鍙傛暟璇︽儏</div>
+          <el-form v-if="selectedParam"
+                   :model="selectedParam"
+                   label-width="100px"
+                   class="param-detail-form">
+            <el-form-item label="鍙傛暟鍚嶇О">
+              <span class="detail-text">{{ selectedParam.paramName }}</span>
+            </el-form-item>
+            <el-form-item label="鍙傛暟妯″紡">
+              <el-tag size="small"
+                      :type="selectedParam.valueMode == '1' ? 'success' : 'warning'">
+                {{ selectedParam.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+              </el-tag>
+            </el-form-item>
+            <el-form-item label="鍙傛暟绫诲瀷">
+              <el-tag size="small"
+                      :type="getParamTypeTag(selectedParam.paramType)">
+                {{ getParamTypeText(selectedParam.paramType) }}
+              </el-tag>
+            </el-form-item>
+            <el-form-item label="鍙傛暟鏍煎紡">
+              <span class="detail-text">{{ selectedParam.paramFormat || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鍗曚綅">
+              <span class="detail-text">{{ selectedParam.unit || '-' }}</span>
+            </el-form-item>
+            <el-form-item label="鏍囧噯鍊�"
+                          v-if="selectedParam.valueMode == '1'">
+              <el-input v-model="selectedParam.standardValue"
+                        type="number"
+                        placeholder="璇疯緭鍏ラ粯璁ゅ��" />
+            </el-form-item>
+            <el-form-item label="鏈�灏忓��"
+                          v-if="selectedParam.valueMode == '2'">
+              <el-input v-model="selectedParam.minValue"
+                        type="number"
+                        placeholder="璇疯緭鍏ユ渶灏忓��" />
+            </el-form-item>
+            <el-form-item label="鏈�澶у��"
+                          v-if="selectedParam.valueMode == '2'">
+              <el-input v-model="selectedParam.maxValue"
+                        type="number"
+                        placeholder="璇疯緭鍏ユ渶澶у��" />
+            </el-form-item>
+            <el-form-item label="鎺掑簭">
+              <el-input v-model="selectedParam.sort"
+                        type="number"
+                        placeholder="璇疯緭鍏ユ帓搴�" />
+            </el-form-item>
+            <el-form-item label="鏄惁蹇呭~">
+              <el-switch v-model="selectedParam.isRequired"
+                         :active-value="1"
+                         :inactive-value="0" />
+            </el-form-item>
+          </el-form>
+          <el-empty v-else
+                    description="璇蜂粠宸︿晶閫夋嫨鍙傛暟" />
+        </div>
+      </div>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="selectParamDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     :disabled="!selectedParam"
+                     @click="handleParamSelectSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 缂栬緫鍙傛暟瀵硅瘽妗� -->
+    <el-dialog v-model="editParamDialogVisible"
+               title="缂栬緫鍙傛暟"
+               width="600px">
+      <el-form :model="editParamForm"
+               :rules="editParamRules"
+               ref="editParamFormRef"
+               label-width="120px">
+        <el-form-item label="鍙傛暟鍚嶇О">
+          <span class="detail-text">{{ editParamForm.paramName }}</span>
+        </el-form-item>
+        <el-form-item label="鍙傛暟妯″紡">
+          <el-tag size="small"
+                  :type="editParamForm.valueMode == '1' ? 'success' : 'warning'">
+            {{ editParamForm.valueMode == '1' ? '鍗曞��' : '鍖洪棿' }}
+          </el-tag>
+        </el-form-item>
+        <el-form-item label="鏍囧噯鍊�"
+                      v-if="editParamForm.valueMode == '1'"
+                      prop="standardValue">
+          <el-input v-model="editParamForm.standardValue"
+                    type="number"
+                    placeholder="璇疯緭鍏ユ爣鍑嗗��" />
+        </el-form-item>
+        <el-form-item label="鏈�灏忓��"
+                      v-if="editParamForm.valueMode == '2'"
+                      prop="minValue">
+          <el-input v-model="editParamForm.minValue"
+                    type="number"
+                    placeholder="璇疯緭鍏ユ渶灏忓��" />
+        </el-form-item>
+        <el-form-item label="鏈�澶у��"
+                      v-if="editParamForm.valueMode == '2'"
+                      prop="maxValue">
+          <el-input v-model="editParamForm.maxValue"
+                    type="number"
+                    placeholder="璇疯緭鍏ユ渶澶у��" />
+        </el-form-item>
+        <el-form-item label="鎺掑簭"
+                      prop="sort">
+          <el-input v-model="editParamForm.sort"
+                    type="number"
+                    placeholder="璇疯緭鍏ユ帓搴�" />
+        </el-form-item>
+        <el-form-item label="鏄惁蹇呭~"
+                      prop="isRequired">
+          <el-switch v-model="editParamForm.isRequired"
+                     :active-value="1"
+                     :inactive-value="0" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="editParamDialogVisible = false">鍙栨秷</el-button>
+          <el-button type="primary"
+                     @click="handleEditParamSubmit">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import NewProcess from "@/views/productionManagement/processRoute/New.vue";
-import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
-import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
-import {listPage, del} from "@/api/productionManagement/processRoute.js";
-import { useRouter } from 'vue-router'
+  import { ref, reactive, getCurrentInstance } from "vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import {
+    Plus,
+    Edit,
+    Delete,
+    ArrowUp,
+    ArrowDown,
+    Right,
+    Search,
+    Check,
+    Close,
+    Box,
+    Document,
+  } from "@element-plus/icons-vue";
+  import { listType } from "@/api/system/dict/type";
+  import { getByModel } from "@/api/productionManagement/productBom.js";
+  import { add, update, del } from "@/api/productionManagement/processRoute.js";
+  import { list as getProcessListApi } from "@/api/productionManagement/productionProcess.js";
+  import { getBaseParamList } from "@/api/basicData/parameterMaintenance.js";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
 
-const router = useRouter()
-const data = reactive({
-  searchForm: {
-    model: "",
-  },
-});
-const { searchForm } = toRefs(data);
-const tableColumn = ref([
-  {
-    label: "宸ヨ壓璺嚎缂栧彿",
-    prop: "processRouteCode",
-  },
-  {
-    label: "浜у搧鍚嶇О",
-    prop: "productName",
-  },
-  {
-    label: "瑙勬牸鍚嶇О",
-    prop: "model",
-  },
-  {
-    label: "鎻忚堪",
-    prop: "description",
-  },
-  {
-    dataType: "action",
-    label: "鎿嶄綔",
-    align: "center",
-    fixed: "right",
-    width: 280,
-    operation: [
+  // 宸ヨ壓璺嚎鍒楄〃
+  const routeList = ref([]);
+  const dictTypes = ref([]);
+
+  // 鑾峰彇鍏ㄥ眬瀹炰緥
+  const { proxy } = getCurrentInstance();
+
+  // 浜у搧閫夋嫨鍜孊OM鐩稿叧
+  const showProductSelectDialog = ref(false);
+  const bomOptions = ref([]);
+
+  // 宸ヨ壓璺嚎瀵硅瘽妗�
+  const routeDialogVisible = ref(false);
+  const isRouteEdit = ref(false);
+  const routeFormRef = ref(null);
+  const routeForm = reactive({
+    id: null,
+    productModelId: null,
+    productName: "",
+    productModelName: "",
+    bomId: null,
+    routeCode: "",
+    description: "",
+    status: true,
+  });
+  const routeRules = {
+    productModelId: [
+      { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+    ],
+    bomId: [{ required: true, message: "璇烽�夋嫨BOM", trigger: "change" }],
+  };
+
+  // 宸ュ簭瀵硅瘽妗�
+  const processDialogVisible = ref(false);
+  const isProcessEdit = ref(false);
+  const processFormRef = ref(null);
+  const currentRouteId = ref(null);
+  const processForm = reactive({
+    id: null,
+    no: "",
+    name: "",
+    remark: "",
+    status: true,
+  });
+  const processRules = {
+    no: [{ required: true, message: "璇疯緭鍏ュ伐搴忕紪鐮�", trigger: "blur" }],
+    name: [{ required: true, message: "璇疯緭鍏ュ伐搴忓悕绉�", trigger: "blur" }],
+  };
+
+  // 閫夋嫨宸ュ簭瀵硅瘽妗�
+  const selectProcessDialogVisible = ref(false);
+  const availableProcessList = ref([]);
+  const filteredProcessList = ref([]);
+  const selectedProcessItem = ref(null);
+  const processSearchKeyword = ref("");
+  const currentRouteIndex = ref(null);
+
+  // 鍙傛暟瀵硅瘽妗�
+  const paramDialogVisible = ref(false);
+  const isParamEdit = ref(false);
+  const paramFormRef = ref(null);
+  const currentProcessId = ref(null);
+  const paramForm = reactive({
+    id: null,
+    parameterCode: "",
+    parameterName: "",
+    parameterType2: "1",
+    parameterType: "",
+    parameterFormat: "",
+    standardValue: "",
+    unit: "",
+  });
+  const paramRules = {
+    parameterCode: [
+      { required: true, message: "璇疯緭鍏ュ弬鏁扮紪鍙�", trigger: "blur" },
+    ],
+    parameterName: [
+      { required: true, message: "璇疯緭鍏ュ弬鏁板悕绉�", trigger: "blur" },
+    ],
+    parameterType: [
+      { required: true, message: "璇烽�夋嫨鍙傛暟绫诲瀷", trigger: "change" },
+    ],
+  };
+
+  // 閫夋嫨鍙傛暟瀵硅瘽妗�
+  const selectParamDialogVisible = ref(false);
+  const availableParamList = ref([]);
+  const filteredParamList = ref([]);
+  const selectedParam = ref(null);
+  const paramSearchKeyword = ref("");
+
+  // 鍙�夊弬鏁板垎椤�
+  const paramPage = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  // 缂栬緫鍙傛暟瀵硅瘽妗�
+  const editParamDialogVisible = ref(false);
+  const editParamFormRef = ref(null);
+  const editParamForm = reactive({
+    id: null,
+    processId: null,
+    paramId: null,
+    paramName: "",
+    valueMode: "1",
+    standardValue: null,
+    minValue: null,
+    maxValue: null,
+    sort: 1,
+    isRequired: 0,
+  });
+  const editParamRules = reactive({
+    standardValue: [
       {
-        name: "缂栬緫",
-        type: "text",
-        clickFun: (row) => {
-          showEditModal(row);
-        }
+        required: true,
+        message: "璇疯緭鍏ユ爣鍑嗗��",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("璇疯緭鍏ユ爣鍑嗗��"));
+          } else {
+            callback();
+          }
+        },
       },
+    ],
+    minValue: [
       {
-        name: "璺嚎椤圭洰",
-        type: "text",
-        clickFun: (row) => {
-          showItemModal(row);
+        required: true,
+        message: "璇疯緭鍏ユ渶灏忓��",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("璇疯緭鍏ユ渶灏忓��"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+    maxValue: [
+      {
+        required: true,
+        message: "璇疯緭鍏ユ渶澶у��",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("璇疯緭鍏ユ渶澶у��"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+    sort: [
+      {
+        required: true,
+        message: "璇疯緭鍏ユ帓搴�",
+        trigger: "blur",
+        validator: (rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("璇疯緭鍏ユ帓搴�"));
+          } else if (isNaN(value) || value < 1) {
+            callback(new Error("鎺掑簭蹇呴』鏄ぇ浜�0鐨勬暣鏁�"));
+          } else {
+            callback();
+          }
+        },
+      },
+    ],
+  });
+
+  // 鎷栨嫿鐩稿叧
+  const draggedItem = ref(null);
+  const draggedRouteId = ref(null);
+
+  // 鑾峰彇宸ヨ壓璺嚎鍒楄〃
+  const getRouteList = () => {
+    // 瀵煎叆 listPage 鏂规硶
+    import("@/api/productionManagement/processRoute.js").then(({ listPage }) => {
+      listPage({ pageNum: 1, pageSize: 100 })
+        .then(res => {
+          // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
+          routeList.value = (res.data?.records || []).map(item => ({
+            id: item.id,
+            productModelId: item.productModelId,
+            productName: item.productName,
+            productModelName: item.model || item.productModelName,
+            bomId: item.bomId,
+            bomNo: item.bomNo,
+            routeCode: item.processRouteCode || item.routeCode,
+            description: item.description || item.description,
+            status: item.status,
+            expanded: false,
+            processList: item.processList || [],
+          }));
+        })
+        .catch(err => {
+          console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", err);
+          routeList.value = [];
+        });
+    });
+  };
+
+  // 灞曞紑/鏀惰捣宸ヨ壓璺嚎
+  const toggleExpand = route => {
+    route.expanded = !route.expanded;
+  };
+
+  // 灞曞紑/鏀惰捣宸ュ簭鍙傛暟
+  const toggleProcessParams = process => {
+    process.expanded = !process.expanded;
+  };
+
+  // 宸ヨ壓璺嚎鎿嶄綔
+  const handleAddRoute = () => {
+    isRouteEdit.value = false;
+    routeForm.id = null;
+    routeForm.productModelId = null;
+    routeForm.productName = "";
+    routeForm.productModelName = "";
+    routeForm.bomId = null;
+    routeForm.routeCode = "";
+    routeForm.description = "";
+    routeForm.status = false;
+    bomOptions.value = [];
+    routeDialogVisible.value = true;
+  };
+
+  const handleEditRoute = route => {
+    isRouteEdit.value = true;
+    routeForm.id = route.id;
+    routeForm.productModelId = route.productModelId;
+    routeForm.productName = route.productName;
+    routeForm.productModelName = route.productModelName;
+    routeForm.bomId = route.bomId;
+    routeForm.routeCode = route.routeCode;
+    routeForm.description = route.description;
+    routeForm.status = route.status;
+    routeDialogVisible.value = true;
+  };
+
+  const handleDeleteRoute = route => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      del(route.id)
+        .then(res => {
+          ElMessage.success("鍒犻櫎鎴愬姛");
+          getRouteList();
+        })
+        .catch(err => {
+          ElMessage.error("鍒犻櫎澶辫触");
+        });
+    });
+  };
+
+  const handleRouteSubmit = () => {
+    routeFormRef.value.validate(valid => {
+      if (valid) {
+        // 鏋勫缓鎻愪氦鏁版嵁
+        const submitData = {
+          ...routeForm,
+          // 娉ㄦ剰锛欰PI 鏈熸湜鐨勫瓧娈靛悕鍙兘涓庤〃鍗曞瓧娈靛悕涓嶅悓
+          productId: routeForm.productModelId,
+          productModelId: routeForm.productModelId,
+          description: routeForm.description,
+        };
+
+        if (isRouteEdit.value) {
+          // 缂栬緫鎿嶄綔
+          update(submitData)
+            .then(res => {
+              ElMessage.success("缂栬緫鎴愬姛");
+              routeDialogVisible.value = false;
+              getRouteList();
+            })
+            .catch(err => {
+              ElMessage.error("缂栬緫澶辫触");
+            });
+        } else {
+          // 鏂板鎿嶄綔
+          add(submitData)
+            .then(res => {
+              ElMessage.success("鏂板鎴愬姛");
+              routeDialogVisible.value = false;
+              getRouteList();
+            })
+            .catch(err => {
+              ElMessage.error("鏂板澶辫触");
+            });
         }
       }
-    ]
-  }
-]);
-const tableData = ref([]);
-const selectedRows = ref([]);
-const tableLoading = ref(false);
-const isShowNewModal = ref(false);
-const isShowEditModal = ref(false);
-const isShowItemModal = ref(false);
-const record = ref({});
-const page = reactive({
-  current: 1,
-  size: 100,
-  total: 0,
-});
-const { proxy } = getCurrentInstance()
+    });
+  };
 
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-  page.current = 1;
-  getList();
-};
+  // 浜у搧閫夋嫨澶勭悊
+  const handleProductSelect = async products => {
+    if (products && products.length > 0) {
+      const product = products[0];
+      // 鍏堟煡璇OM鍒楄〃锛堝繀閫夛級
+      try {
+        const res = await getByModel(product.id);
+        // 澶勭悊杩斿洖鐨凚OM鏁版嵁锛氬彲鑳芥槸鏁扮粍銆佸璞℃垨鍖呭惈data瀛楁
+        let bomList = [];
+        if (Array.isArray(res)) {
+          bomList = res;
+        } else if (res && res.data) {
+          bomList = Array.isArray(res.data) ? res.data : [res.data];
+        } else if (res && typeof res === "object") {
+          bomList = [res];
+        }
 
-const pagination = (obj) => {
-  page.current = obj.page;
-  page.size = obj.limit;
-  getList();
-};
-const getList = () => {
-  tableLoading.value = true;
-  const params = { ...searchForm.value, ...page };
-  params.entryDate = undefined
-  listPage(params).then(res => {
-    tableLoading.value = false;
-    tableData.value = res.data.records.map(item => ({
-      ...item,
-    }));
-    page.total = res.data.total;
-  }).catch(err => {
-    tableLoading.value = false;
-  })
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-  selectedRows.value = selection;
-};
-
-// 鎵撳紑鏂板寮规
-const showNewModal = () => {
-  isShowNewModal.value = true
-};
-
-const showEditModal = (row) => {
-  isShowEditModal.value = true
-  record.value = row
-};
-
-const showItemModal = (row) => {
-  router.push({
-    path: '/productionManagement/processRouteItem',
-    query: {
-      id: row.id
+        if (bomList.length > 0) {
+          routeForm.productModelId = product.id;
+          routeForm.productName = product.productName;
+          routeForm.productModelName = product.model;
+          routeForm.bomId = undefined; // 閲嶇疆BOM閫夋嫨
+          bomOptions.value = bomList;
+          showProductSelectDialog.value = false;
+          // 瑙﹀彂琛ㄥ崟楠岃瘉鏇存柊
+          proxy.$refs["routeFormRef"]?.validateField("productModelId");
+        } else {
+          proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+        }
+      } catch (error) {
+        // 濡傛灉鎺ュ彛杩斿洖404鎴栧叾浠栭敊璇紝璇存槑娌℃湁BOM
+        proxy.$modal.msgError("璇ヤ骇鍝佹病鏈塀OM锛岃鍏堝垱寤築OM");
+      }
     }
-  })
-};
+  };
 
-// 鍒犻櫎
-function handleDelete() {
-  const ids = selectedRows.value.map((item) => item.id);
-  proxy.$modal
-      .confirm('鏄惁纭鍒犻櫎宸插嬀閫夌殑鏁版嵁椤癸紵')
-      .then(function () {
-        return del(ids);
-      })
-      .then(() => {
-        getList();
-        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-      })
-      .catch(() => {});
-}
+  const handleApproveRoute = route => {
+    ElMessageBox.confirm("纭畾瑕佹壒鍑嗚宸ヨ壓璺嚎鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "info",
+    }).then(() => {
+      // 璋冪敤淇敼鎺ュ彛锛屽彧淇敼status瀛楁涓烘壒鍑嗙姸鎬�
+      update({ id: route.id, status: true })
+        .then(res => {
+          ElMessage.success("鎵瑰噯鎴愬姛");
+          getRouteList();
+        })
+        .catch(err => {
+          ElMessage.error("鎵瑰噯澶辫触");
+        });
+    });
+  };
 
-onMounted(() => {
-  getList();
-});
+  const handleRevokeApproveRoute = route => {
+    ElMessageBox.confirm("纭畾瑕佹挙閿�鎵瑰噯璇ュ伐鑹鸿矾绾垮悧锛�", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      // 璋冪敤淇敼鎺ュ彛锛屽彧淇敼status瀛楁涓鸿崏绋跨姸鎬�
+      update({ id: route.id, status: false })
+        .then(res => {
+          ElMessage.success("鎾ら攢鎵瑰噯鎴愬姛");
+          getRouteList();
+        })
+        .catch(err => {
+          ElMessage.error("鎾ら攢鎵瑰噯澶辫触");
+        });
+    });
+  };
+
+  // 宸ュ簭鎿嶄綔
+  const handleSelectProcess = (route, index) => {
+    currentRouteId.value = route.id;
+    currentRouteIndex.value = index;
+    // 鑾峰彇鍙�夊伐搴忓垪琛�
+    getProcessListApi()
+      .then(res => {
+        // 澶勭悊杩斿洖鐨勬暟鎹紝鏄犲皠鍒伴〉闈㈤渶瑕佺殑鏍煎紡
+        availableProcessList.value = (res.data || []).map(item => ({
+          id: item.id,
+          no: item.no || item.no,
+          name: item.name || item.name,
+          remark: item.remark || item.remark,
+          status: item.status,
+          isQuality: item.isQuality,
+        }));
+        filteredProcessList.value = availableProcessList.value;
+        processSearchKeyword.value = "";
+        selectedProcessItem.value = null;
+        selectProcessDialogVisible.value = true;
+      })
+      .catch(() => {
+        ElMessage.error("鑾峰彇宸ュ簭鍒楄〃澶辫触");
+      });
+  };
+
+  const handleEditProcess = (routeId, process) => {
+    currentRouteId.value = routeId;
+    isProcessEdit.value = true;
+    processForm.id = process.id;
+    processForm.no = process.no;
+    processForm.name = process.name;
+    processForm.remark = process.remark;
+    processForm.status = process.status;
+    processDialogVisible.value = true;
+  };
+
+  const handleDeleteProcess = (routeId, process) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ宸ュ簭鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      getRouteList();
+    });
+  };
+
+  const handleProcessSubmit = () => {
+    processFormRef.value.validate(valid => {
+      if (valid) {
+        ElMessage.success(isProcessEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+        processDialogVisible.value = false;
+        getRouteList();
+      }
+    });
+  };
+
+  // 閫夋嫨宸ュ簭鐩稿叧鏂规硶
+  const handleProcessSearch = () => {
+    const keyword = processSearchKeyword.value.trim().toLowerCase();
+    if (!keyword) {
+      filteredProcessList.value = availableProcessList.value;
+    } else {
+      filteredProcessList.value = availableProcessList.value.filter(item =>
+        item.name.toLowerCase().includes(keyword)
+      );
+    }
+  };
+
+  const handleProcessSelect = row => {
+    selectedProcessItem.value = row;
+  };
+
+  const handleProcessSelectSubmit = () => {
+    if (!selectedProcessItem.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓伐搴�");
+      return;
+    }
+
+    // 妫�鏌ュ伐搴忔槸鍚﹀凡瀛樺湪
+    const route = routeList.value[currentRouteIndex.value];
+    const exists = route.processList.some(
+      p => p.id === selectedProcessItem.value.id
+    );
+    if (exists) {
+      ElMessage.warning("璇ュ伐搴忓凡瀛樺湪浜庡伐鑹鸿矾绾夸腑");
+      return;
+    }
+
+    // 娣诲姞宸ュ簭鍒板伐鑹鸿矾绾�
+    const newProcess = {
+      id: Date.now(),
+      no: selectedProcessItem.value.no,
+      name: selectedProcessItem.value.name,
+      remark: selectedProcessItem.value.remark,
+      status: selectedProcessItem.value.status,
+      paramList: [],
+      expanded: false,
+    };
+
+    route.processList.push(newProcess);
+    ElMessage.success("娣诲姞宸ュ簭鎴愬姛");
+    selectProcessDialogVisible.value = false;
+  };
+
+  // 鍙傛暟鎿嶄綔
+  const handleAddParam = (routeId, process) => {
+    currentRouteId.value = routeId;
+    currentProcessId.value = process.id;
+    selectedParam.value = null;
+    paramSearchKeyword.value = "";
+    paramPage.current = 1;
+    // 鑾峰彇鍙�夊弬鏁板垪琛�
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+    selectParamDialogVisible.value = true;
+  };
+
+  const handleEditParam = (routeId, process, param) => {
+    currentRouteId.value = routeId;
+    currentProcessId.value = process.id;
+    editParamForm.id = param.id;
+    editParamForm.processId = process.id;
+    editParamForm.paramId = param.paramId;
+    editParamForm.paramName = param.parameterName || param.paramName;
+    editParamForm.valueMode = param.parameterType2 || param.valueMode || "1";
+    editParamForm.standardValue = param.standardValue;
+    editParamForm.minValue = param.minValue;
+    editParamForm.maxValue = param.maxValue;
+    editParamForm.sort = param.sort || 1;
+    editParamForm.isRequired = param.isRequired || 0;
+    editParamDialogVisible.value = true;
+  };
+
+  const handleDeleteParam = (routeId, process, param) => {
+    ElMessageBox.confirm("纭畾瑕佸垹闄よ鍙傛暟鍚楋紵", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      ElMessage.success("鍒犻櫎鎴愬姛");
+      getRouteList();
+    });
+  };
+
+  const handleParamSubmit = () => {
+    paramFormRef.value.validate(valid => {
+      if (valid) {
+        ElMessage.success(isParamEdit.value ? "缂栬緫鎴愬姛" : "鏂板鎴愬姛");
+        paramDialogVisible.value = false;
+        getRouteList();
+      }
+    });
+  };
+
+  const handleParamTypeChange = () => {
+    if (paramForm.parameterType === "鏁板�兼牸寮�") {
+      paramForm.parameterFormat = "#.0000";
+    } else if (paramForm.parameterType === "鏃堕棿鏍煎紡") {
+      paramForm.parameterFormat = "YYYY-MM-DD HH:mm:ss";
+    } else {
+      paramForm.parameterFormat = "";
+    }
+  };
+
+  const getParamTypeTag = type => {
+    const typeMap = {
+      1: "primary",
+      2: "info",
+      3: "warning",
+      4: "success",
+      鏁板�兼牸寮�: "primary",
+      鏂囨湰鏍煎紡: "info",
+      涓嬫媺閫夐」: "warning",
+      鏃堕棿鏍煎紡: "success",
+    };
+    return typeMap[type] || "default";
+  };
+
+  const getParamTypeText = type => {
+    const typeMap = {
+      1: "鏁板�兼牸寮�",
+      2: "鏂囨湰鏍煎紡",
+      3: "涓嬫媺閫夐」",
+      4: "鏃堕棿鏍煎紡",
+      鏁板�兼牸寮�: "鏁板�兼牸寮�",
+      鏂囨湰鏍煎紡: "鏂囨湰鏍煎紡",
+      涓嬫媺閫夐」: "涓嬫媺閫夐」",
+      鏃堕棿鏍煎紡: "鏃堕棿鏍煎紡",
+    };
+    return typeMap[type] || "鏈煡鍙傛暟绫诲瀷";
+  };
+
+  // 閫夋嫨鍙傛暟鐩稿叧鏂规硶
+  const handleParamSearch = () => {
+    // 閲嶇疆鍒嗛〉
+    paramPage.current = 1;
+    // 閲嶆柊鍔犺浇鏁版嵁
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+  };
+
+  const handleParamSelect = row => {
+    selectedParam.value = row;
+  };
+
+  // 澶勭悊鍒嗛〉澶у皬鍙樺寲
+  const handleParamSizeChange = size => {
+    paramPage.size = size;
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+  };
+
+  // 澶勭悊褰撳墠椤电爜鍙樺寲
+  const handleParamCurrentChange = current => {
+    paramPage.current = current;
+    getBaseParamList({
+      paramName: paramSearchKeyword.value,
+      current: paramPage.current,
+      size: paramPage.size,
+    }).then(res => {
+      if (res.code === 200) {
+        filteredParamList.value = res.data?.records || [];
+        paramPage.total = res.data?.total || 0;
+      } else {
+        ElMessage.error(res.msg || "鏌ヨ澶辫触");
+      }
+    });
+  };
+
+  const handleParamSelectSubmit = () => {
+    if (!selectedParam.value) {
+      ElMessage.warning("璇峰厛閫夋嫨涓�涓弬鏁�");
+      return;
+    }
+
+    // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾垮拰宸ュ簭
+    const route = routeList.value.find(r => r.id === currentRouteId.value);
+    const process = route?.processList.find(p => p.id === currentProcessId.value);
+
+    if (route && process) {
+      // 妫�鏌ュ弬鏁版槸鍚﹀凡瀛樺湪
+      const exists = process.paramList?.some(
+        p =>
+          p.paramId === selectedParam.value.id ||
+          p.parameterCode === selectedParam.value.paramCode
+      );
+      if (exists) {
+        ElMessage.warning("璇ュ弬鏁板凡瀛樺湪浜庡伐搴忎腑");
+        return;
+      }
+
+      // 娣诲姞宸ュ簭鍒板伐鑹鸿矾绾�
+      const newParam = {
+        id: Date.now(),
+        paramId: selectedParam.value.id,
+        parameterCode: selectedParam.value.paramCode,
+        parameterName: selectedParam.value.paramName,
+        parameterType2: selectedParam.value.valueMode || "1",
+        parameterType: selectedParam.value.paramType,
+        parameterFormat: selectedParam.value.paramFormat,
+        standardValue: selectedParam.value.standardValue,
+        minValue: selectedParam.value.minValue,
+        maxValue: selectedParam.value.maxValue,
+        unit: selectedParam.value.unit,
+        sort: selectedParam.value.sort || 1,
+        isRequired: selectedParam.value.isRequired || 0,
+      };
+
+      if (!process.paramList) {
+        process.paramList = [];
+      }
+
+      process.paramList.push(newParam);
+      ElMessage.success("娣诲姞鍙傛暟鎴愬姛");
+      selectParamDialogVisible.value = false;
+    }
+  };
+
+  const handleEditParamSubmit = () => {
+    editParamFormRef.value.validate(valid => {
+      if (valid) {
+        // 鎵惧埌瀵瑰簲鐨勫伐鑹鸿矾绾裤�佸伐搴忓拰鍙傛暟
+        const route = routeList.value.find(r => r.id === currentRouteId.value);
+        const process = route?.processList.find(
+          p => p.id === currentProcessId.value
+        );
+        const param = process?.paramList.find(p => p.id === editParamForm.id);
+
+        if (param) {
+          // 鏇存柊鍙傛暟淇℃伅
+          param.standardValue = editParamForm.standardValue;
+          param.minValue = editParamForm.minValue;
+          param.maxValue = editParamForm.maxValue;
+          param.sort = editParamForm.sort;
+          param.isRequired = editParamForm.isRequired;
+
+          ElMessage.success("缂栬緫鎴愬姛");
+          editParamDialogVisible.value = false;
+        }
+      }
+    });
+  };
+
+  // 鎷栨嫿鎺掑簭
+  const handleDragStart = (event, index, routeId) => {
+    draggedItem.value = index;
+    draggedRouteId.value = routeId;
+    event.dataTransfer.effectAllowed = "move";
+  };
+
+  const handleDragOver = event => {
+    event.preventDefault();
+    event.dataTransfer.dropEffect = "move";
+  };
+
+  const handleDrop = (event, dropIndex, routeId) => {
+    event.preventDefault();
+    if (draggedItem.value === null || draggedItem.value === dropIndex) return;
+
+    const route = routeList.value.find(r => r.id === routeId);
+    if (route && route.processList) {
+      const draggedProcess = route.processList[draggedItem.value];
+      route.processList.splice(draggedItem.value, 1);
+      route.processList.splice(dropIndex, 0, draggedProcess);
+      ElMessage.success("鎺掑簭鎴愬姛");
+    }
+  };
+
+  const handleDragEnd = () => {
+    draggedItem.value = null;
+    draggedRouteId.value = null;
+  };
+
+  // 鑾峰彇鏁版嵁瀛楀吀
+  const getDictTypes = () => {
+    listType({ pageNum: 1, pageSize: 1000 }).then(res => {
+      dictTypes.value = res.rows || [];
+    });
+  };
+
+  getRouteList();
+  getDictTypes();
 </script>
 
-<style scoped></style>
+<style scoped lang="scss">
+  .app-container {
+    padding: 20px;
+    background-color: #f0f2f5;
+    min-height: calc(100vh - 84px);
+  }
+
+  .route-header {
+    margin-bottom: 20px;
+
+    .add-route-btn {
+      width: 100%;
+      display: inline-flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      min-width: 120px;
+      height: 100px;
+      border: 2px dashed #dcdfe6;
+      border-radius: 12px;
+      background: #fafafa;
+      cursor: pointer;
+      transition: all 0.3s ease;
+      color: #909399;
+      padding: 0 20px;
+
+      .el-icon {
+        font-size: 24px;
+        margin-bottom: 8px;
+      }
+
+      span {
+        font-size: 13px;
+      }
+
+      &:hover {
+        border-color: #409eff;
+        background: #ecf5ff;
+        color: #409eff;
+      }
+    }
+  }
+
+  .route-card-list {
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+  }
+
+  .route-card {
+    background: #fff;
+    border-radius: 8px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    overflow: hidden;
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 20px 40px;
+      border-bottom: 1px solid #ebeef5;
+      background: #f8f9fa;
+
+      .route-info {
+        display: flex;
+        // flex-direction: column;
+        // justify-content: center;
+        // items-align: center;
+        gap: 4px;
+
+        .route-code {
+          font-size: 12px;
+          color: #909399;
+          font-family: "Courier New", monospace;
+          line-height: 30px;
+        }
+
+        .route-name {
+          font-size: 18px;
+          font-weight: 600;
+          color: #303133;
+          display: flex;
+          align-items: center;
+        }
+      }
+
+      .route-actions {
+        display: flex;
+        gap: 8px;
+
+        // .el-button {
+        //   color: #409eff;
+        // }
+      }
+    }
+
+    .card-body {
+      padding: 16px 40px;
+
+      .route-desc {
+        font-size: 14px;
+        color: #606266;
+        margin-bottom: 12px;
+      }
+
+      .route-meta {
+        display: flex;
+        gap: 24px;
+        margin-bottom: 12px;
+        padding: 10px 14px;
+        background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
+        border-radius: 8px;
+        border-left: 3px solid #409eff;
+
+        .meta-item {
+          display: flex;
+          align-items: center;
+          gap: 6px;
+          font-size: 13px;
+          margin-right: 40px;
+
+          .el-icon {
+            font-size: 14px;
+            color: #409eff;
+          }
+
+          .meta-label {
+            color: #909399;
+            font-weight: 500;
+          }
+
+          .meta-value {
+            color: #303133;
+            font-weight: 600;
+          }
+        }
+      }
+
+      .expand-btn-wrapper {
+        display: flex;
+        justify-content: center;
+        margin-top: 8px;
+
+        .expand-btn {
+          padding: 8px 20px;
+          border-radius: 20px;
+          background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
+          border: 1px solid #b3d8ff;
+          transition: all 0.3s ease;
+
+          .btn-text {
+            font-size: 13px;
+            font-weight: 500;
+            color: #409eff;
+            margin-right: 6px;
+          }
+
+          .expand-icon {
+            font-size: 14px;
+            color: #409eff;
+            transition: transform 0.3s ease;
+          }
+
+          &:hover {
+            background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
+            border-color: #409eff;
+            box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
+
+            .btn-text,
+            .expand-icon {
+              color: #fff;
+            }
+          }
+
+          &.expanded {
+            background: linear-gradient(135deg, #f0f9eb 0%, #e1f3d8 100%);
+            border-color: #a5d69a;
+
+            .btn-text,
+            .expand-icon {
+              color: #67c23a;
+            }
+
+            &:hover {
+              background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
+              border-color: #67c23a;
+              box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
+
+              .btn-text,
+              .expand-icon {
+                color: #fff;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .process-route {
+      padding: 0 20px 20px;
+      background: #f5f7fa;
+      border-top: 1px solid #ebeef5;
+
+      .process-flow {
+        display: flex;
+        align-items: flex-start;
+        gap: 8px;
+        padding: 20px 0;
+        overflow-x: auto;
+        overflow-y: hidden;
+
+        .process-flow-item {
+          display: flex;
+          align-items: center;
+          gap: 8px;
+
+          .process-node {
+            background: #fff;
+            border-radius: 12px;
+            padding: 16px;
+            border: 2px solid #ebeef5;
+            cursor: move;
+            transition: all 0.3s ease;
+            // min-width: 180px;
+            // max-width: 220px;
+            width: 300px;
+
+            &.expanded {
+              width: 400px;
+            }
+
+            &:hover {
+              box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+              transform: translateY(-2px);
+              border-color: #409eff;
+            }
+
+            &:active {
+              cursor: grabbing;
+            }
+
+            .process-node-header {
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              margin-bottom: 12px;
+
+              .process-number {
+                width: 28px;
+                height: 28px;
+                border-radius: 50%;
+                background: #409eff;
+                color: #ffffff;
+                font-size: 12px;
+                font-weight: 600;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+              }
+
+              .process-actions {
+                display: flex;
+                gap: 4px;
+              }
+            }
+
+            .process-node-body {
+              text-align: center;
+              margin-bottom: 12px;
+
+              .process-code {
+                font-size: 11px;
+                color: #909399;
+                font-family: "Courier New", monospace;
+                margin-bottom: 4px;
+              }
+
+              .process-name {
+                font-size: 15px;
+                font-weight: 600;
+                color: #303133;
+                margin-bottom: 6px;
+              }
+
+              .process-desc {
+                font-size: 12px;
+                color: #606266;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+              }
+            }
+
+            .process-node-footer {
+              display: flex;
+              justify-content: flex-end;
+              align-items: center;
+              padding-top: 10px;
+              border-top: 1px solid #ebeef5;
+            }
+
+            .process-params-section {
+              margin-top: 12px;
+              padding-top: 12px;
+              border-top: 1px solid #ebeef5;
+
+              .params-header {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                margin-bottom: 8px;
+                font-size: 13px;
+                font-weight: 600;
+                color: #303133;
+              }
+
+              .params-list {
+                display: flex;
+                flex-direction: column;
+                gap: 6px;
+                max-height: 200px;
+                overflow-y: auto;
+
+                .param-item {
+                  display: flex;
+                  justify-content: space-between;
+                  align-items: center;
+                  padding: 6px 8px;
+                  background: #fafafa;
+                  border-radius: 4px;
+                  border-left: 2px solid #409eff;
+                  font-size: 12px;
+
+                  .param-info {
+                    display: flex;
+                    flex-direction: row;
+                    align-items: center;
+                    gap: 6px;
+                    flex: 1;
+                    min-width: 0;
+
+                    .param-code {
+                      font-size: 11px;
+                      color: #e6a23c;
+                      font-family: "Courier New", monospace;
+                      margin-right: 20px;
+                    }
+
+                    .param-name {
+                      font-size: 12px;
+                      color: #303133;
+                      font-weight: 500;
+                      margin-right: 20px;
+                    }
+
+                    .param-value {
+                      font-size: 11px;
+                      color: #606266;
+                    }
+                  }
+
+                  .param-actions {
+                    display: flex;
+                    gap: 4px;
+                    flex-shrink: 0;
+                  }
+                }
+              }
+            }
+          }
+
+          .flow-arrow {
+            display: flex;
+            align-items: center;
+            color: #c0c4cc;
+            font-size: 24px;
+            padding: 0 4px;
+
+            .el-icon {
+              font-size: 20px;
+            }
+          }
+        }
+
+        .add-process-node {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+          justify-content: center;
+          min-width: 100px;
+          height: 175px;
+          border: 2px dashed #dcdfe6;
+          border-radius: 12px;
+          background: #fafafa;
+          cursor: pointer;
+          transition: all 0.3s ease;
+          color: #909399;
+          // margin-left: 10px;
+
+          .el-icon {
+            font-size: 24px;
+            margin-bottom: 8px;
+          }
+
+          span {
+            font-size: 13px;
+          }
+
+          &:hover {
+            border-color: #409eff;
+            background: #ecf5ff;
+            color: #409eff;
+          }
+        }
+      }
+    }
+  }
+
+  // 鎷栨嫿鏃剁殑鏍峰紡
+  .process-flow-item.dragging {
+    opacity: 0.5;
+    transform: scale(0.98);
+  }
+
+  // 閫夋嫨宸ュ簭瀵硅瘽妗嗘牱寮�
+  .process-select-container {
+    display: flex;
+    gap: 20px;
+    height: 450px;
+
+    .process-list-area {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 12px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .search-box {
+        margin-bottom: 12px;
+
+        .el-input {
+          width: 100%;
+        }
+      }
+    }
+
+    .process-detail-area {
+      width: 380px;
+      display: flex;
+      flex-direction: column;
+      background: #f5f7fa;
+      border-radius: 8px;
+      padding: 16px;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 16px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .process-detail-form {
+        .el-form-item {
+          margin-bottom: 12px;
+
+          .el-form-item__label {
+            color: #606266;
+            font-weight: 500;
+          }
+        }
+
+        .detail-text {
+          color: #303133;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+
+  // 閫夋嫨鍙傛暟瀵硅瘽妗嗘牱寮�
+  .param-select-container {
+    display: flex;
+    gap: 20px;
+    height: 450px;
+
+    .param-list-area {
+      // flex: 1;
+      width: 380px;
+      display: flex;
+      flex-direction: column;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 12px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .search-box {
+        margin-bottom: 12px;
+
+        .el-input {
+          width: 100%;
+        }
+      }
+    }
+
+    .param-detail-area {
+      // width: 380px;
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      background: #f5f7fa;
+      border-radius: 8px;
+      padding: 16px;
+
+      .area-title {
+        font-size: 14px;
+        font-weight: 600;
+        color: #303133;
+        margin-bottom: 16px;
+        padding-bottom: 8px;
+        border-bottom: 1px solid #ebeef5;
+      }
+
+      .param-detail-form {
+        .el-form-item {
+          margin-bottom: 12px;
+
+          .el-form-item__label {
+            color: #606266;
+            font-weight: 500;
+          }
+        }
+
+        .detail-text {
+          color: #303133;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+</style>

--
Gitblit v1.9.3