Prechádzať zdrojové kódy

能耗监测模型改造

learshaw 5 mesiacov pred
rodič
commit
8b2c654695

+ 189 - 64
ems-ui-cloud/src/views/adapter/nhjc/index.vue

@@ -233,16 +233,22 @@
                         <div v-for="(channel, idx) in getDeviceChannels(props.row.deviceCode)" :key="idx">
                           <h5>{{ channel.name }} ({{ channel.points.length }}个测点)</h5>
                           <el-table :data="channel.points" size="mini" max-height="200">
-                            <el-table-column prop="name" label="测点名称"></el-table-column>
-                            <el-table-column prop="key" label="测点标识"></el-table-column>
-                            <el-table-column prop="value" label="当前值">
+                            <el-table-column prop="name" label="测点名称" min-width="180"></el-table-column>
+                            <el-table-column prop="key" label="测点标识" width="150"></el-table-column>
+                            <el-table-column prop="location" label="位置" min-width="120"></el-table-column>
+                            <el-table-column prop="desc" label="描述" min-width="120"></el-table-column>
+                            <el-table-column label="当前值" width="120">
                               <template slot-scope="scope">
-                                {{ scope.row.value || '-' }}
+                                <span>{{ scope.row.value || '-' }}</span>
+                                <span v-if="scope.row.unit" style="margin-left: 4px; color: #909399; font-size: 12px;">
+                                  {{ scope.row.unit }}
+                                </span>
                               </template>
                             </el-table-column>
-                            <el-table-column prop="updateTime" label="更新时间"></el-table-column>
+                            <el-table-column prop="updateTime" label="更新时间" width="160"></el-table-column>
                           </el-table>
                         </div>
+                        <el-empty v-if="getDeviceChannels(props.row.deviceCode).length === 0" description="暂无测点数据"></el-empty>
                       </el-tab-pane>
                     </el-tabs>
                   </div>
@@ -561,7 +567,7 @@
                   style="flex: 0 0 120px;">
                 </el-input-number>
                 <span style="color: #909399; font-size: 13px;">
-                  调节范围{{ abilitySliderMin }} - {{ abilitySliderMax }}
+                  调节范围:{{ abilitySliderMin }} - {{ abilitySliderMax }}
                 </span>
               </div>
             </div>
@@ -627,6 +633,9 @@ export default {
       },
       // 所有模型代码(系统 + 设备)
       allModelCodes: [],
+      // 子设备相关
+      subDeviceAttrs: {}, // 子设备属性缓存
+      subDeviceGatewayMap: {}, // 子设备到网关的映射
       // 调用日志查询
       callLogQuery: {
         dateRange: [],
@@ -831,6 +840,7 @@ export default {
           data: res.data
         }
 
+        // 如果是抄表能力,刷新设备数据
         if (ability.abilityKey === 'MeterReadingGw') {
           this.loadDevices()
         }
@@ -920,6 +930,7 @@ export default {
         this.$message.success('执行成功')
         this.abilityDialogVisible = false
 
+        // 如果是抄表相关能力,刷新数据
         if (this.abilityForm.abilityKey.toLowerCase().includes('meter') ||
           this.abilityForm.abilityKey.toLowerCase().includes('reading')) {
           if (this.currentObjType === 3) {
@@ -1014,6 +1025,7 @@ export default {
           detailTab: 'attrs'
         }))
 
+        // 收集设备模型
         const deviceModelCodes = new Set()
         this.deviceList.forEach(device => {
           if (device.deviceModel) {
@@ -1031,6 +1043,7 @@ export default {
         this.deviceStats.online = this.deviceList.filter(d => d.deviceStatus === 1).length
         this.deviceStats.offline = this.deviceList.filter(d => d.deviceStatus !== 1).length
 
+        // 按模型分组设备
         const devicesByModel = {}
         this.deviceList.forEach(device => {
           if (device.deviceModel) {
@@ -1041,9 +1054,14 @@ export default {
           }
         })
 
+        // 批量加载网关设备属性(包含subDev)
         for (const modelCode in devicesByModel) {
           await this.loadDeviceAttrsBatch(modelCode, devicesByModel[modelCode])
         }
+
+        // 加载所有子设备的属性
+        await this.loadAllSubDeviceAttrs()
+
       } catch (error) {
         this.$message.error('加载设备列表失败')
       } finally {
@@ -1051,19 +1069,18 @@ export default {
       }
     },
 
-    // 批量加载设备属性
+    // 批量加载网关设备属性
     async loadDeviceAttrsBatch(modelCode, devices) {
       try {
         const res = await getObjAttrBatch(2, modelCode)
         const batchData = res.data || {}
 
-        let totalPoints = 0
-
         devices.forEach(device => {
           const deviceCode = device.deviceCode
           const attrData = batchData[deviceCode]
 
           if (attrData) {
+            // 保存基础属性
             const baseAttrs = {}
             if (attrData.Base) {
               attrData.Base.forEach(attr => {
@@ -1071,80 +1088,144 @@ export default {
               })
             }
             this.deviceAttrs[deviceCode] = baseAttrs
-
-            const channels = []
-            if (attrData.Measure) {
-              attrData.Measure.forEach(attr => {
-                if (attr.attrKey.startsWith('interface')) {
-                  try {
-                    const points = JSON.parse(attr.attrValue || '[]')
-                    channels.push({
-                      name: attr.attrName,
-                      key: attr.attrKey,
-                      points: points
-                    })
-                    totalPoints += points.length
-                  } catch (e) {
-                    console.error(`解析设备 ${deviceCode} 的测点数据失败:`, e)
-                  }
-                }
-              })
-            }
-            this.deviceChannels[deviceCode] = channels
           }
         })
-
-        this.deviceStats.points += totalPoints
       } catch (error) {
         console.error(`批量加载模型 ${modelCode} 的设备属性失败:`, error)
-        for (const device of devices) {
-          await this.loadDeviceAttrs(device.deviceCode)
+      }
+    },
+
+    // 加载所有子设备属性
+    async loadAllSubDeviceAttrs() {
+      // 收集所有子设备信息
+      const subDevicesByModel = {} // { modelCode: [deviceCodes] }
+
+      this.deviceList.forEach(device => {
+        const subDevAttr = this.deviceAttrs[device.deviceCode]?.subDev
+        if (subDevAttr) {
+          try {
+            const subDevices = JSON.parse(subDevAttr)
+            subDevices.forEach(subDev => {
+              if (!subDevicesByModel[subDev.modelCode]) {
+                subDevicesByModel[subDev.modelCode] = []
+              }
+              subDevicesByModel[subDev.modelCode].push(subDev.deviceCode)
+
+              // 保存子设备与网关的关联关系
+              this.subDeviceGatewayMap[subDev.deviceCode] = device.deviceCode
+            })
+          } catch (e) {
+            console.error(`解析设备 ${device.deviceCode} 的subDev失败:`, e)
+          }
         }
+      })
+
+      // 批量查询各个模型的子设备属性
+      let totalPoints = 0
+      for (const modelCode in subDevicesByModel) {
+        const pointsCount = await this.loadSubDeviceAttrsBatch(modelCode)
+        totalPoints += pointsCount
       }
+
+      this.deviceStats.points = totalPoints
+
+      // 组织成通道结构供展示
+      this.organizeChannelsFromSubDevices()
     },
 
-    // 单个加载设备属性
-    async loadDeviceAttrs(deviceCode) {
+    // 批量加载子设备属性
+    async loadSubDeviceAttrsBatch(modelCode) {
       try {
-        const res = await getObjAttr(2, deviceCode)
-        const attrData = res.data
+        const res = await getObjAttrBatch(2, modelCode)
+        const batchData = res.data || {}
 
-        const baseAttrs = {}
-        if (attrData.Base) {
-          attrData.Base.forEach(attr => {
-            baseAttrs[attr.attrKey] = attr.attrValue
-          })
+        let pointsCount = 0
+
+        // 保存子设备属性
+        for (const deviceCode in batchData) {
+          const attrData = batchData[deviceCode]
+          this.subDeviceAttrs[deviceCode] = attrData
+          pointsCount++
         }
-        this.deviceAttrs[deviceCode] = baseAttrs
 
-        const channels = []
-        let pointsCount = 0
-        if (attrData.Measure) {
-          attrData.Measure.forEach(attr => {
-            if (attr.attrKey.startsWith('interface')) {
-              try {
-                const points = JSON.parse(attr.attrValue || '[]')
-                channels.push({
-                  name: attr.attrName,
-                  key: attr.attrKey,
-                  points: points
-                })
-                pointsCount += points.length
-              } catch (e) {
-                console.error(`解析测点数据失败:`, e)
+        return pointsCount
+      } catch (error) {
+        console.error(`批量加载子设备模型 ${modelCode} 的属性失败:`, error)
+        return 0
+      }
+    },
+
+    // 将子设备组织成通道结构
+    organizeChannelsFromSubDevices() {
+      this.deviceList.forEach(device => {
+        const deviceCode = device.deviceCode
+        const subDevAttr = this.deviceAttrs[deviceCode]?.subDev
+
+        if (!subDevAttr) {
+          this.deviceChannels[deviceCode] = []
+          return
+        }
+
+        try {
+          const subDevices = JSON.parse(subDevAttr)
+
+          // 按通道分组
+          const channelMap = {}
+
+          subDevices.forEach(subDev => {
+            const subDevCode = subDev.deviceCode
+            const subDevAttrData = this.subDeviceAttrs?.[subDevCode]
+
+            if (!subDevAttrData) return
+
+            // 获取通道信息
+            const interfaceAttr = subDevAttrData.Base?.find(attr => attr.attrKey === 'interface')
+            const channelName = interfaceAttr?.attrValue || '未知通道'
+
+            if (!channelMap[channelName]) {
+              channelMap[channelName] = {
+                name: this.formatChannelName(channelName),
+                key: channelName,
+                points: []
               }
             }
+
+            // 构建测点信息
+            const point = {
+              key: subDevCode,
+              name: subDevAttrData.Base?.find(attr => attr.attrKey === 'deviceName')?.attrValue || subDevCode,
+              value: subDevAttrData.Measure?.find(attr => attr.attrKey === 'value')?.attrValue || '-',
+              updateTime: subDevAttrData.Measure?.find(attr => attr.attrKey === 'value')?.updateTime || '-',
+              unit: subDevAttrData.Measure?.find(attr => attr.attrKey === 'value')?.attrUnit || '',
+              location: subDevAttrData.Base?.find(attr => attr.attrKey === 'location')?.attrValue || '-',
+              desc: subDevAttrData.Base?.find(attr => attr.attrKey === 'desc')?.attrValue || '-'
+            }
+
+            channelMap[channelName].points.push(point)
           })
-        }
 
-        if (!this.deviceChannels[deviceCode]) {
-          this.deviceStats.points += pointsCount
+          // 转换为数组并排序
+          const channels = Object.values(channelMap).sort((a, b) => {
+            return a.key.localeCompare(b.key)
+          })
+
+          this.deviceChannels[deviceCode] = channels
+
+        } catch (e) {
+          console.error(`组织设备 ${deviceCode} 的通道数据失败:`, e)
+          this.deviceChannels[deviceCode] = []
         }
+      })
+    },
 
-        this.deviceChannels[deviceCode] = channels
-      } catch (error) {
-        console.error(`加载设备 ${deviceCode} 属性失败:`, error)
+    // 格式化通道名称
+    formatChannelName(channelKey) {
+      // 将类似 "1003D1口" 转换为 "D1口通道"
+      const match = channelKey.match(/(\d+)(D\d+)口/)
+      if (match) {
+        return `${match[2]}口通道`
       }
+      return channelKey
     },
 
     // 获取设备属性
@@ -1157,6 +1238,48 @@ export default {
       return this.deviceChannels[deviceCode] || []
     },
 
+    // 单个加载设备属性(用于刷新)
+    async loadDeviceAttrs(deviceCode) {
+      try {
+        // 加载网关属性
+        const res = await getObjAttr(2, deviceCode)
+        const attrData = res.data
+
+        const baseAttrs = {}
+        if (attrData.Base) {
+          attrData.Base.forEach(attr => {
+            baseAttrs[attr.attrKey] = attr.attrValue
+          })
+        }
+        this.deviceAttrs[deviceCode] = baseAttrs
+
+        // 重新加载该网关的所有子设备
+        const subDevAttr = baseAttrs.subDev
+        if (subDevAttr) {
+          const subDevices = JSON.parse(subDevAttr)
+          const subDevicesByModel = {}
+
+          subDevices.forEach(subDev => {
+            if (!subDevicesByModel[subDev.modelCode]) {
+              subDevicesByModel[subDev.modelCode] = []
+            }
+            subDevicesByModel[subDev.modelCode].push(subDev.deviceCode)
+          })
+
+          // 批量查询子设备属性
+          for (const modelCode in subDevicesByModel) {
+            await this.loadSubDeviceAttrsBatch(modelCode)
+          }
+
+          // 重新组织该网关的通道数据
+          this.organizeChannelsFromSubDevices()
+        }
+
+      } catch (error) {
+        console.error(`加载设备 ${deviceCode} 属性失败:`, error)
+      }
+    },
+
     // 加载设备能力
     async handleAbilityLoad(visible, device) {
       if (visible && !this.deviceAbilities[device.deviceCode]) {
@@ -1191,6 +1314,7 @@ export default {
 
           this.$message.success(`${ability.abilityName}执行成功`)
 
+          // 如果是抄表相关能力,刷新测点数据
           if (ability.abilityKey.toLowerCase().includes('meter') ||
             ability.abilityKey.toLowerCase().includes('reading')) {
             await this.loadDeviceAttrs(device.deviceCode)
@@ -1217,6 +1341,7 @@ export default {
 
             this.$message.success(`${ability.abilityName}执行成功`)
 
+            // 如果是抄表相关能力,刷新测点数据
             if (ability.abilityKey.toLowerCase().includes('meter') ||
               ability.abilityKey.toLowerCase().includes('reading')) {
               await this.loadDeviceAttrs(device.deviceCode)

+ 93 - 45
ems-ui-cloud/src/views/basecfg/device/index.vue

@@ -543,6 +543,45 @@
                         <el-tabs v-model="props.row.detailTab">
                           <!-- 子设备属性标签页 -->
                           <el-tab-pane label="设备属性" name="attrs">
+                            <!-- 基础属性 -->
+                            <div v-if="getSubDeviceModelAttrs(props.row.deviceCode, 'Base').length > 0"
+                                 style="margin-top: 20px;"
+                            >
+                              <h5 class="attr-group-title">基础属性</h5>
+                              <el-descriptions :column="2" border size="small" class="sub-device-descriptions">
+                                <el-descriptions-item
+                                  v-for="attr in getSubDeviceModelAttrs(props.row.deviceCode, 'Base')"
+                                  :key="attr.attrKey"
+                                  :label="attr.attrName"
+                                >
+                                  <template v-if="attr.attrValueType === 'Enum'">
+                                    <el-tag size="small"
+                                            :type="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey) === '0' ? 'success' : 'danger'"
+                                    >
+                                      {{
+                                        getSubDeviceAttrEnumName(props.row.deviceCode, attr.attrKey, attr.valueEnums)
+                                      }}
+                                    </el-tag>
+                                  </template>
+                                  <template v-else-if="attr.attrKey === 'imageUrl'">
+                                    <el-image
+                                      v-if="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)"
+                                      style="width: 100px; height: 75px"
+                                      :src="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)"
+                                      :preview-src-list="[getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)]"
+                                      fit="cover"
+                                    >
+                                    </el-image>
+                                    <span v-else>-</span>
+                                  </template>
+                                  <template v-else>
+                                    {{ getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey) || '-' }}
+                                    <span v-if="attr.attrUnit">{{ attr.attrUnit }}</span>
+                                  </template>
+                                </el-descriptions-item>
+                              </el-descriptions>
+                            </div>
+
                             <!-- 状态属性 -->
                             <div v-if="getSubDeviceModelAttrs(props.row.deviceCode, 'State').length > 0">
                               <h5 class="attr-group-title">状态属性</h5>
@@ -586,45 +625,6 @@
                               </el-descriptions>
                             </div>
 
-                            <!-- 基础属性 -->
-                            <div v-if="getSubDeviceModelAttrs(props.row.deviceCode, 'Base').length > 0"
-                                 style="margin-top: 20px;"
-                            >
-                              <h5 class="attr-group-title">基础属性</h5>
-                              <el-descriptions :column="2" border size="small" class="sub-device-descriptions">
-                                <el-descriptions-item
-                                  v-for="attr in getSubDeviceModelAttrs(props.row.deviceCode, 'Base')"
-                                  :key="attr.attrKey"
-                                  :label="attr.attrName"
-                                >
-                                  <template v-if="attr.attrValueType === 'Enum'">
-                                    <el-tag size="small"
-                                            :type="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey) === '0' ? 'success' : 'danger'"
-                                    >
-                                      {{
-                                        getSubDeviceAttrEnumName(props.row.deviceCode, attr.attrKey, attr.valueEnums)
-                                      }}
-                                    </el-tag>
-                                  </template>
-                                  <template v-else-if="attr.attrKey === 'imageUrl'">
-                                    <el-image
-                                      v-if="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)"
-                                      style="width: 100px; height: 75px"
-                                      :src="getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)"
-                                      :preview-src-list="[getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey)]"
-                                      fit="cover"
-                                    >
-                                    </el-image>
-                                    <span v-else>-</span>
-                                  </template>
-                                  <template v-else>
-                                    {{ getSubDeviceAttrValue(props.row.deviceCode, attr.attrKey) || '-' }}
-                                    <span v-if="attr.attrUnit">{{ attr.attrUnit }}</span>
-                                  </template>
-                                </el-descriptions-item>
-                              </el-descriptions>
-                            </div>
-
                             <!-- 更新时间 -->
                             <div class="update-time" style="margin-top: 15px;">
                               <i class="el-icon-time"></i>
@@ -687,22 +687,22 @@
                     </template>
                   </el-table-column>
 
-                  <el-table-column  label="设备名称" min-width="80">
+                  <el-table-column  label="设备名称" min-width="150">
                     <template slot-scope="scope">
                       {{ getSubDeviceAttrValue(scope.row.deviceCode, 'deviceName') }}
                     </template>
                   </el-table-column>
 
-                  <el-table-column prop="deviceCode" label="设备代码" min-width="180"></el-table-column>
+                  <el-table-column prop="deviceCode" label="设备代码" min-width="120"></el-table-column>
 
                   <!-- 优化后的设备状态列 -->
                   <el-table-column label="设备状态" width="100" align="center">
                     <template slot-scope="scope">
                       <el-tag
                         size="small"
-                        :type="getEnumTagType(scope.row.deviceCode, 'deviceStatus', 'success', 'danger')"
+                        :type="getSubDeviceStatusType(scope.row.deviceCode)"
                       >
-                        {{ getSubDeviceAttrEnumDisplay(scope.row.deviceCode, 'deviceStatus') }}
+                        {{ getSubDeviceStatusDisplay(scope.row.deviceCode) }}
                       </el-tag>
                     </template>
                   </el-table-column>
@@ -1488,6 +1488,53 @@ export default {
     },
 
     /**
+     * 获取子设备状态显示文本(支持继承父设备状态)
+     * @param {String} deviceCode - 子设备代码
+     * @returns {String} 状态显示文本
+     */
+    getSubDeviceStatusDisplay(deviceCode) {
+      // 尝试获取子设备自己的 deviceStatus 属性
+      const subDeviceStatus = this.getSubDeviceAttrValue(deviceCode, 'deviceStatus')
+
+      if (subDeviceStatus !== null && subDeviceStatus !== undefined && subDeviceStatus !== '') {
+        // 子设备有自己的状态,使用枚举显示
+        return this.getSubDeviceAttrEnumDisplay(deviceCode, 'deviceStatus')
+      }
+
+      // 子设备没有状态属性,继承父设备状态
+      if (this.curRow && this.curRow.deviceStatus !== undefined) {
+        return this.curRow.deviceStatus === 1 ? '在线' : '离线'
+      }
+
+      // 兜底:都没有则显示未知
+      return '未知'
+    },
+
+    /**
+     * 获取子设备状态Tag类型(支持继承父设备状态)
+     * @param {String} deviceCode - 子设备代码
+     * @returns {String} Tag类型(success/danger/info)
+     */
+    getSubDeviceStatusType(deviceCode) {
+      // 尝试获取子设备自己的 deviceStatus 属性
+      const subDeviceStatus = this.getSubDeviceAttrValue(deviceCode, 'deviceStatus')
+
+      if (subDeviceStatus !== null && subDeviceStatus !== undefined && subDeviceStatus !== '') {
+        // 子设备有自己的状态,使用标准枚举判断
+        const normalizedValue = String(subDeviceStatus).replace(/\.0+$/, '')
+        return normalizedValue === '1' ? 'success' : 'danger'
+      }
+
+      // 子设备没有状态属性,继承父设备状态
+      if (this.curRow && this.curRow.deviceStatus !== undefined) {
+        return this.curRow.deviceStatus === 1 ? 'success' : 'danger'
+      }
+
+      // 兜底:都没有则显示灰色
+      return 'info'
+    },
+
+    /**
      * 根据枚举值获取图标类名
      * @param {String} deviceCode - 设备代码
      * @param {String} attrKey - 属性键
@@ -1534,8 +1581,9 @@ export default {
      */
     getOnlineSubDeviceCount() {
       return this.subDeviceList.filter(device => {
-        const deviceStatus = this.getSubDeviceAttrValue(device.deviceCode, 'deviceStatus')
-        return deviceStatus === '1'
+        // 获取子设备状态(包含继承逻辑)
+        const statusType = this.getSubDeviceStatusType(device.deviceCode)
+        return statusType === 'success'
       }).length
     },
 

+ 52 - 4
ems-ui-cloud/src/views/devmgr/device/index.vue

@@ -440,9 +440,9 @@
                     <template slot-scope="scope">
                       <el-tag
                         size="small"
-                        :type="getEnumTagType(scope.row.deviceCode, 'deviceStatus', 'success', 'danger')"
+                        :type="getSubDeviceStatusType(scope.row.deviceCode)"
                       >
-                        {{ getSubDeviceAttrEnumDisplay(scope.row.deviceCode, 'deviceStatus') }}
+                        {{ getSubDeviceStatusDisplay(scope.row.deviceCode) }}
                       </el-tag>
                     </template>
                   </el-table-column>
@@ -1047,6 +1047,53 @@ export default {
     },
 
     /**
+     * 获取子设备状态显示文本(支持继承父设备状态)
+     * @param {String} deviceCode - 子设备代码
+     * @returns {String} 状态显示文本
+     */
+    getSubDeviceStatusDisplay(deviceCode) {
+      // 尝试获取子设备自己的 deviceStatus 属性
+      const subDeviceStatus = this.getSubDeviceAttrValue(deviceCode, 'deviceStatus')
+
+      if (subDeviceStatus !== null && subDeviceStatus !== undefined && subDeviceStatus !== '') {
+        // 子设备有自己的状态,使用枚举显示
+        return this.getSubDeviceAttrEnumDisplay(deviceCode, 'deviceStatus')
+      }
+
+      // 子设备没有状态属性,继承父设备状态
+      if (this.curRow && this.curRow.deviceStatus !== undefined) {
+        return this.curRow.deviceStatus === 1 ? '在线' : '离线'
+      }
+
+      // 兜底:都没有则显示未知
+      return '未知'
+    },
+
+    /**
+     * 获取子设备状态Tag类型(支持继承父设备状态)
+     * @param {String} deviceCode - 子设备代码
+     * @returns {String} Tag类型(success/danger/info)
+     */
+    getSubDeviceStatusType(deviceCode) {
+      // 尝试获取子设备自己的 deviceStatus 属性
+      const subDeviceStatus = this.getSubDeviceAttrValue(deviceCode, 'deviceStatus')
+
+      if (subDeviceStatus !== null && subDeviceStatus !== undefined && subDeviceStatus !== '') {
+        // 子设备有自己的状态,使用标准枚举判断
+        const normalizedValue = String(subDeviceStatus).replace(/\.0+$/, '')
+        return normalizedValue === '1' ? 'success' : 'danger'
+      }
+
+      // 子设备没有状态属性,继承父设备状态
+      if (this.curRow && this.curRow.deviceStatus !== undefined) {
+        return this.curRow.deviceStatus === 1 ? 'success' : 'danger'
+      }
+
+      // 兜底:都没有则显示灰色
+      return 'info'
+    },
+
+    /**
      * 获取枚举值的显示名称
      * @param {String} deviceCode - 设备代码
      * @param {String} attrKey - 属性键
@@ -1163,8 +1210,9 @@ export default {
 
     getOnlineSubDeviceCount() {
       return this.subDeviceList.filter(device => {
-        const deviceStatus = this.getSubDeviceAttrValue(device.deviceCode, 'deviceStatus')
-        return deviceStatus === '1'
+        // 获取子设备状态(包含继承逻辑)
+        const statusType = this.getSubDeviceStatusType(device.deviceCode)
+        return statusType === 'success'
       }).length
     },