|
@@ -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>
|