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/DeviceGroupList.vue |  748 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 748 insertions(+), 0 deletions(-)

diff --git a/mes-web/src/views/device/DeviceGroupList.vue b/mes-web/src/views/device/DeviceGroupList.vue
new file mode 100644
index 0000000..9d4163c
--- /dev/null
+++ b/mes-web/src/views/device/DeviceGroupList.vue
@@ -0,0 +1,748 @@
+<template>
+  <div class="device-group-list">
+    <!-- 鎼滅储鍜岀瓫閫夊尯鍩� -->
+    <div class="search-section">
+      <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-select>
+        </el-form-item>
+        <el-form-item label="缁勭姸鎬�">
+          <el-select v-model="searchForm.groupStatus" placeholder="閫夋嫨缁勭姸鎬�" clearable>
+            <el-option label="鍚敤" value="ENABLED" />
+            <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="selectedGroups.length > 0">
+      <el-alert
+        :title="`宸查�夋嫨 ${selectedGroups.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 size="small" @click="clearSelection">鍙栨秷閫夋嫨</el-button>
+      </div>
+    </div>
+
+    <!-- 璁惧缁勫垪琛� -->
+    <div class="table-section">
+      <el-table
+        ref="groupTable"
+        v-loading="tableLoading"
+        :data="groupList"
+        @selection-change="handleSelectionChange"
+        border
+        stripe
+        style="width: 100%"
+      >
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="groupName" label="缁勫悕绉�" min-width="150" />
+        <el-table-column prop="groupCode" label="缁勭紪鐮�" width="130" />
+        <el-table-column prop="groupType" label="缁勭被鍨�" width="100">
+          <template #default="scope">
+            <el-tag :type="getGroupTypeTag(scope.row.groupType)">
+              {{ scope.row.groupType }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="description" label="鎻忚堪" min-width="200" />
+        <el-table-column prop="groupStatus" label="缁勭姸鎬�" width="100">
+          <template #default="scope">
+            <el-tag :type="getGroupStatusTag(scope.row.groupStatus)" size="small">
+              {{ getGroupStatusText(scope.row.groupStatus) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="enabled" label="鍚敤鐘舵��" width="100">
+          <template #default="scope">
+            <el-switch
+              v-model="scope.row.enabled"
+              @change="handleStatusChange(scope.row)"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column prop="deviceCount" label="璁惧鏁伴噺" width="100" align="center">
+          <template #default="scope">
+            <el-tag type="info" size="small">{{ scope.row.deviceCount || 0 }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="onlineDeviceCount" label="鍦ㄧ嚎璁惧" width="100" align="center">
+          <template #default="scope">
+            <el-tag type="success" size="small">{{ scope.row.onlineDeviceCount || 0 }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sortOrder" label="鎺掑簭" width="80" align="center" />
+        <el-table-column label="鎿嶄綔" width="280" fixed="right">
+          <template #default="scope">
+            <el-button type="primary" size="small" @click="editGroup(scope.row)">
+              缂栬緫
+            </el-button>
+            <el-button type="success" size="small" @click="manageDevices(scope.row)">
+              璁惧绠$悊
+            </el-button>
+            <el-button type="warning" size="small" :loading="groupPlcLoading" @click="triggerGroupPlcRequest(scope.row)">
+              PLC璇锋眰
+            </el-button>
+            <el-button type="info" size="small" @click="viewGroupPlcStatus(scope.row)">
+              PLC鐘舵��
+            </el-button>
+            <el-button type="success" size="small" @click="viewStatistics(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">澶嶅埗閰嶇疆</el-dropdown-item>
+                  <el-dropdown-item command="export">瀵煎嚭閰嶇疆</el-dropdown-item>
+                  <el-dropdown-item command="test">娴嬭瘯杩炴帴</el-dropdown-item>
+                  <el-dropdown-item command="plc-report">PLC姹囨姤</el-dropdown-item>
+                  <el-dropdown-item command="delete" divided>鍒犻櫎缁�</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>
+
+    <!-- 璁惧绠$悊寮圭獥 -->
+    <el-dialog
+      v-model="deviceDialogVisible"
+      :title="`璁惧缁勶細${currentGroup?.groupName} - 璁惧绠$悊`"
+      width="80%"
+      :close-on-click-modal="false"
+    >
+      <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)" />
+          </div>
+          <div class="dialog-buttons">
+            <el-button type="primary" @click="addDevices">娣诲姞璁惧</el-button>
+            <el-button type="danger" @click="removeDevices" :disabled="selectedDevicesInGroup.length === 0">
+              绉婚櫎璁惧
+            </el-button>
+          </div>
+        </div>
+        
+        <el-table
+          v-loading="deviceLoading"
+          :data="groupDeviceList"
+          @selection-change="handleDeviceSelectionChange"
+          border
+          stripe
+        >
+          <el-table-column type="selection" width="55" />
+          <el-table-column prop="deviceName" label="璁惧鍚嶇О" />
+          <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="璁惧鐘舵��">
+            <template #default="scope">
+              <el-tag :type="getDeviceStatusTag(scope.row.deviceStatus)" size="small">
+                {{ getDeviceStatusText(scope.row.deviceStatus) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      
+      <template #footer>
+        <el-button @click="deviceDialogVisible = false">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 缁熻璇︽儏寮圭獥 -->
+    <el-dialog
+      v-model="statisticsDialogVisible"
+      title="璁惧缁勭粺璁�"
+      width="60%"
+    >
+      <div class="statistics-content">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <h4>璁惧绫诲瀷鍒嗗竷</h4>
+            <div class="chart-container" style="height: 200px; background: #f5f7fa; display: flex; align-items: center; justify-content: center;">
+              <p>鍥捐〃鍔熻兘寮�鍙戜腑...</p>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <h4>璁惧鐘舵�佸垎甯�</h4>
+            <div class="chart-container" style="height: 200px; background: #f5f7fa; display: flex; align-items: center; justify-content: center;">
+              <p>鍥捐〃鍔熻兘寮�鍙戜腑...</p>
+            </div>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20" style="margin-top: 20px;">
+          <el-col :span="24">
+            <h4>璇︾粏淇℃伅</h4>
+            <el-descriptions :column="2" border>
+              <el-descriptions-item label="鎬昏澶囨暟">{{ currentGroup?.deviceCount || 0 }}</el-descriptions-item>
+              <el-descriptions-item label="鍦ㄧ嚎璁惧">{{ currentGroup?.onlineDeviceCount || 0 }}</el-descriptions-item>
+              <el-descriptions-item label="绂荤嚎璁惧">{{ (currentGroup?.deviceCount || 0) - (currentGroup?.onlineDeviceCount || 0) }}</el-descriptions-item>
+              <el-descriptions-item label="鍚敤鐜�">{{ getEnableRate() }}%</el-descriptions-item>
+            </el-descriptions>
+          </el-col>
+        </el-row>
+      </div>
+    </el-dialog>
+  </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 { deviceGroupApi, devicePlcApi } from '@/api/device/deviceManagement'
+
+// 鍝嶅簲寮忔暟鎹�
+const groupTable = ref(null)
+const tableLoading = ref(false)
+const groupList = ref([])
+const selectedGroups = ref([])
+const groupPlcLoading = ref(false)
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  groupType: '',
+  groupStatus: '',
+  keyword: ''
+})
+
+// 鍒嗛〉淇℃伅
+const pagination = reactive({
+  page: 1,
+  size: 10,
+  total: 0
+})
+
+// 璁惧绠$悊寮圭獥鐩稿叧
+const deviceDialogVisible = ref(false)
+const deviceLoading = ref(false)
+const currentGroup = ref(null)
+const groupDeviceList = ref([])
+const selectedDevicesInGroup = ref([])
+
+// 缁熻寮圭獥
+const statisticsDialogVisible = ref(false)
+
+// 浜嬩欢瀹氫箟
+const emit = defineEmits(['group-selected', 'refresh-statistics'])
+
+// 鏂规硶瀹氫箟
+const loadGroupList = async () => {
+  try {
+    tableLoading.value = true
+    const params = {
+      page: pagination.page,
+      size: pagination.size,
+      groupType: searchForm.groupType || undefined,
+      groupStatus: searchForm.groupStatus || undefined,
+      keyword: searchForm.keyword || undefined
+    }
+    
+    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 || []
+      pagination.total = response.data.total || response.data.totalElements || 0
+    } else {
+      groupList.value = []
+      pagination.total = 0
+    }
+  } catch (error) {
+    console.error('鍔犺浇璁惧缁勫垪琛ㄥけ璐�:', error)
+    ElMessage.error('鍔犺浇璁惧缁勫垪琛ㄥけ璐�: ' + (error.response?.data?.message || error.message))
+    groupList.value = []
+    pagination.total = 0
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+const handleSearch = () => {
+  pagination.page = 1
+  loadGroupList()
+}
+
+const resetSearch = () => {
+  searchForm.groupType = ''
+  searchForm.groupStatus = ''
+  searchForm.keyword = ''
+  pagination.page = 1
+  loadGroupList()
+}
+
+const handleSelectionChange = (selection) => {
+  selectedGroups.value = selection
+}
+
+const clearSelection = () => {
+  groupTable.value?.clearSelection()
+  selectedGroups.value = []
+}
+
+const groupPlcLabelMap = {
+  request: 'PLC璇锋眰',
+  report: 'PLC姹囨姤'
+}
+
+const handleGroupPlcOperation = async (groupId, operation) => {
+  if (!groupId) {
+    ElMessage.warning('璇峰厛閫夋嫨璁惧缁�')
+    return
+  }
+  groupPlcLoading.value = true
+  try {
+    let response
+    switch (operation) {
+      case 'request':
+        response = await devicePlcApi.triggerGroupRequest(groupId)
+        break
+      case 'report':
+        response = await devicePlcApi.triggerGroupReport(groupId)
+        break
+      default:
+        throw new Error('鏈煡鐨勮澶囩粍PLC鎿嶄綔')
+    }
+    const results = response?.data || []
+    const successCount = results.filter(item => item.success).length
+    const label = groupPlcLabelMap[operation] || 'PLC鎿嶄綔'
+    if (results.length === 0) {
+      ElMessage.info(`${label}鏈繑鍥炵粨鏋渀)
+    } else if (successCount === results.length) {
+      ElMessage.success(`${label}鎴愬姛锛�${successCount}/${results.length}锛塦)
+    } else {
+      ElMessage.warning(`${label}閮ㄥ垎鎴愬姛锛�${successCount}/${results.length}锛塦)
+    }
+  } catch (error) {
+    const label = groupPlcLabelMap[operation] || 'PLC鎿嶄綔'
+    console.error('璁惧缁凱LC鎿嶄綔澶辫触:', error)
+    ElMessage.error(`${label}澶辫触锛�${error.response?.data?.message || error.message}`)
+  } finally {
+    groupPlcLoading.value = false
+  }
+}
+
+const triggerGroupPlcRequest = (row) => handleGroupPlcOperation(row.id || row.groupId, 'request')
+const triggerGroupPlcReport = (row) => handleGroupPlcOperation(row.id || row.groupId, 'report')
+
+const viewGroupPlcStatus = async (row) => {
+  try {
+    const response = await devicePlcApi.getGroupStatus(row.id || row.groupId)
+    const statusList = response?.data || []
+    if (!statusList.length) {
+      ElMessage.info('鏈幏鍙栧埌PLC鐘舵�佹暟鎹�')
+      return
+    }
+    const htmlContent = statusList.map(item => {
+      const header = `${item.deviceName || item.deviceCode || item.deviceId}`
+      const body = JSON.stringify(item.data || {}, null, 2)
+      return `<div style="margin-bottom:12px"><strong>${header}</strong><pre style="margin:4px 0 0;max-height:260px;overflow:auto;">${body}</pre></div>`
+    }).join('')
+    await ElMessageBox.alert(htmlContent, `PLC鐘舵�� - ${row.groupName || row.groupCode}`, {
+      dangerouslyUseHTMLString: true,
+      confirmButtonText: '鍏抽棴'
+    })
+  } catch (error) {
+    console.error('鑾峰彇璁惧缁凱LC鐘舵�佸け璐�:', error)
+    ElMessage.error('鑾峰彇璁惧缁凱LC鐘舵�佸け璐�')
+  }
+}
+
+const handleStatusChange = async (row) => {
+  try {
+    const groupId = row.id || row.groupId
+    if (row.enabled) {
+      await deviceGroupApi.enable({ groupId })
+      ElMessage.success('璁惧缁勫惎鐢ㄦ垚鍔�')
+    } else {
+      await deviceGroupApi.disable({ groupId })
+      ElMessage.success('璁惧缁勭鐢ㄦ垚鍔�')
+    }
+    emit('refresh-statistics')
+    loadGroupList() // 鍒锋柊鍒楄〃
+  } catch (error) {
+    console.error('鏇存柊璁惧缁勭姸鎬佸け璐�:', error)
+    row.enabled = !row.enabled // 鎭㈠鐘舵��
+    ElMessage.error('鏇存柊璁惧缁勭姸鎬佸け璐�: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchEnable = async () => {
+  try {
+    const groupIds = selectedGroups.value.map(item => item.id || item.groupId)
+    await deviceGroupApi.batchEnable({ groupIds })
+    ElMessage.success(`鎴愬姛鍚敤 ${groupIds.length} 涓澶囩粍`)
+    clearSelection()
+    loadGroupList()
+    emit('refresh-statistics')
+  } catch (error) {
+    console.error('鎵归噺鍚敤澶辫触:', error)
+    ElMessage.error('鎵归噺鍚敤澶辫触: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchDisable = async () => {
+  try {
+    const groupIds = selectedGroups.value.map(item => item.id || item.groupId)
+    await deviceGroupApi.batchDisable({ groupIds })
+    ElMessage.success(`鎴愬姛绂佺敤 ${groupIds.length} 涓澶囩粍`)
+    clearSelection()
+    loadGroupList()
+    emit('refresh-statistics')
+  } catch (error) {
+    console.error('鎵归噺绂佺敤澶辫触:', error)
+    ElMessage.error('鎵归噺绂佺敤澶辫触: ' + (error.response?.data?.message || error.message))
+  }
+}
+
+const batchDelete = async () => {
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ら�変腑鐨� ${selectedGroups.value.length} 涓澶囩粍鍚楋紵姝ゆ搷浣滀笉鍙仮澶嶏紒`,
+      '鎵归噺鍒犻櫎纭',
+      {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+    
+    const groupIds = selectedGroups.value.map(item => item.id || item.groupId)
+    // 閫愪釜鍒犻櫎
+    for (const groupId of groupIds) {
+      await deviceGroupApi.delete({ groupId })
+    }
+    ElMessage.success(`鎴愬姛鍒犻櫎 ${groupIds.length} 涓澶囩粍`)
+    clearSelection()
+    loadGroupList()
+    emit('refresh-statistics')
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('鎵归噺鍒犻櫎澶辫触:', error)
+      ElMessage.error('鎵归噺鍒犻櫎澶辫触')
+    }
+  }
+}
+
+const editGroup = (row) => {
+  emit('group-selected', row)
+}
+
+const manageDevices = async (row) => {
+  currentGroup.value = row
+  deviceDialogVisible.value = true
+  await loadGroupDevices(row.id || row.groupId)
+}
+
+const loadGroupDevices = async (groupId) => {
+  try {
+    deviceLoading.value = true
+    const response = await deviceGroupApi.getGroupDevices(groupId)
+    if (response && response.data) {
+      groupDeviceList.value = response.data || []
+    } else {
+      groupDeviceList.value = []
+    }
+  } catch (error) {
+    console.error('鍔犺浇缁勮澶囧垪琛ㄥけ璐�:', error)
+    ElMessage.error('鍔犺浇缁勮澶囧垪琛ㄥけ璐�: ' + (error.response?.data?.message || error.message))
+    groupDeviceList.value = []
+  } finally {
+    deviceLoading.value = false
+  }
+}
+
+const viewStatistics = (row) => {
+  currentGroup.value = row
+  statisticsDialogVisible.value = true
+}
+
+const handleDeviceSelectionChange = (selection) => {
+  selectedDevicesInGroup.value = selection
+}
+
+const addDevices = () => {
+  // 娣诲姞璁惧鍒扮粍閫昏緫
+  ElMessage.info('娣诲姞璁惧鍔熻兘寮�鍙戜腑...')
+}
+
+const removeDevices = async () => {
+  try {
+    if (selectedDevicesInGroup.value.length === 0) {
+      ElMessage.warning('璇烽�夋嫨瑕佺Щ闄ょ殑璁惧')
+      return
+    }
+    
+    await ElMessageBox.confirm(
+      `纭畾瑕佷粠璁惧缁勪腑绉婚櫎閫変腑鐨� ${selectedDevicesInGroup.value.length} 涓澶囧悧锛焋,
+      '绉婚櫎璁惧纭'
+    )
+    
+    const deviceIds = selectedDevicesInGroup.value.map(item => item.id || item.deviceId)
+    // 鎵归噺绉婚櫎璁惧
+    await deviceGroupApi.batchRemoveDevicesFromGroup({
+      groupId: currentGroup.value.id || currentGroup.value.groupId,
+      deviceIds
+    })
+    ElMessage.success(`鎴愬姛绉婚櫎 ${deviceIds.length} 涓澶嘸)
+    loadGroupDevices(currentGroup.value.id || currentGroup.value.groupId)
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('绉婚櫎璁惧澶辫触:', error)
+      ElMessage.error('绉婚櫎璁惧澶辫触')
+    }
+  }
+}
+
+const handleCommand = async (command, row) => {
+  switch (command) {
+    case 'view':
+      // 鏌ョ湅璇︽儏閫昏緫
+      ElMessage.info('鏌ョ湅璇︽儏鍔熻兘寮�鍙戜腑...')
+      break
+    case 'copy':
+      // 澶嶅埗閰嶇疆閫昏緫
+      ElMessage.info('澶嶅埗閰嶇疆鍔熻兘寮�鍙戜腑...')
+      break
+    case 'export':
+      // 瀵煎嚭閰嶇疆閫昏緫
+      ElMessage.info('瀵煎嚭閰嶇疆鍔熻兘寮�鍙戜腑...')
+      break
+    case 'test':
+      // 娴嬭瘯杩炴帴閫昏緫
+      try {
+        const response = await deviceGroupApi.healthCheck({ groupId: row.id || row.groupId })
+        ElMessage.success('杩炴帴娴嬭瘯鎴愬姛')
+      } catch (error) {
+        ElMessage.error('杩炴帴娴嬭瘯澶辫触: ' + (error.response?.data?.message || error.message))
+      }
+      break
+    case 'plc-report':
+      await triggerGroupPlcReport(row)
+      break
+    case 'delete':
+      await ElMessageBox.confirm('纭畾瑕佸垹闄よ璁惧缁勫悧锛�', '鍒犻櫎纭', {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      })
+      await deviceGroupApi.delete({ groupId: row.id || row.groupId })
+      ElMessage.success('璁惧缁勫垹闄ゆ垚鍔�')
+      loadGroupList()
+      emit('refresh-statistics')
+      break
+  }
+}
+
+const handleSizeChange = (size) => {
+  pagination.size = size
+  pagination.page = 1
+  loadGroupList()
+}
+
+const handleCurrentChange = (page) => {
+  pagination.page = page
+  loadGroupList()
+}
+
+// 宸ュ叿鍑芥暟
+const getGroupTypeTag = (type) => {
+  const typeMap = {
+    '璁惧缁�': 'primary',
+    '绠$悊缁�': 'success',
+    '鐩戞帶缁�': 'warning',
+    '缁存姢缁�': 'info'
+  }
+  return typeMap[type] || 'info'
+}
+
+const getGroupStatusTag = (status) => {
+  const statusMap = {
+    'ENABLED': 'success',
+    'DISABLED': 'info'
+  }
+  return statusMap[status] || 'info'
+}
+
+const getGroupStatusText = (status) => {
+  const statusMap = {
+    'ENABLED': '鍚敤',
+    'DISABLED': '绂佺敤'
+  }
+  return statusMap[status] || status
+}
+
+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 getEnableRate = () => {
+  if (!currentGroup.value || !currentGroup.value.deviceCount) return 0
+  return Math.round((currentGroup.value.onlineDeviceCount / currentGroup.value.deviceCount) * 100)
+}
+
+// 鏆撮湶鏂规硶
+const refresh = () => {
+  loadGroupList()
+}
+
+defineExpose({
+  refresh
+})
+
+// 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
+onMounted(() => {
+  loadGroupList()
+})
+</script>
+
+<style scoped>
+.device-group-list {
+  padding: 20px;
+}
+
+.search-section {
+  margin-bottom: 20px;
+  padding: 16px;
+  background-color: #f5f7fa;
+  border-radius: 8px;
+}
+
+.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;
+}
+
+.device-management {
+  padding: 20px 0;
+}
+
+.dialog-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+  padding-bottom: 16px;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.device-stats {
+  display: flex;
+  gap: 40px;
+}
+
+.dialog-buttons {
+  display: flex;
+  gap: 12px;
+}
+
+.statistics-content {
+  padding: 20px 0;
+}
+
+.chart-container {
+  border: 1px solid #ebeef5;
+  border-radius: 8px;
+}
+
+: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;
+}
+
+:deep(.el-statistic .el-statistic__title) {
+  font-weight: normal;
+  color: #666;
+}
+
+:deep(.el-statistic .el-statistic__content) {
+  font-size: 24px;
+  font-weight: bold;
+  color: #409eff;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.8.0