|
@@ -9,7 +9,20 @@
|
|
|
</div>
|
|
|
<div class="head-container">
|
|
|
<el-tree :data="areaList" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree"
|
|
|
- node-key="id" default-expand-all highlight-current @node-click="handleNodeClick"/>
|
|
|
+ node-key="id" default-expand-all highlight-current @node-click="handleNodeClick">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="custom-tree-node">
|
|
|
+ <span>{{ scope.node.label }}</span>
|
|
|
+ <el-button
|
|
|
+ v-if="scope.node.data.id !== 'all'"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-map-location"
|
|
|
+ @click="showTopology(scope.node.data)"
|
|
|
+ >拓扑图</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-tree>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-if="activeTab === 'organ'">
|
|
@@ -60,57 +73,66 @@
|
|
|
|
|
|
<!-- 地理位置 -->
|
|
|
<div v-if="activeTab === 'area'">
|
|
|
- <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
- <el-form-item label="区域名称" prop="areaName">
|
|
|
- <el-input
|
|
|
- v-model="queryParams.areaName"
|
|
|
- placeholder="请输入区域名称"
|
|
|
- clearable
|
|
|
- @keyup.enter.native="handleQuery"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
- <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <el-table
|
|
|
- :key="tableKey"
|
|
|
- v-if="refreshTable"
|
|
|
- v-loading="loading"
|
|
|
- :data="areaOptions"
|
|
|
- :default-expand-all="isExpandAll"
|
|
|
- row-key="areaCode"
|
|
|
- :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
|
|
- >
|
|
|
+ <el-tabs v-model="activeDeviceTab" @tab-click="handleADTabClick">
|
|
|
+ <el-tab-pane label="电表" name="electricMeter"></el-tab-pane>
|
|
|
+ <el-tab-pane label="水表" name="waterMeter"></el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
|
|
|
- <el-table-column label="区域名称" align="left" prop="areaName" />
|
|
|
- <el-table-column label="区域简称" align="left" prop="shortName" />
|
|
|
- <el-table-column label="区域编码" align="left" prop="areaCode" />
|
|
|
- <el-table-column label="区域状态" align="center" >
|
|
|
- <template slot-scope="scope">
|
|
|
- {{ objStatusMapping[scope.row.status] }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="区块标签" align="center">
|
|
|
- <template slot-scope="scope">
|
|
|
- <span v-for="tag in (scope.row.tagNames && scope.row.tagNames.split(',')) || []" :key="tag" :style="getTagStyle(tag.trim())" class="tag-label">
|
|
|
- {{ tag.trim() }}
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-button
|
|
|
- size="mini"
|
|
|
- type="text"
|
|
|
- icon="el-icon-reading"
|
|
|
- @click="handleDevice(scope.row)"
|
|
|
- v-hasPermi="['ems:area:edit']"
|
|
|
- >计量设备</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
+ <!-- 水表和电表的已绑定和未绑定表格 -->
|
|
|
+ <SubTitle title="已绑定列表" />
|
|
|
+ <el-table v-loading="loading" :data="Bound" style="width: 100%">
|
|
|
+ <el-table-column label="表计编号" align="left" prop="meterDevice" />
|
|
|
+ <el-table-column label="表计名称" align="left" prop="meterDeviceName" />
|
|
|
+ <el-table-column label="边界类型" align="left" prop="objType">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ getObjTypeLabel(scope.row.objType) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button size="mini" type="text" icon="el-icon-arrow-down" @click="downToDevice(scope.row)">取消绑定</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <SubTitle title="未绑定列表" />
|
|
|
+ <el-table v-loading="loading" :data="Unbound" style="width: 100%">
|
|
|
+ <el-table-column label="表计编号" align="left" prop="deviceCode" />
|
|
|
+ <el-table-column label="表计名称" align="left" prop="deviceName" width="200px"/>
|
|
|
+ <el-table-column label="安装位置" align="left" prop="deviceLocation" width="200px"/>
|
|
|
+ <el-table-column label="计量标签" align="center" prop="objTag">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{formatDict(scope.row.objTag,'objTagOptions')}}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="采集方式" align="center" prop="colMode">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ getColModeName(scope.row.colMode) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="采集周期" align="center" prop="colCycle">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span>{{ getColCycleName(scope.row.colCycle) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <!-- 检查是否已绑定 -->
|
|
|
+ <div v-if="isAlreadyBound(scope.row.deviceCode)">
|
|
|
+ <span>已绑定</span>
|
|
|
+ </div>
|
|
|
+ <div v-else>
|
|
|
+ <el-button
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-arrow-up"
|
|
|
+ @click="moveToDevice(scope.row)"
|
|
|
+ >绑定</el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <pagination v-show="total>0" :total="total" :page.sync="MeterQueryParams.pageNum" :limit.sync="MeterQueryParams.pageSize"
|
|
|
+ @pagination="getMeterData" />
|
|
|
</div>
|
|
|
<!-- 组织机构 -->
|
|
|
<div v-if="activeTab === 'organ'">
|
|
@@ -370,17 +392,23 @@
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
</el-row>
|
|
|
+ <!-- 拓扑图弹框 -->
|
|
|
+ <el-dialog :visible.sync="topologyDialogVisible" title="拓扑图" width="1200px">
|
|
|
+ <div ref="topologyChart" style="width: 100%; height: 600px;"></div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import * as echarts from 'echarts';
|
|
|
import Treeselect from '@riophae/vue-treeselect'
|
|
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
|
|
import { areaTreeSelect, listDetailArea } from '@/api/basecfg/area'
|
|
|
import SubTitle from '@/components/SubTitle/index.vue'
|
|
|
import { getDevProcess, getEmsTag } from '@/api/commonApi'
|
|
|
import { delDevice, listDevice } from '@/api/device/meterDevice'
|
|
|
-import { addAllByObj, listByObj } from '@/api/basecfg/meterBoundary'
|
|
|
+import { addAllByObj, listByObj ,getBoundaryTreeByArea } from '@/api/basecfg/meterBoundary'
|
|
|
import { listDept } from '@/api/system/dept'
|
|
|
import { getFacsCategorygetByCode, listAllFacs, listFacs } from '@/api/basecfg/emsfacs'
|
|
|
import { listDevRecursionByArea } from '@/api/device/device'
|
|
@@ -393,6 +421,7 @@ export default {
|
|
|
components: { SubTitle, Treeselect },
|
|
|
data() {
|
|
|
return {
|
|
|
+ topologyDialogVisible: false, // 控制拓扑图弹框的显示与隐藏
|
|
|
activeTab: 'area',
|
|
|
areaOptions: [],
|
|
|
organOptions: [],
|
|
@@ -583,6 +612,109 @@ export default {
|
|
|
|
|
|
},
|
|
|
methods: {
|
|
|
+ showTopology(data) {
|
|
|
+ this.topologyDialogVisible = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ getBoundaryTreeByArea(data.areaCode).then(response => {
|
|
|
+ const topologyData = response.data;
|
|
|
+ console.log("拓扑图接口获取数据",topologyData);
|
|
|
+ this.initTreeECharts(topologyData);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ initTreeECharts(topologyData) {
|
|
|
+ const chartDom = this.$refs.topologyChart;
|
|
|
+ const myChart = echarts.init(chartDom);
|
|
|
+
|
|
|
+ // 递归处理拓扑图数据,生成echarts需要的数据格式
|
|
|
+ const processTopologyData = (data) => {
|
|
|
+ const hasBoundDevices = data.bindElecMeterDevs && data.bindElecMeterDevs.length > 0;
|
|
|
+ const nodeColor = hasBoundDevices ? '#99e170' : '#D3D3D3';
|
|
|
+ return {
|
|
|
+ name: data.objName, // 节点名称取objName
|
|
|
+ children: data.children ? data.children.map(child => processTopologyData(child)) : [],
|
|
|
+ value: [data.bindElecMeterDevs].flat().join(', '),
|
|
|
+ itemStyle: { // 设置节点样式
|
|
|
+ borderColor: '#D8D3D1',
|
|
|
+ borderWidth: 1,
|
|
|
+ color: nodeColor
|
|
|
+ }
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理根节点数据
|
|
|
+ const processedData = processTopologyData(topologyData);
|
|
|
+
|
|
|
+ // 指定图表的配置项和数据
|
|
|
+ const option = {
|
|
|
+
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ triggerOn: 'mousemove',
|
|
|
+ formatter: function (params) {
|
|
|
+ let bindElecMeterDevs = params.data.value;
|
|
|
+ if (!Array.isArray(bindElecMeterDevs)) {
|
|
|
+ bindElecMeterDevs = [bindElecMeterDevs];
|
|
|
+ }
|
|
|
+ const bindElecMeterDevsStr = bindElecMeterDevs.length > 0 ? bindElecMeterDevs.join(', ') : '无';
|
|
|
+ return `绑定电表:${bindElecMeterDevsStr}`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '拓扑图',
|
|
|
+ type: 'tree',
|
|
|
+ layout: 'compactBox',
|
|
|
+ orient: 'TB', // 从上到下的布局
|
|
|
+ data: [processedData], // 直接使用传入的数据
|
|
|
+ top: '5%',
|
|
|
+ left: '1%',
|
|
|
+ bottom: '5%',
|
|
|
+ right: '5%',
|
|
|
+ symbol: 'rect', // 设置节点形状为长方形
|
|
|
+ symbolSize: [100, 40],
|
|
|
+ label: {
|
|
|
+ position: 'inside',
|
|
|
+ verticalAlign: 'middle',
|
|
|
+ align: 'center',
|
|
|
+ fontSize: 12,
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ leaves: {
|
|
|
+ label: {
|
|
|
+ position: 'inside', // 叶子节点标签位置在内部
|
|
|
+ verticalAlign: 'middle',
|
|
|
+ align: 'center'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ expandAndCollapse: true,
|
|
|
+ animationDuration: 550,
|
|
|
+ animationDurationUpdate: 750,
|
|
|
+ itemStyle: {
|
|
|
+ borderColor: '#555',
|
|
|
+ borderWidth: 1,
|
|
|
+ gapWidth: 10, // 增加节点之间的间隔
|
|
|
+ },
|
|
|
+ edgeLabel: {
|
|
|
+ position: 'middle',
|
|
|
+ fontSize: 10
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ itemStyle: {
|
|
|
+ borderColor: '#333',
|
|
|
+ borderWidth: 2
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ myChart.setOption(option);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
getAreaFacsTree(areaCode, recursion) {
|
|
|
areaTreeSelect(areaCode, recursion).then(response => {
|
|
|
this.facsOptions = [{
|
|
@@ -669,23 +801,23 @@ export default {
|
|
|
|
|
|
/**地理位置*/
|
|
|
handleNodeClick(data) {
|
|
|
+ // 不执行任何操作
|
|
|
if (data.id === 'all') {
|
|
|
- this.areaOptions = this.areaList.slice(1).map(item => {
|
|
|
- const { children, ...rest } = item;
|
|
|
- return rest;
|
|
|
- });
|
|
|
- } else {
|
|
|
- // 判断是否为叶子节点
|
|
|
- if (!data.children || data.children.length === 0) {
|
|
|
- // 叶子节点,展示该节点的数据
|
|
|
- this.areaOptions = [data];
|
|
|
- } else {
|
|
|
- this.areaOptions = data.children.map(child => {
|
|
|
- const { children, ...rest } = child;
|
|
|
- return rest;
|
|
|
- });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ console.log("地理位置!!!!",data)
|
|
|
+ this.queryParams.areaCode = data.areaCode;
|
|
|
+ if (data && data.ancestors) {
|
|
|
+ const ancestorsArray = data.ancestors.split(',');
|
|
|
+ if (ancestorsArray.length > 1) {
|
|
|
+ // 当 ancestors 长度大于 1 时,取第二个 areaCode 作为父级 areaCode
|
|
|
+ this.MeterQueryParams.areaCode= ancestorsArray[1];
|
|
|
+ } else if (ancestorsArray.length === 1) {
|
|
|
+ // 当 ancestors 长度等于 1 时,取该对象本身的 areaCode 作为父级 areaCode
|
|
|
+ this.MeterQueryParams.areaCode = data.areaCode;
|
|
|
}
|
|
|
}
|
|
|
+ this.getMeterData(data);
|
|
|
},
|
|
|
|
|
|
/**组织机构*/
|
|
@@ -726,17 +858,11 @@ export default {
|
|
|
this.tableKey += 1; // 改变 key 值以强制重新渲染
|
|
|
// 将“全部”节点添加到区域列表的顶部
|
|
|
this.areaList.unshift(this.allAreaNode);
|
|
|
- this.loading = false;
|
|
|
- this.Node();
|
|
|
+ console.log(" this.areaList", this.areaList)
|
|
|
+ this.handleNodeClick(this.areaList[1]);//常泰北区
|
|
|
+ this.loading = false;
|
|
|
});
|
|
|
},
|
|
|
- Node() {
|
|
|
- if (this.areaList.length > 0) {
|
|
|
- this.handleNodeClick(this.areaList[0]);}
|
|
|
- if (this.areaList[0].children && this.areaList[0].children.length > 1) {
|
|
|
- this.handleNodeClick(this.areaList[0].children[1]);
|
|
|
- }
|
|
|
- },
|
|
|
|
|
|
// 递归提取 tagNames
|
|
|
extractTagNames(list) {
|
|
@@ -986,11 +1112,13 @@ export default {
|
|
|
} else if (this.activeTab === 'device') {
|
|
|
objType = 3;
|
|
|
}
|
|
|
+ console.log("this.MeterQueryParams",this.MeterQueryParams)
|
|
|
listDevice({ ...this.MeterQueryParams, meterCls}).then(response => {
|
|
|
this.Unbound = response.rows;
|
|
|
this.total = response.total;
|
|
|
this.loading = false;
|
|
|
});
|
|
|
+ console.log("this.queryParams.areaCode",this.queryParams.areaCode)
|
|
|
listByObj(objType,meterCls,this.queryParams.areaCode).then(response=>{
|
|
|
this.Bound = response.data;
|
|
|
})
|
|
@@ -998,43 +1126,59 @@ export default {
|
|
|
|
|
|
/**绑定设备*/
|
|
|
moveToDevice(row) {
|
|
|
- let objType;
|
|
|
- if (this.activeTab === 'area') {
|
|
|
- objType = 1;
|
|
|
- } else if (this.activeTab === 'organ') {
|
|
|
- objType = 4;
|
|
|
- } else if (this.activeTab === 'facs') {
|
|
|
- objType = 2;
|
|
|
- } else if (this.activeTab === 'device') {
|
|
|
- objType = 3;
|
|
|
- }
|
|
|
- const index = this.Unbound.indexOf(row);
|
|
|
- if (index !== -1) {
|
|
|
- this.Unbound.splice(index, 1);
|
|
|
- }
|
|
|
- const boundRow = {
|
|
|
- meterDeviceName: row.deviceName,
|
|
|
- meterDevice: row.deviceCode,
|
|
|
- boundaryObj: this.queryParams.areaCode,
|
|
|
- objType: objType
|
|
|
- };
|
|
|
- this.Bound.push(boundRow);
|
|
|
+ this.$confirm('是否确定绑定?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ let objType;
|
|
|
+ if (this.activeTab === 'area') {
|
|
|
+ objType = 1;
|
|
|
+ } else if (this.activeTab === 'organ') {
|
|
|
+ objType = 4;
|
|
|
+ } else if (this.activeTab === 'facs') {
|
|
|
+ objType = 2;
|
|
|
+ } else if (this.activeTab === 'device') {
|
|
|
+ objType = 3;
|
|
|
+ }
|
|
|
+ const index = this.Unbound.indexOf(row);
|
|
|
+ if (index !== -1) {
|
|
|
+ this.Unbound.splice(index, 1);
|
|
|
+ }
|
|
|
+ const boundRow = {
|
|
|
+ meterDeviceName: row.deviceName,
|
|
|
+ meterDevice: row.deviceCode,
|
|
|
+ boundaryObj: this.queryParams.areaCode,
|
|
|
+ objType: objType
|
|
|
+ };
|
|
|
+ this.Bound.push(boundRow);
|
|
|
+ this.saveByObj();
|
|
|
+ }).catch(() => {
|
|
|
+ });
|
|
|
},
|
|
|
isAlreadyBound(deviceCode) {
|
|
|
return this.Bound.some(boundDevice => boundDevice.meterDevice === deviceCode);
|
|
|
},
|
|
|
/**取消绑定设备*/
|
|
|
downToDevice(row){
|
|
|
- const index = this.Bound.indexOf(row);
|
|
|
- if (index !== -1) {
|
|
|
- this.Bound.splice(index, 1);
|
|
|
- }
|
|
|
- this.Unbound.push(row);
|
|
|
- delete row.boundaryObj;
|
|
|
- delete row.meterDevice;
|
|
|
- delete row.objType;
|
|
|
- },
|
|
|
+ this.$confirm('是否取消绑定?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const index = this.Bound.indexOf(row);
|
|
|
+ if (index !== -1) {
|
|
|
+ this.Bound.splice(index, 1);
|
|
|
+ }
|
|
|
+ this.Unbound.push(row);
|
|
|
+ delete row.boundaryObj;
|
|
|
+ delete row.meterDevice;
|
|
|
+ delete row.objType;
|
|
|
+ this.saveByObj();
|
|
|
+ }).catch(() => {
|
|
|
|
|
|
+ });
|
|
|
+ },
|
|
|
/**保存绑定的设备*/
|
|
|
saveByObj() {
|
|
|
// 构建要保存的数据数组
|
|
@@ -1143,7 +1287,6 @@ export default {
|
|
|
},
|
|
|
getTagStyle(tagName) {
|
|
|
// 从tagName找到对应的tagCode
|
|
|
-
|
|
|
const tagCode = this.emsTagOptions.find(tag => tag.label === tagName)?.value;
|
|
|
const color = this.tagCodeToColorMap[tagCode] || '#FFFFFF'; // 默认白色
|
|
|
return {
|
|
@@ -1161,3 +1304,11 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.custom-tree-node {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+</style>
|