From eabb757720375b74900027e23e50303b7e02ca36 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期二, 09 十二月 2025 17:04:23 +0800
Subject: [PATCH] 添加nacos配置中心,可动态更新mes导入工程接口;修改Excel表数据转json格式
---
mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue | 410 ++++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 325 insertions(+), 85 deletions(-)
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index 0d7d3a7..3021519 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,9 +17,18 @@
>
<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>
@@ -31,7 +40,7 @@
v-model="glassIdsInput"
type="textarea"
:rows="4"
- placeholder="鍙�夛細濡傛灉杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯锛堜唬鏇垮崸杞珛鎵爜锛夛紱濡傛灉涓嶈緭鍏ワ紝灏嗕粠鏁版嵁搴撹鍙栨渶杩戞壂鐮佺殑鐜荤拑ID杩涜娴嬭瘯"
+ placeholder="鍙�夛細杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯"
show-word-limit
:maxlength="5000"
/>
@@ -40,63 +49,22 @@
<span v-else>鏈緭鍏ョ幓鐠僆D锛堟甯告ā寮忥細灏嗕粠鏁版嵁搴撹鍙栨渶杩戞壂鐮佺殑鐜荤拑ID锛�</span>
</div>
</el-form-item>
-
- <el-divider content-position="left">鎵ц閰嶇疆</el-divider>
-
- <el-form-item label="鍗曠墖闂撮殧 (绉�)">
- <el-input-number
- v-model="form.glassIntervalSeconds"
- :min="0"
- :max="60"
- :step="0.1"
- :precision="1"
- placeholder="姣忎釜鐜荤拑ID涔嬮棿鐨勯棿闅旀椂闂�"
- />
- <div class="form-tip">澶氫釜鐜荤拑ID鏃讹紝姣忎釜鐜荤拑ID浼犻�掍箣闂寸殑闂撮殧鏃堕棿锛堢锛夛紝鐢ㄤ簬妯℃嫙鐜荤拑姣忕墖杩愬姩鐨勬椂闂淬��0琛ㄧず涓�娆℃�у叏閮ㄤ紶閫�</div>
- </el-form-item>
-
- <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 { 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: {
@@ -107,12 +75,7 @@
const emit = defineEmits(['task-started'])
//閰嶇疆榛樿鍊�
-const form = reactive({
- glassIntervalSeconds: 10, // 鍗曠墖闂撮殧锛岄粯璁�10绉�
- executionInterval: 1000,
- timeoutMinutes: 1,
- retryCount: 3
-})
+const form = reactive({})
const formRef = ref(null)
@@ -146,10 +109,12 @@
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)
watch(
() => props.group,
@@ -186,10 +151,10 @@
const deviceList = Array.isArray(rawList)
? rawList
: Array.isArray(rawList?.records)
- ? rawList.records
- : Array.isArray(rawList?.data)
- ? rawList.data
- : []
+ ? rawList.records
+ : Array.isArray(rawList?.data)
+ ? rawList.data
+ : []
const scannerDevice = deviceList.find((item) => {
const type = normalizeType(item.deviceType)
return type.includes('SCANNER') || type.includes('鎵爜')
@@ -216,7 +181,7 @@
ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
return
}
-
+
// 琛ㄥ崟楠岃瘉
if (!formRef.value) return
try {
@@ -225,49 +190,35 @@
ElMessage.warning('璇锋鏌ヨ〃鍗曡緭鍏�')
return
}
-
+
try {
loading.value = true
-
+
// 鏋勫缓浠诲姟鍙傛暟
// 濡傛灉杈撳叆浜嗙幓鐠僆D锛屼娇鐢ㄨ緭鍏ョ殑锛涘鏋滄病鏈夎緭鍏ワ紝glassIds涓虹┖鏁扮粍锛屽悗绔細浠庢暟鎹簱璇诲彇
- // 灏嗙杞崲涓烘绉掍紶缁欏悗绔�
- const glassIntervalMs = form.glassIntervalSeconds != null && form.glassIntervalSeconds !== undefined
- ? Math.round(form.glassIntervalSeconds * 1000)
- : 1000
const parameters = {
- glassIds: glassIds.value.length > 0 ? glassIds.value : [],
- glassIntervalMs: glassIntervalMs,
- executionInterval: form.executionInterval || 1000
+ glassIds: glassIds.value.length > 0 ? glassIds.value : []
}
-
- // 璁惧鐗瑰畾閰嶇疆宸茬Щ闄わ紝濡傛湁闇�瑕佸彲鍦ㄦ鎵╁睍
- 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 = ''
-
+
// 鎻愮ず鐢ㄦ埛鍙互缁х画鍚姩鍏朵粬璁惧缁�
ElMessage.info('鍙互缁х画閫夋嫨鍏朵粬璁惧缁勫惎鍔ㄦ祴璇曪紝澶氫釜璁惧缁勫皢骞惰鎵ц')
} else {
@@ -311,6 +262,292 @@
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)
+
+ // 灏嗗鍏ョ殑鐜荤拑ID濉厖鍒拌緭鍏ユ锛屾柟渚跨敤鎴锋煡鐪嬪拰缂栬緫
+ const glassIds = glassDataList.map(item => item.glassId).filter(id => id)
+ if (glassIds.length > 0) {
+ glassIdsInput.value = glassIds.join('\n')
+ }
+ } 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>
@@ -362,5 +599,8 @@
margin-top: 4px;
line-height: 1.4;
}
-</style>
+.topology-section {
+ margin-top: 24px;
+}
+</style>
--
Gitblit v1.8.0