|
|
@@ -0,0 +1,2672 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <!-- 系统基础信息卡片 -->
|
|
|
+ <el-card class="system-info-card">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <span class="title">
|
|
|
+ <i class="el-icon-s-data"></i>
|
|
|
+ {{ systemInfo.systemName || '安科瑞电力监控' }}
|
|
|
+ </span>
|
|
|
+ <el-tag :type="systemStatus === '1' ? 'success' : 'danger'" effect="dark">
|
|
|
+ {{ systemStatus === '1' ? '正常' : '异常' }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="info-group">
|
|
|
+ <div class="group-title">系统信息</div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>系统代码:</label>
|
|
|
+ <span class="code">{{ systemInfo.systemCode }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>系统名称:</label>
|
|
|
+ <span>{{ systemInfo.systemName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>模型代码:</label>
|
|
|
+ <span class="code">{{ systemInfo.modelCode }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="info-group">
|
|
|
+ <div class="group-title">厂商信息</div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>对接厂商:</label>
|
|
|
+ <span>{{ systemInfo.manFacturer || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>联系人:</label>
|
|
|
+ <span>{{ systemInfo.contactPerson || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>联系电话:</label>
|
|
|
+ <span>{{ systemInfo.contactNumber || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="info-group">
|
|
|
+ <div class="group-title">维护信息</div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>维护人:</label>
|
|
|
+ <span>{{ systemInfo.maintainerPerson || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>维护电话:</label>
|
|
|
+ <span>{{ systemInfo.maintainerNumber || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="info-group">
|
|
|
+ <div class="group-title">设备统计</div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>保护装置:</label>
|
|
|
+ <span class="stat-number">{{ deviceStats.protectionDevicesTotal }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <label>智能电表:</label>
|
|
|
+ <span class="stat-number">{{ deviceStats.smartMetersTotal }}</span>
|
|
|
+ </div>
|
|
|
+ <div style="text-align: right; margin-top: 10px;">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="mini"
|
|
|
+ icon="el-icon-edit"
|
|
|
+ @click="handleEdit"
|
|
|
+ v-hasPermi="['ems:subsystem:edit']">
|
|
|
+ 编辑
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 系统详情标签页 -->
|
|
|
+ <el-card class="detail-card">
|
|
|
+ <el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
+ <!-- 系统总览标签页 -->
|
|
|
+ <el-tab-pane label="系统总览" name="overview">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 实时监控统计 -->
|
|
|
+ <div class="monitor-panel">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-monitor"></i>
|
|
|
+ 实时监控
|
|
|
+ </h4>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="monitor-card">
|
|
|
+ <div class="monitor-icon total">
|
|
|
+ <i class="el-icon-s-platform"></i>
|
|
|
+ </div>
|
|
|
+ <div class="monitor-info">
|
|
|
+ <div class="monitor-value">{{ overviewStats.totalDevices }}</div>
|
|
|
+ <div class="monitor-label">设备总数</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="monitor-card">
|
|
|
+ <div class="monitor-icon online">
|
|
|
+ <i class="el-icon-circle-check"></i>
|
|
|
+ </div>
|
|
|
+ <div class="monitor-info">
|
|
|
+ <div class="monitor-value">{{ overviewStats.onlineDevices }}</div>
|
|
|
+ <div class="monitor-label">在线设备</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 保护装置设备列表 -->
|
|
|
+ <div class="device-panel">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-box"></i>
|
|
|
+ 保护装置设备
|
|
|
+ </h4>
|
|
|
+ <el-table
|
|
|
+ :data="protectionDeviceList"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ v-loading="protectionLoading"
|
|
|
+ @row-click="handleProtectionDeviceClick"
|
|
|
+ ref="protectionTable"
|
|
|
+ class="protection-device-table">
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template slot-scope="props">
|
|
|
+ <div class="device-detail">
|
|
|
+ <el-tabs v-model="props.row.detailTab || 'base'">
|
|
|
+ <!-- 基础属性 -->
|
|
|
+ <el-tab-pane label="基础属性" name="base">
|
|
|
+ <el-descriptions :column="2" border size="small">
|
|
|
+ <el-descriptions-item label="设备代码">
|
|
|
+ {{ props.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备名称">
|
|
|
+ {{ props.row.deviceName }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点名称">
|
|
|
+ {{ getDeviceAttr(props.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点编号">
|
|
|
+ {{ getDeviceAttr(props.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="安装位置">
|
|
|
+ {{ props.row.location || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备状态">
|
|
|
+ <el-tag size="small" :type="getDeviceStatus(props.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(props.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 电力属性 -->
|
|
|
+ <el-tab-pane label="电力属性" name="power">
|
|
|
+ <div class="power-params">
|
|
|
+ <!-- 电流参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电流参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ia') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ib') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ic') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 电压参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电压参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">AB线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Uab') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">BC线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ubc') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">CA线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Uca') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 分支测点 -->
|
|
|
+ <el-tab-pane
|
|
|
+ v-if="hasSubDevices(props.row.deviceCode)"
|
|
|
+ label="分支测点"
|
|
|
+ name="subdevices">
|
|
|
+ <el-table
|
|
|
+ :data="getSubDevices(props.row.deviceCode)"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ size="small"
|
|
|
+ class="sub-device-table">
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template slot-scope="subProps">
|
|
|
+ <div class="sub-device-detail">
|
|
|
+ <el-tabs v-model="subProps.row.detailTab || 'base'">
|
|
|
+ <!-- 测点基础属性 -->
|
|
|
+ <el-tab-pane label="基础属性" name="base">
|
|
|
+ <el-descriptions :column="2" border size="mini">
|
|
|
+ <el-descriptions-item label="测点代码">
|
|
|
+ {{ subProps.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="测点名称">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'meterName', 'Base') || subProps.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点名称">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点编号">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备状态">
|
|
|
+ <el-tag size="mini" :type="getDeviceStatus(subProps.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(subProps.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 测点电力属性 -->
|
|
|
+ <el-tab-pane label="电力属性" name="power">
|
|
|
+ <div class="power-params">
|
|
|
+ <!-- 电流参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电流参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ia') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ib') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ic') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 电压参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电压参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">AB线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Uab') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">BC线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ubc') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">CA线电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Uca') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subName" label="站点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subId" label="站点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterName" label="测点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterName', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterCode" label="测点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterCode', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="mini" :type="getDeviceStatus(scope.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(scope.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-dropdown
|
|
|
+ trigger="click"
|
|
|
+ @command="handleSubDeviceCommand"
|
|
|
+ @visible-change="(visible) => handleAbilityLoad(visible, scope.row)">
|
|
|
+ <el-button type="text" size="mini">
|
|
|
+ 操作<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <template v-if="deviceAbilities[scope.row.deviceCode] && deviceAbilities[scope.row.deviceCode].length > 0">
|
|
|
+ <template v-for="ability in deviceAbilities[scope.row.deviceCode]">
|
|
|
+ <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>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subName" label="站点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subId" label="站点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterName" label="测点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterName', 'Base') || scope.row.deviceName }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterCode" label="测点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterCode', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="getDeviceStatus(scope.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(scope.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-dropdown
|
|
|
+ trigger="click"
|
|
|
+ @command="handleDeviceCommand"
|
|
|
+ @visible-change="(visible) => handleAbilityLoad(visible, scope.row)">
|
|
|
+ <el-button type="text" size="mini">
|
|
|
+ 操作<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <template v-if="deviceAbilities[scope.row.deviceCode] && deviceAbilities[scope.row.deviceCode].length > 0">
|
|
|
+ <template v-for="ability in deviceAbilities[scope.row.deviceCode]">
|
|
|
+ <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>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 系统属性标签页 -->
|
|
|
+ <el-tab-pane label="系统属性" name="attributes">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 协议属性 -->
|
|
|
+ <div class="attr-section" v-if="protocolAttrs.length > 0">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-connection"></i>
|
|
|
+ 协议信息
|
|
|
+ </h4>
|
|
|
+ <el-table :data="protocolAttrs" border stripe>
|
|
|
+ <el-table-column prop="attrName" label="属性名称" width="200"></el-table-column>
|
|
|
+ <el-table-column label="属性值">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span v-if="scope.row.attrKey === 'url'" class="url-text">
|
|
|
+ {{ scope.row.attrValue }}
|
|
|
+ </span>
|
|
|
+ <span v-else>{{ scope.row.attrValue || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 状态属性 -->
|
|
|
+ <div class="attr-section" v-if="stateAttrs.length > 0">
|
|
|
+ <h4 class="section-title">
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
+ 状态信息
|
|
|
+ </h4>
|
|
|
+ <el-table :data="stateAttrs" border stripe>
|
|
|
+ <el-table-column prop="attrName" label="属性名称" width="200"></el-table-column>
|
|
|
+ <el-table-column label="属性值">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag v-if="scope.row.attrKey === 'interfaceStatus'"
|
|
|
+ :type="scope.row.attrValue === '1' ? 'success' : 'danger'">
|
|
|
+ {{ scope.row.attrValueName }}
|
|
|
+ </el-tag>
|
|
|
+ <span v-else>{{ scope.row.attrValueName || scope.row.attrValue || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="updateTime" label="更新时间" width="180"></el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </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="200" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div v-if="getParamType(scope.row.paramDefinition) === 'Options'" style="display: inline-block;">
|
|
|
+ <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="handleSystemAbilityClick(scope.row)"
|
|
|
+ :loading="scope.row.executing">
|
|
|
+ 执行
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 智能电表标签页 -->
|
|
|
+ <el-tab-pane label="智能电表" name="smartMeters">
|
|
|
+ <div class="tab-content">
|
|
|
+ <!-- 功率统计 -->
|
|
|
+ <div class="power-stats">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="stat-card active">
|
|
|
+ <div class="stat-value">{{ meterPowerStats.totalActivePower }}</div>
|
|
|
+ <div class="stat-label">总有功功率 (kW)</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="stat-card reactive">
|
|
|
+ <div class="stat-value">{{ meterPowerStats.totalReactivePower }}</div>
|
|
|
+ <div class="stat-label">总无功功率 (kVar)</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="stat-card apparent">
|
|
|
+ <div class="stat-value">{{ meterPowerStats.totalApparentPower }}</div>
|
|
|
+ <div class="stat-label">总视在功率 (kVA)</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 智能电表设备列表 -->
|
|
|
+ <el-table
|
|
|
+ :data="smartMeterDeviceList"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ v-loading="meterLoading"
|
|
|
+ @row-click="handleMeterDeviceClick"
|
|
|
+ ref="meterTable"
|
|
|
+ class="meter-device-table">
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template slot-scope="props">
|
|
|
+ <div class="device-detail">
|
|
|
+ <el-tabs v-model="props.row.detailTab || 'base'">
|
|
|
+ <!-- 基础属性 -->
|
|
|
+ <el-tab-pane label="基础属性" name="base">
|
|
|
+ <el-descriptions :column="2" border size="small">
|
|
|
+ <el-descriptions-item label="设备代码">
|
|
|
+ {{ props.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备名称">
|
|
|
+ {{ props.row.deviceName }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点名称">
|
|
|
+ {{ getDeviceAttr(props.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点编号">
|
|
|
+ {{ getDeviceAttr(props.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="安装位置">
|
|
|
+ {{ props.row.location || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备状态">
|
|
|
+ <el-tag size="small" :type="getDeviceStatus(props.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(props.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 电力属性 -->
|
|
|
+ <el-tab-pane label="电力属性" name="power">
|
|
|
+ <div class="power-params">
|
|
|
+ <!-- 电流参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电流参数</div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ia') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ib') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ic') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 电压参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电压参数</div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ua') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Ub') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Uc') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 功率参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">功率参数</div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总有功功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Pt') || '0.0' }}</span>
|
|
|
+ <span class="unit">kW</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总无功功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'Qt') || '0.0' }}</span>
|
|
|
+ <span class="unit">kVar</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总视在功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'St') || '0.0' }}</span>
|
|
|
+ <span class="unit">kVA</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 能量参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">能量参数</div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="param-card energy-card">
|
|
|
+ <div class="param-label">
|
|
|
+ <i class="el-icon-lightning"></i>
|
|
|
+ 正向有功总电能
|
|
|
+ </div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(props.row.deviceCode, 'EPI') || '0.0' }}</span>
|
|
|
+ <span class="unit">kW·h</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 分支测点 -->
|
|
|
+ <el-tab-pane
|
|
|
+ v-if="hasSubDevices(props.row.deviceCode)"
|
|
|
+ label="分支测点"
|
|
|
+ name="subdevices">
|
|
|
+ <el-table
|
|
|
+ :data="getSubDevices(props.row.deviceCode)"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ size="small"
|
|
|
+ class="sub-device-table">
|
|
|
+ <el-table-column type="expand">
|
|
|
+ <template slot-scope="subProps">
|
|
|
+ <div class="sub-device-detail">
|
|
|
+ <el-tabs v-model="subProps.row.detailTab || 'base'">
|
|
|
+ <!-- 测点基础属性 -->
|
|
|
+ <el-tab-pane label="基础属性" name="base">
|
|
|
+ <el-descriptions :column="2" border size="mini">
|
|
|
+ <el-descriptions-item label="测点代码">
|
|
|
+ {{ subProps.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="测点名称">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'meterName', 'Base') || subProps.row.deviceCode }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点名称">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="站点编号">
|
|
|
+ {{ getDeviceAttr(subProps.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备状态">
|
|
|
+ <el-tag size="mini" :type="getDeviceStatus(subProps.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(subProps.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 测点电力属性 -->
|
|
|
+ <el-tab-pane label="电力属性" name="power">
|
|
|
+ <div class="power-params">
|
|
|
+ <!-- 电流参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电流参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ia') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ib') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电流</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ic') || '0.0' }}</span>
|
|
|
+ <span class="unit">A</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 电压参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">电压参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">A相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ua') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">B相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Ub') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">C相电压</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Uc') || '0.0' }}</span>
|
|
|
+ <span class="unit">V</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 功率参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">功率参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总有功功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Pt') || '0.0' }}</span>
|
|
|
+ <span class="unit">kW</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总无功功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'Qt') || '0.0' }}</span>
|
|
|
+ <span class="unit">kVar</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="param-card">
|
|
|
+ <div class="param-label">总视在功率</div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'St') || '0.0' }}</span>
|
|
|
+ <span class="unit">kVA</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 能量参数组 -->
|
|
|
+ <div class="param-group">
|
|
|
+ <div class="group-title">能量参数</div>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="param-card energy-card">
|
|
|
+ <div class="param-label">
|
|
|
+ <i class="el-icon-lightning"></i>
|
|
|
+ 正向有功总电能
|
|
|
+ </div>
|
|
|
+ <div class="param-value">
|
|
|
+ <span class="value">{{ getDeviceAttr(subProps.row.deviceCode, 'EPI') || '0.0' }}</span>
|
|
|
+ <span class="unit">kW·h</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subName" label="站点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subId" label="站点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterName" label="测点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterName', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterCode" label="测点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterCode', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="mini" :type="getDeviceStatus(scope.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(scope.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-dropdown
|
|
|
+ trigger="click"
|
|
|
+ @command="handleSubDeviceCommand"
|
|
|
+ @visible-change="(visible) => handleAbilityLoad(visible, scope.row)">
|
|
|
+ <el-button type="text" size="mini">
|
|
|
+ 操作<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <template v-if="deviceAbilities[scope.row.deviceCode] && deviceAbilities[scope.row.deviceCode].length > 0">
|
|
|
+ <template v-for="ability in deviceAbilities[scope.row.deviceCode]">
|
|
|
+ <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>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subName" label="站点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subName', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="subId" label="站点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'subId', 'Base') || '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterName" label="测点名称" min-width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterName', 'Base') || scope.row.deviceName }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="meterCode" label="测点编号" width="120">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getDeviceAttr(scope.row.deviceCode, 'meterCode', 'Base') || scope.row.deviceCode }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="getDeviceStatus(scope.row.deviceCode) === '1' ? 'success' : 'danger'">
|
|
|
+ {{ getDeviceStatusText(scope.row.deviceCode) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="120" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-dropdown
|
|
|
+ trigger="click"
|
|
|
+ @command="handleDeviceCommand"
|
|
|
+ @visible-change="(visible) => handleAbilityLoad(visible, scope.row)">
|
|
|
+ <el-button type="text" size="mini">
|
|
|
+ 操作<i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </el-button>
|
|
|
+ <el-dropdown-menu slot="dropdown">
|
|
|
+ <template v-if="deviceAbilities[scope.row.deviceCode] && deviceAbilities[scope.row.deviceCode].length > 0">
|
|
|
+ <template v-for="ability in deviceAbilities[scope.row.deviceCode]">
|
|
|
+ <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>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 调用日志标签页 -->
|
|
|
+ <el-tab-pane label="调用日志" name="callLogs">
|
|
|
+ <div class="tab-content">
|
|
|
+ <el-form :inline="true" :model="callLogQuery" class="log-filter">
|
|
|
+ <el-form-item label="时间范围">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="callLogQuery.dateRange"
|
|
|
+ type="datetimerange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="能力标识">
|
|
|
+ <el-input v-model="callLogQuery.abilityKey" placeholder="请输入能力标识" clearable></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="调用状态">
|
|
|
+ <el-select v-model="callLogQuery.callStatus" placeholder="全部" clearable>
|
|
|
+ <el-option label="成功" :value="0"></el-option>
|
|
|
+ <el-option label="进行中" :value="1"></el-option>
|
|
|
+ <el-option label="失败" :value="2"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="queryCallLogs">查询</el-button>
|
|
|
+ <el-button @click="resetCallLogQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-table :data="callLogList" border stripe v-loading="logLoading">
|
|
|
+ <el-table-column prop="objCode" label="对象代码" min-width="150"></el-table-column>
|
|
|
+ <el-table-column prop="objName" label="对象名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="abilityName" label="能力名称" min-width="150"></el-table-column>
|
|
|
+ <el-table-column prop="callTime" label="调用时间" width="180"></el-table-column>
|
|
|
+ <el-table-column label="调用状态" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="small" :type="getCallStatusType(scope.row.callStatus)">
|
|
|
+ {{ formatCallStatus(scope.row.callStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button type="text" size="small" @click="handleCallLogDetail(scope.row)">详情</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="callLogTotal > 0"
|
|
|
+ :total="callLogTotal"
|
|
|
+ :page.sync="callLogQuery.pageNum"
|
|
|
+ :limit.sync="callLogQuery.pageSize"
|
|
|
+ @pagination="queryCallLogs" />
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 事件日志标签页 -->
|
|
|
+ <el-tab-pane label="事件日志" name="eventLogs">
|
|
|
+ <div class="tab-content">
|
|
|
+ <el-form :inline="true" :model="eventLogQuery" class="log-filter">
|
|
|
+ <el-form-item label="时间范围">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="eventLogQuery.dateRange"
|
|
|
+ type="datetimerange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss">
|
|
|
+ </el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="事件标识">
|
|
|
+ <el-input v-model="eventLogQuery.eventKey" placeholder="请输入事件标识" clearable></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="queryEventLogs">查询</el-button>
|
|
|
+ <el-button @click="resetEventLogQuery">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <el-table :data="eventLogList" border stripe v-loading="eventLogLoading">
|
|
|
+ <el-table-column prop="objCode" label="对象代码" min-width="150"></el-table-column>
|
|
|
+ <el-table-column prop="objName" label="对象名称" min-width="200"></el-table-column>
|
|
|
+ <el-table-column prop="eventName" label="事件名称" min-width="150"></el-table-column>
|
|
|
+ <el-table-column prop="recTime" label="发生时间" width="180"></el-table-column>
|
|
|
+ <el-table-column label="操作" width="100" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button type="text" size="small" @click="handleEventLogDetail(scope.row)">详情</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <pagination
|
|
|
+ v-show="eventLogTotal > 0"
|
|
|
+ :total="eventLogTotal"
|
|
|
+ :page.sync="eventLogQuery.pageNum"
|
|
|
+ :limit.sync="eventLogQuery.pageSize"
|
|
|
+ @pagination="queryEventLogs" />
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 系统信息编辑弹框 -->
|
|
|
+ <el-dialog
|
|
|
+ title="编辑系统信息"
|
|
|
+ :visible.sync="editDialog.visible"
|
|
|
+ width="600px"
|
|
|
+ :close-on-click-modal="false">
|
|
|
+ <el-form :model="editForm" :rules="editRules" ref="editForm" label-width="100px">
|
|
|
+ <el-form-item label="系统代码" prop="systemCode">
|
|
|
+ <el-input v-model="editForm.systemCode" disabled></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="系统名称" prop="systemName">
|
|
|
+ <el-input v-model="editForm.systemName" placeholder="请输入系统名称"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="对接厂商" prop="manFacturer">
|
|
|
+ <el-input v-model="editForm.manFacturer" placeholder="请输入对接厂商"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="联系人" prop="contactPerson">
|
|
|
+ <el-input v-model="editForm.contactPerson" placeholder="请输入联系人"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="联系电话" prop="contactNumber">
|
|
|
+ <el-input v-model="editForm.contactNumber" placeholder="请输入联系电话"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="维护人" prop="maintainerPerson">
|
|
|
+ <el-input v-model="editForm.maintainerPerson" placeholder="请输入维护人"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="维护电话" prop="maintainerNumber">
|
|
|
+ <el-input v-model="editForm.maintainerNumber" placeholder="请输入维护电话"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="editDialog.visible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitEdit" :loading="editDialog.submitting">确定</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 执行结果对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ :title="executeDialog.title"
|
|
|
+ :visible.sync="executeDialog.visible"
|
|
|
+ width="600px">
|
|
|
+ <div class="execute-result">
|
|
|
+ <el-alert
|
|
|
+ :title="executeDialog.status"
|
|
|
+ :type="executeDialog.type"
|
|
|
+ :description="executeDialog.message"
|
|
|
+ show-icon>
|
|
|
+ </el-alert>
|
|
|
+ <div v-if="executeDialog.data" class="result-data">
|
|
|
+ <pre>{{ JSON.stringify(executeDialog.data, null, 2) }}</pre>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="executeDialog.visible = false">关闭</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 调用日志详情对话框 -->
|
|
|
+ <el-dialog title="调用日志详情" :visible.sync="callLogDetailDialog" width="60%">
|
|
|
+ <el-descriptions v-if="callLogDetailData" :column="2" border>
|
|
|
+ <el-descriptions-item label="对象代码">{{ callLogDetailData.objCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="对象名称">{{ callLogDetailData.objName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="能力名称">{{ callLogDetailData.abilityName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="能力标识">{{ callLogDetailData.abilityKey }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="调用时间">{{ callLogDetailData.callTime }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="调用状态">
|
|
|
+ <el-tag :type="getCallStatusType(callLogDetailData.callStatus)">
|
|
|
+ {{ formatCallStatus(callLogDetailData.callStatus) }}
|
|
|
+ </el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="请求参数" :span="2">
|
|
|
+ <pre>{{ callLogDetailData.request || '-' }}</pre>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="响应结果" :span="2">
|
|
|
+ <pre>{{ callLogDetailData.response || '-' }}</pre>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="callLogDetailDialog = false">关闭</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 事件日志详情对话框 -->
|
|
|
+ <el-dialog title="事件日志详情" :visible.sync="eventLogDetailDialog" width="60%">
|
|
|
+ <el-descriptions v-if="eventLogDetailData" :column="2" border>
|
|
|
+ <el-descriptions-item label="对象代码">{{ eventLogDetailData.objCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="对象名称">{{ eventLogDetailData.objName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="事件名称">{{ eventLogDetailData.eventName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="事件标识">{{ eventLogDetailData.eventKey }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="发生时间">{{ eventLogDetailData.recTime }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="事件数据" :span="2">
|
|
|
+ <pre>{{ eventLogDetailData.eventData || '-' }}</pre>
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="eventLogDetailDialog = false">关闭</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getSubsystemByCode, updateSubsystem } from '@/api/adapter/subsystem'
|
|
|
+import { getModelByCode } from '@/api/basecfg/objModel'
|
|
|
+import { getObjAttr, getObjAttrBatch } from '@/api/basecfg/objAttribute'
|
|
|
+import { callAbility } from '@/api/basecfg/objAbility'
|
|
|
+import { getByCondition } from '@/api/device/device'
|
|
|
+import { listCallLog, listEventLog, getCallLog, getEventLog } from '@/api/basecfg/objLog'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'ElecMonitor',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ systemCode: 'SYS_DLJK',
|
|
|
+ systemInfo: {},
|
|
|
+ systemStatus: '0',
|
|
|
+ activeTab: 'overview',
|
|
|
+
|
|
|
+ // 系统属性
|
|
|
+ protocolAttrs: [],
|
|
|
+ stateAttrs: [],
|
|
|
+ systemAbilities: [],
|
|
|
+ abilityLoading: false,
|
|
|
+
|
|
|
+ // 设备列表
|
|
|
+ protectionDeviceList: [],
|
|
|
+ smartMeterDeviceList: [],
|
|
|
+
|
|
|
+ // 设备加载状态
|
|
|
+ protectionLoading: false,
|
|
|
+ meterLoading: false,
|
|
|
+
|
|
|
+ // 设备统计
|
|
|
+ deviceStats: {
|
|
|
+ protectionDevices: 0,
|
|
|
+ protectionDevicesTotal: 0,
|
|
|
+ smartMeters: 0,
|
|
|
+ smartMetersTotal: 0
|
|
|
+ },
|
|
|
+
|
|
|
+ // 总览统计
|
|
|
+ overviewStats: {
|
|
|
+ totalDevices: 0,
|
|
|
+ onlineDevices: 0
|
|
|
+ },
|
|
|
+
|
|
|
+ // 电表功率统计
|
|
|
+ meterPowerStats: {
|
|
|
+ totalActivePower: '0.0',
|
|
|
+ totalReactivePower: '0.0',
|
|
|
+ totalApparentPower: '0.0'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 设备属性缓存
|
|
|
+ deviceAttrsMap: {},
|
|
|
+
|
|
|
+ // 设备能力缓存
|
|
|
+ modelAbilitiesCache: {},
|
|
|
+ deviceAbilities: {},
|
|
|
+
|
|
|
+ // 所有模型代码列表
|
|
|
+ allModelCodes: [],
|
|
|
+
|
|
|
+ // 日志相关
|
|
|
+ callLogList: [],
|
|
|
+ callLogTotal: 0,
|
|
|
+ callLogQuery: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ dateRange: [],
|
|
|
+ abilityKey: '',
|
|
|
+ callStatus: ''
|
|
|
+ },
|
|
|
+ logLoading: false,
|
|
|
+
|
|
|
+ eventLogList: [],
|
|
|
+ eventLogTotal: 0,
|
|
|
+ eventLogQuery: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ dateRange: [],
|
|
|
+ eventKey: ''
|
|
|
+ },
|
|
|
+ eventLogLoading: false,
|
|
|
+
|
|
|
+ // 对话框
|
|
|
+ callLogDetailDialog: false,
|
|
|
+ callLogDetailData: null,
|
|
|
+ eventLogDetailDialog: false,
|
|
|
+ eventLogDetailData: null,
|
|
|
+
|
|
|
+ // 编辑系统信息对话框
|
|
|
+ editDialog: {
|
|
|
+ visible: false,
|
|
|
+ submitting: false
|
|
|
+ },
|
|
|
+ editForm: {
|
|
|
+ systemCode: '',
|
|
|
+ systemName: '',
|
|
|
+ manFacturer: '',
|
|
|
+ contactPerson: '',
|
|
|
+ contactNumber: '',
|
|
|
+ maintainerPerson: '',
|
|
|
+ maintainerNumber: ''
|
|
|
+ },
|
|
|
+ editRules: {
|
|
|
+ systemName: [
|
|
|
+ { required: true, message: '请输入系统名称', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+
|
|
|
+ // 能力执行对话框
|
|
|
+ executeDialog: {
|
|
|
+ visible: false,
|
|
|
+ title: '',
|
|
|
+ status: '',
|
|
|
+ type: 'success',
|
|
|
+ message: '',
|
|
|
+ data: null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadSystemInfo()
|
|
|
+ this.initDateRange()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // ==================== 参数解析 ====================
|
|
|
+
|
|
|
+ // 解析参数定义类型
|
|
|
+ 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 []
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 系统信息加载 ====================
|
|
|
+
|
|
|
+ // 加载系统信息
|
|
|
+ async loadSystemInfo() {
|
|
|
+ try {
|
|
|
+ // 加载系统基本信息
|
|
|
+ const sysRes = await getSubsystemByCode(this.systemCode)
|
|
|
+ this.systemInfo = sysRes.data
|
|
|
+
|
|
|
+ if (this.systemInfo.modelCode) {
|
|
|
+ this.allModelCodes.push(this.systemInfo.modelCode)
|
|
|
+
|
|
|
+ // 加载系统模型信息(包含能力列表)
|
|
|
+ const modelRes = await getModelByCode(this.systemInfo.modelCode)
|
|
|
+ const modelData = modelRes.data
|
|
|
+
|
|
|
+ // 缓存系统能力
|
|
|
+ this.systemAbilities = (modelData.abilityList || []).map(item => ({
|
|
|
+ ...item,
|
|
|
+ executing: false,
|
|
|
+ executingValue: null
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载系统属性值
|
|
|
+ const attrRes = await getObjAttr(3, this.systemCode)
|
|
|
+ const attrData = attrRes.data
|
|
|
+
|
|
|
+ this.protocolAttrs = attrData.Protocol || []
|
|
|
+ this.stateAttrs = attrData.State || []
|
|
|
+
|
|
|
+ // 获取系统状态
|
|
|
+ const statusAttr = this.stateAttrs.find(attr => attr.attrKey === 'interfaceStatus')
|
|
|
+ if (statusAttr) {
|
|
|
+ this.systemStatus = statusAttr.attrValue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载设备列表
|
|
|
+ await this.loadDevices()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('加载系统信息失败:' + error.message)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 系统能力执行 ====================
|
|
|
+
|
|
|
+ // 处理系统能力点击
|
|
|
+ handleSystemAbilityClick(ability) {
|
|
|
+ const paramType = this.getParamType(ability.paramDefinition)
|
|
|
+
|
|
|
+ if (paramType === 'Slider' || paramType === 'Input') {
|
|
|
+ this.$message.warning('该能力需要参数,请实现参数输入对话框')
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ this.executeSystemAbility(ability)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 执行系统能力(带Options参数)
|
|
|
+ executeSystemAbilityWithParam(ability, paramValue) {
|
|
|
+ ability.executing = true
|
|
|
+ ability.executingValue = paramValue
|
|
|
+
|
|
|
+ callAbility({
|
|
|
+ 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
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是同步设备列表的能力,重新加载设备
|
|
|
+ if (ability.abilityKey === 'GetDeviceList') {
|
|
|
+ this.loadDevices()
|
|
|
+ }
|
|
|
+ }).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
|
|
|
+
|
|
|
+ callAbility({
|
|
|
+ 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 === 'GetDeviceList') {
|
|
|
+ this.loadDevices()
|
|
|
+ }
|
|
|
+ }).catch(error => {
|
|
|
+ this.executeDialog = {
|
|
|
+ visible: true,
|
|
|
+ title: '执行结果 - ' + ability.abilityName,
|
|
|
+ status: '执行失败',
|
|
|
+ type: 'error',
|
|
|
+ message: error.message,
|
|
|
+ data: null
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ ability.executing = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 编辑系统信息
|
|
|
+ handleEdit() {
|
|
|
+ this.editForm = {
|
|
|
+ systemCode: this.systemInfo.systemCode,
|
|
|
+ systemName: this.systemInfo.systemName,
|
|
|
+ manFacturer: this.systemInfo.manFacturer || '',
|
|
|
+ contactPerson: this.systemInfo.contactPerson || '',
|
|
|
+ contactNumber: this.systemInfo.contactNumber || '',
|
|
|
+ maintainerPerson: this.systemInfo.maintainerPerson || '',
|
|
|
+ maintainerNumber: this.systemInfo.maintainerNumber || ''
|
|
|
+ }
|
|
|
+ this.editDialog.visible = true
|
|
|
+ },
|
|
|
+
|
|
|
+ // 提交编辑
|
|
|
+ async submitEdit() {
|
|
|
+ this.$refs.editForm.validate(async (valid) => {
|
|
|
+ if (!valid) return
|
|
|
+
|
|
|
+ this.editDialog.submitting = true
|
|
|
+ try {
|
|
|
+ await updateSubsystem({
|
|
|
+ id: this.systemInfo.id,
|
|
|
+ ...this.editForm
|
|
|
+ })
|
|
|
+
|
|
|
+ this.$message.success('系统信息更新成功')
|
|
|
+ this.editDialog.visible = false
|
|
|
+
|
|
|
+ // 重新加载系统信息
|
|
|
+ await this.loadSystemInfo()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('系统信息更新失败:' + error.message)
|
|
|
+ } finally {
|
|
|
+ this.editDialog.submitting = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 设备加载 ====================
|
|
|
+
|
|
|
+ // 加载设备列表
|
|
|
+ async loadDevices() {
|
|
|
+ try {
|
|
|
+ // 加载保护装置设备
|
|
|
+ await this.loadProtectionDevices()
|
|
|
+
|
|
|
+ // 加载智能电表设备
|
|
|
+ await this.loadSmartMeterDevices()
|
|
|
+
|
|
|
+ // 更新总览统计
|
|
|
+ this.overviewStats.totalDevices = this.deviceStats.protectionDevicesTotal + this.deviceStats.smartMetersTotal
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('加载设备列表失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载保护装置设备
|
|
|
+ async loadProtectionDevices() {
|
|
|
+ this.protectionLoading = true
|
|
|
+ try {
|
|
|
+ const res = await getByCondition({
|
|
|
+ subsystemCode: this.systemCode,
|
|
|
+ deviceModel: 'M_W4_DEV_ELEC_MONITOR_BHZZ'
|
|
|
+ })
|
|
|
+
|
|
|
+ const deviceData = res.data || res.rows || []
|
|
|
+ this.protectionDeviceList = deviceData.map(device => ({
|
|
|
+ ...device,
|
|
|
+ detailTab: 'base'
|
|
|
+ }))
|
|
|
+
|
|
|
+ this.deviceStats.protectionDevices = this.protectionDeviceList.length
|
|
|
+
|
|
|
+ // 添加模型代码到列表
|
|
|
+ if (this.protectionDeviceList.length > 0) {
|
|
|
+ const modelCode = this.protectionDeviceList[0].deviceModel
|
|
|
+ if (modelCode && !this.allModelCodes.includes(modelCode)) {
|
|
|
+ this.allModelCodes.push(modelCode)
|
|
|
+ // 预加载模型能力
|
|
|
+ await this.loadModelAbilities(modelCode)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量加载设备属性
|
|
|
+ if (this.protectionDeviceList.length > 0) {
|
|
|
+ await this.loadDeviceAttrsBatch('M_W4_DEV_ELEC_MONITOR_BHZZ', this.protectionDeviceList)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算包含子节点的总数
|
|
|
+ this.calculateTotalDeviceCount('protection')
|
|
|
+
|
|
|
+ // 更新在线设备数
|
|
|
+ this.updateOnlineDeviceCount()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载保护装置失败:', error)
|
|
|
+ } finally {
|
|
|
+ this.protectionLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载智能电表设备
|
|
|
+ async loadSmartMeterDevices() {
|
|
|
+ this.meterLoading = true
|
|
|
+ try {
|
|
|
+ const res = await getByCondition({
|
|
|
+ subsystemCode: this.systemCode,
|
|
|
+ deviceModel: 'M_W4_DEV_ELEC_MONITOR_DB'
|
|
|
+ })
|
|
|
+
|
|
|
+ const deviceData = res.data || res.rows || []
|
|
|
+ this.smartMeterDeviceList = deviceData.map(device => ({
|
|
|
+ ...device,
|
|
|
+ detailTab: 'base'
|
|
|
+ }))
|
|
|
+
|
|
|
+ this.deviceStats.smartMeters = this.smartMeterDeviceList.length
|
|
|
+
|
|
|
+ // 添加模型代码到列表
|
|
|
+ if (this.smartMeterDeviceList.length > 0) {
|
|
|
+ const modelCode = this.smartMeterDeviceList[0].deviceModel
|
|
|
+ if (modelCode && !this.allModelCodes.includes(modelCode)) {
|
|
|
+ this.allModelCodes.push(modelCode)
|
|
|
+ // 预加载模型能力
|
|
|
+ await this.loadModelAbilities(modelCode)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量加载设备属性
|
|
|
+ if (this.smartMeterDeviceList.length > 0) {
|
|
|
+ await this.loadDeviceAttrsBatch('M_W4_DEV_ELEC_MONITOR_DB', this.smartMeterDeviceList)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算包含子节点的总数
|
|
|
+ this.calculateTotalDeviceCount('smartMeter')
|
|
|
+
|
|
|
+ // 计算功率统计
|
|
|
+ this.calculateMeterPowerStats()
|
|
|
+
|
|
|
+ // 更新在线设备数
|
|
|
+ this.updateOnlineDeviceCount()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载智能电表失败:', error)
|
|
|
+ } finally {
|
|
|
+ this.meterLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算设备总数(主节点+子节点)
|
|
|
+ calculateTotalDeviceCount(deviceType) {
|
|
|
+ if (deviceType === 'protection') {
|
|
|
+ let total = this.protectionDeviceList.length
|
|
|
+ this.protectionDeviceList.forEach(device => {
|
|
|
+ total += this.getSubDevicesCount(device.deviceCode)
|
|
|
+ })
|
|
|
+ this.deviceStats.protectionDevicesTotal = total
|
|
|
+ } else if (deviceType === 'smartMeter') {
|
|
|
+ let total = this.smartMeterDeviceList.length
|
|
|
+ this.smartMeterDeviceList.forEach(device => {
|
|
|
+ total += this.getSubDevicesCount(device.deviceCode)
|
|
|
+ })
|
|
|
+ this.deviceStats.smartMetersTotal = total
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 批量加载设备属性
|
|
|
+ async loadDeviceAttrsBatch(modelCode, devices) {
|
|
|
+ try {
|
|
|
+ const res = await getObjAttrBatch(2, modelCode)
|
|
|
+ const batchData = res.data || {}
|
|
|
+
|
|
|
+ devices.forEach(device => {
|
|
|
+ const deviceCode = device.deviceCode
|
|
|
+ const attrData = batchData[deviceCode]
|
|
|
+
|
|
|
+ if (attrData) {
|
|
|
+ // 缓存设备属性数据
|
|
|
+ this.deviceAttrsMap[deviceCode] = attrData
|
|
|
+
|
|
|
+ // 检查子设备数据
|
|
|
+ if (attrData.Base) {
|
|
|
+ const subDevAttr = attrData.Base.find(attr => attr.attrKey === 'subDev')
|
|
|
+ if (subDevAttr && subDevAttr.attrValue) {
|
|
|
+ try {
|
|
|
+ const subDevList = JSON.parse(subDevAttr.attrValue)
|
|
|
+ if (Array.isArray(subDevList) && subDevList.length > 0) {
|
|
|
+ // 加载子设备属性
|
|
|
+ this.loadSubDevicesAttrs(subDevList, modelCode)
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析子设备数据失败:', e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`批量加载模型 ${modelCode} 的设备属性失败:`, error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载子设备属性
|
|
|
+ async loadSubDevicesAttrs(subDevList, modelCode) {
|
|
|
+ try {
|
|
|
+ const res = await getObjAttrBatch(2, modelCode)
|
|
|
+ const batchData = res.data || {}
|
|
|
+
|
|
|
+ subDevList.forEach(subDev => {
|
|
|
+ const deviceCode = subDev.deviceCode || subDev
|
|
|
+ if (typeof deviceCode === 'string' && batchData[deviceCode]) {
|
|
|
+ this.deviceAttrsMap[deviceCode] = batchData[deviceCode]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载子设备属性失败:', error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 更新在线设备数
|
|
|
+ updateOnlineDeviceCount() {
|
|
|
+ let onlineCount = 0
|
|
|
+
|
|
|
+ // 统计保护装置在线设备
|
|
|
+ this.protectionDeviceList.forEach(device => {
|
|
|
+ if (this.getDeviceStatus(device.deviceCode) === '1') {
|
|
|
+ onlineCount++
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 统计智能电表在线设备
|
|
|
+ this.smartMeterDeviceList.forEach(device => {
|
|
|
+ if (this.getDeviceStatus(device.deviceCode) === '1') {
|
|
|
+ onlineCount++
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ this.overviewStats.onlineDevices = onlineCount
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算电表功率统计
|
|
|
+ calculateMeterPowerStats() {
|
|
|
+ let totalActivePower = 0
|
|
|
+ let totalReactivePower = 0
|
|
|
+ let totalApparentPower = 0
|
|
|
+
|
|
|
+ // 遍历每个智能电表主节点
|
|
|
+ this.smartMeterDeviceList.forEach(device => {
|
|
|
+ // 获取该主节点下的所有子节点
|
|
|
+ const subDevices = this.getSubDevices(device.deviceCode)
|
|
|
+
|
|
|
+ if (subDevices && subDevices.length > 0) {
|
|
|
+ // 如果有子节点,累加子节点的功率
|
|
|
+ subDevices.forEach(subDevice => {
|
|
|
+ const Pt = parseFloat(this.getDeviceAttr(subDevice.deviceCode, 'Pt')) || 0
|
|
|
+ const Qt = parseFloat(this.getDeviceAttr(subDevice.deviceCode, 'Qt')) || 0
|
|
|
+ const St = parseFloat(this.getDeviceAttr(subDevice.deviceCode, 'St')) || 0
|
|
|
+
|
|
|
+ totalActivePower += Pt
|
|
|
+ totalReactivePower += Qt
|
|
|
+ totalApparentPower += St
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 如果没有子节点,使用主节点的功率(兼容没有子节点的情况)
|
|
|
+ const Pt = parseFloat(this.getDeviceAttr(device.deviceCode, 'Pt')) || 0
|
|
|
+ const Qt = parseFloat(this.getDeviceAttr(device.deviceCode, 'Qt')) || 0
|
|
|
+ const St = parseFloat(this.getDeviceAttr(device.deviceCode, 'St')) || 0
|
|
|
+
|
|
|
+ totalActivePower += Pt
|
|
|
+ totalReactivePower += Qt
|
|
|
+ totalApparentPower += St
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ this.meterPowerStats.totalActivePower = totalActivePower.toFixed(2)
|
|
|
+ this.meterPowerStats.totalReactivePower = totalReactivePower.toFixed(2)
|
|
|
+ this.meterPowerStats.totalApparentPower = totalApparentPower.toFixed(2)
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 设备属性获取 ====================
|
|
|
+
|
|
|
+ // 获取设备属性值
|
|
|
+ getDeviceAttr(deviceCode, attrKey, attrGroup = 'State') {
|
|
|
+ const attrData = this.deviceAttrsMap[deviceCode]
|
|
|
+ if (!attrData || !attrData[attrGroup]) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ const attr = attrData[attrGroup].find(item => item.attrKey === attrKey)
|
|
|
+ return attr ? attr.attrValue : null
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取设备状态
|
|
|
+ getDeviceStatus(deviceCode) {
|
|
|
+ // 优先从属性缓存中读取deviceStatus
|
|
|
+ const statusValue = this.getDeviceAttr(deviceCode, 'deviceStatus', 'State')
|
|
|
+ if (statusValue !== null) {
|
|
|
+ return String(statusValue)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果属性中没有,则从设备列表中读取
|
|
|
+ const device = [...this.protectionDeviceList, ...this.smartMeterDeviceList]
|
|
|
+ .find(d => d.deviceCode === deviceCode)
|
|
|
+
|
|
|
+ return device ? String(device.deviceStatus) : '0'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取设备状态文本
|
|
|
+ getDeviceStatusText(deviceCode) {
|
|
|
+ return this.getDeviceStatus(deviceCode) === '1' ? '在线' : '离线'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 检查是否有子设备
|
|
|
+ hasSubDevices(deviceCode) {
|
|
|
+ const subDevAttr = this.getDeviceAttr(deviceCode, 'subDev', 'Base')
|
|
|
+ if (!subDevAttr) return false
|
|
|
+
|
|
|
+ try {
|
|
|
+ const subDevList = JSON.parse(subDevAttr)
|
|
|
+ return Array.isArray(subDevList) && subDevList.length > 0
|
|
|
+ } catch (e) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取子设备列表
|
|
|
+ getSubDevices(deviceCode) {
|
|
|
+ const subDevAttr = this.getDeviceAttr(deviceCode, 'subDev', 'Base')
|
|
|
+ if (!subDevAttr) return []
|
|
|
+
|
|
|
+ try {
|
|
|
+ const subDevList = JSON.parse(subDevAttr)
|
|
|
+ if (Array.isArray(subDevList)) {
|
|
|
+ return subDevList.map(subDev => {
|
|
|
+ if (typeof subDev === 'string') {
|
|
|
+ return {
|
|
|
+ deviceCode: subDev,
|
|
|
+ detailTab: 'base',
|
|
|
+ modelCode: 'M_W4_DEV_ELEC_MONITOR_BHZZ'
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return {
|
|
|
+ ...subDev,
|
|
|
+ detailTab: 'base',
|
|
|
+ modelCode: subDev.modelCode || 'M_W4_DEV_ELEC_MONITOR_BHZZ'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析子设备列表失败:', e)
|
|
|
+ }
|
|
|
+ return []
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取子设备数量
|
|
|
+ getSubDevicesCount(deviceCode) {
|
|
|
+ return this.getSubDevices(deviceCode).length
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 设备能力管理 ====================
|
|
|
+
|
|
|
+ // 预加载模型能力
|
|
|
+ async loadModelAbilities(modelCode) {
|
|
|
+ if (this.modelAbilitiesCache[modelCode]) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await getModelByCode(modelCode)
|
|
|
+ const modelData = res.data
|
|
|
+ const abilities = (modelData.abilityList || []).filter(item => item.hiddenFlag === 1)
|
|
|
+
|
|
|
+ // 缓存模型能力
|
|
|
+ this.modelAbilitiesCache[modelCode] = abilities.map(item => ({
|
|
|
+ ...item,
|
|
|
+ executing: false
|
|
|
+ }))
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`加载模型 ${modelCode} 能力失败:`, error)
|
|
|
+ this.modelAbilitiesCache[modelCode] = []
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载设备能力
|
|
|
+ async handleAbilityLoad(visible, device) {
|
|
|
+ if (visible && !this.deviceAbilities[device.deviceCode]) {
|
|
|
+ try {
|
|
|
+ const modelCode = device.deviceModel || device.modelCode
|
|
|
+ if (!modelCode) {
|
|
|
+ this.$set(this.deviceAbilities, device.deviceCode, [])
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果缓存中没有,则加载
|
|
|
+ if (!this.modelAbilitiesCache[modelCode]) {
|
|
|
+ await this.loadModelAbilities(modelCode)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从缓存中获取
|
|
|
+ this.$set(this.deviceAbilities, device.deviceCode, this.modelAbilitiesCache[modelCode] || [])
|
|
|
+ } catch (error) {
|
|
|
+ this.$set(this.deviceAbilities, device.deviceCode, [])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 设备能力执行 ====================
|
|
|
+
|
|
|
+ // 处理设备操作命令
|
|
|
+ async handleDeviceCommand(command) {
|
|
|
+ const { device, ability, paramValue } = command
|
|
|
+
|
|
|
+ try {
|
|
|
+ await callAbility({
|
|
|
+ objCode: device.deviceCode,
|
|
|
+ objType: 2,
|
|
|
+ modelCode: device.deviceModel || device.modelCode,
|
|
|
+ abilityKey: ability.abilityKey,
|
|
|
+ abilityParam: paramValue !== undefined ? paramValue : null
|
|
|
+ })
|
|
|
+
|
|
|
+ this.$message.success(`${ability.abilityName}执行成功`)
|
|
|
+
|
|
|
+ // 刷新设备属性
|
|
|
+ await this.loadDeviceAttrsBatch(device.deviceModel || device.modelCode, [device])
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理子设备操作命令
|
|
|
+ async handleSubDeviceCommand(command) {
|
|
|
+ const { device, ability, paramValue } = command
|
|
|
+
|
|
|
+ try {
|
|
|
+ await callAbility({
|
|
|
+ objCode: device.deviceCode,
|
|
|
+ objType: 2,
|
|
|
+ modelCode: device.modelCode,
|
|
|
+ abilityKey: ability.abilityKey,
|
|
|
+ abilityParam: paramValue !== undefined ? paramValue : null
|
|
|
+ })
|
|
|
+
|
|
|
+ this.$message.success(`${ability.abilityName}执行成功`)
|
|
|
+
|
|
|
+ // 刷新子设备属性
|
|
|
+ await this.loadSubDevicesAttrs([device], device.modelCode)
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(`${ability.abilityName}执行失败:${error.message}`)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 表格行点击 ====================
|
|
|
+
|
|
|
+ // 保护装置设备行点击
|
|
|
+ handleProtectionDeviceClick(row) {
|
|
|
+ this.$refs.protectionTable?.toggleRowExpansion(row)
|
|
|
+ },
|
|
|
+
|
|
|
+ // 智能电表设备行点击
|
|
|
+ handleMeterDeviceClick(row) {
|
|
|
+ this.$refs.meterTable?.toggleRowExpansion(row)
|
|
|
+ },
|
|
|
+
|
|
|
+ // ==================== 日志查询 ====================
|
|
|
+
|
|
|
+ // 标签页切换
|
|
|
+ handleTabClick(tab) {
|
|
|
+ if (tab.name === 'callLogs' && this.callLogList.length === 0) {
|
|
|
+ this.queryCallLogs()
|
|
|
+ } else if (tab.name === 'eventLogs' && this.eventLogList.length === 0) {
|
|
|
+ this.queryEventLogs()
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化时间范围
|
|
|
+ initDateRange() {
|
|
|
+ const now = new Date()
|
|
|
+ const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0)
|
|
|
+ const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)
|
|
|
+
|
|
|
+ this.callLogQuery.dateRange = [
|
|
|
+ this.formatDate(startOfDay),
|
|
|
+ this.formatDate(endOfDay)
|
|
|
+ ]
|
|
|
+ this.eventLogQuery.dateRange = [
|
|
|
+ this.formatDate(startOfDay),
|
|
|
+ this.formatDate(endOfDay)
|
|
|
+ ]
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化日期
|
|
|
+ formatDate(date) {
|
|
|
+ if (!date) return ''
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
|
+ const day = date.getDate().toString().padStart(2, '0')
|
|
|
+ const hours = date.getHours().toString().padStart(2, '0')
|
|
|
+ const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
|
+ const seconds = date.getSeconds().toString().padStart(2, '0')
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
+ },
|
|
|
+
|
|
|
+ // 查询调用日志
|
|
|
+ async queryCallLogs() {
|
|
|
+ this.logLoading = true
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ modelCodes: this.allModelCodes,
|
|
|
+ pageNum: this.callLogQuery.pageNum,
|
|
|
+ pageSize: this.callLogQuery.pageSize
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.callLogQuery.dateRange && this.callLogQuery.dateRange.length === 2) {
|
|
|
+ params.startRecTime = this.callLogQuery.dateRange[0]
|
|
|
+ params.endRecTime = this.callLogQuery.dateRange[1]
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.callLogQuery.abilityKey) {
|
|
|
+ params.abilityKey = this.callLogQuery.abilityKey
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.callLogQuery.callStatus !== '') {
|
|
|
+ params.callStatus = this.callLogQuery.callStatus
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = await listCallLog(params)
|
|
|
+ this.callLogList = res.rows || []
|
|
|
+ this.callLogTotal = res.total || 0
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('查询调用日志失败')
|
|
|
+ } finally {
|
|
|
+ this.logLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 重置调用日志查询
|
|
|
+ resetCallLogQuery() {
|
|
|
+ this.initDateRange()
|
|
|
+ this.callLogQuery.abilityKey = ''
|
|
|
+ this.callLogQuery.callStatus = ''
|
|
|
+ this.callLogQuery.pageNum = 1
|
|
|
+ this.queryCallLogs()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 查询事件日志
|
|
|
+ async queryEventLogs() {
|
|
|
+ this.eventLogLoading = true
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ modelCodes: this.allModelCodes,
|
|
|
+ pageNum: this.eventLogQuery.pageNum,
|
|
|
+ pageSize: this.eventLogQuery.pageSize
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.eventLogQuery.dateRange && this.eventLogQuery.dateRange.length === 2) {
|
|
|
+ params.startRecTime = this.eventLogQuery.dateRange[0]
|
|
|
+ params.endRecTime = this.eventLogQuery.dateRange[1]
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.eventLogQuery.eventKey) {
|
|
|
+ params.eventKey = this.eventLogQuery.eventKey
|
|
|
+ }
|
|
|
+
|
|
|
+ const res = await listEventLog(params)
|
|
|
+ this.eventLogList = res.rows || []
|
|
|
+ this.eventLogTotal = res.total || 0
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('查询事件日志失败')
|
|
|
+ } finally {
|
|
|
+ this.eventLogLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 重置事件日志查询
|
|
|
+ resetEventLogQuery() {
|
|
|
+ this.initDateRange()
|
|
|
+ this.eventLogQuery.eventKey = ''
|
|
|
+ this.eventLogQuery.pageNum = 1
|
|
|
+ this.queryEventLogs()
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化调用状态
|
|
|
+ formatCallStatus(status) {
|
|
|
+ const statusMap = {
|
|
|
+ 0: '成功',
|
|
|
+ 1: '进行中',
|
|
|
+ 2: '失败'
|
|
|
+ }
|
|
|
+ return statusMap[status] || '未知'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取调用状态类型
|
|
|
+ getCallStatusType(status) {
|
|
|
+ const typeMap = {
|
|
|
+ 0: 'success',
|
|
|
+ 1: 'warning',
|
|
|
+ 2: 'danger'
|
|
|
+ }
|
|
|
+ return typeMap[status] || 'info'
|
|
|
+ },
|
|
|
+
|
|
|
+ // 查看调用日志详情
|
|
|
+ async handleCallLogDetail(row) {
|
|
|
+ try {
|
|
|
+ const res = await getCallLog(row.id)
|
|
|
+ this.callLogDetailData = res.data
|
|
|
+ this.callLogDetailDialog = true
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取调用日志详情失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 查看事件日志详情
|
|
|
+ async handleEventLogDetail(row) {
|
|
|
+ try {
|
|
|
+ const res = await getEventLog(row.id)
|
|
|
+ this.eventLogDetailData = res.data
|
|
|
+ this.eventLogDetailDialog = true
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取事件日志详情失败')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.app-container {
|
|
|
+ padding: 20px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
+}
|
|
|
+
|
|
|
+.system-info-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-group {
|
|
|
+ .group-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #606266;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-bottom: 6px;
|
|
|
+ border-bottom: 1px solid #EBEEF5;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-item {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+
|
|
|
+ label {
|
|
|
+ color: #909399;
|
|
|
+ margin-right: 8px;
|
|
|
+ display: inline-block;
|
|
|
+ min-width: 80px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .code {
|
|
|
+ font-family: monospace;
|
|
|
+ color: #409EFF;
|
|
|
+ background: #f0f9ff;
|
|
|
+ padding: 2px 6px;
|
|
|
+ border-radius: 3px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-number {
|
|
|
+ color: #409EFF;
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 16px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.detail-card {
|
|
|
+ .tab-content {
|
|
|
+ min-height: 400px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 属性部分样式
|
|
|
+.attr-section {
|
|
|
+ margin-bottom: 30px;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ margin: 0 0 15px 0;
|
|
|
+ color: #303133;
|
|
|
+ font-size: 16px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .url-text {
|
|
|
+ color: #409EFF;
|
|
|
+ font-family: monospace;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 监控面板
|
|
|
+.monitor-panel {
|
|
|
+ margin-bottom: 30px;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ margin: 0 0 15px 0;
|
|
|
+ color: #303133;
|
|
|
+ font-size: 16px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .monitor-card {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #EBEEF5;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .monitor-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 20px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.total {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.online {
|
|
|
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .monitor-info {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .monitor-value {
|
|
|
+ font-size: 32px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .monitor-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 设备面板
|
|
|
+.device-panel {
|
|
|
+ margin-bottom: 30px;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ margin: 0 0 15px 0;
|
|
|
+ color: #303133;
|
|
|
+ font-size: 16px;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 功率统计
|
|
|
+.power-stats {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ .stat-card {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #EBEEF5;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 20px;
|
|
|
+ text-align: center;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-value {
|
|
|
+ font-size: 28px;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active .stat-value {
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.reactive .stat-value {
|
|
|
+ color: #67C23A;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.apparent .stat-value {
|
|
|
+ color: #E6A23C;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 设备详情
|
|
|
+.device-detail {
|
|
|
+ padding: 20px;
|
|
|
+ background: #f9fafc;
|
|
|
+
|
|
|
+ .power-params {
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ .param-group {
|
|
|
+ margin-bottom: 20px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .group-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ padding-left: 10px;
|
|
|
+ border-left: 3px solid #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-card {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 12px;
|
|
|
+ text-align: center;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-value {
|
|
|
+ .value {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #409EFF;
|
|
|
+
|
|
|
+ &.highlight {
|
|
|
+ color: #FF9800;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .unit {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #606266;
|
|
|
+ margin-left: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.energy-card {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ .param-label {
|
|
|
+ color: #fff;
|
|
|
+ font-weight: 600;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-value {
|
|
|
+ .value {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .unit {
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 子设备详情
|
|
|
+.sub-device-detail {
|
|
|
+ padding: 15px;
|
|
|
+ background: #fafbfc;
|
|
|
+
|
|
|
+ .power-params {
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ .param-group {
|
|
|
+ margin-bottom: 15px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .group-title {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding-left: 8px;
|
|
|
+ border-left: 3px solid #409EFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-card {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 10px;
|
|
|
+ text-align: center;
|
|
|
+ transition: all 0.3s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
|
+ transform: translateY(-2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-label {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-value {
|
|
|
+ .value {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #409EFF;
|
|
|
+ }
|
|
|
+
|
|
|
+ .unit {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #606266;
|
|
|
+ margin-left: 3px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.energy-card {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ .param-label {
|
|
|
+ color: #fff;
|
|
|
+ font-weight: 600;
|
|
|
+
|
|
|
+ i {
|
|
|
+ margin-right: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .param-value {
|
|
|
+ .value {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .unit {
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 日志过滤器
|
|
|
+.log-filter {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ padding: 15px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+// 执行结果
|
|
|
+.execute-result {
|
|
|
+ margin-top: 20px;
|
|
|
+
|
|
|
+ .result-data {
|
|
|
+ margin-top: 15px;
|
|
|
+
|
|
|
+ pre {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 15px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: auto;
|
|
|
+ max-height: 400px;
|
|
|
+ font-family: monospace;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.dialog-footer {
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+pre {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: auto;
|
|
|
+ max-height: 300px;
|
|
|
+ font-family: monospace;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+// 表格样式
|
|
|
+.protection-device-table,
|
|
|
+.meter-device-table {
|
|
|
+ ::v-deep .el-table__expanded-cell {
|
|
|
+ padding: 15px 20px;
|
|
|
+ background: #fafbfc;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.sub-device-table {
|
|
|
+ margin-top: 10px;
|
|
|
+
|
|
|
+ ::v-deep .el-table__expanded-cell {
|
|
|
+ padding: 10px 15px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|