From 9dcde5b27b70a4b0c0885347af5405eb2d1ef089 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期五, 12 十二月 2025 17:00:54 +0800
Subject: [PATCH] 修改前端状态显示变更,保持前端实时更新

---
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue |  658 ++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 511 insertions(+), 147 deletions(-)

diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index 8cf1feb..5f2b44e 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -8,7 +8,7 @@
         <p v-if="group && loadDeviceName" class="sub-info">褰撳墠璁惧锛歿{ loadDeviceName }}</p>
       </div>
       <div class="action-buttons">
-        <el-button
+        <!-- <el-button
           type="danger"
           plain
           :disabled="!group || !loadDeviceId || loadDeviceLoading"
@@ -17,113 +17,93 @@
         >
           <el-icon><Delete /></el-icon>
           娓呯┖PLC
+        </el-button> -->
+        <el-button type="success" :disabled="!group" :loading="importLoading" @click="handleImportExcel">
+          <el-icon>
+            <Upload />
+          </el-icon>
+          瀵煎叆Excel鏁版嵁
         </el-button>
+        <input ref="fileInputRef" type="file" accept=".xlsx,.xls" style="display: none" @change="handleFileChange" />
         <el-button type="primary" :disabled="!group" :loading="loading" @click="handleSubmit">
-          <el-icon><Promotion /></el-icon>
+          <el-icon>
+            <Promotion />
+          </el-icon>
           鍚姩娴嬭瘯
         </el-button>
       </div>
     </div>
 
     <el-form :model="form" label-width="120px" :rules="rules" ref="formRef">
-      <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds" required>
+      <div style="width: 350px; margin-bottom: 12px; margin-left: 120px;">
+          <el-select 
+            v-model="selectedEngineeringId" 
+            placeholder="閫夋嫨宸ョ▼鍙凤紙閫夋嫨鍚庤嚜鍔ㄥ~鍏呯幓鐠僆D锛�"
+            clearable
+            filterable
+            :disabled="!group"
+            :loading="engineeringListLoading"
+            @change="handleEngineeringChange"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in engineeringList"
+              :key="item.engineeringId"
+              :label="item.engineeringId"
+              :value="item.engineeringId"
+            >
+              <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
+                <div style="flex: 1;">
+                  <span>{{ item.engineeringId }}</span>
+                  <span style="margin-left: 8px; color: #8492a6; font-size: 12px">
+                    {{ item.date ? new Date(item.date).toLocaleDateString() : '' }}
+                  </span>
+                </div>
+                <el-button
+                  type="danger"
+                  link
+                  size="small"
+                  :loading="deletingEngineeringId === item.engineeringId"
+                  @click.stop="handleDeleteEngineering(item.engineeringId)"
+                  style="margin-left: 8px; padding: 0 4px;"
+                >
+                  <el-icon><Delete /></el-icon>
+                </el-button>
+              </div>
+            </el-option>
+          </el-select>
+        </div>
+        
+      <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds">
         <el-input
           v-model="glassIdsInput"
           type="textarea"
           :rows="4"
-          placeholder="璇疯緭鍏ョ幓鐠冩潯鐮侊紝鏀寔澶氳鎴栭�楀彿鍒嗛殧锛屾瘡琛屼竴涓垨閫楀彿鍒嗛殧"
+          placeholder="鍙�夛細杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯锛堟垨閫氳繃涓婃柟閫夋嫨宸ョ▼鍙疯嚜鍔ㄥ~鍏咃級"
           show-word-limit
           :maxlength="5000"
         />
         <div class="form-tip">
-          宸茶緭鍏� {{ glassIds.length }} 涓幓鐠僆D
+          <span v-if="glassIds.length > 0">宸茶緭鍏� {{ glassIds.length }} 涓幓鐠僆D锛堟祴璇曟ā寮忥細浣跨敤杈撳叆鐨処D锛�</span>
+          <span v-else>鏈緭鍏ョ幓鐠僆D锛堟甯告ā寮忥細灏嗕粠鏁版嵁搴撹鍙栨渶杩戞壂鐮佺殑鐜荤拑ID锛�</span>
         </div>
       </el-form-item>
-      
-      <el-divider content-position="left">璁惧鐗瑰畾閰嶇疆</el-divider>
-      
-      <el-form-item label="浣嶇疆缂栫爜">
-        <el-input
-          v-model="form.positionCode"
-          placeholder="渚嬪锛歅OS1"
-          clearable
-        />
-        <div class="form-tip">涓婂ぇ杞﹁澶囩殑浣嶇疆缂栫爜</div>
-      </el-form-item>
-      
-      <el-form-item label="浣嶇疆鍊�">
-        <el-input-number
-          v-model="form.positionValue"
-          :min="0"
-          :max="9999"
-          placeholder="浣嶇疆鏁板��"
-        />
-        <div class="form-tip">涓婂ぇ杞﹁澶囩殑浣嶇疆鏁板��</div>
-      </el-form-item>
-      
-      <el-form-item label="瀛樺偍浣嶇疆">
-        <el-input-number
-          v-model="form.storagePosition"
-          :min="1"
-          :max="200"
-          placeholder="瀛樺偍浣嶇疆缂栧彿"
-        />
-        <div class="form-tip">鐜荤拑瀛樺偍璁惧鐨勫瓨鍌ㄤ綅缃�</div>
-      </el-form-item>
-      
-      <el-form-item label="澶勭悊绫诲瀷">
-        <el-select v-model="form.processType" placeholder="閫夋嫨澶勭悊绫诲瀷" clearable>
-          <el-option label="鏍囧噯澶勭悊" :value="1" />
-          <el-option label="蹇�熷鐞�" :value="2" />
-          <el-option label="鎱㈤�熷鐞�" :value="3" />
-        </el-select>
-        <div class="form-tip">澶х悊鐗囪澶囩殑澶勭悊绫诲瀷</div>
-      </el-form-item>
-      
-      <el-divider content-position="left">鎵ц閰嶇疆</el-divider>
-      
-      <el-form-item label="鎵ц闂撮殧 (ms)">
-        <el-input-number
-          v-model="form.executionInterval"
-          :min="100"
-          :max="10000"
-          :step="100"
-          placeholder="璁惧鎿嶄綔闂撮殧鏃堕棿"
-        />
-        <div class="form-tip">姣忎釜璁惧鎿嶄綔涔嬮棿鐨勯棿闅旀椂闂达紙姣锛�</div>
-      </el-form-item>
-      
-      <el-form-item label="瓒呮椂鏃堕棿 (鍒嗛挓)">
-        <el-input-number
-          v-model="form.timeoutMinutes"
-          :min="1"
-          :max="60"
-          :step="1"
-          placeholder="浠诲姟瓒呮椂鏃堕棿"
-        />
-        <div class="form-tip">浠诲姟鎵ц鐨勬渶澶ц秴鏃舵椂闂�</div>
-      </el-form-item>
-      
-      <el-form-item label="閲嶈瘯娆℃暟">
-        <el-input-number
-          v-model="form.retryCount"
-          :min="0"
-          :max="10"
-          :step="1"
-          placeholder="澶辫触閲嶈瘯娆℃暟"
-        />
-        <div class="form-tip">璁惧鎿嶄綔澶辫触鏃剁殑鏈�澶ч噸璇曟鏁�</div>
-      </el-form-item>
     </el-form>
+
+    <!-- 璁惧缁勬嫇鎵戝浘 -->
+    <GroupTopology v-if="group" :group="group" class="topology-section" />
   </div>
 </template>
 
 <script setup>
-import { computed, reactive, ref, watch } from 'vue'
-import { ElMessage } from 'element-plus'
-import { Delete, Promotion } from '@element-plus/icons-vue'
+import { computed, reactive, ref, watch, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Delete, Promotion, Upload } from '@element-plus/icons-vue'
+import * as XLSX from 'xlsx'
 import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
 import { deviceGroupApi, deviceInteractionApi } from '@/api/device/deviceManagement'
+import { engineeringApi } from '@/api/engineering'
+import GroupTopology from '../DeviceGroup/GroupTopology.vue'
 
 const props = defineProps({
   group: {
@@ -133,16 +113,8 @@
 })
 
 const emit = defineEmits(['task-started'])
-
-const form = reactive({
-  positionCode: '',
-  positionValue: null,
-  storagePosition: null,
-  processType: null,
-  executionInterval: 1000,
-  timeoutMinutes: 30,
-  retryCount: 3
-})
+//閰嶇疆榛樿鍊�
+const form = reactive({})
 
 const formRef = ref(null)
 
@@ -150,8 +122,10 @@
   glassIds: [
     {
       validator: (rule, value, callback) => {
+        // 濡傛灉杈撳叆浜嗙幓鐠僆D锛屽垯杩涜楠岃瘉锛涘鏋滄病鏈夎緭鍏ワ紝鍒欏厑璁革紙灏嗕粠鏁版嵁搴撹鍙栵級
         if (glassIds.value.length === 0) {
-          callback(new Error('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D'))
+          // 鍏佽涓虹┖锛屽皢浠庢暟鎹簱璇诲彇鏈�杩戞壂鐮佺殑鐜荤拑ID
+          callback()
         } else if (glassIds.value.length > 100) {
           callback(new Error('鐜荤拑ID鏁伴噺涓嶈兘瓒呰繃100涓�'))
         } else {
@@ -174,18 +148,34 @@
 
 const glassIdsInput = ref('')
 const loading = ref(false)
+const importLoading = ref(false)
 const clearLoading = ref(false)
 const loadDeviceId = ref(null)
 const loadDeviceName = ref('')
 const loadDeviceLoading = ref(false)
+const fileInputRef = ref(null)
+
+// 宸ョ▼鍙风浉鍏�
+const selectedEngineeringId = ref('')
+const engineeringList = ref([])
+const engineeringListLoading = ref(false)
+const glassIdsLoading = ref(false)
+const deletingEngineeringId = ref('')
 
 watch(
   () => props.group,
   () => {
     glassIdsInput.value = ''
+    selectedEngineeringId.value = ''
     fetchLoadDevice()
+    fetchEngineeringList()
   }
 )
+
+// 缁勪欢鎸傝浇鏃跺姞杞藉伐绋嬪彿鍒楄〃
+onMounted(() => {
+  fetchEngineeringList()
+})
 
 const glassIds = computed(() => {
   if (!glassIdsInput.value) return []
@@ -194,6 +184,110 @@
     .map((item) => item.trim())
     .filter((item) => item.length > 0)
 })
+
+// 鑾峰彇宸ョ▼鍙峰垪琛�
+const fetchEngineeringList = async () => {
+  try {
+    engineeringListLoading.value = true
+    const response = await engineeringApi.getEngineeringList()
+
+    if (Array.isArray(response)) {
+      engineeringList.value = response
+    } else if (Array.isArray(response?.data)) {
+      engineeringList.value = response.data
+    } else {
+      engineeringList.value = []
+    }
+    // 鎸夋棩鏈熷�掑簭鎺掑垪
+    engineeringList.value.sort((a, b) => {
+      const dateA = a.date ? new Date(a.date).getTime() : 0
+      const dateB = b.date ? new Date(b.date).getTime() : 0
+      return dateB - dateA
+    })
+  } catch (error) {
+    console.error('鑾峰彇宸ョ▼鍙峰垪琛ㄥけ璐�:', error)
+    ElMessage.error(error?.message || '鑾峰彇宸ョ▼鍙峰垪琛ㄥけ璐�')
+    engineeringList.value = []
+  } finally {
+    engineeringListLoading.value = false
+  }
+}
+
+// 澶勭悊宸ョ▼鍙烽�夋嫨鍙樺寲
+const handleEngineeringChange = async (engineeringId) => {
+  if (!engineeringId) {
+    // 娓呯┖閫夋嫨鏃讹紝涓嶆竻绌哄凡杈撳叆鐨勭幓鐠僆D锛岃鐢ㄦ埛淇濈暀
+    return
+  }
+  
+  try {
+    glassIdsLoading.value = true
+    const response = await engineeringApi.getGlassIdsByEngineeringId(engineeringId)
+
+    const glassIds = response?.glassIds || response?.data?.glassIds || []
+    
+    if (glassIds.length > 0) {
+      glassIdsInput.value = glassIds.join('\n')
+      ElMessage.success(`宸插姞杞藉伐绋嬪彿 ${engineeringId} 鐨� ${glassIds.length} 涓幓鐠僆D`)
+    } else {
+      ElMessage.warning(`宸ョ▼鍙� ${engineeringId} 涓嬫病鏈夋壘鍒扮幓鐠僆D`)
+    }
+  } catch (error) {
+    console.error('鑾峰彇鐜荤拑ID鍒楄〃澶辫触:', error)
+    ElMessage.error(error?.message || '鑾峰彇鐜荤拑ID鍒楄〃澶辫触')
+  } finally {
+    glassIdsLoading.value = false
+  }
+}
+
+// 澶勭悊鍒犻櫎宸ョ▼鍙�
+const handleDeleteEngineering = async (engineeringId) => {
+  if (!engineeringId) {
+    return
+  }
+
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ゅ伐绋嬪彿 "${engineeringId}" 鍙婂叾鍏宠仈鐨勬墍鏈夌幓鐠冧俊鎭悧锛熸鎿嶄綔涓嶅彲鎭㈠锛乣,
+      '纭鍒犻櫎',
+      {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning',
+        dangerouslyUseHTMLString: false
+      }
+    )
+
+    deletingEngineeringId.value = engineeringId
+    const response = await engineeringApi.deleteEngineering(engineeringId)
+    
+    const result = response?.data || response
+    if (result?.success !== false) {
+      const deletedCount = result?.deletedGlassCount || 0
+      ElMessage.success(`宸插垹闄ゅ伐绋嬪彿 ${engineeringId}锛屽叡鍒犻櫎 ${deletedCount} 鏉$幓鐠冧俊鎭痐)
+      
+      // 濡傛灉鍒犻櫎鐨勬槸褰撳墠閫変腑鐨勫伐绋嬪彿锛屾竻绌洪�夋嫨
+      if (selectedEngineeringId.value === engineeringId) {
+        selectedEngineeringId.value = ''
+        glassIdsInput.value = ''
+      }
+      
+      // 鍒锋柊宸ョ▼鍙峰垪琛�
+      await fetchEngineeringList()
+    } else {
+      throw new Error(result?.message || '鍒犻櫎澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('鍒犻櫎宸ョ▼鍙峰け璐�:', error)
+      ElMessage.error(error?.message || '鍒犻櫎宸ョ▼鍙峰け璐�')
+    }
+  } finally {
+    deletingEngineeringId.value = ''
+  }
+}
+
+const normalizeType = (type) => (type || '').trim().toUpperCase()
 
 const fetchLoadDevice = async () => {
   loadDeviceId.value = null
@@ -212,13 +306,19 @@
     const deviceList = Array.isArray(rawList)
       ? rawList
       : Array.isArray(rawList?.records)
-      ? rawList.records
-      : Array.isArray(rawList?.data)
-      ? rawList.data
-      : []
-    const targetDevice =
-      deviceList.find((item) => (item.deviceType || '').toUpperCase() === 'LOAD_VEHICLE') ||
-      deviceList[0]
+        ? rawList.records
+        : Array.isArray(rawList?.data)
+          ? rawList.data
+          : []
+    const scannerDevice = deviceList.find((item) => {
+      const type = normalizeType(item.deviceType)
+      return type.includes('SCANNER') || type.includes('鎵爜')
+    })
+    const loadVehicleDevice = deviceList.find((item) => {
+      const type = normalizeType(item.deviceType)
+      return type.includes('LOAD_VEHICLE') || type.includes('澶ц溅')
+    })
+    const targetDevice = scannerDevice || loadVehicleDevice || deviceList[0]
     if (targetDevice && targetDevice.id) {
       loadDeviceId.value = targetDevice.id
       loadDeviceName.value = targetDevice.deviceName || targetDevice.deviceCode || `ID: ${targetDevice.id}`
@@ -236,7 +336,7 @@
     ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
     return
   }
-  
+
   // 琛ㄥ崟楠岃瘉
   if (!formRef.value) return
   try {
@@ -245,64 +345,35 @@
     ElMessage.warning('璇锋鏌ヨ〃鍗曡緭鍏�')
     return
   }
-  
-  if (glassIds.value.length === 0) {
-    ElMessage.warning('璇疯嚦灏戣緭鍏ヤ竴涓幓鐠僆D')
-    return
-  }
-  
+
   try {
     loading.value = true
-    
+
     // 鏋勫缓浠诲姟鍙傛暟
+    // 濡傛灉杈撳叆浜嗙幓鐠僆D锛屼娇鐢ㄨ緭鍏ョ殑锛涘鏋滄病鏈夎緭鍏ワ紝glassIds涓虹┖鏁扮粍锛屽悗绔細浠庢暟鎹簱璇诲彇
     const parameters = {
-      glassIds: glassIds.value,
-      executionInterval: form.executionInterval || 1000
+      glassIds: glassIds.value.length > 0 ? glassIds.value : []
     }
-    
-    // 娣诲姞鍙�夊弬鏁�
-    if (form.positionCode) {
-      parameters.positionCode = form.positionCode
-    }
-    if (form.positionValue !== null) {
-      parameters.positionValue = form.positionValue
-    }
-    if (form.storagePosition !== null) {
-      parameters.storagePosition = form.storagePosition
-    }
-    if (form.processType !== null) {
-      parameters.processType = form.processType
-    }
-    if (form.timeoutMinutes) {
-      parameters.timeoutMinutes = form.timeoutMinutes
-    }
-    if (form.retryCount !== null) {
-      parameters.retryCount = form.retryCount
-    }
-    
+
     // 寮傛鍚姩浠诲姟锛岀珛鍗宠繑鍥烇紝涓嶉樆濉�
     const response = await multiDeviceTaskApi.startTask({
       groupId: props.group.id || props.group.groupId,
       parameters
     })
-    
+
     const task = response?.data
     if (task && task.taskId) {
       ElMessage.success(`浠诲姟宸插惎鍔紙寮傛鎵ц锛�: ${task.taskId}`)
       emit('task-started', task)
-      
+
       // 绔嬪嵆鍒锋柊鐩戞帶鍒楄〃锛屾樉绀烘柊鍚姩鐨勪换鍔�
       setTimeout(() => {
         emit('task-started')
       }, 500)
-      
-      // 閲嶇疆琛ㄥ崟锛堜繚鐣欓儴鍒嗛厤缃級锛屾柟渚跨户缁惎鍔ㄥ叾浠栬澶囩粍
+
+      // 閲嶇疆琛ㄥ崟锛堜繚鐣欐墽琛岄厤缃級锛屾柟渚跨户缁惎鍔ㄥ叾浠栬澶囩粍
       glassIdsInput.value = ''
-      form.positionCode = ''
-      form.positionValue = null
-      form.storagePosition = null
-      form.processType = null
-      
+
       // 鎻愮ず鐢ㄦ埛鍙互缁х画鍚姩鍏朵粬璁惧缁�
       ElMessage.info('鍙互缁х画閫夋嫨鍏朵粬璁惧缁勫惎鍔ㄦ祴璇曪紝澶氫釜璁惧缁勫皢骞惰鎵ц')
     } else {
@@ -321,17 +392,15 @@
     return
   }
   if (!loadDeviceId.value) {
-    ElMessage.warning('鏈壘鍒颁笂澶ц溅璁惧锛屾棤娉曟竻绌篜LC')
+    ElMessage.warning('鏈壘鍒板搴旇澶囷紝鏃犳硶娓呯┖PLC')
     return
   }
   try {
     clearLoading.value = true
     const response = await deviceInteractionApi.executeOperation({
       deviceId: loadDeviceId.value,
-      operation: 'clearGlass',
-      params: {
-        positionCode: form.positionCode || null
-      }
+      operation: 'clearPlc',
+      params: {}
     })
     if (response?.code !== 200) {
       throw new Error(response?.message || 'PLC娓呯┖澶辫触')
@@ -348,6 +417,298 @@
     ElMessage.error(error?.message || 'PLC娓呯┖澶辫触')
   } finally {
     clearLoading.value = false
+  }
+}
+
+// 澶勭悊瀵煎叆Excel鎸夐挳鐐瑰嚮
+const handleImportExcel = () => {
+  if (!props.group) {
+    ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
+    return
+  }
+  if (fileInputRef.value) {
+    fileInputRef.value.click()
+  }
+}
+
+// 澶勭悊鏂囦欢閫夋嫨
+const handleFileChange = async (event) => {
+  const file = event.target.files?.[0]
+  if (!file) {
+    return
+  }
+
+  // 楠岃瘉鏂囦欢绫诲瀷
+  const fileName = file.name.toLowerCase()
+  if (!fileName.endsWith('.xlsx') && !fileName.endsWith('.xls')) {
+    ElMessage.error('璇烽�夋嫨 Excel 鏂囦欢锛�.xlsx 鎴� .xls锛�')
+    event.target.value = ''
+    return
+  }
+
+  try {
+    importLoading.value = true
+
+    // 璇诲彇鏂囦欢
+    const fileReader = new FileReader()
+    fileReader.onload = (e) => {
+      try {
+        const data = new Uint8Array(e.target.result)
+        const workbook = XLSX.read(data, { type: 'array' })
+
+        // 璇诲彇绗竴涓伐浣滆〃
+        const firstSheetName = workbook.SheetNames[0]
+        const worksheet = workbook.Sheets[firstSheetName]
+
+        // 杞崲涓� JSON 鏁扮粍
+        const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
+
+        if (!jsonData || jsonData.length === 0) {
+          ElMessage.error('Excel 鏂囦欢涓虹┖')
+          event.target.value = ''
+          return
+        }
+
+        // 瑙f瀽鏁版嵁锛堝亣璁剧涓�琛屾槸琛ㄥご锛�
+        const parsedData = parseExcelData(jsonData)
+
+        if (parsedData.length === 0) {
+          ElMessage.error('鏈兘瑙f瀽鍒版湁鏁堟暟鎹紝璇锋鏌� Excel 鏍煎紡')
+          event.target.value = ''
+          return
+        }
+
+        // 鍙戦�佹暟鎹�
+        submitGlassData(parsedData)
+
+      } catch (error) {
+        console.error('瑙f瀽 Excel 澶辫触:', error)
+        ElMessage.error('瑙f瀽 Excel 鏂囦欢澶辫触: ' + (error.message || '鏈煡閿欒'))
+      } finally {
+        event.target.value = ''
+        importLoading.value = false
+      }
+    }
+
+    fileReader.onerror = () => {
+      ElMessage.error('璇诲彇鏂囦欢澶辫触')
+      event.target.value = ''
+      importLoading.value = false
+    }
+
+    fileReader.readAsArrayBuffer(file)
+
+  } catch (error) {
+    console.error('瀵煎叆 Excel 澶辫触:', error)
+    ElMessage.error('瀵煎叆 Excel 澶辫触: ' + (error.message || '鏈煡閿欒'))
+    importLoading.value = false
+    event.target.value = ''
+  }
+}
+
+// 瑙f瀽 Excel 鏁版嵁
+const parseExcelData = (jsonData) => {
+  if (!jsonData || jsonData.length < 2) {
+    return []
+  }
+
+  // 灏濊瘯璇嗗埆琛ㄥご锛堟敮鎸佷腑鑻辨枃锛�
+  const headerRow = jsonData[0]
+  const headerMap = {}
+
+  headerRow.forEach((header, index) => {
+    if (!header) return
+    const headerStr = String(header).trim().toLowerCase()
+
+    // 鐜荤拑ID
+    if (headerStr.includes('鐜荤拑id') || headerStr.includes('glassid') ||
+      (headerStr.includes('鐜荤拑') && headerStr.includes('id')) ||
+      headerStr === 'id' || headerStr === 'glass_id') {
+      headerMap.glassId = index
+    }
+    // 瀹藉害
+    else if (headerStr.includes('瀹�') || headerStr.includes('width') ||
+      headerStr === 'w' || headerStr === '瀹藉害') {
+      headerMap.width = index
+    }
+    // 楂樺害
+    else if (headerStr.includes('楂�') || headerStr.includes('height') ||
+      headerStr === 'h' || headerStr === '楂樺害') {
+      headerMap.height = index
+    }
+    // 鍘氬害
+    else if (headerStr.includes('鍘�') || headerStr.includes('thickness') ||
+      headerStr === 't' || headerStr === '鍘氬害') {
+      headerMap.thickness = index
+    }
+    // 鏁伴噺
+    else if (headerStr.includes('鏁伴噺') || headerStr.includes('quantity') ||
+      headerStr.includes('qty') || headerStr === '鏁伴噺') {
+      headerMap.quantity = index
+    }
+    // 鑶滅郴
+    else if (headerStr.includes('鑶滅郴') || headerStr.includes('films') ||
+      headerStr.includes('film') || headerStr === '鑶滅郴id') {
+      headerMap.filmsId = index
+    }
+    // 娴佺▼鍗D
+    else if (headerStr.includes('娴佺▼鍗�') || headerStr.includes('flowcard') ||
+      headerStr.includes('flow') || headerStr === '娴佺▼鍗d') {
+      headerMap.flowCardId = index
+    }
+    // 浜у搧鍚嶇О
+    else if (headerStr.includes('浜у搧') || headerStr.includes('product') ||
+      headerStr === '浜у搧鍚嶇О') {
+      headerMap.productName = index
+    }
+    // 瀹㈡埛鍚嶇О
+    else if (headerStr.includes('瀹㈡埛') || headerStr.includes('customer') ||
+      headerStr === '瀹㈡埛鍚嶇О') {
+      headerMap.customerName = index
+    }
+  })
+
+  // 濡傛灉娌℃湁鎵惧埌琛ㄥご锛屽皾璇曚娇鐢ㄧ涓�琛屼綔涓鸿〃澶达紙绱㈠紩鏂瑰紡锛�
+  if (Object.keys(headerMap).length === 0 && jsonData.length > 1) {
+    // 榛樿鏍煎紡锛氱幓鐠僆D, 瀹�, 楂�, 鍘�, 鏁伴噺锛堟寜鍒楅『搴忥級
+    headerMap.glassId = 0
+    headerMap.width = 1
+    headerMap.height = 2
+    headerMap.thickness = 3
+    headerMap.quantity = 4
+  }
+
+  // 瑙f瀽鏁版嵁琛�
+  const result = []
+  for (let i = 1; i < jsonData.length; i++) {
+    const row = jsonData[i]
+    if (!row || row.length === 0) continue
+
+    const glassId = row[headerMap.glassId] ? String(row[headerMap.glassId]).trim() : ''
+    const width = row[headerMap.width] ? String(row[headerMap.width]).trim() : ''
+    const height = row[headerMap.height] ? String(row[headerMap.height]).trim() : ''
+    const thickness = row[headerMap.thickness] ? String(row[headerMap.thickness]).trim() : ''
+    const quantity = row[headerMap.quantity] ? String(row[headerMap.quantity]).trim() : ''
+    const filmsId = row[headerMap.filmsId] ? String(row[headerMap.filmsId]).trim() : ''
+    const flowCardId = row[headerMap.flowCardId] ? String(row[headerMap.flowCardId]).trim() : ''
+    const productName = row[headerMap.productName] ? String(row[headerMap.productName]).trim() : ''
+    const customerName = row[headerMap.customerName] ? String(row[headerMap.customerName]).trim() : ''
+
+    // 璺宠繃绌鸿
+    if (!glassId && !width && !height && !thickness && !quantity) {
+      continue
+    }
+
+    // 楠岃瘉蹇呭~瀛楁
+    if (!glassId) {
+      ElMessage.warning(`绗� ${i + 1} 琛岋細鐜荤拑ID涓虹┖锛屽凡璺宠繃`)
+      continue
+    }
+
+    // 杞崲鏁板�肩被鍨嬶紝纭繚鏍煎紡姝g‘
+    const parseNumber = (value) => {
+      if (!value) return '0'
+      const num = parseFloat(value)
+      return isNaN(num) ? '0' : String(num)
+    }
+
+    // 澶勭悊鏁伴噺锛氬鏋滄暟閲忓ぇ浜�1锛岄渶瑕佺敓鎴愬鏉¤褰�
+    const qty = parseInt(quantity) || 1
+    for (let j = 0; j < qty; j++) {
+      // 濡傛灉鏁伴噺澶т簬1锛屼负姣忔潯璁板綍鐢熸垚鍞竴鐨勭幓鐠僆D锛堣拷鍔犲簭鍙凤級
+      const finalGlassId = qty > 1 ? `${glassId}${j + 1}` : glassId
+
+      result.push({
+        glassId: finalGlassId,
+        width: parseNumber(width),
+        height: parseNumber(height),
+        thickness: parseNumber(thickness),
+        quantity: '1', // 姣忔潯璁板綍鏁伴噺涓�1
+        filmsId: filmsId,
+        flowCardId: flowCardId || finalGlassId,
+        productName: productName,
+        customerName: customerName
+      })
+    }
+  }
+
+  return result
+}
+
+// 鎻愪氦鐜荤拑鏁版嵁鍒板悗绔紝鐢卞悗绔畬鎴� JSON 杞崲骞惰皟鐢� MES 鎺ュ彛
+const submitGlassData = async (glassDataList) => {
+  if (!props.group) {
+    ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
+    return
+  }
+
+  try {
+    importLoading.value = true
+
+    // 浼犻�掑師濮嬭В鏋愭暟鎹粰鍚庣锛屽悗绔畬鎴愯浆鎹笌 MES 璋冪敤
+    const requestData = { excelRows: glassDataList }
+
+    // 鎵撳嵃鍘熷鏁版嵁渚涜皟璇�
+    console.log('涓婁紶鍒板悗绔殑鍘熷 Excel 鏁版嵁:', JSON.stringify(requestData, null, 2))
+
+    const response = await engineeringApi.importEngineer(requestData)
+
+    // 妫�鏌� MES 鎺ュ彛鏄惁璋冪敤鎴愬姛
+    // MES 鎺ュ彛鎴愬姛鏃惰繑鍥炴牸寮忥細{ code: 200/0, data: true/false, message: "..." }
+    if (response?.code === 200 || response?.code === 0) {
+      // MES 鎺ュ彛璋冪敤鎴愬姛
+      const engineerId = response?.data?.engineerId || response?.engineerId || ''
+      const successMsg = engineerId 
+        ? `鎴愬姛瀵煎叆 ${glassDataList.length} 鏉$幓鐠冩暟鎹紝宸ョ▼鍙凤細${engineerId}`
+        : `鎴愬姛瀵煎叆 ${glassDataList.length} 鏉$幓鐠冩暟鎹甡
+      ElMessage.success(successMsg)
+
+      // 鎴愬姛鍚庡埛鏂板伐绋嬪彿涓嬫媺鍒楄〃锛屽苟閫変腑鏈�鏂板伐绋嬪彿
+      try {
+        await fetchEngineeringList()
+        if (engineerId) {
+          selectedEngineeringId.value = engineerId
+          // 鍒锋柊骞跺洖濉悗绔繚瀛樼殑 glassId锛堝甫宸ョ▼鍙峰墠缂�锛夛紝閬垮厤浣跨敤鍓嶇鍘熷鍊�
+          await handleEngineeringChange(engineerId)
+        }
+      } catch (refreshErr) {
+        console.error('鍒锋柊宸ョ▼鍙峰垪琛ㄥけ璐�:', refreshErr)
+      }
+    } else {
+      // MES 鎺ュ彛杩斿洖澶辫触
+      throw new Error(response?.message || 'MES 鎺ュ彛杩斿洖澶辫触')
+    }
+  } catch (error) {
+    console.error('鎻愪氦鐜荤拑鏁版嵁澶辫触:', error)
+
+    // 鍒ゆ柇閿欒绫诲瀷锛岀粰鍑烘洿鍙嬪ソ鐨勬彁绀�
+    let errorMsg = '鏈煡閿欒'
+    
+    // 妫�鏌ユ槸鍚︽槸鍚庣杩斿洖鐨勯敊璇搷搴旓紙鍚庣杞彂 MES 澶辫触锛�
+    if (error?.response?.status === 500 && error?.response?.data) {
+      // 鍚庣杩斿洖鐨勭粺涓�閿欒鏍煎紡
+      errorMsg = error.response.data.message || error.response.data || '杞彂 MES 鎺ュ彛澶辫触'
+    } else if (error?.response?.data?.message) {
+      // MES 鎺ュ彛杩斿洖鐨勯敊璇�
+      errorMsg = error.response.data.message
+    } else if (error?.message) {
+      errorMsg = error.message
+    }
+    
+    ElMessage.error('鎻愪氦鏁版嵁澶辫触: ' + errorMsg)
+
+    // 鍗充娇澶辫触锛屼篃灏濊瘯濉厖鐜荤拑ID鍒拌緭鍏ユ
+    try {
+      const glassIds = glassDataList.map(item => item.glassId).filter(id => id)
+      if (glassIds.length > 0) {
+        glassIdsInput.value = glassIds.join('\n')
+        ElMessage.info('宸插皢鐜荤拑ID濉厖鍒拌緭鍏ユ锛屾偍鍙互鎵嬪姩鎻愪氦')
+      }
+    } catch (e) {
+      console.error('濉厖鏁版嵁澶辫触:', e)
+    }
+  } finally {
+    importLoading.value = false
   }
 }
 </script>
@@ -399,5 +760,8 @@
   margin-top: 4px;
   line-height: 1.4;
 }
-</style>
 
+.topology-section {
+  margin-top: 24px;
+}
+</style>

--
Gitblit v1.8.0