<div class="bottom">
<div class="topli">
<p>用电统计</p>
<div class="tabs">
<div class="tab" :class="{ active: active.tab1 === index }"
v-for="(item, index) in tabsList1" :key="index" @click="changeTab(1, index)">
{{ item.label }}
</div>
</div>
</div>
<div class="list">
<div id="sum-electricity" v-if="!loading.tab1 && active.tab1 === 0">
<TestEleChart pid="sum-electricity" title="月用电统计" :eleData="eleData"
:eleXdata="eleXdata"></TestEleChart>
</div>
<div id="day-electricity" v-if="!loading.tab1 && active.tab1 === 1">
<TestEleChart pid="day-electricity" title="日用电统计" :eleData="eleData"
:eleXdata="eleXdata"></TestEleChart>
</div>
</div>
</div>
<div class="sitestutus">
<div class="top">
<p>用水统计</p>
<div class="tabs">
<div class="tab" :class="{ active: active.tab2 === index }"
v-for="(item, index) in tabsList" :key="index" @click="changeTab(2, index)">
{{ item.label }}
</div>
</div>
</div>
<div class="sitebutom">
<div id="monthWate" v-if="!loading.tab2 && active.tab2 === 0">
<TestWaterChart pid="monthWate" title="月用水" :waterData="waterData"
:waterXdata="waterXdata"></TestWaterChart>
</div>
<div id="dayWater" v-if="!loading.tab2 && active.tab2 === 1">
<TestWaterChart pid="dayWater" title="日用水" :waterData="waterData"
:waterXdata="waterXdata"></TestWaterChart>
</div>
</div>
</div>
<script setup lang="ts">
// tabs切换
const tabsList = [
{ label: '日' },
{ label: '月' },
];
const tabsList1 = [
{ label: '日' },
{ label: '月' },
];
const active = ref({
tab1: 0,
tab2: 0,
});
const loading = ref({
tab1: false,
tab2: false,
});
const waterData = ref<number[]>([]);
const waterXdata = ref<string[]>([]);
const eleData = ref<number[]>([]);
const eleXdata = ref<string[]>([]);
// const loading = ref(false)
const fetchData = async (type: string, tabGroup: number) => {
if (tabGroup === 1) {
loading.value.tab1 = true;
const store = siteStore();
const res = await reqGetEleStatistics(store.curSite.id, type);
eleXdata.value = res.data[type === 'day' ? 'elcWrDay' : 'elcWrMonth'].map((item: any) => item.createTime);
eleData.value = res.data[type === 'day' ? 'elcWrDay' : 'elcWrMonth'].map((item: any) => item.value);
if (eleData.value.length === 0) {
eleXdata.value = ["无数据"];
eleData.value = [0];
}
loading.value.tab1 = false;
} else if (tabGroup === 2) {
loading.value.tab2 = true;
const store = siteStore();
const res = await reqGetWaterStatistics(store.curSite.id, type);
waterXdata.value = res.data[type === 'day' ? 'elcWrDay' : 'elcWrMonth'].map((item: any) => item.createTime);
waterData.value = res.data[type === 'day' ? 'elcWrDay' : 'elcWrMonth'].map((item: any) => item.value);
if (waterData.value.length === 0) {
waterXdata.value = ["无数据"];
waterData.value = [0];
}
loading.value.tab2 = false;
}
};
const changeTab = async (tabGroup: number, index: number) => {
if (tabGroup === 1) {
active.value.tab1 = index;
const type = index === 0 ? 'day' : 'month';
await fetchData(type, tabGroup);
} else if (tabGroup === 2) {
active.value.tab2 = index;
const type = index === 0 ? 'day' : 'month';
await fetchData(type, tabGroup);
}
};
// 初始化时获取数据
onMounted(async () => {
await changeTab(1, active.value.tab1);
await changeTab(2, active.value.tab2);
});
// scss
.bottom {
position: absolute;
top: vh(635);
width: vw(390);
height: 400px;
.topli {
display: flex;
}
p {
width: vw(435);
margin-left: vw(10);
line-height: vh(40);
color: #fff !important;
text-indent: 1rem;
font-weight: 700;
font-size: 25px;
}
#sum-electricity,
#day-electricity {
width: vw(385);
height: vh(300);
}
}
.sitestutus {
position: absolute;
top: vh(300);
width: vw(385);
height: 350px;
p {
width: vw(435);
margin-left: vw(10);
line-height: vh(40);
color: #fff !important;
// background: linear-gradient(84deg, #1d4591 0%, rgba(57, 123, 243, 0) 100%) !important;
text-indent: 1rem;
font-weight: 700;
font-size: 25px;
}
.sitebutom {
color: #fff;
font-size: 20px;
}
#dayWater,
#monthWate {
width: vw(385);
height: vh(300);
}
}
</script>
封装的 柱状图 组件 TestEleChart
成品如图所示
<!-- 水电大屏用电趋势 -->
<template>
<div>
</div>
</template>
<script setup lang="ts">
import { nextTick, watch, computed, onMounted, ref, onUpdated, onBeforeUnmount } from 'vue';
import echarts from '@/assets/ts/echarts';
import { ZRColor } from 'echarts/types/dist/shared';
import useResizeChart from '@/components/CommonChart/hooks/useResizeChart';
import { color } from 'echarts';
import { EffectScatterChart } from 'echarts/charts';
echarts.use([EffectScatterChart]);
const props = defineProps({
pid: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
// 饼图 颜色
color: {
type: Array as () => ZRColor[],
default: [],
},
eleData: {
type: Array,
required: true,
},
// 横坐标值
eleXdata: {
type: Array,
required: true,
}
})
const sideData = props.eleData.map((item: any) => item + 7.5)
// nextTick(() => {
// // 2. 容器
// const container = document.querySelector('#' + props.pid) as HTMLElement;
// if (container) renderChart(container);
// });
// // 3. 初始化 echarts 实例, 将配置添加给 echarts 实例
let myChart: echarts.ECharts | null = null;
// const renderChart = (container?: HTMLElement) => {
// if (!myChart) {
// myChart = echarts.init(container as HTMLElement);
// // 自适应 chart
// useResizeChart(container as HTMLElement, myChart as echarts.ECharts);
// }
// myChart.setOption(option);
// };
// 改进
const initChart = async () => {
await nextTick(); // 确保 DOM 更新完成
const container = document.querySelector('#' + props.pid) as HTMLElement;
if (container) {
myChart = echarts.init(container);
// 自适应 chart
// @ts-ignore
useResizeChart(container as HTMLElement, myChart);
renderChart();
}
}
// 4.配置项
const renderChart = () => {
if(!myChart) return;
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
formatter: "{b} : {c}",
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: {
data: props.eleXdata,
//坐标轴
axisLine: {
lineStyle: {
color: '#3eb2e8'
}
},
//坐标值标注
axisLabel: {
show: true,
textStyle: {
color: '#fff',
}
}
},
yAxis: {
//坐标轴
axisLine: {
show: false
},
//坐标值标注
axisLabel: {
show: true,
textStyle: {
color: '#fff',
}
},
//分格线
splitLine: {
lineStyle: {
color: '#4784e8'
}
}
},
series: [{
name: 'a',
tooltip: {
show: false
},
type: 'bar',
// 柱子的宽度
barWidth: 15,
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
offset: 0,
color: "#0B4EC3" // 0% 处的颜色
}, {
offset: 0.6,
color: "#138CEB" // 60% 处的颜色
}, {
offset: 1,
color: "#17AAFE" // 100% 处的颜色
}], false)
}
},
data: props.eleData,
barGap: 0
}, {
type: 'bar',
barWidth: 8,
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
offset: 0,
color: "#09337C" // 0% 处的颜色
}, {
offset: 0.6,
color: "#0761C0" // 60% 处的颜色
}, {
offset: 1,
color: "#0575DE" // 100% 处的颜色
}], false)
}
},
barGap: 0,
data: sideData
}, {
name: 'b',
tooltip: {
show: false
},
type: 'pictorialBar',
itemStyle: {
borderWidth: 0,
borderColor: '#0571D5',
color: '#1779E0'
},
symbol: 'path://M 0,-10 l 110,0 l -12,85 l -110,0 z',
// key[0]为盖子宽度 key[1]为倾斜程度 值小倾斜小
symbolSize: ['23', '8'],
// key[1] y轴
symbolOffset: ['0', '-8'],
//symbolRotate: -5,
symbolPosition: 'end',
data: props.eleData,
z: 3
}]
};
myChart.setOption(option);
}
onMounted(initChart);
onBeforeUnmount(() => {
if (myChart) {
myChart.dispose();
myChart = null;
}
});
watch(
[() => props.eleData, () => props.eleXdata],
() => {
renderChart();
},
{ deep: true }
);
</script>
<style scoped></style>
封装的 柱状图 组件 TestEleChart
成品如图所示
<!-- 水务大屏柱状图 -->
<template>
<div :id="props.pid" style="width: 100%; height: 100%;"></div>
</template>
<script setup lang="ts">
import { nextTick, watch, ref, onMounted, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
import { ZRColor } from 'echarts/types/dist/shared';
import useResizeChart from '@/components/CommonChart/hooks/useResizeChart';
const props = defineProps({
pid: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
color: {
type: Array as () => ZRColor[],
default: () => [],
},
waterData: {
type: Array,
required: true,
},
waterXdata: {
type: Array,
required: true,
}
});
let myChart: echarts.ECharts | null = null;
const initChart = async () => {
await nextTick(); // 确保 DOM 更新完成
const container = document.querySelector('#' + props.pid) as HTMLElement;
if (container) {
myChart = echarts.init(container);
// 自适应 chart
// @ts-ignore
useResizeChart(container as HTMLElement, myChart);
renderChart();
}
};
const renderChart = () => {
if (!myChart) return;
const option = {
backgroundColor: "transparent",
color: ["#3cefff"],
tooltip: {},
grid: {
containLabel: true,
},
xAxis: [
{
type: "category",
data: props.waterXdata,
axisTick: {
alignWithLabel: true,
},
nameTextStyle: {
color: "#82b0ec",
},
axisLine: {
lineStyle: {
color: "#82b0ec",
},
},
axisLabel: {
textStyle: {
color: "#82b0ec",
},
},
},
],
yAxis: [
{
type: "value",
axisLabel: {
textStyle: {
color: "#82b0ec",
},
formatter: "{value}%",
},
splitLine: {
lineStyle: {
color: "#0c2c5a",
},
},
axisLine: {
show: false,
},
},
],
series: [
{
name: "",
type: "pictorialBar",
symbolSize: [20, 10],
symbolOffset: [0, -5],
symbolPosition: "end",
z: 12,
label: {
show: true,
position: "top",
formatter: "{c}%",
},
data: props.waterData,
},
{
name: "",
type: "pictorialBar",
symbolSize: [20, 10],
symbolOffset: [0, 5],
z: 12,
data: props.waterData,
},
{
type: "bar",
itemStyle: {
opacity: 0.7,
},
barWidth: "20",
data: props.waterData,
markLine: {
silent: true,
symbol: "none",
label: {
position: "middle",
formatter: "{b}",
},
data: [
{
name: "目标值",
yAxis: 80,
lineStyle: {
color: "#ffc832",
},
label: {
position: "end",
formatter: "{b}\n {c}%",
},
},
],
},
},
{
type: "effectScatter",
silent: true,
tooltip: {
show: false,
},
zlevel: 3,
symbolSize: 10,
showEffectOn: "render",
rippleEffect: {
brushType: "stroke",
color: "#3cefff",
scale: 5,
},
itemStyle: {
color: "#3cefff",
},
hoverAnimation: true,
data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0],
},
],
};
myChart.setOption(option);
};
onMounted(initChart);
onBeforeUnmount(() => {
if (myChart) {
myChart.dispose();
myChart = null;
}
});
watch(
[() => props.waterData, () => props.waterXdata],
() => {
renderChart();
},
{ deep: true }
);
</script>
<style scoped>
/* 样式代码 */
</style>