From 14763d895151f3ddad09906f2233057b8b967881 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期五, 19 十二月 2025 17:06:18 +0800
Subject: [PATCH] 添加plc通讯协议工厂,支持后续多种plc协议
---
mes-web/src/views/device/DeviceGroupList.vue | 399 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 363 insertions(+), 36 deletions(-)
diff --git a/mes-web/src/views/device/DeviceGroupList.vue b/mes-web/src/views/device/DeviceGroupList.vue
index ae5c1ce..d629cfd 100644
--- a/mes-web/src/views/device/DeviceGroupList.vue
+++ b/mes-web/src/views/device/DeviceGroupList.vue
@@ -5,10 +5,9 @@
<el-form :model="searchForm" :inline="true" class="search-form">
<el-form-item label="缁勭被鍨�">
<el-select v-model="searchForm.groupType" placeholder="閫夋嫨缁勭被鍨�" clearable>
- <el-option label="璁惧缁�" value="璁惧缁�" />
- <el-option label="绠$悊缁�" value="绠$悊缁�" />
- <el-option label="鐩戞帶缁�" value="鐩戞帶缁�" />
- <el-option label="缁存姢缁�" value="缁存姢缁�" />
+ <el-option label="鐢熶骇绾�" value="鐢熶骇绾�" />
+ <el-option label="娴嬭瘯绾�" value="娴嬭瘯绾�" />
+ <el-option label="杈呭姪璁惧缁�" value="杈呭姪璁惧缁�" />
</el-select>
</el-form-item>
<el-form-item label="缁勭姸鎬�">
@@ -67,6 +66,13 @@
<template #default="scope">
<el-tag :type="getGroupTypeTag(scope.row.groupType)">
{{ scope.row.groupType }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="executionMode" label="鎵ц妯″紡" width="110">
+ <template #default="scope">
+ <el-tag :type="getExecutionModeTag(scope.row)">
+ {{ getExecutionModeText(scope.row) }}
</el-tag>
</template>
</el-table-column>
@@ -157,12 +163,26 @@
<div class="device-management">
<div class="dialog-header">
<div class="device-stats">
- <el-statistic title="鎬昏澶囨暟" :value="currentGroup?.deviceCount || 0" />
- <el-statistic title="鍦ㄧ嚎璁惧" :value="currentGroup?.onlineDeviceCount || 0" />
- <el-statistic title="绂荤嚎璁惧" :value="(currentGroup?.deviceCount || 0) - (currentGroup?.onlineDeviceCount || 0)" />
+ <el-statistic title="鎬昏澶囨暟" :value="groupDeviceList.length" />
+ <el-statistic title="鍦ㄧ嚎璁惧" :value="onlineDeviceCount" />
+ <el-statistic title="绂荤嚎璁惧" :value="offlineDeviceCount" />
</div>
<div class="dialog-buttons">
- <el-button type="primary" @click="addDevices">娣诲姞璁惧</el-button>
+ <el-select
+ v-model="selectedDeviceIds"
+ multiple
+ filterable
+ placeholder="閫夋嫨瑕佹坊鍔犵殑璁惧"
+ style="width: 300px; margin-right: 12px;"
+ @change="handleDeviceSelectChange"
+ >
+ <el-option
+ v-for="device in availableDeviceList"
+ :key="device.id || device.deviceId"
+ :label="`${device.deviceName} (${device.deviceCode})`"
+ :value="device.id || device.deviceId"
+ />
+ </el-select>
<el-button type="danger" @click="removeDevices" :disabled="selectedDevicesInGroup.length === 0">
绉婚櫎璁惧
</el-button>
@@ -181,20 +201,43 @@
<el-table-column prop="deviceCode" label="璁惧缂栫爜" />
<el-table-column prop="deviceType" label="璁惧绫诲瀷" />
<el-table-column prop="plcIp" label="PLC IP" />
- <el-table-column prop="deviceStatus" label="璁惧鐘舵��">
+ <el-table-column prop="isOnline" label="鍦ㄧ嚎鐘舵��" width="120">
<template #default="scope">
- <el-tag :type="getDeviceStatusTag(scope.row.deviceStatus)" size="small">
- {{ getDeviceStatusText(scope.row.deviceStatus) }}
+ <el-tag :type="scope.row.isOnline ? 'success' : 'info'" size="small">
+ {{ scope.row.isOnline ? '鍦ㄧ嚎' : '绂荤嚎' }}
</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="200" fixed="right">
+ <template #default="scope">
+ <el-button
+ v-if="scope.row.isOnline"
+ type="warning"
+ size="small"
+ @click="updateDeviceOnlineStatus(scope.row, 'OFFLINE')"
+ :loading="scope.row.statusUpdating"
+ >
+ 璁句负绂荤嚎
+ </el-button>
+ <el-button
+ v-else
+ type="success"
+ size="small"
+ @click="updateDeviceOnlineStatus(scope.row, 'ONLINE')"
+ :loading="scope.row.statusUpdating"
+ >
+ 璁句负鍦ㄧ嚎
+ </el-button>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
- <el-button @click="deviceDialogVisible = false">鍏抽棴</el-button>
+ <el-button @click="handleCloseDeviceDialog">鍏抽棴</el-button>
</template>
</el-dialog>
+
<!-- 缁熻璇︽儏寮圭獥 -->
<el-dialog
@@ -234,10 +277,10 @@
</template>
<script setup>
-import { ref, reactive, onMounted } from 'vue'
+import { ref, reactive, onMounted, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, ArrowDown } from '@element-plus/icons-vue'
-import { deviceGroupApi, devicePlcApi } from '@/api/device/deviceManagement'
+import { deviceGroupApi, devicePlcApi, deviceConfigApi, deviceStatusApi } from '@/api/device/deviceManagement'
// 鍝嶅簲寮忔暟鎹�
const groupTable = ref(null)
@@ -270,6 +313,22 @@
// 缁熻寮圭獥
const statisticsDialogVisible = ref(false)
+// 娣诲姞璁惧鐩稿叧
+const availableDeviceList = ref([])
+const selectedDeviceIds = ref([])
+
+// 璁$畻灞炴�э細鏍规嵁瀹為檯璁惧鍒楄〃璁$畻鍦ㄧ嚎/绂荤嚎璁惧鏁伴噺
+const onlineDeviceCount = computed(() => {
+ return groupDeviceList.value.filter(device => {
+ const status = device.deviceStatus || device.status
+ return status === 'ONLINE' || status === '鍦ㄧ嚎' || device.isOnline === true
+ }).length
+})
+
+const offlineDeviceCount = computed(() => {
+ return groupDeviceList.value.length - onlineDeviceCount.value
+})
+
// 浜嬩欢瀹氫箟
const emit = defineEmits(['group-selected', 'refresh-statistics'])
@@ -288,7 +347,55 @@
const response = await deviceGroupApi.getList(params)
// MyBatis-Plus Page 瀵硅薄缁撴瀯锛歿 records: [], total: 0 }
if (response && response.data) {
- groupList.value = response.data.records || response.data.content || response.data.list || []
+ const records = response.data.records || response.data.content || response.data.list || []
+ // 杞崲鍚庣鐘舵�佸瓧娈靛埌鍓嶇鏍煎紡
+ groupList.value = records.map(item => {
+ // 鍚庣鍙兘杩斿洖鐨� status 鏍煎紡锛�
+ // 1. 鏁板瓧绫诲瀷锛�0=鍋滅敤, 1=鍚敤, 2=缁存姢涓�
+ // 2. 瀛楃涓茬被鍨嬶細"鍚敤"銆�"鍋滅敤"銆�"缁存姢涓�"
+ // 3. 瀛楃涓茬被鍨嬶細"ENABLED"銆�"DISABLED"銆�"MAINTENANCE"
+ let statusNum = 0
+ let statusStr = item.status
+
+ if (typeof statusStr === 'number') {
+ statusNum = statusStr
+ } else if (typeof statusStr === 'string') {
+ // 澶勭悊涓枃鐘舵�佸瓧绗︿覆
+ if (statusStr === '鍚敤' || statusStr === 'ENABLED') {
+ statusNum = 1
+ statusStr = 'ENABLED'
+ } else if (statusStr === '鍋滅敤' || statusStr === 'DISABLED') {
+ statusNum = 0
+ statusStr = 'DISABLED'
+ } else if (statusStr === '缁存姢涓�' || statusStr === 'MAINTENANCE') {
+ statusNum = 2
+ statusStr = 'MAINTENANCE'
+ } else {
+ // 榛樿鍋滅敤
+ statusNum = 0
+ statusStr = 'DISABLED'
+ }
+ } else if (item.groupStatus) {
+ // 濡傛灉鏈� groupStatus 瀛楁锛屼娇鐢ㄥ畠
+ if (item.groupStatus === 'ENABLED') {
+ statusNum = 1
+ statusStr = 'ENABLED'
+ } else if (item.groupStatus === 'MAINTENANCE') {
+ statusNum = 2
+ statusStr = 'MAINTENANCE'
+ } else {
+ statusNum = 0
+ statusStr = 'DISABLED'
+ }
+ }
+
+ return {
+ ...item,
+ status: statusNum,
+ groupStatus: statusStr,
+ enabled: statusNum === 1
+ }
+ })
pagination.total = response.data.total || response.data.totalElements || 0
} else {
groupList.value = []
@@ -400,15 +507,24 @@
if (row.enabled) {
await deviceGroupApi.enable(groupId)
ElMessage.success('璁惧缁勫惎鐢ㄦ垚鍔�')
+ // 鍚屾鏇存柊 groupStatus
+ row.groupStatus = 'ENABLED'
+ row.status = 1
} else {
await deviceGroupApi.disable(groupId)
ElMessage.success('璁惧缁勭鐢ㄦ垚鍔�')
+ // 鍚屾鏇存柊 groupStatus
+ row.groupStatus = 'DISABLED'
+ row.status = 0
}
emit('refresh-statistics')
- loadGroupList() // 鍒锋柊鍒楄〃
+ // 涓嶉噸鏂板姞杞藉垪琛紝鐩存帴鏇存柊褰撳墠琛岀姸鎬�
} catch (error) {
console.error('鏇存柊璁惧缁勭姸鎬佸け璐�:', error)
- row.enabled = !row.enabled // 鎭㈠鐘舵��
+ // 鎭㈠鐘舵��
+ row.enabled = !row.enabled
+ row.groupStatus = row.enabled ? 'ENABLED' : 'DISABLED'
+ row.status = row.enabled ? 1 : 0
ElMessage.error('鏇存柊璁惧缁勭姸鎬佸け璐�: ' + (error.response?.data?.message || error.message))
}
}
@@ -477,7 +593,9 @@
const manageDevices = async (row) => {
currentGroup.value = row
deviceDialogVisible.value = true
+ selectedDeviceIds.value = []
await loadGroupDevices(row.id || row.groupId)
+ await loadAvailableDevices()
}
const loadGroupDevices = async (groupId) => {
@@ -485,7 +603,41 @@
deviceLoading.value = true
const response = await deviceGroupApi.getGroupDevices(groupId)
if (response && response.data) {
- groupDeviceList.value = response.data || []
+ // 杞崲鍚庣杩斿洖鐨勬暟鎹牸寮忥紝灏� status 杞崲涓� deviceStatus锛屽苟杞崲鐘舵�佸��
+ groupDeviceList.value = (response.data || []).map(device => {
+ // 浼樺厛浣跨敤 isOnline 瀛楁锛堟潵鑷� device_status 琛ㄧ殑鏈�鏂扮姸鎬侊級
+ let deviceStatus = 'OFFLINE'
+ if (device.isOnline !== undefined && device.isOnline !== null) {
+ deviceStatus = device.isOnline ? 'ONLINE' : 'OFFLINE'
+ } else if (device.status) {
+ // 濡傛灉娌℃湁 isOnline锛屽垯浣跨敤 status 瀛楁
+ const statusMap = {
+ '鍦ㄧ嚎': 'ONLINE',
+ '绂荤嚎': 'OFFLINE',
+ '缁存姢涓�': 'MAINTENANCE',
+ '鏁呴殰': 'MAINTENANCE',
+ '绂佺敤': 'DISABLED',
+ 'ONLINE': 'ONLINE',
+ 'OFFLINE': 'OFFLINE',
+ 'MAINTENANCE': 'MAINTENANCE',
+ 'DISABLED': 'DISABLED'
+ }
+ // 濡傛灉鏄暟瀛楋紝杞崲涓哄瓧绗︿覆
+ const statusStr = typeof device.status === 'number'
+ ? (device.status === 1 ? 'ONLINE' : 'OFFLINE')
+ : String(device.status)
+ deviceStatus = statusMap[statusStr] || 'OFFLINE'
+ }
+
+ return {
+ ...device,
+ deviceStatus: deviceStatus,
+ // 淇濈暀鍘熷瀛楁浠ヤ究鍏煎
+ status: device.status,
+ isOnline: device.isOnline !== undefined ? device.isOnline : (deviceStatus === 'ONLINE'),
+ statusUpdating: false // 娣诲姞鏇存柊鐘舵�佹爣璁�
+ }
+ })
} else {
groupDeviceList.value = []
}
@@ -507,9 +659,80 @@
selectedDevicesInGroup.value = selection
}
-const addDevices = () => {
- // 娣诲姞璁惧鍒扮粍閫昏緫
- ElMessage.info('娣诲姞璁惧鍔熻兘寮�鍙戜腑...')
+const loadAvailableDevices = async () => {
+ try {
+ // 鑾峰彇鎵�鏈夎澶囧垪琛�
+ const response = await deviceConfigApi.getList({
+ page: 1,
+ size: 1000 // 鑾峰彇瓒冲澶氱殑璁惧
+ })
+
+ if (response && response.data) {
+ const allDevices = response.data.records || response.data.content || response.data.list || []
+ // 杩囨护鎺夊凡缁忓湪璁惧缁勪腑鐨勮澶�
+ const currentDeviceIds = new Set(groupDeviceList.value.map(d => d.id || d.deviceId))
+ availableDeviceList.value = allDevices
+ .filter(device => {
+ const deviceId = device.id || device.deviceId
+ return !currentDeviceIds.has(deviceId)
+ })
+ .map(device => {
+ // 杞崲璁惧鐘舵��
+ let deviceStatus = 'OFFLINE'
+ if (device.deviceStatus) {
+ deviceStatus = device.deviceStatus
+ } else if (device.status) {
+ const statusMap = {
+ '鍦ㄧ嚎': 'ONLINE',
+ '绂荤嚎': 'OFFLINE',
+ '缁存姢涓�': 'MAINTENANCE',
+ '鏁呴殰': 'MAINTENANCE',
+ '绂佺敤': 'DISABLED'
+ }
+ deviceStatus = statusMap[device.status] || 'OFFLINE'
+ }
+
+ return {
+ ...device,
+ deviceStatus: deviceStatus
+ }
+ })
+ } else {
+ availableDeviceList.value = []
+ }
+ } catch (error) {
+ console.error('鍔犺浇鍙敤璁惧澶辫触:', error)
+ ElMessage.error('鍔犺浇鍙敤璁惧澶辫触: ' + (error.response?.data?.message || error.message))
+ availableDeviceList.value = []
+ }
+}
+
+const handleDeviceSelectChange = async (deviceIds) => {
+ if (!deviceIds || deviceIds.length === 0) {
+ return
+ }
+
+ try {
+ const groupId = currentGroup.value.id || currentGroup.value.groupId
+
+ await deviceGroupApi.batchAddDevicesToGroup({
+ groupId: groupId,
+ deviceIds: deviceIds
+ })
+
+ ElMessage.success(`鎴愬姛娣诲姞 ${deviceIds.length} 涓澶囧埌璁惧缁刞)
+ // 娓呯┖閫夋嫨
+ selectedDeviceIds.value = []
+ // 鍒锋柊璁惧鍒楄〃鍜屽彲鐢ㄨ澶囧垪琛�
+ await loadGroupDevices(groupId)
+ await loadAvailableDevices()
+ emit('refresh-statistics')
+ } catch (error) {
+ console.error('娣诲姞璁惧澶辫触:', error)
+ ElMessage.error('娣诲姞璁惧澶辫触: ' + (error.response?.data?.message || error.message))
+ // 娓呯┖閫夋嫨浠ヤ究閲嶈瘯
+ selectedDeviceIds.value = []
+ }
}
const removeDevices = async () => {
@@ -519,11 +742,6 @@
return
}
- await ElMessageBox.confirm(
- `纭畾瑕佷粠璁惧缁勪腑绉婚櫎閫変腑鐨� ${selectedDevicesInGroup.value.length} 涓澶囧悧锛焋,
- '绉婚櫎璁惧纭'
- )
-
const deviceIds = selectedDevicesInGroup.value.map(item => item.id || item.deviceId)
// 鎵归噺绉婚櫎璁惧
await deviceGroupApi.batchRemoveDevicesFromGroup({
@@ -531,13 +749,73 @@
deviceIds
})
ElMessage.success(`鎴愬姛绉婚櫎 ${deviceIds.length} 涓澶嘸)
- loadGroupDevices(currentGroup.value.id || currentGroup.value.groupId)
+ // 娓呯┖閫夋嫨
+ selectedDevicesInGroup.value = []
+ const groupId = currentGroup.value.id || currentGroup.value.groupId
+ await loadGroupDevices(groupId)
+ await loadAvailableDevices()
+ emit('refresh-statistics')
} catch (error) {
- if (error !== 'cancel') {
console.error('绉婚櫎璁惧澶辫触:', error)
- ElMessage.error('绉婚櫎璁惧澶辫触')
- }
+ ElMessage.error('绉婚櫎璁惧澶辫触: ' + (error.response?.data?.message || error.message))
}
+}
+
+// 鏇存柊璁惧鍦ㄧ嚎鐘舵��
+const updateDeviceOnlineStatus = async (device, status) => {
+ try {
+ // 璁剧疆鏇存柊涓姸鎬�
+ device.statusUpdating = true
+
+ const deviceId = device.id || device.deviceId
+ if (!deviceId) {
+ ElMessage.warning('璁惧ID涓嶅瓨鍦�')
+ return
+ }
+
+ await deviceStatusApi.updateDeviceOnlineStatus({
+ deviceId: deviceId,
+ status: status
+ })
+
+ // 鏇存柊鏈湴鐘舵��
+ device.isOnline = status === 'ONLINE'
+ // 鍚屾椂鏇存柊 deviceStatus 瀛楁浠ヤ繚鎸佷竴鑷存��
+ if (status === 'ONLINE') {
+ device.deviceStatus = 'ONLINE'
+ } else if (status === 'OFFLINE') {
+ device.deviceStatus = 'OFFLINE'
+ }
+
+ ElMessage.success(`璁惧鐘舵�佸凡鏇存柊涓猴細${status === 'ONLINE' ? '鍦ㄧ嚎' : '绂荤嚎'}`)
+
+ // 鍒锋柊璁惧鍒楄〃浠ヨ幏鍙栨渶鏂扮姸鎬�
+ const groupId = currentGroup.value.id || currentGroup.value.groupId
+ await loadGroupDevices(groupId)
+
+ // 鍒锋柊璁惧缁勫垪琛ㄤ互鏇存柊鍦ㄧ嚎璁惧鏁伴噺缁熻
+ await loadGroupList()
+
+ // 鏇存柊褰撳墠璁惧缁勭殑鍦ㄧ嚎璁惧鏁伴噺锛堝鏋滃綋鍓嶈澶囩粍鍦ㄥ垪琛ㄤ腑锛�
+ const currentGroupInList = groupList.value.find(g => (g.id || g.groupId) === groupId)
+ if (currentGroupInList) {
+ // 閲嶆柊璁$畻鍦ㄧ嚎璁惧鏁伴噺
+ const onlineCount = groupDeviceList.value.filter(d => d.isOnline === true).length
+ currentGroupInList.onlineDeviceCount = onlineCount
+ }
+ } catch (error) {
+ console.error('鏇存柊璁惧鍦ㄧ嚎鐘舵�佸け璐�:', error)
+ ElMessage.error('鏇存柊璁惧鍦ㄧ嚎鐘舵�佸け璐�: ' + (error.response?.data?.message || error.message))
+ } finally {
+ device.statusUpdating = false
+ }
+}
+
+// 鍏抽棴璁惧绠$悊寮圭獥
+const handleCloseDeviceDialog = async () => {
+ deviceDialogVisible.value = false
+ // 鍏抽棴鏃跺埛鏂拌澶囩粍鍒楄〃锛岀‘淇濆湪绾胯澶囨暟閲忔槸鏈�鏂扮殑
+ await loadGroupList()
}
const handleCommand = async (command, row) => {
@@ -594,10 +872,9 @@
// 宸ュ叿鍑芥暟
const getGroupTypeTag = (type) => {
const typeMap = {
- '璁惧缁�': 'primary',
- '绠$悊缁�': 'success',
- '鐩戞帶缁�': 'warning',
- '缁存姢缁�': 'info'
+ '鐢熶骇绾�': 'primary',
+ '娴嬭瘯绾�': 'success',
+ '杈呭姪璁惧缁�': 'warning'
}
return typeMap[type] || 'info'
}
@@ -605,7 +882,8 @@
const getGroupStatusTag = (status) => {
const statusMap = {
'ENABLED': 'success',
- 'DISABLED': 'info'
+ 'DISABLED': 'info',
+ 'MAINTENANCE': 'warning'
}
return statusMap[status] || 'info'
}
@@ -613,9 +891,47 @@
const getGroupStatusText = (status) => {
const statusMap = {
'ENABLED': '鍚敤',
- 'DISABLED': '绂佺敤'
+ 'DISABLED': '绂佺敤',
+ 'MAINTENANCE': '缁存姢涓�'
}
return statusMap[status] || status
+}
+
+// 鑾峰彇鎵ц妯″紡鏍囩绫诲瀷
+const getExecutionModeTag = (row) => {
+ const mode = getExecutionMode(row)
+ return mode === 'PARALLEL' ? 'success' : 'primary'
+}
+
+// 鑾峰彇鎵ц妯″紡鏂囨湰
+const getExecutionModeText = (row) => {
+ const mode = getExecutionMode(row)
+ return mode === 'PARALLEL' ? '骞惰鎵ц' : '涓茶鎵ц'
+}
+
+// 浠巈xtraConfig鎴朿ustomParams涓彁鍙栨墽琛屾ā寮�
+const getExecutionMode = (row) => {
+ // 浼樺厛浠巈xtraConfig涓鍙�
+ if (row.extraConfig) {
+ try {
+ const extraConfig = typeof row.extraConfig === 'string' ? JSON.parse(row.extraConfig) : row.extraConfig
+ if (extraConfig.executionMode) {
+ return extraConfig.executionMode.toUpperCase()
+ }
+ } catch (e) {
+ console.warn('瑙f瀽extraConfig澶辫触:', e)
+ }
+ }
+ // 浠巆ustomParams涓鍙�
+ if (row.customParams && row.customParams.executionMode) {
+ return String(row.customParams.executionMode).toUpperCase()
+ }
+ // 濡傛灉鏈塵axConcurrentDevices涓斿ぇ浜�1锛岄粯璁ゅ苟琛�
+ if (row.maxConcurrentDevices && row.maxConcurrentDevices > 1) {
+ return 'PARALLEL'
+ }
+ // 榛樿涓茶
+ return 'SERIAL'
}
const getDeviceStatusTag = (status) => {
@@ -745,4 +1061,15 @@
font-weight: bold;
color: #409eff;
}
+
+.add-device-dialog {
+ padding: 10px 0;
+}
+
+.dialog-search {
+ margin-bottom: 16px;
+ padding: 16px;
+ background-color: #f5f7fa;
+ border-radius: 8px;
+}
</style>
\ No newline at end of file
--
Gitblit v1.8.0