|
@@ -1,311 +1,1691 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="app-container">
|
|
|
|
|
- <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
|
|
- <el-form-item label="流动介质" prop="emsCls">
|
|
|
|
|
- <treeselect v-model="queryParams.emsCls" :options="emsClsOptions" :show-count="true" placeholder="请选择流动介质" :style="{ width: '200px' }"/>
|
|
|
|
|
- </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-row :gutter="10" class="mb8">
|
|
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- plain
|
|
|
|
|
- icon="el-icon-plus"
|
|
|
|
|
- size="mini"
|
|
|
|
|
- @click="handleAdd"
|
|
|
|
|
- v-hasPermi="['basecfg:flowrel:add']"
|
|
|
|
|
- >新增</el-button>
|
|
|
|
|
|
|
+ <div class="flow-rel-container">
|
|
|
|
|
+ <!-- 顶部工具栏 -->
|
|
|
|
|
+ <el-card class="toolbar-card" shadow="never">
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="selectedArea"
|
|
|
|
|
+ placeholder="请选择区域"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ @change="handleAreaChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="area in areaOptions"
|
|
|
|
|
+ :key="area.id"
|
|
|
|
|
+ :label="area.label"
|
|
|
|
|
+ :value="area.id"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <!-- 使用 vue-treeselect 替代 el-cascader -->
|
|
|
|
|
+ <treeselect
|
|
|
|
|
+ v-model="selectedEnergyType"
|
|
|
|
|
+ :options="energyTypeOptions"
|
|
|
|
|
+ :normalizer="normalizer"
|
|
|
|
|
+ :show-count="true"
|
|
|
|
|
+ placeholder="请选择能源类型"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ searchable
|
|
|
|
|
+ :append-to-body="true"
|
|
|
|
|
+ :z-index="9999"
|
|
|
|
|
+ @input="handleEnergyTypeChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12" style="text-align: right;">
|
|
|
|
|
+ <el-button-group>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ icon="el-icon-plus"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="handleAdd"
|
|
|
|
|
+ v-hasPermi="['basecfg:flowrel:add']"
|
|
|
|
|
+ >
|
|
|
|
|
+ 新增能流
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ :type="layoutMode === 'hierarchical' ? 'warning' : 'default'"
|
|
|
|
|
+ icon="el-icon-rank"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="toggleLayoutMode"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ layoutMode === 'hierarchical' ? '层级布局' : '自由布局' }}
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="success"
|
|
|
|
|
+ icon="el-icon-refresh"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="refreshTopology"
|
|
|
|
|
+ >
|
|
|
|
|
+ 刷新拓扑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="info"
|
|
|
|
|
+ icon="el-icon-zoom-in"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="fitTopology"
|
|
|
|
|
+ >
|
|
|
|
|
+ 适应画布
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="warning"
|
|
|
|
|
+ icon="el-icon-download"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="exportTopology"
|
|
|
|
|
+ >
|
|
|
|
|
+ 导出图片
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-button-group>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="default"
|
|
|
|
|
+ :icon="showList ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @click="toggleShowList"
|
|
|
|
|
+ style="margin-left: 10px"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ showList ? '隐藏列表' : '显示列表' }}
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 主内容区 -->
|
|
|
|
|
+ <el-row :gutter="20" class="main-content">
|
|
|
|
|
+ <!-- 左侧:对象树 -->
|
|
|
|
|
+ <el-col :span="5">
|
|
|
|
|
+ <el-card class="tree-card" shadow="never">
|
|
|
|
|
+ <div slot="header" class="card-header">
|
|
|
|
|
+ <span>设施设备树</span>
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="treeFilterText"
|
|
|
|
|
+ placeholder="搜索设施/设备"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ prefix-icon="el-icon-search"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 60%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-tree
|
|
|
|
|
+ ref="objectTree"
|
|
|
|
|
+ :data="objectTreeData"
|
|
|
|
|
+ :props="treeProps"
|
|
|
|
|
+ :filter-node-method="filterNode"
|
|
|
|
|
+ node-key="id"
|
|
|
|
|
+ default-expand-all
|
|
|
|
|
+ :expand-on-click-node="false"
|
|
|
|
|
+ highlight-current
|
|
|
|
|
+ @node-click="handleNodeClick"
|
|
|
|
|
+ >
|
|
|
|
|
+ <span class="custom-tree-node" slot-scope="{ node, data }">
|
|
|
|
|
+ <span class="tree-label">
|
|
|
|
|
+ <i :class="getNodeIcon(data)" :style="{ color: getNodeColor(data) }"></i>
|
|
|
|
|
+ {{ node.label }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="tree-actions" v-if="data.type !== 'area'">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ icon="el-icon-upload2"
|
|
|
|
|
+ @click.stop="handleSetAsSource(data)"
|
|
|
|
|
+ title="设为输出源"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ icon="el-icon-download"
|
|
|
|
|
+ @click.stop="handleSetAsTarget(data)"
|
|
|
|
|
+ title="设为输入目标"
|
|
|
|
|
+ />
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </el-tree>
|
|
|
|
|
+ </el-card>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button
|
|
|
|
|
- type="success"
|
|
|
|
|
- plain
|
|
|
|
|
- icon="el-icon-edit"
|
|
|
|
|
- size="mini"
|
|
|
|
|
- :disabled="single"
|
|
|
|
|
- @click="handleUpdate"
|
|
|
|
|
- v-hasPermi="['basecfg:flowrel:edit']"
|
|
|
|
|
- >修改</el-button>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 中间:拓扑图 -->
|
|
|
|
|
+ <el-col :span="19">
|
|
|
|
|
+ <el-card class="topology-card" shadow="never">
|
|
|
|
|
+ <div slot="header" class="card-header">
|
|
|
|
|
+ <span>能流拓扑图</span>
|
|
|
|
|
+ <div class="topology-legend">
|
|
|
|
|
+ <span class="legend-item" title="电网、配电">
|
|
|
|
|
+ <svg width="20" height="20" viewBox="0 0 1024 1024">
|
|
|
|
|
+ <path d="M563.785143 21.211429L639.926857 0l281.819429 968.850286-76.214857 21.211428L563.785143 21.211429zM45.933714 961.828571L320.073143 0l76.141714 21.211429L122.148571 983.04l-76.141714-21.211429z" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M83.968 841.581714l677.814857-360.667428 38.034286 63.634285-677.741714 360.594286zM167.789714 544.548571l38.034286-63.634285 677.814857 360.594285-38.034286 63.634286z" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M0.219429 148.48h951.954285v70.729143H0.219429zM198.217143 325.339429h555.958857v70.656H198.217143z" fill="#1890ff"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ 网
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="legend-item" title="光伏、风电等发电设施">
|
|
|
|
|
+ <svg width="20" height="20" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="6" y="24" width="52" height="32" rx="2" fill="#1890ff" transform="skewY(-6)"/>
|
|
|
|
|
+ <circle cx="50" cy="12" r="7" fill="#faad14"/>
|
|
|
|
|
+ <path d="M50 2v4M42 12h4M54 12h4" stroke="#faad14" stroke-width="2"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ 源
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="legend-item" title="储能电池、蓄电设施">
|
|
|
|
|
+ <svg width="20" height="20" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="12" y="16" width="40" height="40" rx="4" fill="#722ed1"/>
|
|
|
|
|
+ <rect x="24" y="8" width="16" height="8" rx="2" fill="#722ed1"/>
|
|
|
|
|
+ <rect x="18" y="28" width="28" height="6" rx="1" fill="#d3adf7"/>
|
|
|
|
|
+ <rect x="18" y="40" width="28" height="6" rx="1" fill="#b37feb"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ 储
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="legend-item" title="照明、空调、充电桩等用能设施">
|
|
|
|
|
+ <svg width="20" height="20" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="12" y="12" width="40" height="40" rx="4" fill="#fa8c16"/>
|
|
|
|
|
+ <circle cx="32" cy="32" r="10" fill="none" stroke="#fff" stroke-width="3"/>
|
|
|
|
|
+ <path d="M32 22v6M32 38v4M22 32h6M38 32h4" stroke="#fff" stroke-width="2"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ 荷
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="legend-item" title="能量流动方向">
|
|
|
|
|
+ <svg width="20" height="20" viewBox="0 0 64 64">
|
|
|
|
|
+ <defs>
|
|
|
|
|
+ <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
|
|
|
|
+ <polygon points="0 0, 10 3.5, 0 7" fill="#E6A23C"/>
|
|
|
|
|
+ </marker>
|
|
|
|
|
+ </defs>
|
|
|
|
|
+ <line x1="8" y1="32" x2="48" y2="32" stroke="#E6A23C" stroke-width="3" marker-end="url(#arrowhead)"/>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ 能流
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-loading="topologyLoading"
|
|
|
|
|
+ element-loading-text="加载拓扑数据中..."
|
|
|
|
|
+ class="topology-container"
|
|
|
|
|
+ ref="topologyContainer"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div v-if="!topologyData.nodes || topologyData.nodes.length === 0" class="empty-topology">
|
|
|
|
|
+ <el-empty description="暂无能流数据">
|
|
|
|
|
+ <el-button type="primary" size="small" @click="handleAdd">
|
|
|
|
|
+ 新增能流关系
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-empty>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div id="topology-network" style="width: 100%; height: 100%;"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 底部:能流关系列表 -->
|
|
|
|
|
+ <el-card v-show="showList" class="list-card" shadow="never" style="position: relative; z-index: 10;">
|
|
|
|
|
+ <div slot="header" class="card-header">
|
|
|
|
|
+ <span>能流关系列表</span>
|
|
|
<el-button
|
|
<el-button
|
|
|
type="danger"
|
|
type="danger"
|
|
|
- plain
|
|
|
|
|
icon="el-icon-delete"
|
|
icon="el-icon-delete"
|
|
|
- size="mini"
|
|
|
|
|
- :disabled="multiple"
|
|
|
|
|
- @click="handleDelete"
|
|
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ :disabled="selectedRows.length === 0"
|
|
|
|
|
+ @click="handleBatchDelete"
|
|
|
v-hasPermi="['basecfg:flowrel:remove']"
|
|
v-hasPermi="['basecfg:flowrel:remove']"
|
|
|
- >删除</el-button>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ >
|
|
|
|
|
+ 批量删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ v-loading="listLoading"
|
|
|
|
|
+ :data="flowRelList"
|
|
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
|
|
+ @row-click="handleRowClick"
|
|
|
|
|
+ highlight-current-row
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
+ <el-table-column label="序号" type="index" width="60" align="center" />
|
|
|
|
|
+ <el-table-column label="输出对象" min-width="200" show-overflow-tooltip>
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag :type="scope.row.exportObjType === 1 ? 'primary' : 'success'" size="small">
|
|
|
|
|
+ {{ scope.row.exportObjType === 1 ? '设施' : '设备' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ {{ scope.row.exportObjName }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="流入对象" min-width="200" show-overflow-tooltip>
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag :type="scope.row.inputObjType === 1 ? 'primary' : 'success'" size="small">
|
|
|
|
|
+ {{ scope.row.inputObjType === 1 ? '设施' : '设备' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ {{ scope.row.inputObjName }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="流动介质" align="center" width="120" show-overflow-tooltip>
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ {{ scope.row.emsClsName }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="容量/功率" align="center" width="120">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <span v-if="scope.row.flowCapacity">
|
|
|
|
|
+ {{ scope.row.flowCapacity }} {{ scope.row.flowUnit || 'kW' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="状态" align="center" width="80">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="scope.row.enableStatus"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ @change="handleStatusChange(scope.row)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" align="center" width="200" fixed="right">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ icon="el-icon-view"
|
|
|
|
|
+ @click.stop="handleView(scope.row)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 查看
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ icon="el-icon-edit"
|
|
|
|
|
+ @click.stop="handleUpdate(scope.row)"
|
|
|
|
|
+ v-hasPermi="['basecfg:flowrel:edit']"
|
|
|
|
|
+ >
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ icon="el-icon-delete"
|
|
|
|
|
+ class="danger-text"
|
|
|
|
|
+ @click.stop="handleDelete(scope.row)"
|
|
|
|
|
+ v-hasPermi="['basecfg:flowrel:remove']"
|
|
|
|
|
+ >
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="total > 0"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ :page.sync="queryParams.pageNum"
|
|
|
|
|
+ :limit.sync="queryParams.pageSize"
|
|
|
|
|
+ @pagination="getList"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 新增/编辑能流关系对话框 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ :title="formTitle"
|
|
|
|
|
+ :visible.sync="formDialogVisible"
|
|
|
|
|
+ width="700px"
|
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
|
+ @close="handleFormClose"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="flowRelForm"
|
|
|
|
|
+ :model="flowRelForm"
|
|
|
|
|
+ :rules="formRules"
|
|
|
|
|
+ label-width="120px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="输出对象类型" prop="exportObjType">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="flowRelForm.exportObjType"
|
|
|
|
|
+ placeholder="请选择"
|
|
|
|
|
+ @change="handleExportTypeChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option label="设施" :value="1" />
|
|
|
|
|
+ <el-option label="设备" :value="2" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="输出对象" prop="exportObj">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="flowRelForm.exportObj"
|
|
|
|
|
+ placeholder="请选择输出对象"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ @change="handleExportObjChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in exportObjOptions"
|
|
|
|
|
+ :key="item.code"
|
|
|
|
|
+ :label="item.name"
|
|
|
|
|
+ :value="item.code"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="流入对象类型" prop="inputObjType">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="flowRelForm.inputObjType"
|
|
|
|
|
+ placeholder="请选择"
|
|
|
|
|
+ @change="handleInputTypeChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option label="设施" :value="1" />
|
|
|
|
|
+ <el-option label="设备" :value="2" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="流入对象" prop="inputObj">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="flowRelForm.inputObj"
|
|
|
|
|
+ placeholder="请选择流入对象"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ @change="handleInputObjChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in inputObjOptions"
|
|
|
|
|
+ :key="item.code"
|
|
|
|
|
+ :label="item.name"
|
|
|
|
|
+ :value="item.code"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
|
|
|
- <el-table v-loading="loading" :data="relList" @selection-change="handleSelectionChange">
|
|
|
|
|
- <el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
- <el-table-column label="输出对象" align="center" prop="exportObjName">
|
|
|
|
|
- <template slot-scope="scope">
|
|
|
|
|
- <span>{{ '(' + getObjType(scope.row.exportObjType) + ') ' +scope.row.exportObjName }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="流入对象" align="center" prop="inputObjName">
|
|
|
|
|
- <template slot-scope="scope">
|
|
|
|
|
- <span>{{ '(' + getObjType(scope.row.inputObjType) + ') ' +scope.row.inputObjName }}</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="流动介质" align="center" prop="emsClsName" />
|
|
|
|
|
- <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-edit" @click="handleUpdate(scope.row)" v-hasPermi="['basecfg:flowrel:edit']">
|
|
|
|
|
- 修改</el-button>
|
|
|
|
|
- <el-button size="mini" type="text" icon="el-icon-delete" class="deleteBtn" @click="handleDelete(scope.row)" v-hasPermi="['basecfg:flowrel:remove']">
|
|
|
|
|
- 删除</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- </el-table>
|
|
|
|
|
-
|
|
|
|
|
- <pagination
|
|
|
|
|
- v-show="total>0"
|
|
|
|
|
- :total="total"
|
|
|
|
|
- :page.sync="queryParams.pageNum"
|
|
|
|
|
- :limit.sync="queryParams.pageSize"
|
|
|
|
|
- @pagination="getList"
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <!-- 添加或修改能源设施能流关系对话框 -->
|
|
|
|
|
- <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
|
|
|
|
- <el-form ref="mergeForm" :model="mergeForm" :rules="rules" label-width="80px">
|
|
|
|
|
- <el-form-item label="输出类别" prop="exportObjType">
|
|
|
|
|
- <el-select v-model="mergeForm.exportObjType" @change="setCodePrefix" style="width:100%">
|
|
|
|
|
- <el-option v-for="item in objOptions" :label="item.name" :value="item.code" :key="item.code" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="输出对象" prop="exportObj">
|
|
|
|
|
- <el-input v-model="mergeForm.exportObj" placeholder="输出对象" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="流入类别" prop="inputObjType">
|
|
|
|
|
- <el-select v-model="mergeForm.inputObjType" @change="setCodeCompose" style="width:100%">
|
|
|
|
|
- <el-option v-for="item in objOptions" :label="item.name" :value="item.code" :key="item.code" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="流入对象" prop="inputObj">
|
|
|
|
|
- <el-input v-model="mergeForm.inputObj" placeholder="流入对象" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
<el-form-item label="流动介质" prop="emsCls">
|
|
<el-form-item label="流动介质" prop="emsCls">
|
|
|
- <treeselect v-model="mergeForm.emsCls" :options="emsClsOptions" :show-count="true" placeholder="请选择" />
|
|
|
|
|
|
|
+ <!-- 使用 vue-treeselect 替代 el-cascader -->
|
|
|
|
|
+ <treeselect
|
|
|
|
|
+ v-model="flowRelForm.emsCls"
|
|
|
|
|
+ :options="energyTypeOptions"
|
|
|
|
|
+ :normalizer="normalizer"
|
|
|
|
|
+ :show-count="true"
|
|
|
|
|
+ placeholder="请选择流动介质"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ searchable
|
|
|
|
|
+ :append-to-body="true"
|
|
|
|
|
+ :z-index="10000"
|
|
|
|
|
+ @input="handleEmsClsChange"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="能流描述" prop="flowDesc">
|
|
|
|
|
- <el-input v-model="mergeForm.flowDesc" placeholder="能流描述" />
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="流动容量/功率">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="flowRelForm.flowCapacity"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ placeholder="请输入"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="容量单位">
|
|
|
|
|
+ <el-select v-model="flowRelForm.flowUnit" placeholder="请选择单位" style="width: 100%">
|
|
|
|
|
+ <el-option label="kW" value="kW" />
|
|
|
|
|
+ <el-option label="MW" value="MW" />
|
|
|
|
|
+ <el-option label="kWh" value="kWh" />
|
|
|
|
|
+ <el-option label="MWh" value="MWh" />
|
|
|
|
|
+ <el-option label="m³/h" value="m³/h" />
|
|
|
|
|
+ <el-option label="t/h" value="t/h" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="能流描述">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="flowRelForm.flowDesc"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ placeholder="请输入能流描述信息"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="启用状态">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="flowRelForm.enableStatus"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
<div slot="footer" class="dialog-footer">
|
|
<div slot="footer" class="dialog-footer">
|
|
|
- <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
|
|
- <el-button @click="cancel">取 消</el-button>
|
|
|
|
|
|
|
+ <el-button @click="formDialogVisible = false">取 消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="submitForm" :loading="formSubmitting">确 定</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 查看详情对话框 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ title="能流关系详情"
|
|
|
|
|
+ :visible.sync="viewDialogVisible"
|
|
|
|
|
+ width="600px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-descriptions :column="2" border v-if="currentRow">
|
|
|
|
|
+ <el-descriptions-item label="输出对象类型">
|
|
|
|
|
+ <el-tag :type="currentRow.exportObjType === 1 ? 'primary' : 'success'">
|
|
|
|
|
+ {{ currentRow.exportObjType === 1 ? '设施' : '设备' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="输出对象">
|
|
|
|
|
+ {{ currentRow.exportObjName }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="流入对象类型">
|
|
|
|
|
+ <el-tag :type="currentRow.inputObjType === 1 ? 'primary' : 'success'">
|
|
|
|
|
+ {{ currentRow.inputObjType === 1 ? '设施' : '设备' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="流入对象">
|
|
|
|
|
+ {{ currentRow.inputObjName }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="流动介质" :span="2">
|
|
|
|
|
+ {{ currentRow.emsClsName }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="流动容量" :span="2">
|
|
|
|
|
+ <span v-if="currentRow.flowCapacity">
|
|
|
|
|
+ {{ currentRow.flowCapacity }} {{ currentRow.flowUnit || 'kW' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span v-else>-</span>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="启用状态" :span="2">
|
|
|
|
|
+ <el-tag :type="currentRow.enableStatus === 1 ? 'success' : 'info'">
|
|
|
|
|
+ {{ currentRow.enableStatus === 1 ? '启用' : '禁用' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="能流描述" :span="2">
|
|
|
|
|
+ {{ currentRow.flowDesc || '-' }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="创建时间" :span="2">
|
|
|
|
|
+ {{ currentRow.createTime }}
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
-import { listRel, getRel, delRel, addRel, updateRel } from "@/api/basecfg/flowrel";
|
|
|
|
|
-import { listAllFacs } from "@/api/basecfg/emsfacs"
|
|
|
|
|
-import { getEmsClsTree } from "@/api/commonApi"
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ listFlowRel,
|
|
|
|
|
+ getFlowRel,
|
|
|
|
|
+ addFlowRel,
|
|
|
|
|
+ updateFlowRel,
|
|
|
|
|
+ delFlowRel,
|
|
|
|
|
+ delFlowRelBatch,
|
|
|
|
|
+ getFlowTopology
|
|
|
|
|
+} from '@/api/basecfg/flowrel'
|
|
|
|
|
+import { listAllFacs } from '@/api/basecfg/emsfacs'
|
|
|
|
|
+import { listDevice } from '@/api/device/device'
|
|
|
|
|
+import { areaTreeSelect } from '@/api/basecfg/area'
|
|
|
|
|
+import { getEmsClsTree } from '@/api/commonApi'
|
|
|
|
|
+
|
|
|
|
|
+// 引入 vue-treeselect 组件
|
|
|
import Treeselect from '@riophae/vue-treeselect'
|
|
import Treeselect from '@riophae/vue-treeselect'
|
|
|
-import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
|
|
|
|
|
|
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
|
|
|
|
|
+
|
|
|
|
|
+// 引入vis-network用于拓扑图展示
|
|
|
|
|
+// 需要安装: npm install vis-network --save
|
|
|
|
|
+import { Network } from 'vis-network/standalone/esm/vis-network'
|
|
|
|
|
+
|
|
|
export default {
|
|
export default {
|
|
|
- name: "Rel",
|
|
|
|
|
- components: { Treeselect },
|
|
|
|
|
|
|
+ name: 'FlowRel',
|
|
|
|
|
+ components: {
|
|
|
|
|
+ Treeselect
|
|
|
|
|
+ },
|
|
|
data() {
|
|
data() {
|
|
|
return {
|
|
return {
|
|
|
- // 遮罩层
|
|
|
|
|
- loading: true,
|
|
|
|
|
- // 选中数组
|
|
|
|
|
- ids: [],
|
|
|
|
|
- // 非单个禁用
|
|
|
|
|
- single: true,
|
|
|
|
|
- // 非多个禁用
|
|
|
|
|
- multiple: true,
|
|
|
|
|
- // 显示搜索条件
|
|
|
|
|
- showSearch: true,
|
|
|
|
|
- // 总条数
|
|
|
|
|
- total: 0,
|
|
|
|
|
- // 能源设施能流关系表格数据
|
|
|
|
|
- relList: [],
|
|
|
|
|
- // 弹出层标题
|
|
|
|
|
- title: "",
|
|
|
|
|
- // 是否显示弹出层
|
|
|
|
|
- open: false,
|
|
|
|
|
|
|
+ // 筛选条件
|
|
|
|
|
+ selectedArea: null,
|
|
|
|
|
+ selectedEnergyType: null,
|
|
|
|
|
+ treeFilterText: '',
|
|
|
|
|
+
|
|
|
// 区域选项
|
|
// 区域选项
|
|
|
- areaOptions: undefined,
|
|
|
|
|
- // 设施选项
|
|
|
|
|
- facsOptions: undefined,
|
|
|
|
|
- // 能源分类树
|
|
|
|
|
- emsClsOptions: [],
|
|
|
|
|
- // 查询参数
|
|
|
|
|
|
|
+ areaOptions: [],
|
|
|
|
|
+
|
|
|
|
|
+ // 能源类型选项
|
|
|
|
|
+ energyTypeOptions: [],
|
|
|
|
|
+
|
|
|
|
|
+ // vue-treeselect 的字段映射
|
|
|
|
|
+ normalizer(node) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: node.code || node.id,
|
|
|
|
|
+ label: node.name || node.label,
|
|
|
|
|
+ children: node.children && node.children.length > 0 ? node.children : undefined
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 对象树数据
|
|
|
|
|
+ objectTreeData: [],
|
|
|
|
|
+ treeProps: {
|
|
|
|
|
+ children: 'children',
|
|
|
|
|
+ label: 'label'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 拓扑图
|
|
|
|
|
+ topologyLoading: false,
|
|
|
|
|
+ topologyData: {
|
|
|
|
|
+ nodes: [],
|
|
|
|
|
+ edges: []
|
|
|
|
|
+ },
|
|
|
|
|
+ network: null,
|
|
|
|
|
+ layoutMode: 'hierarchical', // 布局模式:'hierarchical'层级布局 | 'force'力导向布局
|
|
|
|
|
+
|
|
|
|
|
+ // 列表
|
|
|
|
|
+ showList: false,
|
|
|
|
|
+ listLoading: false,
|
|
|
|
|
+ flowRelList: [],
|
|
|
|
|
+ selectedRows: [],
|
|
|
|
|
+ total: 0,
|
|
|
queryParams: {
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
- exportObjType: null,
|
|
|
|
|
|
|
+ areaCode: null,
|
|
|
|
|
+ emsClsCode: null
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 表单
|
|
|
|
|
+ formDialogVisible: false,
|
|
|
|
|
+ viewDialogVisible: false,
|
|
|
|
|
+ formTitle: '',
|
|
|
|
|
+ formSubmitting: false,
|
|
|
|
|
+ flowRelForm: {
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ exportObjType: 1,
|
|
|
exportObj: null,
|
|
exportObj: null,
|
|
|
- inputObjType: null,
|
|
|
|
|
|
|
+ exportObjName: null,
|
|
|
|
|
+ inputObjType: 2,
|
|
|
inputObj: null,
|
|
inputObj: null,
|
|
|
|
|
+ inputObjName: null,
|
|
|
emsCls: null,
|
|
emsCls: null,
|
|
|
|
|
+ emsClsName: null,
|
|
|
|
|
+ flowCapacity: null,
|
|
|
|
|
+ flowUnit: 'kW',
|
|
|
flowDesc: null,
|
|
flowDesc: null,
|
|
|
|
|
+ areaCode: null,
|
|
|
|
|
+ enableStatus: 1
|
|
|
},
|
|
},
|
|
|
- queryFacsParams: {
|
|
|
|
|
- areaCode: ''
|
|
|
|
|
- },
|
|
|
|
|
- objOptions: [
|
|
|
|
|
- { code: 0, name: "设施"},
|
|
|
|
|
- { code: 2, name: "设备"},
|
|
|
|
|
- { code: 3, name: "部件"}
|
|
|
|
|
- ],
|
|
|
|
|
- // 表单参数
|
|
|
|
|
- mergeForm: {},
|
|
|
|
|
- // 表单校验
|
|
|
|
|
- rules: {
|
|
|
|
|
- exportObj: [
|
|
|
|
|
- { required: true, message: "能源输出对象不能为空", trigger: "blur" }
|
|
|
|
|
- ],
|
|
|
|
|
- inputObj: [
|
|
|
|
|
- { required: true, message: "能源流入对象不能为空", trigger: "blur" }
|
|
|
|
|
- ],
|
|
|
|
|
- emsCls: [
|
|
|
|
|
- { required: true, message: "能源流动介质不能为空", trigger: "blur" }
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ formRules: {
|
|
|
|
|
+ exportObjType: [{ required: true, message: '请选择输出对象类型', trigger: 'change' }],
|
|
|
|
|
+ exportObj: [{ required: true, message: '请选择输出对象', trigger: 'change' }],
|
|
|
|
|
+ inputObjType: [{ required: true, message: '请选择流入对象类型', trigger: 'change' }],
|
|
|
|
|
+ inputObj: [{ required: true, message: '请选择流入对象', trigger: 'change' }],
|
|
|
|
|
+ emsCls: [{ required: true, message: '请选择流动介质', trigger: 'change' }]
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ // 对象选项
|
|
|
|
|
+ exportObjOptions: [],
|
|
|
|
|
+ inputObjOptions: [],
|
|
|
|
|
+
|
|
|
|
|
+ // 当前行
|
|
|
|
|
+ currentRow: null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ watch: {
|
|
|
|
|
+ treeFilterText(val) {
|
|
|
|
|
+ this.$refs.objectTree.filter(val)
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
|
- this.getList();
|
|
|
|
|
- this.getAllFacs();
|
|
|
|
|
- this.getEmsCls();
|
|
|
|
|
|
|
+ this.init()
|
|
|
|
|
+ },
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+ // 监听窗口大小变化,调整拓扑图
|
|
|
|
|
+ window.addEventListener('resize', this.handleResize)
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ window.removeEventListener('resize', this.handleResize)
|
|
|
|
|
+ if (this.network) {
|
|
|
|
|
+ this.network.destroy()
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
- /** 查询能源设施能流关系列表 */
|
|
|
|
|
- getList() {
|
|
|
|
|
- this.loading = true;
|
|
|
|
|
- listRel(this.queryParams).then(response => {
|
|
|
|
|
- this.relList = response.rows;
|
|
|
|
|
- this.total = response.total;
|
|
|
|
|
- this.loading = false;
|
|
|
|
|
- });
|
|
|
|
|
- },
|
|
|
|
|
- // 取消按钮
|
|
|
|
|
- cancel() {
|
|
|
|
|
- this.open = false;
|
|
|
|
|
- this.reset();
|
|
|
|
|
- },
|
|
|
|
|
- // 表单重置
|
|
|
|
|
- reset() {
|
|
|
|
|
- this.mergeForm = {
|
|
|
|
|
- id: null,
|
|
|
|
|
- code: null,
|
|
|
|
|
- exportFacsCode: null,
|
|
|
|
|
- inputFacsCode: null,
|
|
|
|
|
- emsCls: null,
|
|
|
|
|
- state: null,
|
|
|
|
|
- actionType: null,
|
|
|
|
|
- createTime: null,
|
|
|
|
|
- updateTime: null
|
|
|
|
|
- };
|
|
|
|
|
- this.resetForm("mergeForm");
|
|
|
|
|
- },
|
|
|
|
|
- /** 搜索按钮操作 */
|
|
|
|
|
- handleQuery() {
|
|
|
|
|
- this.queryParams.pageNum = 1;
|
|
|
|
|
- this.getList();
|
|
|
|
|
- },
|
|
|
|
|
- /** 重置按钮操作 */
|
|
|
|
|
- resetQuery() {
|
|
|
|
|
- this.resetForm("queryForm");
|
|
|
|
|
- this.handleQuery();
|
|
|
|
|
- },
|
|
|
|
|
- // 多选框选中数据
|
|
|
|
|
|
|
+ // ==================== 初始化 ====================
|
|
|
|
|
+ async init() {
|
|
|
|
|
+ await this.getAreaOptions()
|
|
|
|
|
+ await this.getEnergyTypeOptions()
|
|
|
|
|
+ await this.getObjectTree()
|
|
|
|
|
+ await this.getList()
|
|
|
|
|
+ await this.loadTopology()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取区域选项
|
|
|
|
|
+ async getAreaOptions() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await areaTreeSelect('0', 2)
|
|
|
|
|
+ this.areaOptions = this.flattenTree(res.data)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取区域选项失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 扁平化树结构
|
|
|
|
|
+ flattenTree(tree, result = []) {
|
|
|
|
|
+ tree.forEach(node => {
|
|
|
|
|
+ result.push({ id: node.id, label: node.label })
|
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
|
+ this.flattenTree(node.children, result)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ return result
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取能源类型选项
|
|
|
|
|
+ async getEnergyTypeOptions() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getEmsClsTree()
|
|
|
|
|
+ this.energyTypeOptions = res.data || []
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取能源类型失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取对象树(设施+设备)
|
|
|
|
|
+ async getObjectTree() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const areaRes = await areaTreeSelect('0', 3)
|
|
|
|
|
+ const facsRes = await listAllFacs({})
|
|
|
|
|
+ const deviceRes = await listDevice({ pageNum: 1, pageSize: 9999 })
|
|
|
|
|
+
|
|
|
|
|
+ // 构建树结构
|
|
|
|
|
+ const tree = this.buildObjectTree(areaRes.data, facsRes.data, deviceRes.rows)
|
|
|
|
|
+ this.objectTreeData = tree
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取对象树失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 构建对象树结构
|
|
|
|
|
+ buildObjectTree(areas, facilities, devices) {
|
|
|
|
|
+ const tree = []
|
|
|
|
|
+ const areaMap = new Map()
|
|
|
|
|
+
|
|
|
|
|
+ // 构建区域树
|
|
|
|
|
+ const buildAreaTree = (nodes, parentId = '0') => {
|
|
|
|
|
+ return nodes
|
|
|
|
|
+ .filter(node => {
|
|
|
|
|
+ const pid = node.parentId || '0'
|
|
|
|
|
+ return pid === parentId
|
|
|
|
|
+ })
|
|
|
|
|
+ .map(node => {
|
|
|
|
|
+ const treeNode = {
|
|
|
|
|
+ id: node.id,
|
|
|
|
|
+ label: node.label,
|
|
|
|
|
+ type: 'area',
|
|
|
|
|
+ children: []
|
|
|
|
|
+ }
|
|
|
|
|
+ areaMap.set(node.id, treeNode)
|
|
|
|
|
+ treeNode.children = buildAreaTree(nodes, node.id)
|
|
|
|
|
+ return treeNode
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const areaTree = buildAreaTree(areas)
|
|
|
|
|
+
|
|
|
|
|
+ // 添加设施到对应区域
|
|
|
|
|
+ facilities.forEach(facs => {
|
|
|
|
|
+ const areaNode = areaMap.get(facs.refArea)
|
|
|
|
|
+ if (areaNode) {
|
|
|
|
|
+ const facsNode = {
|
|
|
|
|
+ id: `facs_${facs.facsCode}`,
|
|
|
|
|
+ label: facs.facsName,
|
|
|
|
|
+ code: facs.facsCode,
|
|
|
|
|
+ type: 'facility',
|
|
|
|
|
+ objType: 1,
|
|
|
|
|
+ children: []
|
|
|
|
|
+ }
|
|
|
|
|
+ areaNode.children.push(facsNode)
|
|
|
|
|
+
|
|
|
|
|
+ // 添加该设施下的设备
|
|
|
|
|
+ const facsDevices = devices.filter(dev => dev.refFacs === facs.facsCode)
|
|
|
|
|
+ facsDevices.forEach(dev => {
|
|
|
|
|
+ facsNode.children.push({
|
|
|
|
|
+ id: `dev_${dev.deviceCode}`,
|
|
|
|
|
+ label: dev.deviceName,
|
|
|
|
|
+ code: dev.deviceCode,
|
|
|
|
|
+ type: 'device',
|
|
|
|
|
+ objType: 2
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return areaTree
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 列表操作 ====================
|
|
|
|
|
+ async getList() {
|
|
|
|
|
+ this.listLoading = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await listFlowRel(this.queryParams)
|
|
|
|
|
+ this.flowRelList = res.rows || []
|
|
|
|
|
+ this.total = res.total || 0
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ this.$message.error('获取能流关系列表失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.listLoading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
handleSelectionChange(selection) {
|
|
handleSelectionChange(selection) {
|
|
|
- this.ids = selection.map(item => item.id)
|
|
|
|
|
- this.single = selection.length!==1
|
|
|
|
|
- this.multiple = !selection.length
|
|
|
|
|
|
|
+ this.selectedRows = selection
|
|
|
},
|
|
},
|
|
|
- /** 新增按钮操作 */
|
|
|
|
|
- handleAdd() {
|
|
|
|
|
- this.reset();
|
|
|
|
|
- this.open = true;
|
|
|
|
|
- this.title = "添加能源设施能流关系";
|
|
|
|
|
- this.getAllFacs();
|
|
|
|
|
|
|
+
|
|
|
|
|
+ handleRowClick(row) {
|
|
|
|
|
+ this.currentRow = row
|
|
|
|
|
+ // 在拓扑图中高亮显示
|
|
|
|
|
+ if (this.network) {
|
|
|
|
|
+ this.network.selectNodes([
|
|
|
|
|
+ `${row.exportObjType}_${row.exportObj}`,
|
|
|
|
|
+ `${row.inputObjType}_${row.inputObj}`
|
|
|
|
|
+ ])
|
|
|
|
|
+ this.network.selectEdges([`${row.id}`])
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
- /** 修改按钮操作 */
|
|
|
|
|
- handleUpdate(row) {
|
|
|
|
|
- this.reset();
|
|
|
|
|
- const id = row.id || this.ids
|
|
|
|
|
- getRel(id).then(response => {
|
|
|
|
|
- this.mergeForm = response.data;
|
|
|
|
|
- this.open = true;
|
|
|
|
|
- this.title = "修改能源设施能流关系";
|
|
|
|
|
- });
|
|
|
|
|
- this.getAllFacs();
|
|
|
|
|
- },
|
|
|
|
|
- /** 提交按钮 */
|
|
|
|
|
- submitForm() {
|
|
|
|
|
- this.$refs["mergeForm"].validate(valid => {
|
|
|
|
|
- if (valid) {
|
|
|
|
|
- if (this.mergeForm.id != null) {
|
|
|
|
|
- updateRel(this.mergeForm).then(response => {
|
|
|
|
|
- this.$modal.msgSuccess("修改成功");
|
|
|
|
|
- this.open = false;
|
|
|
|
|
- this.getList();
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- addRel(this.mergeForm).then(response => {
|
|
|
|
|
- this.$modal.msgSuccess("新增成功");
|
|
|
|
|
- this.open = false;
|
|
|
|
|
- this.getList();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 拓扑图操作 ====================
|
|
|
|
|
+ async loadTopology() {
|
|
|
|
|
+ this.topologyLoading = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ areaCode: this.queryParams.areaCode,
|
|
|
|
|
+ emsClsCode: this.queryParams.emsClsCode
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await getFlowTopology(params)
|
|
|
|
|
+
|
|
|
|
|
+ if (res.data) {
|
|
|
|
|
+ this.topologyData = res.data
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.initTopology()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('加载拓扑数据失败:', error)
|
|
|
|
|
+ this.$message.error('加载拓扑数据失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.topologyLoading = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initTopology() {
|
|
|
|
|
+ const container = document.getElementById('topology-network')
|
|
|
|
|
+ if (!container) return
|
|
|
|
|
+
|
|
|
|
|
+ // 数据验证
|
|
|
|
|
+ if (!this.topologyData || !this.topologyData.nodes || !this.topologyData.edges) {
|
|
|
|
|
+ console.warn('拓扑数据为空或格式不正确:', this.topologyData)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('拓扑数据:', this.topologyData)
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 源网荷储 SVG 图标定义 ====================
|
|
|
|
|
+ // 电网/电塔图标 (网) - 使用电塔造型
|
|
|
|
|
+ const gridSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
|
|
|
|
+ <path d="M563.785143 21.211429L639.926857 0l281.819429 968.850286-76.214857 21.211428L563.785143 21.211429zM45.933714 961.828571L320.073143 0l76.141714 21.211429L122.148571 983.04l-76.141714-21.211429z" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M83.968 841.581714l677.814857-360.667428 38.034286 63.634285-677.741714 360.594286-38.034286-63.634286z" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M167.789714 544.548571l38.034286-63.634285 677.814857 360.594285-38.034286 63.634286-677.814857-360.594286zM678.034286 381.805714l228.425143-212.114285 53.394285 49.444571-228.498285 212.114286-53.394286-49.444572zM0.219429 219.209143l53.321142-49.517714 228.425143 212.187428-53.248 49.517714-228.498285-212.114285zM320.073143 0h319.853714v70.729143H320.073143V0z" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M0.219429 148.48h951.954285v70.729143H0.219429V148.48zM198.217143 325.339429h555.958857v70.656H198.217143v-70.656z" fill="#1890ff"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 光伏图标 (源) - 太阳能板 + 太阳
|
|
|
|
|
+ const pvSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <defs>
|
|
|
|
|
+ <linearGradient id="pvGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
|
|
|
+ <stop offset="0%" style="stop-color:#1890ff"/>
|
|
|
|
|
+ <stop offset="100%" style="stop-color:#0050b3"/>
|
|
|
|
|
+ </linearGradient>
|
|
|
|
|
+ </defs>
|
|
|
|
|
+ <rect x="6" y="24" width="52" height="32" rx="2" fill="url(#pvGrad)" transform="skewY(-8)"/>
|
|
|
|
|
+ <line x1="6" y1="36" x2="58" y2="28" stroke="#fff" stroke-width="1.5" opacity="0.5"/>
|
|
|
|
|
+ <line x1="6" y1="48" x2="58" y2="40" stroke="#fff" stroke-width="1.5" opacity="0.5"/>
|
|
|
|
|
+ <line x1="22" y1="16" x2="22" y2="56" stroke="#fff" stroke-width="1.5" opacity="0.5"/>
|
|
|
|
|
+ <line x1="38" y1="16" x2="38" y2="56" stroke="#fff" stroke-width="1.5" opacity="0.5"/>
|
|
|
|
|
+ <circle cx="50" cy="12" r="8" fill="#faad14"/>
|
|
|
|
|
+ <path d="M50 0v4M50 20v4M38 12h4M58 12h4M42 4l2.8 2.8M55.2 17.2l2.8 2.8M42 20l2.8-2.8M55.2 6.8l2.8-2.8" stroke="#faad14" stroke-width="2" fill="none"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 风电图标 (源) - 风力发电机
|
|
|
|
|
+ const windSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="30" y="28" width="4" height="34" fill="#8c8c8c"/>
|
|
|
|
|
+ <polygon points="32,60 26,62 38,62" fill="#8c8c8c"/>
|
|
|
|
|
+ <circle cx="32" cy="24" r="4" fill="#52c41a" stroke="#389e0d" stroke-width="2"/>
|
|
|
|
|
+ <path d="M32 24 L32 2 C32 2 44 12 32 24" fill="#73d13d" stroke="#389e0d" stroke-width="1"/>
|
|
|
|
|
+ <path d="M32 24 L52 34 C52 34 44 44 32 24" fill="#73d13d" stroke="#389e0d" stroke-width="1"/>
|
|
|
|
|
+ <path d="M32 24 L12 34 C12 34 20 44 32 24" fill="#73d13d" stroke="#389e0d" stroke-width="1"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 储能图标 (储) - 电池组
|
|
|
|
|
+ const storageSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="10" y="16" width="44" height="44" rx="4" fill="#722ed1" stroke="#531dab" stroke-width="2"/>
|
|
|
|
|
+ <rect x="24" y="8" width="16" height="8" rx="2" fill="#722ed1" stroke="#531dab" stroke-width="2"/>
|
|
|
|
|
+ <rect x="16" y="24" width="32" height="8" rx="1" fill="#d3adf7"/>
|
|
|
|
|
+ <rect x="16" y="36" width="32" height="8" rx="1" fill="#b37feb"/>
|
|
|
|
|
+ <rect x="16" y="48" width="24" height="6" rx="1" fill="#52c41a"/>
|
|
|
|
|
+ <path d="M28 27h8M32 25v4" stroke="#722ed1" stroke-width="2"/>
|
|
|
|
|
+ <text x="44" y="53" font-size="8" fill="#fff" font-family="Arial">%</text>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 负荷-照明图标 (荷) - 灯泡
|
|
|
|
|
+ const lightSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <path d="M32 6 C20 6 12 16 12 26 C12 34 18 40 22 44 L22 50 L42 50 L42 44 C46 40 52 34 52 26 C52 16 44 6 32 6Z" fill="#ffc53d" stroke="#d48806" stroke-width="2"/>
|
|
|
|
|
+ <rect x="22" y="50" width="20" height="4" fill="#8c8c8c"/>
|
|
|
|
|
+ <rect x="24" y="54" width="16" height="4" fill="#8c8c8c"/>
|
|
|
|
|
+ <rect x="26" y="58" width="12" height="3" rx="1" fill="#8c8c8c"/>
|
|
|
|
|
+ <path d="M26 20 Q32 14 38 20" stroke="#fff" stroke-width="2.5" fill="none" opacity="0.6"/>
|
|
|
|
|
+ <path d="M24 28 Q32 22 40 28" stroke="#fff" stroke-width="2" fill="none" opacity="0.4"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 负荷-空调图标 (荷) - 空调机组
|
|
|
|
|
+ const hvacSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="6" y="10" width="52" height="32" rx="4" fill="#13c2c2" stroke="#08979c" stroke-width="2"/>
|
|
|
|
|
+ <rect x="12" y="16" width="40" height="14" rx="2" fill="#36cfc9"/>
|
|
|
|
|
+ <line x1="12" y1="34" x2="52" y2="34" stroke="#08979c" stroke-width="1"/>
|
|
|
|
|
+ <line x1="12" y1="36" x2="52" y2="36" stroke="#08979c" stroke-width="1"/>
|
|
|
|
|
+ <line x1="12" y1="38" x2="52" y2="38" stroke="#08979c" stroke-width="1"/>
|
|
|
|
|
+ <circle cx="46" cy="23" r="3" fill="#fff" opacity="0.5"/>
|
|
|
|
|
+ <path d="M20 48 Q24 44 28 48 Q32 52 36 48 Q40 44 44 48" stroke="#87e8de" stroke-width="3" fill="none"/>
|
|
|
|
|
+ <path d="M24 54 Q28 50 32 54 Q36 58 40 54" stroke="#87e8de" stroke-width="2" fill="none" opacity="0.6"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 负荷-充电桩图标 (荷)
|
|
|
|
|
+ const chargeSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="14" y="8" width="28" height="48" rx="4" fill="#595959" stroke="#262626" stroke-width="2"/>
|
|
|
|
|
+ <rect x="20" y="14" width="16" height="20" rx="2" fill="#1890ff"/>
|
|
|
|
|
+ <path d="M26 18 L24 26 L28 26 L26 34" stroke="#fff" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
|
|
|
|
+ <circle cx="28" cy="44" r="5" fill="#52c41a" stroke="#389e0d" stroke-width="1"/>
|
|
|
|
|
+ <rect x="42" y="24" width="10" height="6" rx="1" fill="#262626"/>
|
|
|
|
|
+ <path d="M52 27 L58 27 L58 38 Q58 42 54 42 L50 42" stroke="#262626" stroke-width="3" fill="none"/>
|
|
|
|
|
+ <circle cx="50" cy="42" r="3" fill="#262626"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 负荷-通用设备图标 (荷)
|
|
|
|
|
+ const loadSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <rect x="10" y="10" width="44" height="44" rx="6" fill="#fa8c16" stroke="#d46b08" stroke-width="2"/>
|
|
|
|
|
+ <circle cx="32" cy="32" r="14" fill="none" stroke="#fff" stroke-width="3"/>
|
|
|
|
|
+ <circle cx="32" cy="32" r="4" fill="#fff"/>
|
|
|
|
|
+ <path d="M32 14v8M32 42v8M14 32h8M42 32h8" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 水系统图标
|
|
|
|
|
+ const waterSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <path d="M32 4 C32 4 52 24 52 38 C52 52 44 58 32 58 C20 58 12 52 12 38 C12 24 32 4 32 4Z" fill="#1890ff" stroke="#0050b3" stroke-width="2"/>
|
|
|
|
|
+ <ellipse cx="32" cy="42" rx="14" ry="6" fill="#69c0ff" opacity="0.5"/>
|
|
|
|
|
+ <path d="M22 36 Q32 32 42 36" stroke="#fff" stroke-width="2" fill="none" opacity="0.6"/>
|
|
|
|
|
+ <path d="M26 28 Q32 24 38 28" stroke="#fff" stroke-width="1.5" fill="none" opacity="0.4"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 默认设施图标
|
|
|
|
|
+ const defaultFacilitySvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <path d="M8 28 L32 8 L56 28 L56 56 L8 56 Z" fill="#409EFF" stroke="#2a6dc0" stroke-width="2"/>
|
|
|
|
|
+ <rect x="16" y="32" width="12" height="10" fill="#fff" opacity="0.4"/>
|
|
|
|
|
+ <rect x="36" y="32" width="12" height="10" fill="#fff" opacity="0.4"/>
|
|
|
|
|
+ <rect x="26" y="42" width="12" height="14" fill="#2a6dc0"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 默认设备图标
|
|
|
|
|
+ const defaultDeviceSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
|
|
|
+ <circle cx="32" cy="32" r="26" fill="#67C23A" stroke="#4a9e31" stroke-width="2"/>
|
|
|
|
|
+ <rect x="22" y="22" width="20" height="20" rx="3" fill="#fff" opacity="0.3"/>
|
|
|
|
|
+ <circle cx="32" cy="32" r="6" fill="#fff"/>
|
|
|
|
|
+ <circle cx="32" cy="32" r="2" fill="#4a9e31"/>
|
|
|
|
|
+ </svg>`
|
|
|
|
|
+
|
|
|
|
|
+ // 根据节点信息获取对应的SVG图标和类别
|
|
|
|
|
+ const getNodeCategory = (node) => {
|
|
|
|
|
+ if (!node) return { category: 'default', level: 1 }
|
|
|
|
|
+
|
|
|
|
|
+ const label = (node.label || '').toLowerCase()
|
|
|
|
|
+ const code = (node.code || '').toLowerCase()
|
|
|
|
|
+
|
|
|
|
|
+ // 电网 (网) - level 0
|
|
|
|
|
+ if (label.includes('电网') || label.includes('变压器') || label.includes('配电') ||
|
|
|
|
|
+ label.includes('供电') || code.includes('grid') || code.includes('transformer')) {
|
|
|
|
|
+ return { category: 'grid', level: 0, svg: gridSvg, color: '#1890ff', name: '电网' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 光伏 (源) - level 0
|
|
|
|
|
+ if (label.includes('光伏') || label.includes('pv') || label.includes('太阳能') ||
|
|
|
|
|
+ code.includes('pv') || code.includes('solar')) {
|
|
|
|
|
+ return { category: 'pv', level: 0, svg: pvSvg, color: '#faad14', name: '光伏' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 风电 (源) - level 0
|
|
|
|
|
+ if (label.includes('风电') || label.includes('风力') || label.includes('风机') ||
|
|
|
|
|
+ code.includes('wind')) {
|
|
|
|
|
+ return { category: 'wind', level: 0, svg: windSvg, color: '#52c41a', name: '风电' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 储能 (储) - level 1
|
|
|
|
|
+ if (label.includes('储能') || label.includes('电池') || label.includes('蓄电') ||
|
|
|
|
|
+ label.includes('储电') || code.includes('battery') || code.includes('storage') ||
|
|
|
|
|
+ code.includes('ess')) {
|
|
|
|
|
+ return { category: 'storage', level: 1, svg: storageSvg, color: '#722ed1', name: '储能' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 照明 (荷) - level 2
|
|
|
|
|
+ if (label.includes('照明') || label.includes('灯') || label.includes('路灯') ||
|
|
|
|
|
+ code.includes('light') || code.includes('lamp')) {
|
|
|
|
|
+ return { category: 'light', level: 2, svg: lightSvg, color: '#ffc53d', name: '照明' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 空调/暖通 (荷) - level 2
|
|
|
|
|
+ if (label.includes('空调') || label.includes('暖通') || label.includes('制冷') ||
|
|
|
|
|
+ label.includes('冷水') || label.includes('热水') || label.includes('hvac') ||
|
|
|
|
|
+ code.includes('hvac') || code.includes('ac')) {
|
|
|
|
|
+ return { category: 'hvac', level: 2, svg: hvacSvg, color: '#13c2c2', name: '暖通' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 充电桩 (荷) - level 2
|
|
|
|
|
+ if (label.includes('充电') || label.includes('充电桩') || label.includes('充电站') ||
|
|
|
|
|
+ code.includes('charge') || code.includes('ev')) {
|
|
|
|
|
+ return { category: 'charge', level: 2, svg: chargeSvg, color: '#595959', name: '充电' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 水系统
|
|
|
|
|
+ if (label.includes('水') || label.includes('给水') || label.includes('供水') ||
|
|
|
|
|
+ code.includes('water')) {
|
|
|
|
|
+ return { category: 'water', level: 2, svg: waterSvg, color: '#1890ff', name: '水系统' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 其他负荷设施 (荷) - level 2
|
|
|
|
|
+ if (node.type === 1) {
|
|
|
|
|
+ return { category: 'facility', level: 2, svg: defaultFacilitySvg, color: '#409EFF', name: '设施' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设备 - level 3
|
|
|
|
|
+ return { category: 'device', level: 3, svg: defaultDeviceSvg, color: '#67C23A', name: '设备' }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将SVG转换为Data URL
|
|
|
|
|
+ const svgToDataUrl = (svg) => {
|
|
|
|
|
+ return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 节点数据
|
|
|
|
|
+ const nodes = this.topologyData.nodes.map(node => {
|
|
|
|
|
+ const categoryInfo = getNodeCategory(node)
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: node.id,
|
|
|
|
|
+ label: node.label,
|
|
|
|
|
+ shape: 'image',
|
|
|
|
|
+ image: svgToDataUrl(categoryInfo.svg),
|
|
|
|
|
+ size: 30,
|
|
|
|
|
+ level: categoryInfo.level,
|
|
|
|
|
+ color: {
|
|
|
|
|
+ border: categoryInfo.color,
|
|
|
|
|
+ highlight: {
|
|
|
|
|
+ border: '#F56C6C'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ font: {
|
|
|
|
|
+ color: '#303133',
|
|
|
|
|
+ size: 12,
|
|
|
|
|
+ face: 'Microsoft YaHei, Arial',
|
|
|
|
|
+ background: 'rgba(255,255,255,0.8)',
|
|
|
|
|
+ strokeWidth: 0
|
|
|
|
|
+ },
|
|
|
|
|
+ title: `【${categoryInfo.name}】${node.label}`,
|
|
|
|
|
+ // 保存原始信息
|
|
|
|
|
+ _category: categoryInfo.category,
|
|
|
|
|
+ _categoryName: categoryInfo.name
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 边数据
|
|
|
|
|
+ const edges = this.topologyData.edges.map(edge => ({
|
|
|
|
|
+ id: edge.id,
|
|
|
|
|
+ from: edge.from,
|
|
|
|
|
+ to: edge.to,
|
|
|
|
|
+ label: edge.label,
|
|
|
|
|
+ arrows: 'to',
|
|
|
|
|
+ color: {
|
|
|
|
|
+ color: '#E6A23C',
|
|
|
|
|
+ highlight: '#F56C6C'
|
|
|
|
|
+ },
|
|
|
|
|
+ width: 2,
|
|
|
|
|
+ smooth: {
|
|
|
|
|
+ type: 'cubicBezier',
|
|
|
|
|
+ roundness: 0.4
|
|
|
|
|
+ },
|
|
|
|
|
+ title: `${edge.label}\n容量: ${edge.capacity || '-'}`
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ const data = { nodes, edges }
|
|
|
|
|
+
|
|
|
|
|
+ // 根据布局模式配置不同的选项
|
|
|
|
|
+ let layoutConfig, physicsConfig
|
|
|
|
|
+
|
|
|
|
|
+ if (this.layoutMode === 'hierarchical') {
|
|
|
|
|
+ // 层级布局配置
|
|
|
|
|
+ layoutConfig = {
|
|
|
|
|
+ hierarchical: {
|
|
|
|
|
+ enabled: true,
|
|
|
|
|
+ direction: 'UD', // 从上到下(Up-Down)
|
|
|
|
|
+ sortMethod: 'directed', // 根据边的方向排序
|
|
|
|
|
+ levelSeparation: 200, // 层级间距
|
|
|
|
|
+ nodeSpacing: 150, // 同层节点间距
|
|
|
|
|
+ treeSpacing: 200, // 树之间的间距
|
|
|
|
|
+ blockShifting: true, // 允许块移动以减少边交叉
|
|
|
|
|
+ edgeMinimization: true, // 最小化边交叉
|
|
|
|
|
+ parentCentralization: true // 父节点居中
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
|
|
+ physicsConfig = {
|
|
|
|
|
+ enabled: false // 层级布局时禁用物理引擎
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 力导向布局配置
|
|
|
|
|
+ layoutConfig = {
|
|
|
|
|
+ randomSeed: 2 // 固定随机种子使布局稳定
|
|
|
|
|
+ }
|
|
|
|
|
+ physicsConfig = {
|
|
|
|
|
+ enabled: true,
|
|
|
|
|
+ barnesHut: {
|
|
|
|
|
+ gravitationalConstant: -8000,
|
|
|
|
|
+ centralGravity: 0.3,
|
|
|
|
|
+ springLength: 200,
|
|
|
|
|
+ springConstant: 0.04,
|
|
|
|
|
+ damping: 0.09,
|
|
|
|
|
+ avoidOverlap: 0.5
|
|
|
|
|
+ },
|
|
|
|
|
+ stabilization: {
|
|
|
|
|
+ iterations: 200,
|
|
|
|
|
+ updateInterval: 25
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const options = {
|
|
|
|
|
+ nodes: {
|
|
|
|
|
+ borderWidth: 2,
|
|
|
|
|
+ borderWidthSelected: 3,
|
|
|
|
|
+ size: 25,
|
|
|
|
|
+ font: {
|
|
|
|
|
+ size: 14,
|
|
|
|
|
+ color: '#ffffff'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ edges: {
|
|
|
|
|
+ width: 2,
|
|
|
|
|
+ selectionWidth: 3,
|
|
|
|
|
+ font: {
|
|
|
|
|
+ size: 12,
|
|
|
|
|
+ color: '#606266',
|
|
|
|
|
+ background: '#ffffff',
|
|
|
|
|
+ strokeWidth: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ layout: layoutConfig,
|
|
|
|
|
+ physics: physicsConfig,
|
|
|
|
|
+ interaction: {
|
|
|
|
|
+ hover: true,
|
|
|
|
|
+ tooltipDelay: 100,
|
|
|
|
|
+ zoomView: true,
|
|
|
|
|
+ dragView: true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 销毁旧实例
|
|
|
|
|
+ if (this.network) {
|
|
|
|
|
+ this.network.destroy()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建新实例
|
|
|
|
|
+ this.network = new Network(container, data, options)
|
|
|
|
|
+
|
|
|
|
|
+ // 绑定事件
|
|
|
|
|
+ this.network.on('click', this.handleTopologyClick)
|
|
|
|
|
+ this.network.on('doubleClick', this.handleTopologyDoubleClick)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleTopologyClick(params) {
|
|
|
|
|
+ if (params.nodes.length > 0) {
|
|
|
|
|
+ const nodeId = params.nodes[0]
|
|
|
|
|
+ console.log('点击节点:', nodeId)
|
|
|
|
|
+ } else if (params.edges.length > 0) {
|
|
|
|
|
+ const edgeId = params.edges[0]
|
|
|
|
|
+ const edge = this.flowRelList.find(item => String(item.id) === edgeId)
|
|
|
|
|
+ if (edge) {
|
|
|
|
|
+ this.handleView(edge)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleTopologyDoubleClick(params) {
|
|
|
|
|
+ if (params.edges.length > 0) {
|
|
|
|
|
+ const edgeId = params.edges[0]
|
|
|
|
|
+ const edge = this.flowRelList.find(item => String(item.id) === edgeId)
|
|
|
|
|
+ if (edge) {
|
|
|
|
|
+ this.handleUpdate(edge)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ refreshTopology() {
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ this.$message.success('拓扑图已刷新')
|
|
|
},
|
|
},
|
|
|
- /** 删除按钮操作 */
|
|
|
|
|
|
|
+
|
|
|
|
|
+ fitTopology() {
|
|
|
|
|
+ if (this.network) {
|
|
|
|
|
+ this.network.fit({
|
|
|
|
|
+ animation: {
|
|
|
|
|
+ duration: 500,
|
|
|
|
|
+ easingFunction: 'easeInOutQuad'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ exportTopology() {
|
|
|
|
|
+ if (!this.network) return
|
|
|
|
|
+
|
|
|
|
|
+ const canvas = this.$refs.topologyContainer.querySelector('canvas')
|
|
|
|
|
+ if (canvas) {
|
|
|
|
|
+ canvas.toBlob(blob => {
|
|
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
|
|
+ const a = document.createElement('a')
|
|
|
|
|
+ a.href = url
|
|
|
|
|
+ a.download = `能流拓扑图_${new Date().getTime()}.png`
|
|
|
|
|
+ a.click()
|
|
|
|
|
+ URL.revokeObjectURL(url)
|
|
|
|
|
+ this.$message.success('导出成功')
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ toggleLayoutMode() {
|
|
|
|
|
+ // 切换布局模式
|
|
|
|
|
+ this.layoutMode = this.layoutMode === 'hierarchical' ? 'force' : 'hierarchical'
|
|
|
|
|
+
|
|
|
|
|
+ // 重新初始化拓扑图
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.initTopology()
|
|
|
|
|
+ this.$message.success(`已切换到${this.layoutMode === 'hierarchical' ? '层级' : '自由'}布局`)
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleResize() {
|
|
|
|
|
+ if (this.network) {
|
|
|
|
|
+ this.network.redraw()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 切换显示列表
|
|
|
|
|
+ toggleShowList() {
|
|
|
|
|
+ this.showList = !this.showList
|
|
|
|
|
+ if (this.showList) {
|
|
|
|
|
+ // 显示列表时,延迟滚动到列表位置
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ const listCard = document.querySelector('.list-card')
|
|
|
|
|
+ if (listCard) {
|
|
|
|
|
+ listCard.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 树操作 ====================
|
|
|
|
|
+ filterNode(value, data) {
|
|
|
|
|
+ if (!value) return true
|
|
|
|
|
+ return data.label.indexOf(value) !== -1
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ getNodeIcon(data) {
|
|
|
|
|
+ const iconMap = {
|
|
|
|
|
+ area: 'el-icon-office-building',
|
|
|
|
|
+ facility: 'el-icon-school',
|
|
|
|
|
+ device: 'el-icon-cpu'
|
|
|
|
|
+ }
|
|
|
|
|
+ return iconMap[data.type] || 'el-icon-folder'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ getNodeColor(data) {
|
|
|
|
|
+ const colorMap = {
|
|
|
|
|
+ area: '#909399',
|
|
|
|
|
+ facility: '#409EFF',
|
|
|
|
|
+ device: '#67C23A'
|
|
|
|
|
+ }
|
|
|
|
|
+ return colorMap[data.type] || '#909399'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleNodeClick(data) {
|
|
|
|
|
+ if (data.type === 'area') return
|
|
|
|
|
+ console.log('选中节点:', data)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleSetAsSource(data) {
|
|
|
|
|
+ this.flowRelForm.exportObjType = data.objType
|
|
|
|
|
+ this.flowRelForm.exportObj = data.code
|
|
|
|
|
+ this.flowRelForm.exportObjName = data.label
|
|
|
|
|
+ this.handleAdd()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleSetAsTarget(data) {
|
|
|
|
|
+ this.flowRelForm.inputObjType = data.objType
|
|
|
|
|
+ this.flowRelForm.inputObj = data.code
|
|
|
|
|
+ this.flowRelForm.inputObjName = data.label
|
|
|
|
|
+ this.handleAdd()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 筛选操作 ====================
|
|
|
|
|
+ handleAreaChange(value) {
|
|
|
|
|
+ this.queryParams.areaCode = value
|
|
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleEnergyTypeChange(value) {
|
|
|
|
|
+ this.queryParams.emsClsCode = value
|
|
|
|
|
+ this.queryParams.pageNum = 1
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 表单操作 ====================
|
|
|
|
|
+ handleAdd() {
|
|
|
|
|
+ this.resetForm()
|
|
|
|
|
+ this.formTitle = '新增能流关系'
|
|
|
|
|
+ this.formDialogVisible = true
|
|
|
|
|
+ this.loadExportObjOptions()
|
|
|
|
|
+ this.loadInputObjOptions()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleUpdate(row) {
|
|
|
|
|
+ this.resetForm()
|
|
|
|
|
+ getFlowRel(row.id).then(res => {
|
|
|
|
|
+ this.flowRelForm = { ...res.data }
|
|
|
|
|
+ this.formTitle = '编辑能流关系'
|
|
|
|
|
+ this.formDialogVisible = true
|
|
|
|
|
+ this.loadExportObjOptions()
|
|
|
|
|
+ this.loadInputObjOptions()
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleView(row) {
|
|
|
|
|
+ this.currentRow = row
|
|
|
|
|
+ this.viewDialogVisible = true
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
handleDelete(row) {
|
|
handleDelete(row) {
|
|
|
- const ids = row.id || this.ids;
|
|
|
|
|
- this.$modal.confirm('是否确认删除能源设施能流关系编号为"' + ids + '"的数据项?').then(function() {
|
|
|
|
|
- return delRel(ids);
|
|
|
|
|
|
|
+ this.$confirm(`确认删除从"${row.exportObjName}"到"${row.inputObjName}"的能流关系吗?`, '警告', {
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning'
|
|
|
}).then(() => {
|
|
}).then(() => {
|
|
|
- this.getList();
|
|
|
|
|
- this.$modal.msgSuccess("删除成功");
|
|
|
|
|
- }).catch(() => {});
|
|
|
|
|
- },
|
|
|
|
|
- getObjType(objType) {
|
|
|
|
|
- const objTypeMap = {
|
|
|
|
|
- 1: '设施',
|
|
|
|
|
- 2: '设备',
|
|
|
|
|
- 3: '部件'
|
|
|
|
|
- };
|
|
|
|
|
- return objTypeMap[objType] || '未知';
|
|
|
|
|
- },
|
|
|
|
|
- getAllFacs() {
|
|
|
|
|
- listAllFacs(this.queryFacsParams).then(response =>{
|
|
|
|
|
- this.facsOptions = response.data;
|
|
|
|
|
|
|
+ delFlowRel(row.id).then(() => {
|
|
|
|
|
+ this.$message.success('删除成功')
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
- /** 查询能源树结构 */
|
|
|
|
|
- getEmsCls() {
|
|
|
|
|
- getEmsClsTree().then(response => {
|
|
|
|
|
- this.emsClsOptions = response.data;
|
|
|
|
|
- });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ handleBatchDelete() {
|
|
|
|
|
+ const ids = this.selectedRows.map(item => item.id).join(',')
|
|
|
|
|
+ this.$confirm('确认删除选中的能流关系吗?', '警告', {
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消',
|
|
|
|
|
+ type: 'warning'
|
|
|
|
|
+ }).then(() => {
|
|
|
|
|
+ delFlowRelBatch(ids).then(() => {
|
|
|
|
|
+ this.$message.success('删除成功')
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
- setCodePrefix() {
|
|
|
|
|
- this.mergeForm.code = this.mergeForm.exportFacsCode;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ handleStatusChange(row) {
|
|
|
|
|
+ updateFlowRel(row).then(() => {
|
|
|
|
|
+ this.$message.success('状态更新成功')
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ row.enableStatus = row.enableStatus === 1 ? 0 : 1
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
- setCodeCompose() {
|
|
|
|
|
- this.mergeForm.code = this.mergeForm.exportFacsCode + '_' +this.mergeForm.inputFacsCode;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ submitForm() {
|
|
|
|
|
+ this.$refs.flowRelForm.validate(valid => {
|
|
|
|
|
+ if (!valid) return
|
|
|
|
|
+
|
|
|
|
|
+ this.formSubmitting = true
|
|
|
|
|
+
|
|
|
|
|
+ const formData = { ...this.flowRelForm }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取能源类型名称
|
|
|
|
|
+ if (formData.emsCls) {
|
|
|
|
|
+ formData.emsClsName = this.getEmsClsName(formData.emsCls)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const apiMethod = formData.id ? updateFlowRel : addFlowRel
|
|
|
|
|
+
|
|
|
|
|
+ apiMethod(formData).then(() => {
|
|
|
|
|
+ this.$message.success(formData.id ? '修改成功' : '新增成功')
|
|
|
|
|
+ this.formDialogVisible = false
|
|
|
|
|
+ this.getList()
|
|
|
|
|
+ this.loadTopology()
|
|
|
|
|
+ }).finally(() => {
|
|
|
|
|
+ this.formSubmitting = false
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
|
|
+
|
|
|
|
|
+ resetForm() {
|
|
|
|
|
+ this.flowRelForm = {
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ exportObjType: 1,
|
|
|
|
|
+ exportObj: null,
|
|
|
|
|
+ exportObjName: null,
|
|
|
|
|
+ inputObjType: 2,
|
|
|
|
|
+ inputObj: null,
|
|
|
|
|
+ inputObjName: null,
|
|
|
|
|
+ emsCls: null,
|
|
|
|
|
+ emsClsName: null,
|
|
|
|
|
+ flowCapacity: null,
|
|
|
|
|
+ flowUnit: 'kW',
|
|
|
|
|
+ flowDesc: null,
|
|
|
|
|
+ areaCode: null,
|
|
|
|
|
+ enableStatus: 1
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.$refs.flowRelForm) {
|
|
|
|
|
+ this.$refs.flowRelForm.resetFields()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleFormClose() {
|
|
|
|
|
+ this.resetForm()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 表单联动 ====================
|
|
|
|
|
+ handleExportTypeChange() {
|
|
|
|
|
+ this.flowRelForm.exportObj = null
|
|
|
|
|
+ this.flowRelForm.exportObjName = null
|
|
|
|
|
+ this.loadExportObjOptions()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleInputTypeChange() {
|
|
|
|
|
+ this.flowRelForm.inputObj = null
|
|
|
|
|
+ this.flowRelForm.inputObjName = null
|
|
|
|
|
+ this.loadInputObjOptions()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async loadExportObjOptions() {
|
|
|
|
|
+ if (this.flowRelForm.exportObjType === 1) {
|
|
|
|
|
+ // 加载设施
|
|
|
|
|
+ const res = await listAllFacs({})
|
|
|
|
|
+ this.exportObjOptions = res.data.map(item => ({
|
|
|
|
|
+ code: item.facsCode,
|
|
|
|
|
+ name: item.facsName
|
|
|
|
|
+ }))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 加载设备
|
|
|
|
|
+ const res = await listDevice({ pageNum: 1, pageSize: 9999 })
|
|
|
|
|
+ this.exportObjOptions = res.rows.map(item => ({
|
|
|
|
|
+ code: item.deviceCode,
|
|
|
|
|
+ name: item.deviceName
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async loadInputObjOptions() {
|
|
|
|
|
+ if (this.flowRelForm.inputObjType === 1) {
|
|
|
|
|
+ // 加载设施
|
|
|
|
|
+ const res = await listAllFacs({})
|
|
|
|
|
+ this.inputObjOptions = res.data.map(item => ({
|
|
|
|
|
+ code: item.facsCode,
|
|
|
|
|
+ name: item.facsName
|
|
|
|
|
+ }))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 加载设备
|
|
|
|
|
+ const res = await listDevice({ pageNum: 1, pageSize: 9999 })
|
|
|
|
|
+ this.inputObjOptions = res.rows.map(item => ({
|
|
|
|
|
+ code: item.deviceCode,
|
|
|
|
|
+ name: item.deviceName
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleExportObjChange(value) {
|
|
|
|
|
+ const obj = this.exportObjOptions.find(item => item.code === value)
|
|
|
|
|
+ if (obj) {
|
|
|
|
|
+ this.flowRelForm.exportObjName = obj.name
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleInputObjChange(value) {
|
|
|
|
|
+ const obj = this.inputObjOptions.find(item => item.code === value)
|
|
|
|
|
+ if (obj) {
|
|
|
|
|
+ this.flowRelForm.inputObjName = obj.name
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleEmsClsChange(value) {
|
|
|
|
|
+ if (value) {
|
|
|
|
|
+ this.flowRelForm.emsClsName = this.getEmsClsName(value)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 工具方法 ====================
|
|
|
|
|
+ getEmsClsName(code) {
|
|
|
|
|
+ // 根据code获取名称(递归查找)
|
|
|
|
|
+ const findName = (nodes, targetCode) => {
|
|
|
|
|
+ for (const node of nodes) {
|
|
|
|
|
+ const nodeCode = node.code || node.id
|
|
|
|
|
+ const nodeName = node.name || node.label
|
|
|
|
|
+ if (nodeCode === targetCode) {
|
|
|
|
|
+ return nodeName
|
|
|
|
|
+ }
|
|
|
|
|
+ if (node.children && node.children.length > 0) {
|
|
|
|
|
+ const result = findName(node.children, targetCode)
|
|
|
|
|
+ if (result) return result
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return ''
|
|
|
|
|
+ }
|
|
|
|
|
+ return findName(this.energyTypeOptions, code)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-};
|
|
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.flow-rel-container {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ background: #f0f2f5;
|
|
|
|
|
+ min-height: calc(100vh - 84px);
|
|
|
|
|
+ overflow: visible; /* 确保内容不被截断 */
|
|
|
|
|
+
|
|
|
|
|
+ .toolbar-card {
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 100;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .main-content {
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tree-card,
|
|
|
|
|
+ .topology-card,
|
|
|
|
|
+ .list-card {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+
|
|
|
|
|
+ .card-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .topology-legend {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .legend-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+ cursor: default;
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ transition: background-color 0.2s;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ background-color: #f5f7fa;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ svg {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ i {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tree-card {
|
|
|
|
|
+ ::v-deep .el-card__body {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ max-height: 600px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .custom-tree-node {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ padding-right: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ .tree-label {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ i {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tree-actions {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+
|
|
|
|
|
+ .el-button {
|
|
|
|
|
+ padding: 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &:hover .tree-actions {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .topology-card {
|
|
|
|
|
+ ::v-deep .el-card__body {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .topology-container {
|
|
|
|
|
+ height: 600px;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+
|
|
|
|
|
+ .empty-topology {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .list-card {
|
|
|
|
|
+ ::v-deep .el-card__body {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 确保列表卡片在画布上方正确显示 */
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 10;
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ margin-top: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .danger-text {
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ color: #f78989;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* vue-treeselect 样式调整 */
|
|
|
|
|
+::v-deep .vue-treeselect {
|
|
|
|
|
+ .vue-treeselect__control {
|
|
|
|
|
+ height: 32px;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .vue-treeselect__placeholder,
|
|
|
|
|
+ .vue-treeselect__single-value {
|
|
|
|
|
+ line-height: 30px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|
|
|
|
|
+
|
|
|
|
|
+<!-- 全局样式,用于修复 vue-treeselect 下拉菜单层级问题 -->
|
|
|
|
|
+<style lang="scss">
|
|
|
|
|
+/* 修复 vue-treeselect 下拉菜单被遮挡的问题 */
|
|
|
|
|
+.vue-treeselect__menu-container {
|
|
|
|
|
+ z-index: 9999 !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.vue-treeselect__menu {
|
|
|
|
|
+ z-index: 9999 !important;
|
|
|
|
|
+ background-color: #fff !important;
|
|
|
|
|
+ border: 1px solid #dcdfe6 !important;
|
|
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 确保下拉框容器有正确的层级 */
|
|
|
|
|
+.vue-treeselect {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.vue-treeselect--open {
|
|
|
|
|
+ z-index: 9998 !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 工具栏卡片需要有更高的层级以便下拉菜单能正常显示 */
|
|
|
|
|
+.flow-rel-container .toolbar-card {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 100;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|