feat: 数据总览图表开发
parent
fda2cbcb31
commit
1a8c712487
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="Frame">
|
||||||
|
<path id="Vector" d="M12 22C14.7614 22 17.2614 20.8807 19.0711 19.0711C20.8807 17.2614 22 14.7614 22 12C22 9.2386 20.8807 6.7386 19.0711 4.92893C17.2614 3.11929 14.7614 2 12 2C9.2386 2 6.7386 3.11929 4.92893 4.92893C3.11929 6.7386 2 9.2386 2 12C2 14.7614 3.11929 17.2614 4.92893 19.0711C6.7386 20.8807 9.2386 22 12 22Z" fill="white"/>
|
||||||
|
<path id="Vector_2" d="M13 17.25C13 17.8023 12.5523 18.25 12 18.25C11.4477 18.25 11 17.8023 11 17.25C11 16.6977 11.4477 16.25 12 16.25C12.5523 16.25 13 16.6977 13 17.25Z" fill="#E80D0D" stroke="#E80D0D" stroke-width="0.5"/>
|
||||||
|
<g id="Vector_3">
|
||||||
|
<path d="M12 6V14V6Z" fill="#E80D0D"/>
|
||||||
|
<path d="M12 6V14" stroke="#E80D0D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 846 B |
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="Frame">
|
||||||
|
<path id="Vector" d="M12 22C14.7614 22 17.2614 20.8807 19.0711 19.0711C20.8807 17.2614 22 14.7614 22 12C22 9.2386 20.8807 6.7386 19.0711 4.92893C17.2614 3.11929 14.7614 2 12 2C9.2386 2 6.7386 3.11929 4.92893 4.92893C3.11929 6.7386 2 9.2386 2 12C2 14.7614 3.11929 17.2614 4.92893 19.0711C6.7386 20.8807 9.2386 22 12 22Z" fill="white"/>
|
||||||
|
<path id="Vector_2" d="M8 12L11 15L17 9" stroke="#52C41A" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 582 B |
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path id="Vector" d="M10 0C4.47713 0 0 4.47715 0 10C0 15.5229 4.47713 20 10 20C15.5229 20 20 15.5229 20 10C20 4.47715 15.5229 0 10 0ZM15.3891 14.2784L14.2784 15.3891L10 11.1106L5.72156 15.3891L4.61094 14.2784L8.88937 10L4.61094 5.72156L5.72156 4.61094L10 8.88937L14.2785 4.61094L15.3891 5.72156L11.1106 10L15.3891 14.2784Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 442 B |
@ -1,195 +1,167 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="data-overview-wrap">
|
<div class="data-overview-wrap">
|
||||||
<!-- 检测总量汇总 -->
|
<!-- 检测总量汇总 -->
|
||||||
<div class="grid-container">
|
<div class="grid-container">
|
||||||
<div class="grid-item">
|
<div class="grid-item">
|
||||||
<div class="module-header">
|
<div class="module-header">
|
||||||
<ContentHeader bgLayout="918">
|
<ContentHeader bgLayout="918">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[200px] bg_title bg_title_0">
|
<div class="w-[200px] bg_title bg_title_0"></div>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
<template #extra>
|
||||||
<template #extra>
|
<div></div>
|
||||||
<div></div>
|
</template>
|
||||||
</template>
|
</ContentHeader>
|
||||||
</ContentHeader>
|
</div>
|
||||||
</div>
|
<div class="chart-container">
|
||||||
<div class="chart-container" id="totalChart"></div>
|
<BarChart
|
||||||
|
:xData="xData"
|
||||||
|
:legendArr="legendArr"
|
||||||
|
:datas="datas"
|
||||||
|
:colorArr="colorArr"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 设备信息 -->
|
||||||
|
<div class="grid-item">
|
||||||
|
<div class="module-header">
|
||||||
|
<ContentHeader bgLayout="918">
|
||||||
|
<template #title>
|
||||||
|
<div class="w-[200px] bg_title bg_title_1"></div>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
</ContentHeader>
|
||||||
|
</div>
|
||||||
|
<div class="device-info">
|
||||||
|
<div class="total-device">
|
||||||
|
<div class="device-icon"></div>
|
||||||
|
<div class="device-count">
|
||||||
|
<div>设备总数</div>
|
||||||
|
<div class="count-number">37</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 设备信息 -->
|
</div>
|
||||||
<div class="grid-item">
|
<div class="device-list">
|
||||||
<div class="module-header">
|
<div class="device-card">
|
||||||
<ContentHeader bgLayout="918">
|
<div class="mb-3">车体检测设备: 12</div>
|
||||||
<template #title>
|
<DeviceStatus :deviceStatus="deviceStatus" />
|
||||||
<div class="w-[200px] bg_title bg_title_1">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #extra>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
</ContentHeader>
|
|
||||||
</div>
|
|
||||||
<div class="device-info">
|
|
||||||
<div class="total-device">
|
|
||||||
<div class="device-icon"></div>
|
|
||||||
<div class="device-count">
|
|
||||||
<div>设备总数</div>
|
|
||||||
<div class="count-number">37</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-list">
|
|
||||||
<div class="device-card">
|
|
||||||
<div>车体检测设备: 12</div>
|
|
||||||
<div class="status-bar">
|
|
||||||
<el-progress type="line" :percentage="(8 / 12) * 100" status="success"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(2 / 12) * 100" status="warning"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(2 / 12) * 100" status="danger"></el-progress>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-card">
|
|
||||||
<div>撑杆检测设备: 10</div>
|
|
||||||
<div class="status-bar">
|
|
||||||
<el-progress type="line" :percentage="(9 / 10) * 100" status="success"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(1 / 10) * 100" status="warning"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(0 / 10) * 100" status="danger"></el-progress>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="device-card">
|
|
||||||
<div>钩机检测设备: 15</div>
|
|
||||||
<div class="status-bar">
|
|
||||||
<el-progress type="line" :percentage="(8 / 15) * 100" status="success"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(2 / 15) * 100" status="warning"></el-progress>
|
|
||||||
<el-progress type="line" :percentage="(2 / 15) * 100" status="danger"></el-progress>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="device-card">
|
||||||
|
<div class="mb-3">撑杆检测设备: 10</div>
|
||||||
<!-- 中部检测模块 -->
|
<DeviceStatus :deviceStatus="deviceStatus" />
|
||||||
<div class="grid-container">
|
|
||||||
<ul class="flex grid-item">
|
|
||||||
<li>
|
|
||||||
<div class="module-header">
|
|
||||||
<ContentHeader bgLayout="450">
|
|
||||||
<template #title>
|
|
||||||
<div class="w-[200px] bg_title bg_title_2">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #extra>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</ContentHeader>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container" id="bodyChart"></div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div class="module-header">
|
|
||||||
<ContentHeader bgLayout="450">
|
|
||||||
<template #title>
|
|
||||||
<div class="w-[200px] bg_title bg_title_3">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #extra>
|
|
||||||
</template>
|
|
||||||
</ContentHeader>
|
|
||||||
</div>
|
|
||||||
<div class="chart-container" id="poleChart"></div>
|
|
||||||
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="grid-item">
|
|
||||||
<div class="module-header">
|
|
||||||
<ContentHeader bgLayout="918">
|
|
||||||
<template #title>
|
|
||||||
<div class="w-[200px] bg_title bg_title_4">
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #extra>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
</ContentHeader>
|
|
||||||
</div>
|
|
||||||
<div class="monitor-images">
|
|
||||||
<img src="https://picsum.photos/300/200?random=1" alt="监控画面">
|
|
||||||
<img src="https://picsum.photos/300/200?random=2" alt="监控画面">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="device-card">
|
||||||
|
<div class="mb-3">钩机检测设备: 15</div>
|
||||||
|
<DeviceStatus :deviceStatus="deviceStatus" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中部检测模块 -->
|
||||||
|
<div class="grid-container">
|
||||||
|
<ul class="flex grid-item">
|
||||||
|
<li>
|
||||||
|
<div class="module-header">
|
||||||
|
<ContentHeader bgLayout="450">
|
||||||
|
<template #title>
|
||||||
|
<div class="w-[200px] bg_title bg_title_2"></div>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
</ContentHeader>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<PieChart
|
||||||
|
:data="[
|
||||||
|
{ value: 26, name: '搭扣未搭' },
|
||||||
|
{ value: 35, name: '下侧门缺失' },
|
||||||
|
{ value: 15, name: '小门搭扣丢失' },
|
||||||
|
{ value: 15, name: '门折页脱落' },
|
||||||
|
{ value: 10, name: '小门外翘' },
|
||||||
|
{ value: 5, name: '小窗裂纹' },
|
||||||
|
]"
|
||||||
|
:colors="[
|
||||||
|
'#4CAF50',
|
||||||
|
'#8BC34A',
|
||||||
|
'#CDDC39',
|
||||||
|
'#FFEB3B',
|
||||||
|
'#FFC107',
|
||||||
|
'#FF5722',
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="module-header">
|
||||||
|
<ContentHeader bgLayout="450">
|
||||||
|
<template #title>
|
||||||
|
<div class="w-[200px] bg_title bg_title_3"></div>
|
||||||
|
</template>
|
||||||
|
<template #extra> </template>
|
||||||
|
</ContentHeader>
|
||||||
|
</div>
|
||||||
|
<div class="chart-container">
|
||||||
|
<PieChartSmall
|
||||||
|
:data="[
|
||||||
|
{ value: 65, name: '撑杆断裂' },
|
||||||
|
{ value: 35, name: '撑杆弯曲' },
|
||||||
|
]"
|
||||||
|
:colors="['#2196F3', '#9C27B0']"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="grid-item">
|
||||||
|
<div class="module-header">
|
||||||
|
<ContentHeader bgLayout="918">
|
||||||
|
<template #title>
|
||||||
|
<div class="w-[200px] bg_title bg_title_4"></div>
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
</ContentHeader>
|
||||||
|
</div>
|
||||||
|
<div class="monitor-images">
|
||||||
|
<img src="https://picsum.photos/300/200?random=1" alt="监控画面" />
|
||||||
|
<img src="https://picsum.photos/300/200?random=2" alt="监控画面" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from "echarts";
|
||||||
import ContentHeader from '@/components/ContentHeader.vue';
|
import ContentHeader from "@/components/ContentHeader.vue";
|
||||||
|
import BarChart from "./components/BarChart.vue";
|
||||||
|
import PieChart from "./components/PieChart.vue";
|
||||||
|
import PieChartSmall from "./components/PieChartSmall.vue";
|
||||||
|
import DeviceStatus from "./components/DeviceStatus.vue";
|
||||||
|
const xData = ref(["1月", "2月", "3月", "4月", "5月"]);
|
||||||
|
const legendArr = ["车体检测", "撑杆检测"];
|
||||||
|
const datas = [
|
||||||
|
[1528, 1266.02, 2468.39, 2982.67, 3165.91],
|
||||||
|
[2844.44, 6505.07, 8016.12, 6350.87, 1474.61],
|
||||||
|
];
|
||||||
|
const colorArr = [
|
||||||
|
["#3B9FFE", "#5070F2"],
|
||||||
|
["#FFDA8D", "#FFAC06"],
|
||||||
|
];
|
||||||
|
const deviceStatus = ref({
|
||||||
|
onlineCount: 50,
|
||||||
|
errorCount: 10,
|
||||||
|
outlineCount: 10
|
||||||
|
});
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始化检测总量汇总图表
|
|
||||||
const totalChart = echarts.init(document.getElementById('totalChart') as HTMLDivElement);
|
|
||||||
totalChart.setOption({
|
|
||||||
xAxis: {
|
|
||||||
data: ['1月', '2月', '3月', '4月', '5月', '6月']
|
|
||||||
},
|
|
||||||
yAxis: {},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '车体检测',
|
|
||||||
type: 'bar',
|
|
||||||
data: [200, 200, 150, 150, 120, 220]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '撑杆检测',
|
|
||||||
type: 'bar',
|
|
||||||
data: [80, 150, 80, 100, 180, 80]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化车体检测图表
|
|
||||||
const bodyChart = echarts.init(document.getElementById('bodyChart') as HTMLDivElement);
|
|
||||||
bodyChart.setOption({
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '车体检测',
|
|
||||||
type: 'pie',
|
|
||||||
radius: '70%',
|
|
||||||
data: [
|
|
||||||
{ value: 26, name: '搭扣未搭' },
|
|
||||||
{ value: 35, name: '下侧门缺失' },
|
|
||||||
{ value: 15, name: '小门搭扣丢失' },
|
|
||||||
{ value: 15, name: '门折页脱落' },
|
|
||||||
{ value: 10, name: '小门外翘' },
|
|
||||||
{ value: 5, name: '小窗裂纹' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化撑杆检测图表
|
|
||||||
const poleChart = echarts.init(document.getElementById('poleChart') as HTMLDivElement);
|
|
||||||
poleChart.setOption({
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '撑杆检测',
|
|
||||||
type: 'pie',
|
|
||||||
radius: ['50%', '70%'],
|
|
||||||
data: [
|
|
||||||
{ value: 65, name: '撑杆断裂' },
|
|
||||||
{ value: 35, name: '撑杆弯曲' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import url('./DataOverview.scss');
|
@import url("./DataOverview.scss");
|
||||||
</style>
|
</style>
|
||||||
|
@ -0,0 +1,320 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref, watch, nextTick } from "vue";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
|
// 定义组件接收的props
|
||||||
|
const props = defineProps({
|
||||||
|
xData: {
|
||||||
|
type: Array as PropType<Array<string>>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
legendArr: {
|
||||||
|
type: Array as PropType<Array<string>>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
datas: {
|
||||||
|
type: Array as PropType<Array<Array<number>>>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
colorArr: {
|
||||||
|
type: Array as PropType<Array<Array<string>>>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
|
|
||||||
|
// 自定义3D形状
|
||||||
|
const CubeLeft = echarts.graphic.extendShape({
|
||||||
|
shape: { x: 0, y: 0 },
|
||||||
|
buildPath: (ctx, shape) => {
|
||||||
|
const { xAxisPoint } = shape;
|
||||||
|
const [c0, c1, c2, c3] = [
|
||||||
|
[shape.x, shape.y],
|
||||||
|
[shape.x - 12, shape.y - 6],
|
||||||
|
[xAxisPoint[0] - 12, xAxisPoint[1] - 6],
|
||||||
|
[xAxisPoint[0], xAxisPoint[1]],
|
||||||
|
];
|
||||||
|
ctx
|
||||||
|
.moveTo(c0[0], c0[1])
|
||||||
|
.lineTo(c1[0], c1[1])
|
||||||
|
.lineTo(c2[0], c2[1])
|
||||||
|
.lineTo(c3[0], c3[1])
|
||||||
|
.closePath();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const CubeRight = echarts.graphic.extendShape({
|
||||||
|
shape: { x: 0, y: 0 },
|
||||||
|
buildPath: (ctx, shape) => {
|
||||||
|
const { xAxisPoint } = shape;
|
||||||
|
const [c1, c2, c3, c4] = [
|
||||||
|
[shape.x, shape.y],
|
||||||
|
[xAxisPoint[0], xAxisPoint[1]],
|
||||||
|
[xAxisPoint[0] + 12, xAxisPoint[1] - 6],
|
||||||
|
[shape.x + 12, shape.y - 6],
|
||||||
|
];
|
||||||
|
ctx
|
||||||
|
.moveTo(c1[0], c1[1])
|
||||||
|
.lineTo(c2[0], c2[1])
|
||||||
|
.lineTo(c3[0], c3[1])
|
||||||
|
.lineTo(c4[0], c4[1])
|
||||||
|
.closePath();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const CubeTop = echarts.graphic.extendShape({
|
||||||
|
shape: { x: 0, y: 0 },
|
||||||
|
buildPath: (ctx, shape) => {
|
||||||
|
const [c1, c2, c3, c4] = [
|
||||||
|
[shape.x, shape.y],
|
||||||
|
[shape.x + 12, shape.y - 6],
|
||||||
|
[shape.x, shape.y - 12],
|
||||||
|
[shape.x - 12, shape.y - 6],
|
||||||
|
];
|
||||||
|
ctx
|
||||||
|
.moveTo(c1[0], c1[1])
|
||||||
|
.lineTo(c2[0], c2[1])
|
||||||
|
.lineTo(c3[0], c3[1])
|
||||||
|
.lineTo(c4[0], c4[1])
|
||||||
|
.closePath();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 注册三个面图形
|
||||||
|
echarts.graphic.registerShape("CubeLeft", CubeLeft);
|
||||||
|
echarts.graphic.registerShape("CubeRight", CubeRight);
|
||||||
|
echarts.graphic.registerShape("CubeTop", CubeTop);
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
|
||||||
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
setTimeout(() => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
updateChart();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新图表配置
|
||||||
|
const updateChart = () => {
|
||||||
|
if (!chartInstance) return;
|
||||||
|
const series = props.datas
|
||||||
|
.map((item, index) => [
|
||||||
|
{
|
||||||
|
type: "custom",
|
||||||
|
name: props.legendArr[index],
|
||||||
|
renderItem: (params, api) => ({
|
||||||
|
type: "group",
|
||||||
|
x: (index - props.datas.length / 2) * 30 + 15,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "CubeLeft",
|
||||||
|
shape: {
|
||||||
|
api,
|
||||||
|
xValue: api.value(0),
|
||||||
|
yValue: api.value(1),
|
||||||
|
x: api.coord([api.value(0), api.value(1)])[0],
|
||||||
|
y: api.coord([api.value(0), api.value(1)])[1],
|
||||||
|
xAxisPoint: api.coord([api.value(0), 0]),
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][0],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "CubeRight",
|
||||||
|
shape: {
|
||||||
|
api,
|
||||||
|
xValue: api.value(0),
|
||||||
|
yValue: api.value(1),
|
||||||
|
x: api.coord([api.value(0), api.value(1)])[0],
|
||||||
|
y: api.coord([api.value(0), api.value(1)])[1],
|
||||||
|
xAxisPoint: api.coord([api.value(0), 0]),
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][0],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "CubeTop",
|
||||||
|
shape: {
|
||||||
|
api,
|
||||||
|
xValue: api.value(0),
|
||||||
|
yValue: api.value(1),
|
||||||
|
x: api.coord([api.value(0), api.value(1)])[0],
|
||||||
|
y: api.coord([api.value(0), api.value(1)])[1],
|
||||||
|
xAxisPoint: api.coord([api.value(0), 0]),
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: props.colorArr[index % props.colorArr.length][1],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
data: item,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "bar",
|
||||||
|
name: props.legendArr[index],
|
||||||
|
barWidth: 25,
|
||||||
|
// label: {
|
||||||
|
// normal: {
|
||||||
|
// show: true,
|
||||||
|
// position: "top",
|
||||||
|
// fontSize: 16,
|
||||||
|
// color: "#fff",
|
||||||
|
// offset: [0, -10],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
itemStyle: { color: "transparent" },
|
||||||
|
data: item,
|
||||||
|
yAxisIndex: 0, // 使用左y轴
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "bar",
|
||||||
|
name: props.legendArr[index],
|
||||||
|
barWidth: 25,
|
||||||
|
itemStyle: { color: "transparent" },
|
||||||
|
data: item,
|
||||||
|
yAxisIndex: 1, // 使用左y轴
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
chartInstance.setOption({
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
borderWidth: 0,
|
||||||
|
backgroundColor: "rgba(8,36,68,.9)",
|
||||||
|
color: "#fff",
|
||||||
|
textStyle: { color: "#fff" },
|
||||||
|
formatter: (params) => {
|
||||||
|
let str = params[0].name + "</br>";
|
||||||
|
params.forEach((item, index) => {
|
||||||
|
if (item.seriesType === "custom") {
|
||||||
|
str += `
|
||||||
|
<div style='display:flex;justify-content:space-between;align-items:center'>
|
||||||
|
<div style='margin-right:20px;'>
|
||||||
|
<span style="display:inline-block;width:10px;height:10px;border-radius:5px;background-color:${
|
||||||
|
props.colorArr[index % props.colorArr.length][0]
|
||||||
|
}"></span>
|
||||||
|
${item.seriesName}
|
||||||
|
</div>
|
||||||
|
<span> ${item.value ? item.value : "-"}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: "5%",
|
||||||
|
right: "5%",
|
||||||
|
top: "5%",
|
||||||
|
bottom: "10%",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
left: "center",
|
||||||
|
top: "90%",
|
||||||
|
itemWidth: 12, // 图例项宽度
|
||||||
|
itemHeight: 8,
|
||||||
|
textStyle: { color: "#fff", fontSize: 12 },
|
||||||
|
data: props.legendArr.map((name, index) => ({
|
||||||
|
name,
|
||||||
|
textStyle: { color: "#fff", fontSize: 12 },
|
||||||
|
itemStyle: { color: props.colorArr[index % props.colorArr.length][1] },
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: props.xData,
|
||||||
|
axisLine: { lineStyle: { color: "rgba(239, 247, 253, .1)" } },
|
||||||
|
axisLabel: { fontSize: 12, color: "#fff", margin: 12 },
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
// name: "kWh",
|
||||||
|
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: { type: "dashed", color: "rgba(80,112,242,0.3)" },
|
||||||
|
},
|
||||||
|
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
||||||
|
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
||||||
|
// scale: true, // 同步刻度
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// name: "kWh",
|
||||||
|
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
||||||
|
splitLine: { lineStyle: { color: "transparent" } },
|
||||||
|
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
||||||
|
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
||||||
|
position: "right",
|
||||||
|
// scale: true, // 同步刻度
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleResize = () => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
};
|
||||||
|
// 生命周期管理
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
initChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
chartInstance?.resize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 响应式更新
|
||||||
|
watch(
|
||||||
|
() => props,
|
||||||
|
async () => {
|
||||||
|
await nextTick();
|
||||||
|
updateChart();
|
||||||
|
// delay(600).then(() => resize());
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||||
|
</template>
|
@ -0,0 +1,103 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
// import deviceStatusOnline from "@/assets/svg/deviceStatus/online.svg?component";
|
||||||
|
// import deviceStatusError from "@/assets/svg/deviceStatus/error.svg?component";
|
||||||
|
// import deviceStatusOutline from "@/assets/svg/deviceStatus/outline.svg?component";
|
||||||
|
defineOptions({
|
||||||
|
name: "DeviceStatus"
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
deviceStatus: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const deviceStatusData = ref({ ...props.deviceStatus });
|
||||||
|
|
||||||
|
const deviceStatusOptions = ref<Record<string, any>[]>([
|
||||||
|
{
|
||||||
|
label: "在线",
|
||||||
|
color: "#52C41A",
|
||||||
|
bgColor: "#52C41A",
|
||||||
|
valueKey: "onlineCount" // 在线数量
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "离线",
|
||||||
|
color: "#ccc",
|
||||||
|
bgColor: "#999999",
|
||||||
|
valueKey: "outlineCount" // 故障数量
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "故障",
|
||||||
|
color: "#E80D0D",
|
||||||
|
bgColor: "#E80D0D",
|
||||||
|
valueKey: "errorCount" // 故障数量
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul class="w-full text-sm deviceStatus_box">
|
||||||
|
<li
|
||||||
|
class="flex items-center justify-between w-full"
|
||||||
|
:style="{
|
||||||
|
marginBottom: '16px'
|
||||||
|
}"
|
||||||
|
v-for="(v, k) in deviceStatusOptions"
|
||||||
|
:key="k"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-center"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: v.bgColor,
|
||||||
|
width: '40px',
|
||||||
|
height: '40px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
marginRight: '12px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div v-if="v.valueKey === 'onlineCount'" class="deviceStatusOnline"></div>
|
||||||
|
<div v-if="v.valueKey === 'errorCount'" class="deviceStatusError"></div>
|
||||||
|
<div v-if="v.valueKey === 'outlineCount'" class="deviceStatusOutline"></div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col flex-1">
|
||||||
|
<div class="flex justify-between" style="margin-bottom: 4px">
|
||||||
|
<span>{{ v.label }}</span>
|
||||||
|
<span>{{ deviceStatusData[v.valueKey] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="w-full">
|
||||||
|
<el-progress
|
||||||
|
:show-text="false"
|
||||||
|
:stroke-width="8"
|
||||||
|
:percentage="deviceStatusData[v.valueKey]"
|
||||||
|
:color="v.color"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.deviceStatus_box {
|
||||||
|
li {
|
||||||
|
div {
|
||||||
|
.deviceStatusOnline {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: url('@/assets/svg/deviceStatus/online.svg') no-repeat center center;
|
||||||
|
}
|
||||||
|
.deviceStatusError {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: url('@/assets/svg/deviceStatus/error.svg') no-repeat center center;
|
||||||
|
}
|
||||||
|
.deviceStatusOutline {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: url('@/assets/svg/deviceStatus/outline.svg') no-repeat center center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,92 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array as PropType<Array<{ value: number; name: string }>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
colors: { type: Array as PropType<Array<string>>, default: () => [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
setTimeout(() => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
updateChart();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新图表配置
|
||||||
|
const updateChart = () => {
|
||||||
|
if (!chartInstance) return;
|
||||||
|
|
||||||
|
chartInstance.setOption({
|
||||||
|
legend: {
|
||||||
|
type: "scroll",
|
||||||
|
orient: "vertical",
|
||||||
|
left: "70%",
|
||||||
|
align: "left",
|
||||||
|
top: "middle",
|
||||||
|
itemWidth: 16, // 图例项宽度
|
||||||
|
itemHeight: 8,
|
||||||
|
textStyle: { color: "#8C8C8C" },
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
radius: ["30%", "80%"],
|
||||||
|
center: ["35%", "50%"],
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) =>
|
||||||
|
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: props.colors[params.dataIndex] },
|
||||||
|
{ offset: 1, color: "#FFFFFF" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
data: props.data,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生命周期管理
|
||||||
|
onMounted(() => {
|
||||||
|
initChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 响应式更新
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
await nextTick();
|
||||||
|
updateChart();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 窗口大小监听
|
||||||
|
// onMounted(() => {
|
||||||
|
// window.addEventListener("resize", () => chartInstance?.resize());
|
||||||
|
// });
|
||||||
|
|
||||||
|
// onUnmounted(() => {
|
||||||
|
// window.removeEventListener("resize", () => chartInstance?.resize());
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||||
|
</template>
|
@ -0,0 +1,92 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array as PropType<Array<{ value: number; name: string }>>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
colors: { type: Array as PropType<Array<string>>, default: () => [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
||||||
|
let chartInstance: echarts.ECharts | null = null;
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
setTimeout(() => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
updateChart();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新图表配置
|
||||||
|
const updateChart = () => {
|
||||||
|
if (!chartInstance) return;
|
||||||
|
|
||||||
|
chartInstance.setOption({
|
||||||
|
legend: {
|
||||||
|
type: "scroll",
|
||||||
|
orient: "vertical",
|
||||||
|
left: "70%",
|
||||||
|
align: "left",
|
||||||
|
top: "middle",
|
||||||
|
itemWidth: 16, // 图例项宽度
|
||||||
|
itemHeight: 8,
|
||||||
|
textStyle: { color: "#8C8C8C" },
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: "pie",
|
||||||
|
radius: ["40%", "80%"],
|
||||||
|
center: ["35%", "50%"],
|
||||||
|
label: { show: false },
|
||||||
|
itemStyle: {
|
||||||
|
color: (params) =>
|
||||||
|
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||||
|
{ offset: 0, color: props.colors[params.dataIndex] },
|
||||||
|
{ offset: 1, color: "#FFFFFF" },
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
data: props.data,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生命周期管理
|
||||||
|
onMounted(() => {
|
||||||
|
initChart();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.dispose();
|
||||||
|
chartInstance = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 响应式更新
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
async () => {
|
||||||
|
await nextTick();
|
||||||
|
updateChart();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 窗口大小监听
|
||||||
|
// onMounted(() => {
|
||||||
|
// window.addEventListener("resize", () => chartInstance?.resize());
|
||||||
|
// });
|
||||||
|
|
||||||
|
// onUnmounted(() => {
|
||||||
|
// window.removeEventListener("resize", () => chartInstance?.resize());
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
||||||
|
</template>
|
Loading…
Reference in New Issue