From e76f0739e647fe8a7e0e2618914e2faff554b1b7 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期一, 17 十一月 2025 17:33:23 +0800
Subject: [PATCH] 解决冲突

---
 mes-web/src/views/device/DeviceConfigList.vue |  538 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 538 insertions(+), 0 deletions(-)

diff --git a/mes-web/src/views/device/DeviceConfigList.vue b/mes-web/src/views/device/DeviceConfigList.vue
new file mode 100644
index 0000000..adad720
--- /dev/null
+++ b/mes-web/src/views/device/DeviceConfigList.vue
@@ -0,0 +1,538 @@
+<template>
+  <div class="device-config-list">
+    <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+    <div class="search-section">
+      <el-form :model="searchForm" :inline="true" class="search-form">
+        <el-form-item label="璁惧绫诲瀷">
+          <el-select v-model="searchForm.deviceType" placeholder="閫夋嫨璁惧绫诲瀷" clearable>
+            <el-option label="涓婂ぇ杞�" value="涓婂ぇ杞�" />
+            <el-option label="澶х悊鐗�" value="澶х悊鐗�" />
+            <el-option label="鐜荤拑瀛樺偍" value="鐜荤拑瀛樺偍" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="璁惧鐘舵��">
+          <el-select v-model="searchForm.deviceStatus" placeholder="閫夋嫨璁惧鐘舵��" clearable>
+            <el-option label="鍦ㄧ嚎" value="ONLINE" />
+            <el-option label="绂荤嚎" value="OFFLINE" />
+            <el-option label="缁存姢涓�" value="MAINTENANCE" />
+            <el-option label="绂佺敤" value="DISABLED" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎼滅储鍏抽敭璇�">
+          <el-input v-model="searchForm.keyword" placeholder="璁惧鍚嶇О鎴栫紪鐮�" clearable style="width: 200px;">
+            <template #append>
+              <el-button @click="handleSearch">
+                <el-icon><Search /></el-icon>
+              </el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 鎵归噺鎿嶄綔鍖哄煙 -->
+    <div class="batch-operation" v-if="selectedDevices.length > 0">
+      <el-alert
+        :title="`宸查�夋嫨 ${selectedDevices.length} 涓澶嘸"
+        type="info"
+        show-icon
+        :closable="false"
+      />
+      <div class="batch-buttons">
+        <el-button type="success" size="small" @click="batchEnable">鎵归噺鍚敤</el-button>
+        <el-button type="warning" size="small" @click="batchDisable">鎵归噺绂佺敤</el-button>
+        <el-button type="danger" size="small" @click="batchDelete">鎵归噺鍒犻櫎</el-button>
+        <el-button type="info" size="small" :loading="plcOperationLoading" @click="batchPlcRequest">鎵归噺PLC璇锋眰</el-button>
+        <el-button type="info" size="small" :loading="plcOperationLoading" @click="batchPlcReport">鎵归噺PLC姹囨姤</el-button>
+        <el-button type="info" size="small" :loading="plcOperationLoading" @click="batchPlcReset">鎵归噺PLC閲嶇疆</el-button>
+        <el-button size="small" @click="clearSelection">鍙栨秷閫夋嫨</el-button>
+      </div>
+    </div>
+
+    <!-- 璁惧鍒楄〃 -->
+    <div class="table-section">
+      <el-table
+        ref="deviceTable"
+        v-loading="tableLoading"
+        :data="deviceList"
+        @selection-change="handleSelectionChange"
+        border
+        stripe
+        style="width: 100%"
+      >
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="deviceName" label="璁惧鍚嶇О" min-width="120" />
+        <el-table-column prop="deviceCode" label="璁惧缂栫爜" width="120" />
+        <el-table-column prop="deviceType" label="璁惧绫诲瀷" width="100">
+          <template #default="scope">
+            <el-tag :type="getDeviceTypeTag(scope.row.deviceType)">
+              {{ scope.row.deviceType }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="plcIp" label="PLC IP" width="130" />
+        <el-table-column prop="plcType" label="PLC绫诲瀷" width="100" />
+        <el-table-column prop="moduleName" label="妯″潡鍚嶇О" min-width="120" />
+        <el-table-column prop="isPrimary" label="涓绘帶璁惧" width="100" align="center">
+          <template #default="scope">
+            <el-tag v-if="scope.row.isPrimary" type="success" size="small">涓绘帶</el-tag>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="deviceStatus" label="璁惧鐘舵��" width="100">
+          <template #default="scope">
+            <el-tag :type="getDeviceStatusTag(scope.row.deviceStatus)" size="small">
+              {{ getDeviceStatusText(scope.row.deviceStatus) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="enabled" label="鍚敤鐘舵��" width="100">
+          <template #default="scope">
+            <el-switch
+              v-model="scope.row.enabled"
+              :disabled="scope.row.isPrimary"
+              @change="handleStatusChange(scope.row)"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column prop="lastHeartbeat" label="鏈�鍚庡績璺�" width="150">
+          <template #default="scope">
+            {{ formatDateTime(scope.row.lastHeartbeat) }}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="200" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" size="small" @click="editDevice(scope.row)">
+              缂栬緫
+            </el-button>
+            <el-button type="warning" size="small" :loading="plcOperationLoading" @click="handleSinglePlcRequest(scope.row)">
+              PLC璇锋眰
+            </el-button>
+            <el-button type="success" size="small" @click="healthCheck(scope.row)">
+              鍋ュ悍妫�鏌�
+            </el-button>
+            <el-dropdown @command="(command) => handleCommand(command, scope.row)">
+              <el-button type="info" size="small">
+                鏇村<el-icon><ArrowDown /></el-icon>
+              </el-button>
+              <template #dropdown>
+                <el-dropdown-menu>
+                  <el-dropdown-item command="view">鏌ョ湅璇︽儏</el-dropdown-item>
+                  <el-dropdown-item command="copy" :disabled="scope.row.isPrimary">澶嶅埗閰嶇疆</el-dropdown-item>
+                  <el-dropdown-item command="reset">閲嶇疆璁惧</el-dropdown-item>
+                  <el-dropdown-item command="plc-report">PLC姹囨姤</el-dropdown-item>
+                  <el-dropdown-item command="plc-reset">PLC閲嶇疆</el-dropdown-item>
+                  <el-dropdown-item command="delete" divided :disabled="scope.row.isPrimary">
+                    鍒犻櫎璁惧
+                  </el-dropdown-item>
+                </el-dropdown-menu>
+              </template>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 鍒嗛〉 -->
+    <div class="pagination-section">
+      <el-pagination
+        v-model:current-page="pagination.page"
+        v-model:page-size="pagination.size"
+        :page-sizes="[10, 20, 50, 100]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Search, ArrowDown } from '@element-plus/icons-vue'
+import { deviceConfigApi, devicePlcApi } from '@/api/device/deviceManagement'
+
+// 鍝嶅簲寮忔暟鎹�
+const deviceTable = ref(null)
+const tableLoading = ref(false)
+const deviceList = ref([])
+const selectedDevices = ref([])
+const plcOperationLoading = ref(false)
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  deviceType: '',
+  deviceStatus: '',
+  keyword: ''
+})
+
+// 鍒嗛〉淇℃伅
+const pagination = reactive({
+  page: 1,
+  size: 10,
+  total: 0
+})
+
+// 浜嬩欢瀹氫箟
+const emit = defineEmits(['device-selected', 'refresh-statistics'])
+
+// 鏂规硶瀹氫箟
+const loadDeviceList = async () => {
+  try {
+    tableLoading.value = true
+    const params = {
+      pageNum: pagination.page,
+      pageSize: pagination.size,
+      deviceType: searchForm.deviceType || undefined,
+      status: searchForm.deviceStatus || undefined,
+      deviceCode: searchForm.keyword || undefined
+    }
+    
+    const response = await deviceConfigApi.getList(params)
+    // MyBatis-Plus Page 瀵硅薄缁撴瀯锛歿 records: [], total: 0 }
+    if (response && response.data) {
+      deviceList.value = response.data.records || response.data.content || response.data.list || []
+      pagination.total = response.data.total || response.data.totalElements || 0
+    } else {
+      deviceList.value = []
+      pagination.total = 0
+    }
+  } catch (error) {
+    console.error('鍔犺浇璁惧鍒楄〃澶辫触:', error)
+    ElMessage.error('鍔犺浇璁惧鍒楄〃澶辫触: ' + (error.response?.data?.message || error.message))
+    deviceList.value = []
+    pagination.total = 0
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+const handleSearch = () => {
+  pagination.page = 1
+  loadDeviceList()
+}
+
+const resetSearch = () => {
+  searchForm.deviceType = ''
+  searchForm.deviceStatus = ''
+  searchForm.keyword = ''
+  pagination.page = 1
+  loadDeviceList()
+}
+
+const handleSelectionChange = (selection) => {
+  selectedDevices.value = selection
+}
+
+const clearSelection = () => {
+  deviceTable.value?.clearSelection()
+  selectedDevices.value = []
+}
+
+const getSelectedDeviceIds = () => selectedDevices.value.map(item => item.id || item.deviceId)
+
+const plcOperationLabelMap = {
+  request: 'PLC璇锋眰',
+  report: 'PLC姹囨姤',
+  reset: 'PLC閲嶇疆'
+}
+
+const executePlcOperation = async (deviceIds, operation) => {
+  const ids = deviceIds?.filter(Boolean) || []
+  if (ids.length === 0) {
+    ElMessage.warning('璇峰厛閫夋嫨璁惧')
+    return
+  }
+  plcOperationLoading.value = true
+  try {
+    let response
+    switch (operation) {
+      case 'request':
+        response = await devicePlcApi.triggerRequests(ids)
+        break
+      case 'report':
+        response = await devicePlcApi.triggerReports(ids)
+        break
+      case 'reset':
+        response = await devicePlcApi.resetDevices(ids)
+        break
+      default:
+        throw new Error('鏈煡鐨凱LC鎿嶄綔绫诲瀷')
+    }
+    const results = response?.data || []
+    const successCount = results.filter(item => item.success).length
+    const label = plcOperationLabelMap[operation] || 'PLC鎿嶄綔'
+    if (results.length === 0) {
+      ElMessage.warning(`${label}鏈繑鍥炵粨鏋渀)
+    } else if (successCount === results.length) {
+      ElMessage.success(`${label}鎴愬姛锛�${successCount}/${results.length}锛塦)
+    } else {
+      ElMessage.warning(`${label}閮ㄥ垎鎴愬姛锛�${successCount}/${results.length}锛塦)
+    }
+  } catch (error) {
+    console.error('鎵цPLC鎿嶄綔澶辫触:', error)
+    const label = plcOperationLabelMap[operation] || 'PLC鎿嶄綔'
+    ElMessage.error(`${label}澶辫触锛�${error.response?.data?.message || error.message}`)
+  } finally {
+    plcOperationLoading.value = false
+  }
+}
+
+const handleSinglePlcRequest = (row) => executePlcOperation([row.id || row.deviceId], 'request')
+const handleSinglePlcReport = (row) => executePlcOperation([row.id || row.deviceId], 'report')
+const handleSinglePlcReset = (row) => executePlcOperation([row.id || row.deviceId], 'reset')
+
+const batchPlcRequest = () => executePlcOperation(getSelectedDeviceIds(), 'request')
+const batchPlcReport = () => executePlcOperation(getSelectedDeviceIds(), 'report')
+const batchPlcReset = () => executePlcOperation(getSelectedDeviceIds(), 'reset')
+
+const handleStatusChange = async (row) => {
+  try {
+    if (row.enabled) {
+      await deviceConfigApi.enable(row.id || row.deviceId)
+      ElMessage.success('璁惧鍚敤鎴愬姛')
+    } else {
+      await deviceConfigApi.disable(row.id || row.deviceId)
+      ElMessage.success('璁惧绂佺敤鎴愬姛')
+    }
+    emit('refresh-statistics')
+    loadDeviceList() // 鍒锋柊鍒楄〃
+  } catch (error) {
+    console.error('鏇存柊璁惧鐘舵�佸け璐�:', error)
+    row.enabled = !row.enabled // 鎭㈠鐘舵��
+    ElMessage.error('鏇存柊璁惧鐘舵�佸け璐�: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchEnable = async () => {
+  try {
+    const deviceIds = selectedDevices.value.map(item => item.id || item.deviceId)
+    await deviceConfigApi.batchEnable(deviceIds)
+    ElMessage.success(`鎴愬姛鍚敤 ${deviceIds.length} 涓澶嘸)
+    clearSelection()
+    loadDeviceList()
+    emit('refresh-statistics')
+  } catch (error) {
+    console.error('鎵归噺鍚敤澶辫触:', error)
+    ElMessage.error('鎵归噺鍚敤澶辫触: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchDisable = async () => {
+  try {
+    const deviceIds = selectedDevices.value.map(item => item.id || item.deviceId)
+    await deviceConfigApi.batchDisable(deviceIds)
+    ElMessage.success(`鎴愬姛绂佺敤 ${deviceIds.length} 涓澶嘸)
+    clearSelection()
+    loadDeviceList()
+    emit('refresh-statistics')
+  } catch (error) {
+    console.error('鎵归噺绂佺敤澶辫触:', error)
+    ElMessage.error('鎵归噺绂佺敤澶辫触: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchDelete = async () => {
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ら�変腑鐨� ${selectedDevices.value.length} 涓澶囧悧锛熸鎿嶄綔涓嶅彲鎭㈠锛乣,
+      '鎵归噺鍒犻櫎纭',
+      {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+    
+    const deviceIds = selectedDevices.value.map(item => item.id || item.deviceId)
+    // 閫愪釜鍒犻櫎
+    for (const deviceId of deviceIds) {
+      await deviceConfigApi.delete(deviceId)
+    }
+    ElMessage.success(`鎴愬姛鍒犻櫎 ${deviceIds.length} 涓澶嘸)
+    clearSelection()
+    loadDeviceList()
+    emit('refresh-statistics')
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('鎵归噺鍒犻櫎澶辫触:', error)
+      ElMessage.error('鎵归噺鍒犻櫎澶辫触')
+    }
+  }
+}
+
+const editDevice = (row) => {
+  emit('device-selected', row)
+}
+
+const healthCheck = async (row) => {
+  try {
+    const response = await deviceConfigApi.healthCheck(row.id || row.deviceId)
+    if (response && response.data) {
+      ElMessage.success(`璁惧鍋ュ悍妫�鏌ュ畬鎴愶紝鐘舵�侊細${response.data.status || '姝e父'}`)
+    } else {
+      ElMessage.success('璁惧鍋ュ悍妫�鏌ュ畬鎴�')
+    }
+  } catch (error) {
+    console.error('鍋ュ悍妫�鏌ュけ璐�:', error)
+    ElMessage.error('鍋ュ悍妫�鏌ュけ璐�: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const handleCommand = async (command, row) => {
+  switch (command) {
+    case 'view':
+      // 鏌ョ湅璇︽儏閫昏緫
+      ElMessage.info('鏌ョ湅璇︽儏鍔熻兘寮�鍙戜腑...')
+      break
+    case 'copy':
+      // 澶嶅埗閰嶇疆閫昏緫
+      ElMessage.info('澶嶅埗閰嶇疆鍔熻兘寮�鍙戜腑...')
+      break
+    case 'reset':
+      // 閲嶇疆璁惧閫昏緫
+      await ElMessageBox.confirm('纭畾瑕侀噸缃澶囬厤缃悧锛�', '閲嶇疆纭')
+      ElMessage.success('璁惧閲嶇疆鎴愬姛')
+      break
+    case 'plc-report':
+      await executePlcOperation([row.id || row.deviceId], 'report')
+      break
+    case 'plc-reset':
+      await executePlcOperation([row.id || row.deviceId], 'reset')
+      break
+    case 'delete':
+      if (row.isPrimary) {
+        ElMessage.warning('涓绘帶璁惧涓嶈兘鍒犻櫎')
+        return
+      }
+      await ElMessageBox.confirm('纭畾瑕佸垹闄よ璁惧鍚楋紵', '鍒犻櫎纭', {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      })
+      await deviceConfigApi.delete(row.id || row.deviceId)
+      ElMessage.success('璁惧鍒犻櫎鎴愬姛')
+      loadDeviceList()
+      emit('refresh-statistics')
+      break
+  }
+}
+
+const handleSizeChange = (size) => {
+  pagination.size = size
+  pagination.page = 1
+  loadDeviceList()
+}
+
+const handleCurrentChange = (page) => {
+  pagination.page = page
+  loadDeviceList()
+}
+
+// 宸ュ叿鍑芥暟
+const getDeviceTypeTag = (type) => {
+  const typeMap = {
+    '涓婂ぇ杞�': 'primary',
+    '澶х悊鐗�': 'success',
+    '鐜荤拑瀛樺偍': 'warning'
+  }
+  return typeMap[type] || 'info'
+}
+
+const getDeviceStatusTag = (status) => {
+  const statusMap = {
+    'ONLINE': 'success',
+    'OFFLINE': 'info',
+    'MAINTENANCE': 'warning',
+    'DISABLED': 'danger'
+  }
+  return statusMap[status] || 'info'
+}
+
+const getDeviceStatusText = (status) => {
+  const statusMap = {
+    'ONLINE': '鍦ㄧ嚎',
+    'OFFLINE': '绂荤嚎',
+    'MAINTENANCE': '缁存姢涓�',
+    'DISABLED': '绂佺敤'
+  }
+  return statusMap[status] || status
+}
+
+const formatDateTime = (dateTime) => {
+  if (!dateTime) return '-'
+  return new Date(dateTime).toLocaleString()
+}
+
+// 鏆撮湶鏂规硶
+const refresh = () => {
+  loadDeviceList()
+}
+
+defineExpose({
+  refresh
+})
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadDeviceList()
+})
+</script>
+
+<style scoped>
+.device-config-list {
+  padding: 20px;
+}
+
+.search-section {
+  margin-bottom: 20px;
+  padding: 16px;
+  background-color: #f5f7fa;
+  border-radius: 8px;
+}
+
+.search-form {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.batch-operation {
+  margin-bottom: 16px;
+  padding: 16px;
+  background-color: #e6f7ff;
+  border: 1px solid #91d5ff;
+  border-radius: 8px;
+}
+
+.batch-buttons {
+  margin-top: 12px;
+  display: flex;
+  gap: 8px;
+}
+
+.table-section {
+  margin-bottom: 20px;
+}
+
+.pagination-section {
+  display: flex;
+  justify-content: center;
+}
+
+:deep(.el-table .cell) {
+  white-space: nowrap;
+}
+
+:deep(.el-dropdown-menu__item.is-divided) {
+  border-top: 1px solid #ebeef5;
+  margin-top: 6px;
+  padding-top: 10px;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.8.0