浏览代码

负载分析功能模块完成

fanghuisheng 1 月之前
父节点
当前提交
def96a6699

+ 20 - 20
src/router/index.js

@@ -233,26 +233,26 @@ export const asyncRoutes = [
     // },
 
 
-    // {
-    //     meta: { icon: 'realMonitored', title: '实时监测', },
-    //     path: '/realMonitored',
-    //     component: Layout,
-    //     redirect: '/realMonitored/loadAnalysis/index',
-    //     children: [
-    //         {
-    //             meta: { icon: 'loadAnalysis', title: '负载分析', },
-    //             path: 'loadAnalysis',
-    //             component: () =>
-    //                 import('@/views/realMonitored/loadAnalysis/index'),
-    //         },
-    //         {
-    //             meta: { icon: 'loadAnalysis', title: '同比分析报表', },
-    //             path: 'loadAnalysis1',
-    //             component: () =>
-    //                 import('@/views/realMonitored/loadAnalysis/index'),
-    //         },
-    //     ]
-    // },
+    {
+        meta: { icon: 'realMonitored', title: '实时监测', },
+        path: '/realMonitored',
+        component: Layout,
+        redirect: '/realMonitored/loadAnalysis/index',
+        children: [
+            {
+                meta: { icon: 'loadAnalysis', title: '负载分析', },
+                path: 'loadAnalysis',
+                component: () =>
+                    import('@/views/realMonitored/loadAnalysis/index'),
+            },
+            {
+                meta: { icon: 'loadAnalysis', title: '同比分析报表', },
+                path: 'loadAnalysis1',
+                component: () =>
+                    import('@/views/realMonitored/loadAnalysis/index'),
+            },
+        ]
+    },
     {
         meta: { icon: 'dataManage', title: '历史数据', },
         path: '/historyData',

+ 222 - 0
src/views/realMonitored/loadAnalysis/components/loadChart.vue

@@ -0,0 +1,222 @@
+<template>
+    <div shadow="never" class="homeBox">
+        <div class="chart" ref="lineChartBanlance" />
+    </div>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import * as echarts from 'echarts'
+import { ElMessage, ElNotification } from 'element-plus'
+import {
+    ref,
+    onMounted,
+    watch,
+    getCurrentInstance,
+    reactive,
+    toRefs,
+} from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+    height: Number,
+    chartData: Object,
+}) //数据双向绑定
+/*----------------------------------变量声明-----------------------------------*/
+let lineChartBanlance = ref(null)
+
+function initEcharts() {
+    const data = {
+        name: '盛齐充电站',
+        capacity: 2500,
+        power: 155.46,
+        progress: 24.7,
+        children: [
+            {
+                name: 'D4KXG_C1',
+                capacity: 152,
+                power: '--',
+                progress: 36,
+            },
+            {
+                name: 'D4KXG_C2',
+                capacity: 152,
+                power: '--',
+                progress: 36.8,
+            },
+            {
+                name: 'D4KXG_C5',
+                capacity: 38,
+                power: '--',
+                progress: 0,
+            },
+            {
+                name: 'D5KXG_C1',
+                capacity: 152,
+                power: '--',
+                progress: 20.5,
+            },
+            {
+                name: 'D5KXG_C1',
+                capacity: 152,
+                power: '--',
+                progress: 20.5,
+            },
+            {
+                name: 'D5KXG_C1',
+                capacity: 152,
+                power: '--',
+                progress: 20.5,
+            },
+        ],
+    }
+
+    let myChart = echarts.init(lineChartBanlance.value)
+    // 绘制图表
+    myChart.setOption({
+        tooltip: {
+            trigger: 'item',
+            triggerOn: 'mousemove',
+            formatter: function (params) {
+                const item = params.data
+
+                if ('children' in item) {
+                    return `
+                        <div style="font-weight:bold;">${item.name}</div>
+                        <div>功率:${item.power}kW</div>
+                        <div>负载率:${item.progress}%</div>
+                        <div>额定容量:${item.capacity}kVA</div>
+                    `
+                } else {
+                    return `
+                        <div style="font-weight:bold;">${item.name}</div>
+                        <div>功率:${item.power}kW</div>
+                        <div>负载率:${item.progress}%</div>
+                        <div>额定电压:${1}kV</div>
+                        <div>额定容量:${item.capacity}kVA</div>
+                    `
+                }
+            },
+        },
+        series: [
+            {
+                type: 'tree',
+                data: [data],
+                top: '1%',
+                left: '30%',
+                bottom: '1%',
+                right: '30%',
+                symbolSize: 7,
+                label: {
+                    position: 'left',
+                    verticalAlign: 'middle',
+                    color: '#fff',
+                    fontSize: 16,
+                    padding: 15,
+                    backgroundColor: '#354353',
+                    borderRadius: 5,
+                    formatter: function (params) {
+                        const node = params.data
+
+                        const barTotal = 20 // 进度条总长度(字符数)
+                        const barDone = Math.floor(barTotal * (node.progress / 100))
+                        const barRest = barTotal - barDone
+
+                        var res =
+                            `{name|${node.name}} {capacity|${node.capacity}kVA} {power|${node.power}kW}\n\n` +
+                            `{bar|${'■'.repeat(barDone)}${'□'.repeat(barRest)}}{percent|${node.progress}%}`
+                        return res
+                    },
+                    // 富文本样式(关键!区分不同文本块)
+                    rich: {
+                        name: {
+                            fontSize: 16,
+                            fontWeight: 'bold',
+                            color: '#fff',
+                        },
+                        capacity: {
+                            fontSize: 16,
+                            padding: [1, 10, 0, 20],
+                        },
+                        power: {
+                            fontSize: 16,
+                            color: '#5ebddd',
+                            padding: [1, 10, 0, 0],
+                        },
+                        bar: {
+                            color: '#0f0',
+                            // backgroundColor: '#fff',
+                            height: 16,
+                            borderRadius: 8,
+                            padding: [0, 2],
+                            lineHeight: 16,
+                            fontWeight: 'bold',
+                        },
+                        percent: {
+                            color: '#0f0',
+                            fontSize: 14,
+                            padding: [0, 5],
+                        },
+                    },
+                },
+                itemStyle: {
+                    borderColor: '#fff',
+                    borderWidth: 2,
+                    shadowBlur: 10,
+                    shadowColor: 'rgba(0,0,0,0.2)',
+                },
+                leaves: {
+                    label: {
+                        position: 'right',
+                        verticalAlign: 'middle',
+                        align: 'left',
+                    },
+                },
+                emphasis: {
+                    focus: 'descendant',
+                    label: { show: true },
+                    itemStyle: {
+                        borderColor: '#ffd700',
+                        borderWidth: 3,
+                    },
+                },
+                expandAndCollapse: true,
+                animationDuration: 550,
+                animationDurationUpdate: 750,
+            },
+        ],
+    })
+    window.addEventListener('resize', () => {
+        myChart.resize()
+    })
+}
+
+function writeValue(val) {
+    // getData()
+    initEcharts()
+}
+
+//监听变化
+watch(
+    () => props.chartData,
+    (newVal, oldVal, clear) => {
+        // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
+        clear(() => clearTimeout(writeValue(newVal, oldVal)))
+    },
+    { lazy: true }
+)
+
+// 暴露变量
+defineExpose({
+    initEcharts,
+})
+</script>
+<style lang="scss" scoped>
+.homeBox {
+    .chart {
+        height: 100%;
+    }
+}
+</style>

+ 110 - 0
src/views/realMonitored/loadAnalysis/components/powerChart.vue

@@ -0,0 +1,110 @@
+<template>
+    <div shadow="never" class="homeBox">
+        <div class="chart" ref="lineChartBanlance" />
+    </div>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import * as echarts from 'echarts'
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+    height: Number,
+    chartData: Object
+}) //数据双向绑定
+/*----------------------------------变量声明-----------------------------------*/
+let lineChartBanlance = ref(null)
+
+function initEcharts() {
+    let myChart = echarts.init(lineChartBanlance.value)
+    // 绘制图表
+    myChart.setOption({
+        backgroundColor: '#fff',
+        title: {
+            subtext: '',
+            left: 'center'
+        },
+        series: [
+            {
+                type: 'sankey',
+                left: "2%",
+                top: "2%",
+                right: "22%",
+                bottom: "2%",
+                // height: 10,
+                draggable: true,//控制节点拖拽
+                nodeWidth: 30,
+                nodeAlign: "justify",//对齐方式
+                // orient: 'vertical',//桑基图中节点的布局方向
+                label: {
+                    color: 'rgba(0,0,0,0.7)',
+                    fontFamily: 'Arial',
+                    fontSize: 14,
+                    formatter: function (params) {
+                        const node = params.data
+                        var res =
+                            `{name|${node.name}} / {capacity|${node.capacity}kVA} / {power|${node.power}kW} / {progress|${node.progress}%}`
+                        return res
+                    },
+                    rich: {
+                        name: {
+                            color: '#000',
+                        },
+                        capacity: {
+                            color: '#000',
+                        },
+                        power: {
+                            color: '#5dbadb',
+                        },
+                        progress: {
+                            color: '#31bb06',
+                        }
+                    },
+                },
+                lineStyle: {
+                    color: '#7f8253',
+                    curveness: 0.5,
+                    width: 1 // 设置线条粗细
+                },
+                data: props.chartData.seriesData,
+                links: props.chartData.seriesLinks,
+            }
+        ]
+    })
+    window.addEventListener('resize', () => {
+        myChart.resize()
+    })
+}
+
+function writeValue(val) {
+    // getData()
+    initEcharts()
+}
+
+//监听变化
+watch(
+    () => props.chartData,
+    (newVal, oldVal, clear) => {
+        // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务
+        clear(() => clearTimeout(writeValue(newVal, oldVal)))
+    },
+    { lazy: true }
+)
+
+// 暴露变量
+defineExpose({
+    initEcharts,
+});
+</script>
+<style lang="scss" scoped>
+.homeBox {
+    .chart {
+        height: 100%;
+    }
+}
+</style>

+ 43 - 0
src/views/realMonitored/loadAnalysis/components/tableChart.vue

@@ -0,0 +1,43 @@
+<template>
+    <div class="homeBox">
+        <el-table class="table" :data="props.tableData" border stripe>
+            <el-table-column prop="date" label="设备目录" />
+            <el-table-column prop="name" label="设备名称" />
+            <el-table-column prop="address" label="设备sn" />
+            <el-table-column prop="name" label="额定电压(kV)" />
+            <el-table-column prop="address" label="额定容量(kVA)" />
+            <el-table-column prop="name" label="有功功率(kW)" />
+            <el-table-column prop="name" label="负载率(%)" />
+        </el-table>
+    </div>
+</template>
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import * as echarts from 'echarts'
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({
+    height: Number,
+    tableData: Object
+}) //数据双向绑定
+/*----------------------------------变量声明-----------------------------------*/
+
+
+// 暴露变量
+defineExpose({
+});
+</script>
+<style lang="scss" scoped>
+.homeBox {
+    padding: 10px;
+
+    .table {
+        max-height: 100%;
+    }
+}
+</style>

+ 180 - 0
src/views/realMonitored/loadAnalysis/index.vue

@@ -0,0 +1,180 @@
+<template>
+    <div class="app-container page-nesting loadAnalysis">
+        <!-- 筛选start -->
+        <div class="filter-container mb-20" style="justify-content: left">
+            <el-radio-group v-model="timeRangeRadio.value" style="margin-right: 20px" @change="handleTimeRangeChange">
+                <el-radio-button value="realTime">实时</el-radio-button>
+                <el-radio-button value="history">历史</el-radio-button>
+            </el-radio-group>
+
+            <div style="margin:auto 20px auto 0">数据刷新时间:2025-07-24 16:01:26</div>
+            <el-button type="primary" icon="RefreshRight"> 刷新 </el-button>
+            <el-button type="primary" icon="Upload"> 导出 </el-button>
+
+            <el-radio-group v-model="viewTypeRadio.value" style="margin-left:auto" @change="handleViewChange">
+                <el-radio :value="1">功率流图</el-radio>
+                <el-radio :value="2">负载情况</el-radio>
+                <el-radio :value="3">原始数据</el-radio>
+            </el-radio-group>
+        </div>
+        <!-- 筛选end -->
+
+        <!-- 功率流图 -->
+        <powerChart ref="powerChartRef" class="bg-white" v-if="viewTypeRadio.value == 1" :chartData="powerChartData"
+            style="width: 100%;height:calc(100% - 52px) ;" />
+        <!-- 负载情况 -->
+        <loadChart ref="loadChartRef" class="bg-white" v-if="viewTypeRadio.value == 2" :chartData="powerChartData"
+            style="width: 100%;height:calc(100% - 52px) ;" />
+        <!-- 原始数据 -->
+        <tableChart ref="tableChartRef" class="bg-white" v-if="viewTypeRadio.value == 3" :tableData="tableData"
+            style="width: 100%;height:calc(100% - 52px) ;" />
+    </div>
+</template>
+
+<script setup>
+/*----------------------------------依赖引入-----------------------------------*/
+import { ElMessage, ElNotification } from 'element-plus'
+import { ref, onMounted, watch, getCurrentInstance, reactive, toRefs, } from 'vue'
+/*----------------------------------接口引入-----------------------------------*/
+/*----------------------------------组件引入-----------------------------------*/
+import powerChart from './components/powerChart.vue'
+import loadChart from './components/loadChart.vue'
+import tableChart from './components/tableChart.vue'
+/*----------------------------------store引入-----------------------------------*/
+/*----------------------------------公共方法引入-----------------------------------*/
+/*----------------------------------公共变量-----------------------------------*/
+const props = defineProps({}) //数据双向绑定
+const emit = defineEmits([])
+const { proxy } = getCurrentInstance()
+/*----------------------------------变量声明-----------------------------------*/
+const state = reactive({
+    timeRangeRadio: {
+        value: 'realTime',
+    },
+    viewTypeRadio: {
+        value: 1,
+    },
+
+
+    powerChartData: {
+        seriesData: [
+            {
+                depth: 0,
+                name: '盛齐充电站',
+                value: 2500,
+                capacity: 2500,
+                power: 36,
+                progress: 1.4,
+                itemStyle: {
+                    color: '#5ba33b',
+                }
+            },
+            {
+                name: 'D4KXG_C1',
+                value: 30,
+                capacity: 2500,
+                power: "--",
+                progress: 1.4,
+                itemStyle: {
+                    color: '#333333',
+                }
+            },
+            {
+                name: 'D4KXG_C2',
+                value: 30,
+                capacity: 2500,
+                power: "--",
+                progress: 1.4,
+                itemStyle: {
+                    color: '#333333',
+                }
+            },
+            {
+                name: 'D4KXG_C5',
+                value: 30,
+                capacity: 2500,
+                power: "--",
+                progress: 1.4,
+                itemStyle: {
+                    color: '#5ba33b',
+                }
+            },
+            {
+                name: 'D5KXG_C1',
+                value: 30,
+                capacity: 2500,
+                power: "--",
+                progress: 1.4,
+                itemStyle: {
+                    color: '#5ba33b',
+                }
+            },
+        ],
+        seriesLinks: [
+            {
+                source: '盛齐充电站',
+                target: 'D4KXG_C1',
+                value: 0,
+            },
+            {
+                source: '盛齐充电站',
+                target: 'D4KXG_C2',
+                value: 0
+            },
+            {
+                source: '盛齐充电站',
+                target: "D4KXG_C5",
+                value: 0
+            },
+            {
+                source: '盛齐充电站',
+                target: 'D5KXG_C1',
+                value: 0
+            },
+        ]
+    },
+    loadChartData: {
+
+    },
+    tableData: [],
+})
+const { timeRangeRadio, viewTypeRadio, powerChartData, loadChartData, tableData } = toRefs(state)
+
+
+
+function powerChartInit() {
+    proxy.$refs['powerChartRef'].initEcharts();
+}
+
+function handleViewChange(val) {
+    switch (val) {
+        case 1:
+            powerChartInit()
+            break
+        case 2:
+            proxy.$refs['loadChartRef'].initEcharts();
+            break
+        case 3:
+            break
+    }
+
+    console.log(val)
+}
+
+function handleTimeRangeChange(val) {
+    switch (val) {
+        case "realTime":
+
+            break
+        case "history":
+
+            break
+    }
+}
+
+onMounted(() => {
+    proxy.$refs['powerChartRef'].initEcharts();
+})
+</script>
+
+<style lang="scss" scoped></style>