瀏覽代碼

智慧照明对接

learshaw 1 周之前
父節點
當前提交
cf3a378858

+ 12 - 0
ems-ui-cloud/src/api/basecfg/objAttribute.js

@@ -26,6 +26,18 @@ export function getObjAttr(objType, objCode) {
   })
 }
 
+// 查询能源对象属性全部列表
+export function getObjAttrBatch(objType, modelCode) {
+  return request({
+    url: '/ems/object/attr/getObjAttrBatch',
+    method: 'get',
+    params: {
+      objType: objType,
+      modelCode: modelCode
+    }
+  })
+}
+
 // 新增能源对象属性
 export function addAttr(data) {
   return request({

二進制
ems-ui-cloud/src/assets/images/ems-background.jpg


+ 521 - 80
ems-ui-cloud/src/views/adapter/nhjc/index.vue

@@ -107,20 +107,36 @@
           </div>
         </el-tab-pane>
 
-        <!-- 系统能力标签页 -->
+        <!-- 系统能力标签页 - 改进支持新的参数定义 -->
         <el-tab-pane label="系统能力" name="abilities">
           <div class="tab-content">
             <el-table :data="systemAbilities" border stripe v-loading="abilityLoading">
               <el-table-column prop="abilityName" label="能力名称" width="200"></el-table-column>
               <el-table-column prop="abilityKey" label="能力标识" width="180"></el-table-column>
               <el-table-column prop="abilityDesc" label="能力描述"></el-table-column>
-              <el-table-column label="操作" width="150" align="center">
+              <el-table-column label="操作" width="200" align="center">
                 <template slot-scope="scope">
+                  <!-- 根据参数类型展示不同的操作界面 -->
+                  <div v-if="getParamType(scope.row.paramDefinition) === 'Options'" style="display: inline-block;">
+                    <!-- Options类型:显示多个按钮 -->
+                    <el-button-group>
+                      <el-button
+                        v-for="option in parseOptions(scope.row.paramDefinition)"
+                        :key="option.value"
+                        size="mini"
+                        type="primary"
+                        @click="executeSystemAbilityWithParam(scope.row, option.value)"
+                        :loading="scope.row.executing && scope.row.executingValue === option.value">
+                        {{ option.key }}
+                      </el-button>
+                    </el-button-group>
+                  </div>
                   <el-button
+                    v-else
                     type="primary"
                     size="mini"
                     icon="el-icon-caret-right"
-                    @click="executeAbility(scope.row)"
+                    @click="handleSystemAbilityClick(scope.row)"
                     :loading="scope.row.executing">
                     执行
                   </el-button>
@@ -222,7 +238,7 @@
                   </el-tag>
                 </template>
               </el-table-column>
-              <el-table-column label="操作" width="100" align="center">
+              <el-table-column label="操作" width="120" align="center">
                 <template slot-scope="scope">
                   <el-dropdown
                     trigger="click"
@@ -232,15 +248,34 @@
                       操作<i class="el-icon-arrow-down el-icon--right"></i>
                     </el-button>
                     <el-dropdown-menu slot="dropdown">
-                      <el-dropdown-item
-                        v-for="ability in deviceAbilities[scope.row.deviceCode] || []"
-                        :key="ability.abilityKey"
-                        :command="{device: scope.row, ability: ability}">
-                        {{ ability.abilityName }}
-                      </el-dropdown-item>
-                      <el-dropdown-item
-                        v-if="!deviceAbilities[scope.row.deviceCode] || deviceAbilities[scope.row.deviceCode].length === 0"
-                        disabled>
+                      <template v-if="deviceAbilities[scope.row.deviceCode] && deviceAbilities[scope.row.deviceCode].length > 0">
+                        <template v-for="ability in deviceAbilities[scope.row.deviceCode]">
+                          <!-- Options类型:显示子菜单 -->
+                          <el-submenu
+                            v-if="getParamType(ability.paramDefinition) === 'Options'"
+                            :key="ability.abilityKey"
+                            :index="ability.abilityKey">
+                            <template slot="title">
+                              {{ ability.abilityName }}
+                              <i class="el-icon-arrow-right" style="float: right;"></i>
+                            </template>
+                            <el-dropdown-item
+                              v-for="option in parseOptions(ability.paramDefinition)"
+                              :key="option.value"
+                              :command="{device: scope.row, ability: ability, paramValue: option.value}">
+                              {{ option.key }}
+                            </el-dropdown-item>
+                          </el-submenu>
+                          <!-- 其他类型:普通菜单项 -->
+                          <el-dropdown-item
+                            v-else
+                            :key="ability.abilityKey"
+                            :command="{device: scope.row, ability: ability}">
+                            {{ ability.abilityName }}
+                          </el-dropdown-item>
+                        </template>
+                      </template>
+                      <el-dropdown-item v-else disabled>
                         无可用操作
                       </el-dropdown-item>
                     </el-dropdown-menu>
@@ -425,13 +460,77 @@
         </div>
       </div>
     </el-dialog>
+
+    <!-- 新增:能力执行弹窗(支持Slider和Input类型) -->
+    <el-dialog :title="abilityDialogTitle" :visible.sync="abilityDialogVisible" width="500px" append-to-body :close-on-click-modal="false">
+      <el-form ref="abilityForm" :model="abilityForm" label-width="100px">
+        <el-form-item label="能力名称">
+          <el-input v-model="abilityForm.abilityName" disabled></el-input>
+        </el-form-item>
+        <el-form-item label="能力描述">
+          <el-input type="textarea" v-model="abilityForm.abilityDesc" disabled :rows="2"></el-input>
+        </el-form-item>
+
+        <!-- 根据参数类型显示不同的输入组件 -->
+        <el-form-item label="参数设置" v-if="abilityParamType">
+          <!-- Slider类型:显示滑块 -->
+          <template v-if="abilityParamType === 'Slider'">
+            <div style="width: 100%;">
+              <div style="padding: 0 10px;">
+                <el-slider
+                  v-model="abilitySliderValue"
+                  :min="abilitySliderMin"
+                  :max="abilitySliderMax"
+                  :show-tooltip="true"
+                  :format-tooltip="formatTooltip">
+                </el-slider>
+              </div>
+
+              <div style="display: flex; justify-content: space-between; padding: 0 10px; margin-top: -5px; margin-bottom: 20px;">
+                <span style="font-size: 12px; color: #909399;">{{ abilitySliderMin }}%</span>
+                <span style="font-size: 12px; color: #909399;">{{ Math.floor((abilitySliderMin + abilitySliderMax) / 2) }}%</span>
+                <span style="font-size: 12px; color: #909399;">{{ abilitySliderMax }}%</span>
+              </div>
+
+              <div style="display: flex; align-items: center; gap: 15px;">
+                <el-input-number
+                  v-model="abilitySliderValue"
+                  :min="abilitySliderMin"
+                  :max="abilitySliderMax"
+                  :step="1"
+                  :precision="0"
+                  controls-position="right"
+                  style="flex: 0 0 120px;">
+                </el-input-number>
+                <span style="color: #909399; font-size: 13px;">
+                  调节范围:{{ abilitySliderMin }} - {{ abilitySliderMax }}
+                </span>
+              </div>
+            </div>
+          </template>
+
+          <!-- Input类型:显示输入框 -->
+          <template v-else-if="abilityParamType === 'Input'">
+            <el-input
+              v-model="abilityInputValue"
+              placeholder="请输入参数值"
+              clearable>
+            </el-input>
+          </template>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="abilityDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="executeAbilityWithParam" :loading="abilityExecuting">执行</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { getSubsystemByCode } from '@/api/adapter/subsystem'
 import { getModelByCode } from '@/api/basecfg/objModel'
-import { getObjAttr } from '@/api/basecfg/objAttribute'
+import { getObjAttr, getObjAttrBatch } from '@/api/basecfg/objAttribute'
 import { listcallAbility } from '@/api/basecfg/objAbility'
 import { getByCondition } from '@/api/device/device'
 import { listCallLog, listEventLog, getCallLog, getEventLog } from '@/api/basecfg/objLog'
@@ -506,7 +605,25 @@ export default {
         type: 'success',
         message: '',
         data: null
-      }
+      },
+      // 新增:能力执行相关
+      abilityDialogVisible: false,
+      abilityDialogTitle: '能力执行',
+      abilityForm: {
+        abilityName: '',
+        abilityKey: '',
+        abilityDesc: '',
+        paramDefinition: null,
+        modelCode: ''
+      },
+      abilityParamType: null,
+      abilitySliderValue: 50,
+      abilitySliderMin: 0,
+      abilitySliderMax: 100,
+      abilityInputValue: '',
+      abilityExecuting: false,
+      currentDevice: null,
+      currentObjType: null  // 1:设施, 2:设备, 3:系统
     }
   },
   created() {
@@ -514,6 +631,238 @@ export default {
     this.initDateRange()
   },
   methods: {
+    // 新增:格式化滑块tooltip
+    formatTooltip(val) {
+      return `${val}%`
+    },
+
+    // 新增:解析参数定义类型
+    getParamType(paramDefinition) {
+      if (!paramDefinition) return null
+      try {
+        const def = JSON.parse(paramDefinition)
+        return def.type
+      } catch (e) {
+        return null
+      }
+    },
+
+    // 新增:获取参数类型名称
+    getParamTypeName(paramDefinition) {
+      const type = this.getParamType(paramDefinition)
+      const typeNames = {
+        'Options': '选项型',
+        'Slider': '滑块型',
+        'Input': '输入型'
+      }
+      return typeNames[type] || '无参数'
+    },
+
+    // 新增:获取参数类型标签样式
+    getParamTypeTagType(paramDefinition) {
+      const type = this.getParamType(paramDefinition)
+      const typeStyles = {
+        'Options': 'primary',
+        'Slider': 'success',
+        'Input': 'warning'
+      }
+      return typeStyles[type] || 'info'
+    },
+
+    // 新增:解析Options类型的选项
+    parseOptions(paramDefinition) {
+      try {
+        const def = JSON.parse(paramDefinition)
+        if (def.type === 'Options' && def.list) {
+          return def.list
+        }
+      } catch (e) {
+        console.error('解析Options失败:', e)
+      }
+      return []
+    },
+
+    // 新增:处理系统能力点击
+    handleSystemAbilityClick(ability) {
+      const paramType = this.getParamType(ability.paramDefinition)
+
+      if (paramType === 'Slider') {
+        this.showSliderDialog(ability, this.systemInfo, 3)
+      } else if (paramType === 'Input') {
+        this.showInputDialog(ability, this.systemInfo, 3)
+      } else {
+        // 无参数:直接执行
+        this.executeSystemAbility(ability)
+      }
+    },
+
+    // 新增:执行系统能力(带Options参数)
+    executeSystemAbilityWithParam(ability, paramValue) {
+      ability.executing = true
+      ability.executingValue = paramValue
+
+      listcallAbility({
+        objCode: this.systemCode,
+        objType: 3,
+        modelCode: this.systemInfo.modelCode,
+        abilityKey: ability.abilityKey,
+        abilityParam: paramValue
+      }).then(res => {
+        this.executeDialog = {
+          visible: true,
+          title: '执行结果 - ' + ability.abilityName,
+          status: '执行成功',
+          type: 'success',
+          message: '系统能力执行完成',
+          data: res.data
+        }
+      }).catch(error => {
+        this.executeDialog = {
+          visible: true,
+          title: '执行结果 - ' + ability.abilityName,
+          status: '执行失败',
+          type: 'error',
+          message: error.message,
+          data: null
+        }
+      }).finally(() => {
+        ability.executing = false
+        ability.executingValue = null
+      })
+    },
+
+    // 修改:执行系统能力(无参数)
+    executeSystemAbility(ability) {
+      ability.executing = true
+
+      listcallAbility({
+        objCode: this.systemCode,
+        objType: 3,
+        modelCode: this.systemInfo.modelCode,
+        abilityKey: ability.abilityKey,
+        abilityParam: null
+      }).then(res => {
+        this.executeDialog = {
+          visible: true,
+          title: '执行结果 - ' + ability.abilityName,
+          status: '执行成功',
+          type: 'success',
+          message: '系统能力执行完成',
+          data: res.data
+        }
+
+        // 刷新设备数据
+        if (ability.abilityKey === 'MeterReadingGw') {
+          this.loadDevices()
+        }
+      }).catch(error => {
+        this.executeDialog = {
+          visible: true,
+          title: '执行结果 - ' + ability.abilityName,
+          status: '执行失败',
+          type: 'error',
+          message: error.message,
+          data: null
+        }
+      }).finally(() => {
+        ability.executing = false
+      })
+    },
+
+    // 新增:显示滑块弹窗
+    showSliderDialog(ability, obj, objType) {
+      this.currentDevice = obj
+      this.currentObjType = objType
+      this.abilityForm = { ...ability }
+
+      // 根据对象类型设置标题
+      const objName = objType === 3 ? this.systemInfo.systemName : obj.deviceName
+      this.abilityDialogTitle = `${ability.abilityName} - ${objName}`
+      this.abilityParamType = 'Slider'
+
+      try {
+        const def = JSON.parse(ability.paramDefinition)
+        this.abilitySliderMin = def.min || 0
+        this.abilitySliderMax = def.max || 100
+        this.abilitySliderValue = Math.floor((this.abilitySliderMin + this.abilitySliderMax) / 2)
+      } catch (e) {
+        console.error('解析Slider参数失败:', e)
+        this.abilitySliderMin = 0
+        this.abilitySliderMax = 100
+        this.abilitySliderValue = 50
+      }
+
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:显示输入弹窗
+    showInputDialog(ability, obj, objType) {
+      this.currentDevice = obj
+      this.currentObjType = objType
+      this.abilityForm = { ...ability }
+
+      // 根据对象类型设置标题
+      const objName = objType === 3 ? this.systemInfo.systemName : obj.deviceName
+      this.abilityDialogTitle = `${ability.abilityName} - ${objName}`
+      this.abilityParamType = 'Input'
+      this.abilityInputValue = ''
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:执行带参数的能力
+    executeAbilityWithParam() {
+      let paramValue = null
+
+      if (this.abilityParamType === 'Slider') {
+        paramValue = String(this.abilitySliderValue)
+      } else if (this.abilityParamType === 'Input') {
+        if (!this.abilityInputValue) {
+          this.$message.warning('请输入参数值')
+          return
+        }
+        paramValue = this.abilityInputValue
+      }
+
+      this.abilityExecuting = true
+
+      // 构建调用参数
+      const callParams = {
+        objType: this.currentObjType,
+        abilityKey: this.abilityForm.abilityKey,
+        abilityParam: paramValue
+      }
+
+      // 根据对象类型设置不同的参数
+      if (this.currentObjType === 3) {
+        // 系统
+        callParams.objCode = this.systemCode
+        callParams.modelCode = this.systemInfo.modelCode
+      } else if (this.currentObjType === 2) {
+        // 设备
+        callParams.objCode = this.currentDevice.deviceCode
+        callParams.modelCode = this.currentDevice.deviceModel
+      }
+
+      listcallAbility(callParams).then(response => {
+        this.$message.success('执行成功')
+        this.abilityDialogVisible = false
+
+        // 如果是抄表相关能力,重新加载数据
+        if (this.abilityForm.abilityKey.toLowerCase().includes('meter') ||
+          this.abilityForm.abilityKey.toLowerCase().includes('reading')) {
+          if (this.currentObjType === 3) {
+            this.loadDevices()
+          } else if (this.currentObjType === 2) {
+            this.loadDeviceAttrs(this.currentDevice.deviceCode)
+          }
+        }
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        this.abilityExecuting = false
+      })
+    },
+
     // 初始化时间范围(默认今天)
     initDateRange() {
       const now = new Date()
@@ -557,10 +906,11 @@ export default {
           const modelRes = await getModelByCode(this.systemInfo.modelCode)
           const modelData = modelRes.data
 
-          // 获取能力列表
+          // 获取能力列表,添加执行状态
           this.systemAbilities = (modelData.abilityList || []).map(item => ({
             ...item,
-            executing: false
+            executing: false,
+            executingValue: null  // 用于Options类型按钮的加载状态
           }))
         }
 
@@ -584,7 +934,7 @@ export default {
       }
     },
 
-    // 加载设备列表
+    // 修改 loadDevices 方法
     async loadDevices() {
       this.deviceLoading = true
       try {
@@ -620,9 +970,20 @@ export default {
         this.deviceStats.online = this.deviceList.filter(d => d.deviceStatus === 1).length
         this.deviceStats.offline = this.deviceList.filter(d => d.deviceStatus !== 1).length
 
-        // 加载每个设备的属性
-        for (const device of this.deviceList) {
-          await this.loadDeviceAttrs(device.deviceCode)
+        // 批量加载设备属性(按设备模型分组)
+        const devicesByModel = {}
+        this.deviceList.forEach(device => {
+          if (device.deviceModel) {
+            if (!devicesByModel[device.deviceModel]) {
+              devicesByModel[device.deviceModel] = []
+            }
+            devicesByModel[device.deviceModel].push(device)
+          }
+        })
+
+        // 对每个模型的设备批量获取属性
+        for (const modelCode in devicesByModel) {
+          await this.loadDeviceAttrsBatch(modelCode, devicesByModel[modelCode])
         }
       } catch (error) {
         this.$message.error('加载设备列表失败')
@@ -631,7 +992,66 @@ 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 => {
+                baseAttrs[attr.attrKey] = attr.attrValue
+              })
+            }
+            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 loadDeviceAttrs(deviceCode) {
       try {
         const res = await getObjAttr(2, deviceCode)
@@ -648,6 +1068,7 @@ export default {
 
         // 处理测点通道信息
         const channels = []
+        let pointsCount = 0
         if (attrData.Measure) {
           attrData.Measure.forEach(attr => {
             if (attr.attrKey.startsWith('interface')) {
@@ -658,17 +1079,22 @@ export default {
                   key: attr.attrKey,
                   points: points
                 })
-                // 统计测点数
-                this.deviceStats.points += points.length
+                pointsCount += points.length
               } catch (e) {
-                // 忽略解析错误
+                console.error(`解析测点数据失败:`, e)
               }
             }
           })
         }
+
+        // 更新测点统计(仅在首次加载时)
+        if (!this.deviceChannels[deviceCode]) {
+          this.deviceStats.points += pointsCount
+        }
+
         this.deviceChannels[deviceCode] = channels
       } catch (error) {
-        // 忽略加载错误
+        console.error(`加载设备 ${deviceCode} 属性失败:`, error)
       }
     },
 
@@ -702,66 +1128,61 @@ export default {
       }
     },
 
-    // 处理设备操作命令
+    // 处理设备操作命令(改进以支持新参数)
     async handleDeviceCommand(command) {
-      const { device, ability } = command
-      try {
-        await listcallAbility({
-          objCode: device.deviceCode,
-          objType: 2,
-          modelCode: device.deviceModel,
-          abilityKey: ability.abilityKey,
-          abilityParam: ability.abilityParam
-        })
-
-        this.$message.success(`${ability.abilityName}执行成功`)
-
-        // 如果是抄表相关能力,重新加载设备属性
-        if (ability.abilityKey.toLowerCase().includes('meter') ||
-          ability.abilityKey.toLowerCase().includes('reading')) {
-          await this.loadDeviceAttrs(device.deviceCode)
-        }
-      } catch (error) {
-        this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
-      }
-    },
+      const { device, ability, paramValue } = command
 
-    // 执行系统能力
-    async executeAbility(ability) {
-      ability.executing = true
-      try {
-        const res = await listcallAbility({
-          objCode: this.systemCode,
-          objType: 3,
-          modelCode: this.systemInfo.modelCode,
-          abilityKey: ability.abilityKey,
-          abilityParam: ability.abilityParam
-        })
+      // 如果直接提供了参数值(Options类型),直接执行
+      if (paramValue !== undefined) {
+        try {
+          await listcallAbility({
+            objCode: device.deviceCode,
+            objType: 2,
+            modelCode: device.deviceModel,
+            abilityKey: ability.abilityKey,
+            abilityParam: paramValue
+          })
 
-        this.executeDialog = {
-          visible: true,
-          title: '执行结果 - ' + ability.abilityName,
-          status: '执行成功',
-          type: 'success',
-          message: '系统能力执行完成',
-          data: res.data
-        }
+          this.$message.success(`${ability.abilityName}执行成功`)
 
-        // 刷新设备数据
-        if (ability.abilityKey === 'MeterReadingGw') {
-          this.loadDevices()
+          // 如果是抄表相关能力,重新加载设备属性
+          if (ability.abilityKey.toLowerCase().includes('meter') ||
+            ability.abilityKey.toLowerCase().includes('reading')) {
+            await this.loadDeviceAttrs(device.deviceCode)
+          }
+        } catch (error) {
+          this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
         }
-      } catch (error) {
-        this.executeDialog = {
-          visible: true,
-          title: '执行结果 - ' + ability.abilityName,
-          status: '执行失败',
-          type: 'error',
-          message: error.message,
-          data: null
+      } else {
+        // 根据参数类型处理
+        const paramType = this.getParamType(ability.paramDefinition)
+
+        if (paramType === 'Slider') {
+          this.showSliderDialog(ability, device, 2)
+        } else if (paramType === 'Input') {
+          this.showInputDialog(ability, device, 2)
+        } else {
+          // 无参数:直接执行
+          try {
+            await listcallAbility({
+              objCode: device.deviceCode,
+              objType: 2,
+              modelCode: device.deviceModel,
+              abilityKey: ability.abilityKey,
+              abilityParam: null
+            })
+
+            this.$message.success(`${ability.abilityName}执行成功`)
+
+            // 如果是抄表相关能力,重新加载设备属性
+            if (ability.abilityKey.toLowerCase().includes('meter') ||
+              ability.abilityKey.toLowerCase().includes('reading')) {
+              await this.loadDeviceAttrs(device.deviceCode)
+            }
+          } catch (error) {
+            this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
+          }
         }
-      } finally {
-        ability.executing = false
       }
     },
 
@@ -1055,4 +1476,24 @@ export default {
     }
   }
 }
+
+.dialog-footer {
+  text-align: right;
+}
+
+// 下拉菜单中的子菜单样式
+::v-deep .el-dropdown-menu {
+  .el-submenu__title {
+    padding-right: 40px !important;
+
+    &:hover {
+      background-color: #f5f7fa;
+    }
+  }
+
+  .el-submenu__icon-arrow {
+    position: absolute;
+    right: 20px;
+  }
+}
 </style>

文件差異過大導致無法顯示
+ 806 - 116
ems-ui-cloud/src/views/adapter/zm/index.vue


+ 274 - 70
ems-ui-cloud/src/views/basecfg/device/index.vue

@@ -52,11 +52,6 @@
                 <el-option v-for="item in subCategoryOptions" :label="item.name" :value="item.code" :key="item.code"/>
               </el-select>
             </el-form-item>
-            <el-form-item label="归属设施" prop="refFacs">
-              <el-select v-model="queryParams.refFacs" placeholder="请选择">
-                <el-option v-for="item in facsOptions" :label="item.facsName" :value="item.facsCode" :key="item.facsCode"/>
-              </el-select>
-            </el-form-item>
             <el-form-item label="归属系统" prop="subsystemCode">
               <el-select v-model="queryParams.subsystemCode" placeholder="请选择">
                 <el-option v-for="item in subsystemOptions" :label="item.systemName" :value="item.systemCode" :key="item.systemCode"/>
@@ -93,7 +88,6 @@
             <el-table-column type="selection" width="55" align="center"/>
             <el-table-column label="设备名称" align="left" prop="deviceName"/>
             <el-table-column label="安装位置" align="left" prop="areaPath" width="280px"/>
-            <el-table-column label="归属设施" align="center" prop="refFacsName"/>
             <el-table-column label="设备分类" align="center" prop="deviceCategoryName"/>
             <el-table-column label="归属系统" align="center" prop="subsystemName"/>
             <el-table-column label="设备状态" align="center" prop="deviceStatus">
@@ -412,7 +406,7 @@
               </el-card>
             </div>
 
-            <!-- 设备能力 -->
+            <!-- 设备能力 - 改进支持新的参数定义 -->
             <div v-if="activeTab === 'ability'">
               <el-card class="box-card">
                 <el-table :data="abilityData" style="width: 100%">
@@ -420,13 +414,36 @@
                   <el-table-column label="能力名称" prop="abilityName"></el-table-column>
                   <el-table-column label="能力标识" prop="abilityKey"></el-table-column>
                   <el-table-column label="能力描述" prop="abilityDesc"></el-table-column>
-                  <el-table-column label="操作" align="center" width="150">
+                  <el-table-column label="参数类型" align="center" width="120">
+                    <template slot-scope="scope">
+                      <el-tag size="small" :type="getParamTypeTagType(scope.row.paramDefinition)">
+                        {{ getParamTypeName(scope.row.paramDefinition) }}
+                      </el-tag>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" align="center" width="200">
                     <template slot-scope="scope">
+                      <!-- 根据参数类型展示不同的操作界面 -->
+                      <div v-if="getParamType(scope.row.paramDefinition) === 'Options'" style="display: inline-block;">
+                        <!-- Options类型:显示多个按钮 -->
+                        <el-button-group>
+                          <el-button
+                            v-for="option in parseOptions(scope.row.paramDefinition)"
+                            :key="option.value"
+                            size="mini"
+                            type="primary"
+                            @click="handleOperateWithParam(scope.row, option.value)"
+                            :loading="scope.row.executing && scope.row.executingValue === option.value">
+                            {{ option.key }}
+                          </el-button>
+                        </el-button-group>
+                      </div>
                       <el-button
+                        v-else
                         type="primary"
                         size="mini"
                         icon="el-icon-caret-right"
-                        @click="handleAbilityEdit(scope.row)"
+                        @click="handleOperateClick(scope.row)"
                         :loading="scope.row.executing">
                         执行
                       </el-button>
@@ -581,8 +598,8 @@
           </el-table>
         </el-dialog>
 
-        <!-- 能力执行弹窗 -->
-        <el-dialog title="设备能力执行" :visible.sync="abilityDialogVisible" width="50%" append-to-body>
+        <!-- 新增:能力执行弹窗(支持Slider和Input类型) -->
+        <el-dialog :title="abilityDialogTitle" :visible.sync="abilityDialogVisible" width="500px" append-to-body :close-on-click-modal="false">
           <el-form ref="abilityForm" :model="abilityForm" label-width="100px">
             <el-form-item label="能力名称">
               <el-input v-model="abilityForm.abilityName" disabled></el-input>
@@ -591,15 +608,63 @@
               <el-input v-model="abilityForm.abilityKey" disabled></el-input>
             </el-form-item>
             <el-form-item label="能力描述">
-              <el-input type="textarea" v-model="abilityForm.abilityDesc" disabled></el-input>
+              <el-input type="textarea" v-model="abilityForm.abilityDesc" disabled :rows="2"></el-input>
             </el-form-item>
-            <el-form-item label="能力参数">
-              <el-input type="textarea" v-model="abilityForm.abilityParam" placeholder="请输入能力参数" :rows="4"></el-input>
+
+            <!-- 根据参数类型显示不同的输入组件 -->
+            <el-form-item label="参数设置" v-if="abilityParamType">
+              <!-- Slider类型:显示滑块 -->
+              <template v-if="abilityParamType === 'Slider'">
+                <div style="width: 100%;">
+                  <!-- 第一行:纯滑块 -->
+                  <div style="padding: 0 10px;">
+                    <el-slider
+                      v-model="abilitySliderValue"
+                      :min="abilitySliderMin"
+                      :max="abilitySliderMax"
+                      :show-tooltip="true"
+                      :format-tooltip="formatTooltip">
+                    </el-slider>
+                  </div>
+
+                  <!-- 标记点文字 -->
+                  <div style="display: flex; justify-content: space-between; padding: 0 10px; margin-top: -5px; margin-bottom: 20px;">
+                    <span style="font-size: 12px; color: #909399;">{{ abilitySliderMin }}%</span>
+                    <span style="font-size: 12px; color: #909399;">{{ Math.floor((abilitySliderMin + abilitySliderMax) / 2) }}%</span>
+                    <span style="font-size: 12px; color: #909399;">{{ abilitySliderMax }}%</span>
+                  </div>
+
+                  <!-- 第二行:数值输入框 -->
+                  <div style="display: flex; align-items: center; gap: 15px;">
+                    <el-input-number
+                      v-model="abilitySliderValue"
+                      :min="abilitySliderMin"
+                      :max="abilitySliderMax"
+                      :step="1"
+                      :precision="0"
+                      controls-position="right"
+                      style="flex: 0 0 120px;">
+                    </el-input-number>
+                    <span style="color: #909399; font-size: 13px;">
+                      调节范围:{{ abilitySliderMin }} - {{ abilitySliderMax }}
+                    </span>
+                  </div>
+                </div>
+              </template>
+
+              <!-- Input类型:显示输入框 -->
+              <template v-else-if="abilityParamType === 'Input'">
+                <el-input
+                  v-model="abilityInputValue"
+                  placeholder="请输入参数值"
+                  clearable>
+                </el-input>
+              </template>
             </el-form-item>
           </el-form>
           <div slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="saveAbilityEdit">执行</el-button>
-            <el-button @click="cancelAbilityEdit">取消</el-button>
+            <el-button @click="abilityDialogVisible = false">取消</el-button>
+            <el-button type="primary" @click="executeAbilityWithParam" :loading="abilityExecuting">执行</el-button>
           </div>
         </el-dialog>
       </el-col>
@@ -623,10 +688,28 @@ import { listcallAbility } from '@/api/basecfg/objAbility'
 import { getCallLog, listCallLog, listEventLog, getEventLog } from '@/api/basecfg/objLog'
 
 export default {
-  name: 'Device',
+  name: 'DeviceAdmin',
   components: { Treeselect },
   data() {
     return {
+      // 新增:能力执行相关
+      abilityDialogVisible: false,
+      abilityDialogTitle: '设备能力执行',
+      abilityForm: {
+        abilityName: '',
+        abilityKey: '',
+        abilityDesc: '',
+        paramDefinition: null,
+        modelCode: ''
+      },
+      abilityParamType: null,
+      abilitySliderValue: 50,
+      abilitySliderMin: 0,
+      abilitySliderMax: 100,
+      abilityInputValue: '',
+      abilityExecuting: false,
+      currentDevice: null,
+
       // 表格属性详情弹窗状态
       showTableDetail: false,
       // 表格详情数据
@@ -637,14 +720,6 @@ export default {
       subAreaOptions: [],
       // 默认展开的节点
       defaultExpandedKeys: [],
-      abilityDialogVisible: false,
-      abilityForm: {
-        abilityName: '',
-        abilityKey: '',
-        abilityParam: '',
-        abilityDesc: ''
-      },
-      currentAbilityIndex: -1,
       activeTab: 'basic',
       ComponentRow: [],
       componentForm: {},
@@ -849,6 +924,177 @@ export default {
     this.loadAreaOptions()
   },
   methods: {
+    // 新增:格式化滑块tooltip
+    formatTooltip(val) {
+      return `${val}%`
+    },
+
+    // 新增:解析参数定义类型
+    getParamType(paramDefinition) {
+      if (!paramDefinition) return null
+      try {
+        const def = JSON.parse(paramDefinition)
+        return def.type
+      } catch (e) {
+        return null
+      }
+    },
+
+    // 新增:获取参数类型名称
+    getParamTypeName(paramDefinition) {
+      const type = this.getParamType(paramDefinition)
+      const typeNames = {
+        'Options': '选项型',
+        'Slider': '滑块型',
+        'Input': '输入型'
+      }
+      return typeNames[type] || '无参数'
+    },
+
+    // 新增:获取参数类型标签样式
+    getParamTypeTagType(paramDefinition) {
+      const type = this.getParamType(paramDefinition)
+      const typeStyles = {
+        'Options': 'primary',
+        'Slider': 'success',
+        'Input': 'warning'
+      }
+      return typeStyles[type] || 'info'
+    },
+
+    // 新增:解析Options类型的选项
+    parseOptions(paramDefinition) {
+      try {
+        const def = JSON.parse(paramDefinition)
+        if (def.type === 'Options' && def.list) {
+          return def.list
+        }
+      } catch (e) {
+        console.error('解析Options失败:', e)
+      }
+      return []
+    },
+
+    // 新增:处理能力表格中的执行按钮点击
+    handleOperateClick(row) {
+      const paramType = this.getParamType(row.paramDefinition)
+
+      if (paramType === 'Slider') {
+        this.showSliderDialog(row, this.curRow)
+      } else if (paramType === 'Input') {
+        this.showInputDialog(row, this.curRow)
+      } else {
+        // 无参数:直接执行
+        this.handleOperate(row)
+      }
+    },
+
+    // 新增:处理Options类型的直接按钮点击
+    handleOperateWithParam(row, paramValue) {
+      // 设置执行状态
+      row.executing = true
+      row.executingValue = paramValue
+
+      listcallAbility({
+        objCode: this.curRow.deviceCode,
+        objType: '2',
+        modelCode: this.curRow.deviceModel,
+        abilityKey: row.abilityKey,
+        abilityParam: paramValue
+      }).then(response => {
+        this.$message.success('执行成功')
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        // 重置执行状态
+        row.executing = false
+        row.executingValue = null
+      })
+    },
+
+    // 新增:显示滑块弹窗
+    showSliderDialog(ability, device) {
+      this.currentDevice = device
+      this.abilityForm = { ...ability }
+      this.abilityDialogTitle = `${ability.abilityName} - ${device.deviceName}`
+      this.abilityParamType = 'Slider'
+
+      try {
+        const def = JSON.parse(ability.paramDefinition)
+        this.abilitySliderMin = def.min || 0
+        this.abilitySliderMax = def.max || 100
+        this.abilitySliderValue = Math.floor((this.abilitySliderMin + this.abilitySliderMax) / 2)
+      } catch (e) {
+        console.error('解析Slider参数失败:', e)
+        // 使用默认值
+        this.abilitySliderMin = 0
+        this.abilitySliderMax = 100
+        this.abilitySliderValue = 50
+      }
+
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:显示输入弹窗
+    showInputDialog(ability, device) {
+      this.currentDevice = device
+      this.abilityForm = { ...ability }
+      this.abilityDialogTitle = `${ability.abilityName} - ${device.deviceName}`
+      this.abilityParamType = 'Input'
+      this.abilityInputValue = ''
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:执行带参数的能力
+    executeAbilityWithParam() {
+      let paramValue = null
+
+      if (this.abilityParamType === 'Slider') {
+        paramValue = String(this.abilitySliderValue)
+      } else if (this.abilityParamType === 'Input') {
+        if (!this.abilityInputValue) {
+          this.$message.warning('请输入参数值')
+          return
+        }
+        paramValue = this.abilityInputValue
+      }
+
+      this.abilityExecuting = true
+      listcallAbility({
+        objCode: this.currentDevice.deviceCode,
+        objType: '2',
+        modelCode: this.currentDevice.deviceModel,
+        abilityKey: this.abilityForm.abilityKey,
+        abilityParam: paramValue
+      }).then(response => {
+        this.$message.success('执行成功')
+        this.abilityDialogVisible = false
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        this.abilityExecuting = false
+      })
+    },
+
+    // 修改:处理无参数能力执行
+    handleOperate(row) {
+      // 设置执行状态
+      row.executing = true
+
+      listcallAbility({
+        objCode: this.curRow.deviceCode,
+        objType: '2',
+        modelCode: this.curRow.deviceModel,
+        abilityKey: row.abilityKey,
+        abilityParam: null
+      }).then(response => {
+        this.$message({ message: '设备能力执行成功', type: 'success' })
+      }).finally(() => {
+        // 重置执行状态
+        row.executing = false
+      })
+    },
+
     // 获取可见的属性表格(只返回有数据的分组)
     getVisibleAttrTables() {
       const visibleTables = {};
@@ -1242,49 +1488,6 @@ export default {
       this.reset()
     },
 
-    handleAbilityEdit(row) {
-      this.abilityForm = JSON.parse(JSON.stringify(row))
-      this.currentAbilityIndex = this.abilityData.indexOf(row)
-      this.abilityDialogVisible = true
-    },
-
-    saveAbilityEdit() {
-      if (this.currentAbilityIndex !== -1) {
-        const updatedAbility = { ...this.abilityForm }
-        // 设置执行状态
-        this.abilityData[this.currentAbilityIndex].executing = true
-
-        const abilityParams = {
-          objCode: this.curRow.deviceCode,
-          objType: "2",
-          modelCode: updatedAbility.modelCode,
-          abilityKey: updatedAbility.abilityKey,
-          abilityParam: updatedAbility.abilityParam
-        }
-        listcallAbility(abilityParams).then((response) => {
-          if (response.code === 200) {
-            this.$message.success('执行成功')
-            this.abilityData.splice(this.currentAbilityIndex, 1, updatedAbility)
-          }
-        }).finally(() => {
-          // 重置执行状态
-          this.abilityData[this.currentAbilityIndex].executing = false
-        })
-        this.abilityDialogVisible = false
-      }
-    },
-
-    cancelAbilityEdit() {
-      this.abilityDialogVisible = false
-      this.abilityForm = {
-        abilityName: '',
-        abilityKey: '',
-        abilityParam: '',
-        abilityDesc: ''
-      }
-      this.currentAbilityIndex = -1
-    },
-
     reset() {
       this.form = {
         id: null,
@@ -1373,10 +1576,11 @@ export default {
       this.curRow = row
       getModelByCode(this.curRow.deviceModel).then(response => {
         this.eventData = response.data?.eventList || []
-        // 添加执行状态属性
+        // 添加执行状态属性,支持新的paramDefinition字段
         this.abilityData = (response.data?.abilityList || []).map(item => ({
           ...item,
-          executing: false
+          executing: false,
+          executingValue: null  // 用于Options类型按钮的加载状态
         }))
       })
       getObjAttr(2, this.curRow.deviceCode).then(response => {

+ 345 - 22
ems-ui-cloud/src/views/devmgr/device/index.vue

@@ -52,11 +52,6 @@
                 <el-option v-for="item in subCategoryOptions" :label="item.name" :value="item.code" :key="item.code" />
               </el-select>
             </el-form-item>
-            <el-form-item label="归属设施" prop="refFacs">
-              <el-select v-model="queryParams.refFacs" placeholder="请选择">
-                <el-option v-for="item in facsOptions" :label="item.facsName" :value="item.facsCode" :key="item.facsCode" />
-              </el-select>
-            </el-form-item>
             <el-form-item label="归属系统" prop="subsystemCode">
               <el-select v-model="queryParams.subsystemCode" placeholder="请选择">
                 <el-option v-for="item in subsystemOptions" :label="item.systemName" :value="item.systemCode" :key="item.systemCode" />
@@ -73,7 +68,6 @@
             <el-table-column label="归属区域" align="left" prop="areaPath" width="280px"/>
             <el-table-column label="设备分类" align="center" prop="deviceCategoryName"/>
             <el-table-column label="归属设施" align="center" prop="refFacsName"/>
-            <el-table-column label="归属系统" align="center" prop="subsystemName"/>
             <el-table-column label="设备状态" align="center" prop="deviceStatus">
               <template slot-scope="scope">
                 <span :style="{
@@ -94,8 +88,8 @@
             <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
               <template slot-scope="scope">
                 <el-button size="mini" type="text" icon="el-icon-info" @click="handleDetail(scope.row)">详情</el-button>
-                <!-- 下拉菜单 -->
-                <el-dropdown trigger="click">
+                <!-- 改进的下拉菜单 - 支持新的参数定义 -->
+                <el-dropdown trigger="click" @command="handleAbilityCommand">
                   <el-button type="text" size="mini" @click="loadAbilities(scope.row)">
                     操作<i class="el-icon-arrow-down el-icon--right"></i>
                   </el-button>
@@ -104,13 +98,18 @@
                       <el-dropdown-item
                         v-for="ability in abilityDevice"
                         :key="ability.abilityKey"
-                        @click.native="handleDeviceOperate(ability, scope.row)"
+                        :command="{ ability: ability, device: scope.row }"
                       >
+                        <i class="el-icon-caret-right" style="margin-right: 5px;"></i>
                         {{ ability.abilityName }}
+                        <!-- 如果是Options类型,显示子菜单指示器 -->
+                        <i v-if="getParamType(ability.paramDefinition) === 'Options'"
+                           class="el-icon-arrow-right"
+                           style="float: right; margin-left: 10px;"></i>
                       </el-dropdown-item>
                     </template>
                     <el-dropdown-item v-else>
-                      无
+                      无可用操作
                     </el-dropdown-item>
                   </el-dropdown-menu>
                 </el-dropdown>
@@ -235,20 +234,35 @@
               </el-card>
             </div>
 
-            <!-- 设备能力 - 使用统一的执行按钮样式 -->
+            <!-- 设备能力 - 改进的执行按钮样式 -->
             <div v-if="activeTab === 'ability'">
               <el-card class="box-card">
                 <el-table :data="abilityData" style="width: 100%" :show-header="true" :empty-text="'暂无数据'">
                   <el-table-column type="index" label="序号" width="50" align="center"></el-table-column>
                   <el-table-column label="能力名称" prop="abilityName"></el-table-column>
                   <el-table-column label="能力描述" prop="abilityDesc"></el-table-column>
-                  <el-table-column label="操作" align="center" width="150">
+                  <el-table-column label="操作" align="center" width="200">
                     <template slot-scope="scope">
+                      <div v-if="getParamType(scope.row.paramDefinition) === 'Options'" style="display: inline-block;">
+                        <!-- Options类型:显示多个按钮 -->
+                        <el-button-group>
+                          <el-button
+                            v-for="option in parseOptions(scope.row.paramDefinition)"
+                            :key="option.value"
+                            size="mini"
+                            type="primary"
+                            @click="handleOperateWithParam(scope.row, option.value)"
+                            :loading="scope.row.executing && scope.row.executingValue === option.value">
+                            {{ option.key }}
+                          </el-button>
+                        </el-button-group>
+                      </div>
                       <el-button
+                        v-else
                         type="primary"
                         size="mini"
                         icon="el-icon-caret-right"
-                        @click="handleOperate(scope.row)"
+                        @click="handleOperateClick(scope.row)"
                         :loading="scope.row.executing">
                         执行
                       </el-button>
@@ -411,6 +425,91 @@
             </el-table-column>
           </el-table>
         </el-dialog>
+
+        <!-- 新增:能力执行弹窗(支持Slider和Input类型) -->
+        <el-dialog :title="abilityDialogTitle" :visible.sync="abilityDialogVisible" width="500px" append-to-body :close-on-click-modal="false">
+          <el-form ref="abilityForm" :model="abilityForm" label-width="100px">
+            <el-form-item label="能力名称">
+              <el-input v-model="abilityForm.abilityName" disabled></el-input>
+            </el-form-item>
+            <el-form-item label="能力描述">
+              <el-input type="textarea" v-model="abilityForm.abilityDesc" disabled :rows="2"></el-input>
+            </el-form-item>
+
+            <!-- 根据参数类型显示不同的输入组件 -->
+            <el-form-item label="参数设置" v-if="abilityParamType">
+              <!-- Slider类型:显示滑块 -->
+              <template v-if="abilityParamType === 'Slider'">
+                <div style="width: 100%;">
+                  <!-- 第一行:纯滑块 -->
+                  <div style="padding: 0 10px;">
+                    <el-slider
+                      v-model="abilitySliderValue"
+                      :min="abilitySliderMin"
+                      :max="abilitySliderMax"
+                      :show-tooltip="true"
+                      :format-tooltip="formatTooltip">
+                    </el-slider>
+                  </div>
+
+                  <!-- 标记点文字 -->
+                  <div style="display: flex; justify-content: space-between; padding: 0 10px; margin-top: -5px; margin-bottom: 20px;">
+                    <span style="font-size: 12px; color: #909399;">{{ abilitySliderMin }}%</span>
+                    <span style="font-size: 12px; color: #909399;">{{ Math.floor((abilitySliderMin + abilitySliderMax) / 2) }}%</span>
+                    <span style="font-size: 12px; color: #909399;">{{ abilitySliderMax }}%</span>
+                  </div>
+
+                  <!-- 第二行:数值输入框 -->
+                  <div style="display: flex; align-items: center; gap: 15px;">
+                    <el-input-number
+                      v-model="abilitySliderValue"
+                      :min="abilitySliderMin"
+                      :max="abilitySliderMax"
+                      :step="1"
+                      :precision="0"
+                      controls-position="right"
+                      style="flex: 0 0 120px;">
+                    </el-input-number>
+                    <span style="color: #909399; font-size: 13px;">
+                      调节范围:{{ abilitySliderMin }} - {{ abilitySliderMax }}
+                    </span>
+                  </div>
+                </div>
+              </template>
+
+              <!-- Input类型:显示输入框 -->
+              <template v-else-if="abilityParamType === 'Input'">
+                <el-input
+                  v-model="abilityInputValue"
+                  placeholder="请输入参数值"
+                  clearable>
+                </el-input>
+              </template>
+            </el-form-item>
+          </el-form>
+          <div slot="footer" class="dialog-footer">
+            <el-button @click="abilityDialogVisible = false">取消</el-button>
+            <el-button type="primary" @click="executeAbilityWithParam" :loading="abilityExecuting">执行</el-button>
+          </div>
+        </el-dialog>
+
+        <!-- Options类型的快速执行弹窗 -->
+        <el-dialog :title="quickAbilityTitle" :visible.sync="quickAbilityDialogVisible" width="400px" append-to-body>
+          <div style="text-align: center; padding: 20px 0;">
+            <el-button-group>
+              <el-button
+                v-for="option in quickAbilityOptions"
+                :key="option.value"
+                type="primary"
+                size="medium"
+                @click="executeQuickAbility(option.value)"
+                :loading="quickAbilityExecuting && quickAbilityValue === option.value"
+                style="margin: 0 10px;">
+                {{ option.key }}
+              </el-button>
+            </el-button-group>
+          </div>
+        </el-dialog>
       </el-col>
     </el-row>
   </div>
@@ -545,7 +644,33 @@ export default {
       relDevDetailDialog: false,
       relDevDetailData: [],
       relDevDetailTitle: '',
-      relDevColumns: []
+      relDevColumns: [],
+      // 新增:能力执行相关
+      abilityDialogVisible: false,
+      abilityDialogTitle: '设备能力执行',
+      abilityForm: {
+        abilityName: '',
+        abilityKey: '',
+        abilityDesc: '',
+        paramDefinition: null,
+        modelCode: ''
+      },
+      abilityParamType: null,
+      abilitySliderValue: 50,
+      abilitySliderMin: 0,
+      abilitySliderMax: 100,
+      abilityInputValue: '',
+      abilityExecuting: false,
+      currentDevice: null,
+      // 滑块标记点
+      sliderMarks: {},
+      // 快速执行弹窗(Options类型)
+      quickAbilityDialogVisible: false,
+      quickAbilityTitle: '',
+      quickAbilityOptions: [],
+      quickAbilityExecuting: false,
+      quickAbilityValue: null,
+      quickAbilityData: null
     }
   },
   watch: {
@@ -575,6 +700,203 @@ export default {
     this.logDaterangeTime = [this.formatDate(startOfDay), this.formatDate(endOfDay)]
   },
   methods: {
+    // 新增:格式化滑块tooltip
+    formatTooltip(val) {
+      return `${val}%`
+    },
+
+    // 新增:解析参数定义类型
+    getParamType(paramDefinition) {
+      if (!paramDefinition) return null
+      try {
+        const def = JSON.parse(paramDefinition)
+        return def.type
+      } catch (e) {
+        return null
+      }
+    },
+
+    // 新增:解析Options类型的选项
+    parseOptions(paramDefinition) {
+      try {
+        const def = JSON.parse(paramDefinition)
+        if (def.type === 'Options' && def.list) {
+          return def.list
+        }
+      } catch (e) {
+        console.error('解析Options失败:', e)
+      }
+      return []
+    },
+
+    // 新增:处理下拉菜单命令
+    handleAbilityCommand(command) {
+      const { ability, device } = command
+      this.currentDevice = device
+      const paramType = this.getParamType(ability.paramDefinition)
+
+      if (paramType === 'Options') {
+        // Options类型:显示快速执行弹窗
+        this.quickAbilityTitle = ability.abilityName
+        this.quickAbilityOptions = this.parseOptions(ability.paramDefinition)
+        this.quickAbilityData = ability
+        this.quickAbilityDialogVisible = true
+      } else if (paramType === 'Slider') {
+        // Slider类型:显示滑块弹窗
+        this.showSliderDialog(ability, device)
+      } else if (paramType === 'Input') {
+        // Input类型:显示输入弹窗
+        this.showInputDialog(ability, device)
+      } else {
+        // 无参数:直接执行
+        this.executeDirectAbility(ability, device)
+      }
+    },
+
+    // 新增:显示滑块弹窗
+    showSliderDialog(ability, device) {
+      this.currentDevice = device
+      this.abilityForm = { ...ability }
+      this.abilityDialogTitle = `${ability.abilityName} - ${device.deviceName}`
+      this.abilityParamType = 'Slider'
+
+      try {
+        const def = JSON.parse(ability.paramDefinition)
+        this.abilitySliderMin = def.min || 0
+        this.abilitySliderMax = def.max || 100
+        this.abilitySliderValue = Math.floor((this.abilitySliderMin + this.abilitySliderMax) / 2)
+      } catch (e) {
+        console.error('解析Slider参数失败:', e)
+        // 使用默认值
+        this.abilitySliderMin = 0
+        this.abilitySliderMax = 100
+        this.abilitySliderValue = 50
+      }
+
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:显示输入弹窗
+    showInputDialog(ability, device) {
+      this.currentDevice = device
+      this.abilityForm = { ...ability }
+      this.abilityDialogTitle = `${ability.abilityName} - ${device.deviceName}`
+      this.abilityParamType = 'Input'
+      this.abilityInputValue = ''
+      this.abilityDialogVisible = true
+    },
+
+    // 新增:直接执行能力(无参数)
+    executeDirectAbility(ability, device) {
+      this.$confirm(`确认执行 ${ability.abilityName} 操作?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        listcallAbility({
+          objCode: device.deviceCode,
+          objType: '2',
+          modelCode: device.deviceModel,
+          abilityKey: ability.abilityKey,
+          abilityParam: null
+        }).then(response => {
+          this.$message.success('执行成功')
+        }).catch(() => {
+          this.$message.error('执行失败')
+        })
+      })
+    },
+
+    // 新增:执行带参数的能力
+    executeAbilityWithParam() {
+      let paramValue = null
+
+      if (this.abilityParamType === 'Slider') {
+        paramValue = String(this.abilitySliderValue)
+      } else if (this.abilityParamType === 'Input') {
+        if (!this.abilityInputValue) {
+          this.$message.warning('请输入参数值')
+          return
+        }
+        paramValue = this.abilityInputValue
+      }
+
+      this.abilityExecuting = true
+      listcallAbility({
+        objCode: this.currentDevice.deviceCode,
+        objType: '2',
+        modelCode: this.currentDevice.deviceModel,
+        abilityKey: this.abilityForm.abilityKey,
+        abilityParam: paramValue
+      }).then(response => {
+        this.$message.success('执行成功')
+        this.abilityDialogVisible = false
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        this.abilityExecuting = false
+      })
+    },
+
+    // 新增:快速执行Options类型能力
+    executeQuickAbility(value) {
+      this.quickAbilityExecuting = true
+      this.quickAbilityValue = value
+
+      listcallAbility({
+        objCode: this.currentDevice.deviceCode,
+        objType: '2',
+        modelCode: this.currentDevice.deviceModel,
+        abilityKey: this.quickAbilityData.abilityKey,
+        abilityParam: value
+      }).then(response => {
+        this.$message.success('执行成功')
+        this.quickAbilityDialogVisible = false
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        this.quickAbilityExecuting = false
+        this.quickAbilityValue = null
+      })
+    },
+
+    // 修改:处理设备能力表格中的执行按钮点击
+    handleOperateClick(row) {
+      const paramType = this.getParamType(row.paramDefinition)
+
+      if (paramType === 'Slider') {
+        this.showSliderDialog(row, this.curRow)
+      } else if (paramType === 'Input') {
+        this.showInputDialog(row, this.curRow)
+      } else {
+        // 无参数:直接执行
+        this.handleOperate(row)
+      }
+    },
+
+    // 修改:处理Options类型的直接按钮点击
+    handleOperateWithParam(row, paramValue) {
+      // 设置执行状态
+      row.executing = true
+      row.executingValue = paramValue
+
+      listcallAbility({
+        objCode: this.curRow.deviceCode,
+        objType: '2',
+        modelCode: this.curRow.deviceModel,
+        abilityKey: row.abilityKey,
+        abilityParam: paramValue
+      }).then(response => {
+        this.$message.success('执行成功')
+      }).catch(() => {
+        this.$message.error('执行失败')
+      }).finally(() => {
+        // 重置执行状态
+        row.executing = false
+        row.executingValue = null
+      })
+    },
+
     // 控制表格显示的方法
     toggleCollapse(tableName) {
       this.collapsed[tableName] = !this.collapsed[tableName];
@@ -790,7 +1112,7 @@ export default {
     },
 
     loadAbilities(row) {
-      if (!this.curRow) {
+      if (!row) {
         this.$message.warning('请选择一条设备记录')
         return
       }
@@ -798,14 +1120,14 @@ export default {
         this.abilityDevice = response.data?.abilityList.filter(item => item.hiddenFlag === 1) || []
       })
     },
-    /**设备能力-执行按钮*/
+    /**设备能力-执行按钮(无参数)*/
     handleDeviceOperate(row, curRow) {
       listcallAbility({
         objCode: curRow.deviceCode,
         objType: '2',
-        modelCode: curRow.deviceModel,  // 使用设备的modelCode
+        modelCode: curRow.deviceModel,
         abilityKey: row.abilityKey,
-        abilityParam: row.abilityParam
+        abilityParam: null  // 原ability_param字段已改为paramDefinition
       }).then(response => {
         this.$message({ message: '设备能力执行成功', type: 'success' })
       })
@@ -818,9 +1140,9 @@ export default {
       listcallAbility({
         objCode: this.curRow.deviceCode,
         objType: '2',
-        modelCode: this.curRow.deviceModel,  // 使用设备的modelCode
+        modelCode: this.curRow.deviceModel,
         abilityKey: row.abilityKey,
-        abilityParam: row.abilityParam
+        abilityParam: null  // 原ability_param字段已改为paramDefinition
       }).then(response => {
         this.$message({ message: '设备能力执行成功', type: 'success' })
       }).finally(() => {
@@ -927,10 +1249,11 @@ export default {
       this.callLogQueryTotal = 0
       getModelByCode(this.curRow.deviceModel).then(response => {
         this.open = true
-        // 添加执行状态属性
+        // 添加执行状态属性,并处理新的paramDefinition字段
         this.abilityData = (response.data?.abilityList.filter(item => item.hiddenFlag === 1) || []).map(item => ({
           ...item,
-          executing: false
+          executing: false,
+          executingValue: null  // 用于Options类型按钮的加载状态
         }))
         this.attrData = response.data?.attrList || []
         this.eventData = response.data?.eventList || []

部分文件因文件數量過多而無法顯示