2026-07-01 18:23:41 +08:00
|
|
|
<!-- developStatusChart/index.vue -->
|
2026-03-31 10:14:20 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="basic_body">
|
|
|
|
|
<div ref="chartContainer" class="chart-container"></div>
|
2026-07-06 08:54:58 +08:00
|
|
|
|
|
|
|
|
<!-- 水电开发情况弹窗 -->
|
|
|
|
|
<a-modal
|
|
|
|
|
v-model:open="modalVisible"
|
|
|
|
|
title="水电开发情况"
|
2026-07-07 13:50:21 +08:00
|
|
|
width="80%"
|
2026-07-06 08:54:58 +08:00
|
|
|
:footer="null"
|
|
|
|
|
:destroy-on-close="true"
|
|
|
|
|
>
|
|
|
|
|
<ShuiDianKaiFQKTwoLayer
|
|
|
|
|
v-if="modalVisible"
|
|
|
|
|
:defaultJidiInfo="modalProps.defaultJidiInfo"
|
|
|
|
|
:seriesName="modalProps.seriesName"
|
|
|
|
|
:stateName="modalProps.stateName"
|
|
|
|
|
:defaultTab="modalProps.defaultTab"
|
|
|
|
|
/>
|
|
|
|
|
</a-modal>
|
2026-03-31 10:14:20 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2026-07-01 18:23:41 +08:00
|
|
|
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
2026-03-31 10:14:20 +08:00
|
|
|
import * as echarts from 'echarts';
|
2026-07-01 18:23:41 +08:00
|
|
|
import { getVmsstbprptKendoList } from '@/api/home';
|
|
|
|
|
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
2026-07-06 08:54:58 +08:00
|
|
|
import ShuiDianKaiFQKTwoLayer from '@/modules/shuidianhuangjingjieruMod/TwoLayer/ShuiDianKaiFQKTwoLayer.vue';
|
2026-07-07 10:33:29 +08:00
|
|
|
import { useDraggable } from '@/utils/drag';
|
2026-03-31 10:14:20 +08:00
|
|
|
defineOptions({
|
|
|
|
|
name: 'developStatusChart'
|
|
|
|
|
});
|
|
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
|
|
|
|
const baseid = ref('');
|
2026-03-31 10:14:20 +08:00
|
|
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
|
|
|
|
let chartInstance: echarts.ECharts | null = null;
|
2026-07-01 18:23:41 +08:00
|
|
|
const dataLoading = ref(false);
|
2026-03-31 10:14:20 +08:00
|
|
|
|
2026-07-06 08:54:58 +08:00
|
|
|
const modalVisible = ref(false);
|
|
|
|
|
const modalProps = ref<any>({});
|
2026-07-07 10:33:29 +08:00
|
|
|
useDraggable(modalVisible, { boundary: true, resetOnOpen: true });
|
2026-07-06 08:54:58 +08:00
|
|
|
function openModal(props: any) {
|
|
|
|
|
modalProps.value = props;
|
|
|
|
|
modalVisible.value = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
openModal,
|
|
|
|
|
dataLoading
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-31 10:14:20 +08:00
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.dispose();
|
|
|
|
|
chartInstance = null;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
const transUnit = (value: number) => {
|
|
|
|
|
return value * 0.1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getData = async () => {
|
|
|
|
|
if (!baseid.value) return;
|
|
|
|
|
|
|
|
|
|
const baseFilters: any[] = [
|
|
|
|
|
{ field: 'sttpCode', operator: 'eq', dataType: 'string', value: 'ENG' },
|
|
|
|
|
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
|
|
|
|
];
|
|
|
|
|
if (baseid.value !== 'all') {
|
|
|
|
|
baseFilters.unshift({ field: 'baseId', operator: 'eq', dataType: 'string', value: baseid.value });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const params1 = {
|
|
|
|
|
filter: {
|
|
|
|
|
logic: 'and',
|
|
|
|
|
filters: [
|
|
|
|
|
...baseFilters,
|
|
|
|
|
{ field: 'ttpwr', operator: 'isnotnull' }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
group: [{ dir: 'desc', field: 'bldsttCcode', aggregates: [{ field: 'ttpwr', aggregate: 'sum' }] }]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const params2 = {
|
|
|
|
|
filter: {
|
|
|
|
|
logic: 'and',
|
|
|
|
|
filters: [...baseFilters]
|
|
|
|
|
},
|
|
|
|
|
group: [{ dir: 'desc', field: 'bldsttCcode' }]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
dataLoading.value = true;
|
|
|
|
|
const [res1, res2] = await Promise.all([
|
|
|
|
|
getVmsstbprptKendoList(params1),
|
|
|
|
|
getVmsstbprptKendoList(params2)
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const res1Data = res1?.data?.data || [];
|
|
|
|
|
const res2Data = res2?.data?.data || [];
|
|
|
|
|
|
|
|
|
|
if (res1Data.length > 0 || res2Data.length > 0) {
|
|
|
|
|
const ttpwrlist = res1Data.map((item: any) => ({
|
|
|
|
|
key: item.key,
|
|
|
|
|
ttpwr: transUnit(item.items[0].SUM_TTPWR)
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const engTotalList = res2Data.map((item: any) => ({
|
|
|
|
|
key: item.key,
|
|
|
|
|
engTotal: item.count
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const result: any = Object.values(
|
|
|
|
|
[...engTotalList, ...ttpwrlist].reduce((result: any, item: any) => {
|
|
|
|
|
const { key, engTotal = 0, ttpwr = 0 } = item;
|
|
|
|
|
const existingItem = result[key] || { key, engTotal: 0, ttpwr: 0 };
|
|
|
|
|
existingItem.bldstt = item.key == 0 ? '未建' : item.key == 1 ? '在建' : '已建';
|
|
|
|
|
existingItem.engTotal += engTotal;
|
|
|
|
|
existingItem.ttpwr += ttpwr;
|
|
|
|
|
return { ...result, [key]: existingItem };
|
|
|
|
|
}, {})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
result.sort((a: any, b: any) => {
|
|
|
|
|
if (a.bldstt === '已建' && b.bldstt !== '已建') return -1;
|
|
|
|
|
else if (a.bldstt !== '已建' && b.bldstt === '已建') return 1;
|
|
|
|
|
else if (a.bldstt === '在建' && b.bldstt === '未建') return -1;
|
|
|
|
|
else if (a.bldstt === '未建' && b.bldstt === '在建') return 1;
|
|
|
|
|
else return 0;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const legendData: string[] = [];
|
|
|
|
|
const capData: { name: string; value: number }[] = [];
|
|
|
|
|
const countData: { name: string; value: number }[] = [];
|
|
|
|
|
|
|
|
|
|
result.forEach((item: any) => {
|
|
|
|
|
const name = item.bldstt;
|
|
|
|
|
if (name !== '未建') {
|
|
|
|
|
legendData.push(name);
|
|
|
|
|
countData.push({ name, value: Number(item.engTotal) });
|
|
|
|
|
capData.push({ name, value: Number(item.ttpwr).toFixed(2) });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const colors = ['#78c300', '#2196F3'];
|
|
|
|
|
|
|
|
|
|
const option = {
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'item',
|
|
|
|
|
formatter: '{b}电站{a}: {c}',
|
|
|
|
|
position: 'right'
|
|
|
|
|
},
|
|
|
|
|
legend: {
|
|
|
|
|
data: legendData,
|
|
|
|
|
left: '60%',
|
|
|
|
|
orient: 'vertical',
|
|
|
|
|
top: 'center',
|
|
|
|
|
itemWidth: 20,
|
|
|
|
|
itemHeight: 12
|
|
|
|
|
},
|
|
|
|
|
color: colors,
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: '数量(座)',
|
|
|
|
|
type: 'pie',
|
|
|
|
|
radius: [0, '55%'],
|
|
|
|
|
label: { show: false },
|
|
|
|
|
labelLine: { show: false },
|
|
|
|
|
data: countData,
|
|
|
|
|
center: ['30%', '50%'],
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderRadius: 2,
|
|
|
|
|
borderColor: '#fff',
|
|
|
|
|
borderWidth: 2
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '装机容量(万kW)',
|
|
|
|
|
type: 'pie',
|
|
|
|
|
radius: ['70%', '90%'],
|
|
|
|
|
labelLine: { length: 30 },
|
|
|
|
|
label: { show: false },
|
|
|
|
|
data: capData,
|
|
|
|
|
center: ['30%', '50%'],
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderRadius: 2,
|
|
|
|
|
borderColor: '#fff',
|
|
|
|
|
borderWidth: 2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.setOption(option, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('DevelopStatusChart getData error:', error);
|
|
|
|
|
} finally {
|
|
|
|
|
dataLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-31 10:14:20 +08:00
|
|
|
const initChart = () => {
|
|
|
|
|
if (!chartContainer.value) return;
|
|
|
|
|
|
|
|
|
|
chartInstance = echarts.init(chartContainer.value);
|
|
|
|
|
|
|
|
|
|
const option = {
|
2026-07-01 18:23:41 +08:00
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'item',
|
|
|
|
|
formatter: '{a} {b}: {c} ({d}%)',
|
|
|
|
|
position: 'right'
|
|
|
|
|
},
|
2026-03-31 10:14:20 +08:00
|
|
|
legend: {
|
|
|
|
|
data: ['已建', '在建'],
|
2026-07-01 18:23:41 +08:00
|
|
|
left: '60%',
|
|
|
|
|
orient: 'vertical',
|
|
|
|
|
top: 'center',
|
|
|
|
|
itemWidth: 20,
|
|
|
|
|
itemHeight: 12
|
2026-03-31 10:14:20 +08:00
|
|
|
},
|
2026-07-01 18:23:41 +08:00
|
|
|
color: ['#78c300', '#2196F3'],
|
2026-03-31 10:14:20 +08:00
|
|
|
series: [
|
|
|
|
|
{
|
2026-07-01 18:23:41 +08:00
|
|
|
name: '装机容量(万kW)',
|
2026-03-31 10:14:20 +08:00
|
|
|
type: 'pie',
|
2026-07-01 18:23:41 +08:00
|
|
|
radius: ['70%', '90%'],
|
|
|
|
|
center: ['30%', '50%'],
|
|
|
|
|
itemStyle: {
|
|
|
|
|
borderRadius: 2,
|
|
|
|
|
borderColor: '#fff',
|
|
|
|
|
borderWidth: 2
|
2026-03-31 10:14:20 +08:00
|
|
|
},
|
2026-07-01 18:23:41 +08:00
|
|
|
label: { show: false },
|
|
|
|
|
labelLine: { show: false },
|
|
|
|
|
data: []
|
2026-03-31 10:14:20 +08:00
|
|
|
},
|
|
|
|
|
{
|
2026-04-03 16:08:05 +08:00
|
|
|
name: '数量(座)',
|
2026-03-31 10:14:20 +08:00
|
|
|
type: 'pie',
|
2026-07-01 18:23:41 +08:00
|
|
|
radius: ['0%', '50%'],
|
|
|
|
|
center: ['30%', '50%'],
|
2026-03-31 10:14:20 +08:00
|
|
|
itemStyle: {
|
2026-07-01 18:23:41 +08:00
|
|
|
borderRadius: 2,
|
|
|
|
|
borderColor: '#fff',
|
|
|
|
|
borderWidth: 2
|
2026-03-31 10:14:20 +08:00
|
|
|
},
|
2026-07-01 18:23:41 +08:00
|
|
|
label: { show: false },
|
|
|
|
|
labelLine: { show: false },
|
|
|
|
|
data: []
|
2026-03-31 10:14:20 +08:00
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
chartInstance.setOption(option);
|
|
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
chartInstance.on('click', (params: any) => {
|
2026-07-06 08:54:58 +08:00
|
|
|
const currentJidiInfo = JidiSelectEventStore.selectedItem;
|
|
|
|
|
openModal({
|
|
|
|
|
defaultJidiInfo: {
|
|
|
|
|
baseid: currentJidiInfo?.wbsCode || 'all',
|
|
|
|
|
basename: currentJidiInfo?.wbsName || '全部基地'
|
|
|
|
|
},
|
2026-07-01 18:23:41 +08:00
|
|
|
seriesName: params.seriesName,
|
2026-07-06 08:54:58 +08:00
|
|
|
stateName: params.name,
|
|
|
|
|
defaultTab: params.name
|
2026-07-01 18:23:41 +08:00
|
|
|
});
|
2026-03-31 10:14:20 +08:00
|
|
|
});
|
2026-05-15 08:51:17 +08:00
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
|
chartInstance?.resize();
|
2026-05-15 08:51:17 +08:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
onMounted(() => {
|
|
|
|
|
if (chartContainer.value) {
|
|
|
|
|
initChart();
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-05-15 08:51:17 +08:00
|
|
|
|
2026-07-01 18:23:41 +08:00
|
|
|
watch(
|
|
|
|
|
() => JidiSelectEventStore.selectedItem,
|
|
|
|
|
newVal => {
|
|
|
|
|
if (newVal && newVal.wbsCode) {
|
|
|
|
|
baseid.value = newVal.wbsCode;
|
|
|
|
|
getData();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{ deep: true, immediate: true }
|
|
|
|
|
);
|
2026-03-31 10:14:20 +08:00
|
|
|
</script>
|
|
|
|
|
|
2026-04-03 16:08:05 +08:00
|
|
|
<style lang="scss" scoped>
|
2026-03-31 10:14:20 +08:00
|
|
|
.basic_body {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
|
|
|
|
.chart-container {
|
2026-07-01 18:23:41 +08:00
|
|
|
width: 203px;
|
|
|
|
|
height: 100px;
|
2026-05-15 08:51:17 +08:00
|
|
|
}
|
2026-03-31 10:14:20 +08:00
|
|
|
}
|
2026-07-01 18:23:41 +08:00
|
|
|
</style>
|