2025-08-26 15:43:53 +08:00
|
|
|
<template>
|
|
|
|
<div class="go-border-box">
|
2025-09-08 15:04:00 +08:00
|
|
|
<PublicSmallBorder :title-text="option.titleText" :select-option="option.dateTime" @change="handleSelectChange">
|
|
|
|
|
|
|
|
<!-- <div class="content"> -->
|
|
|
|
<v-chart ref="vChartRef" autoresize :init-options="initOptions" :theme="themeColor"
|
|
|
|
:option="option"></v-chart>
|
|
|
|
</PublicSmallBorder>
|
2025-08-26 15:43:53 +08:00
|
|
|
</div>
|
2025-08-27 21:52:06 +08:00
|
|
|
|
2025-08-26 15:43:53 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import { computed, PropType, onMounted, watch, ref, onUnmounted } from 'vue'
|
|
|
|
import VChart from 'vue-echarts'
|
|
|
|
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
|
|
|
|
import { use } from 'echarts/core'
|
|
|
|
import { CanvasRenderer } from 'echarts/renderers'
|
|
|
|
import { PieChart } from 'echarts/charts'
|
|
|
|
import { mergeTheme } from '@/packages/public/chart'
|
|
|
|
import config, { includes } from './config'
|
|
|
|
import { useChartDataFetch } from '@/hooks'
|
|
|
|
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
|
|
|
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent, GraphicComponent } from 'echarts/components'
|
|
|
|
import CustomSelect from './select.vue'
|
|
|
|
import dataJson from './data.json'
|
2025-09-02 13:47:51 +08:00
|
|
|
import axiosInstance from '@/api/axios'
|
2025-09-08 15:04:00 +08:00
|
|
|
import PublicSmallBorder from '../../PublicSmallBorder/index.vue'
|
2025-08-26 15:43:53 +08:00
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
themeSetting: {
|
|
|
|
type: Object,
|
|
|
|
required: true
|
|
|
|
},
|
|
|
|
themeColor: {
|
|
|
|
type: Object,
|
|
|
|
required: true
|
|
|
|
},
|
|
|
|
chartConfig: {
|
|
|
|
type: Object as PropType<config>,
|
|
|
|
required: true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
const initOptions = useCanvasInitOptions(props.chartConfig.option, props.themeSetting)
|
|
|
|
|
|
|
|
use([DatasetComponent, CanvasRenderer, PieChart, GridComponent, TooltipComponent, LegendComponent, GraphicComponent])
|
|
|
|
|
|
|
|
const option = computed(() => {
|
|
|
|
return mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
|
|
|
})
|
|
|
|
|
|
|
|
function calculateTotal(data: any) {
|
|
|
|
if (!data || !data.source) return 0
|
|
|
|
return data.source.reduce((total: number, item: any) => {
|
2025-08-27 21:52:06 +08:00
|
|
|
return total + (item.value || 0)
|
2025-08-26 15:43:53 +08:00
|
|
|
}, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
function calculatePercentage(value: number, total: number) {
|
|
|
|
return total > 0 ? ((value / total) * 100).toFixed(1) : '0.0'
|
|
|
|
}
|
|
|
|
|
|
|
|
const updateChartData = (newData: any) => {
|
2025-08-28 10:33:55 +08:00
|
|
|
if (!newData) {
|
|
|
|
// 如果没有新数据,则设置为空数据集并更新图表
|
|
|
|
props.chartConfig.option.dataset = { dimensions: ['name', 'value'], source: [] };
|
|
|
|
} else {
|
|
|
|
props.chartConfig.option.dataset = newData;
|
|
|
|
}
|
2025-08-26 15:43:53 +08:00
|
|
|
|
2025-08-28 10:33:55 +08:00
|
|
|
|
|
|
|
const total = calculateTotal(props.chartConfig.option.dataset)
|
2025-08-26 15:43:53 +08:00
|
|
|
if (props.chartConfig.option.graphic?.[0]) {
|
|
|
|
props.chartConfig.option.graphic[0].style.text = total.toString()
|
|
|
|
}
|
|
|
|
|
2025-08-27 21:52:06 +08:00
|
|
|
// 统一显示为 "名称 百分比"
|
2025-08-26 15:43:53 +08:00
|
|
|
props.chartConfig.option.legend.formatter = (name: string) => {
|
2025-08-28 10:33:55 +08:00
|
|
|
const item = props.chartConfig.option.dataset.source.find((it: any) => it.name === name)
|
2025-08-27 21:52:06 +08:00
|
|
|
if (!item) return name
|
|
|
|
const val = item.value ?? 0
|
2025-08-26 15:43:53 +08:00
|
|
|
let p = calculatePercentage(val, total)
|
2025-08-28 10:33:55 +08:00
|
|
|
if (Number(p) < 10) {
|
|
|
|
p = ' ' + p
|
2025-08-26 15:43:53 +08:00
|
|
|
}
|
|
|
|
return `{name|${name}}{value|${p}}{unit|%}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 每行独立渐变背景
|
|
|
|
props.chartConfig.option.legend.textStyle = {
|
|
|
|
color: '#fff',
|
|
|
|
rich: {
|
|
|
|
name: {
|
|
|
|
width: 80,
|
|
|
|
padding: [8, 0, 8, 10],
|
|
|
|
color: '#999',
|
|
|
|
backgroundColor: {
|
|
|
|
type: 'linear',
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
x2: 0,
|
|
|
|
y2: 1,
|
|
|
|
colorStops: [
|
|
|
|
{ offset: 0, color: 'rgba(58, 160, 255, 0)' },
|
|
|
|
{ offset: 0.5, color: 'rgba(58, 160, 255, 0.05)' },
|
|
|
|
{ offset: 1, color: 'rgba(58, 160, 255, 0.15)' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
width: 30,
|
|
|
|
padding: [8, 0, 8, 0],
|
|
|
|
textAlign: 'right',
|
|
|
|
fontFamily: 'monospace',
|
|
|
|
backgroundColor: {
|
|
|
|
type: 'linear',
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
x2: 0,
|
|
|
|
y2: 1,
|
|
|
|
colorStops: [
|
|
|
|
{ offset: 0, color: 'rgba(58, 160, 255, 0)' },
|
|
|
|
{ offset: 0.5, color: 'rgba(58, 160, 255, 0.05)' },
|
|
|
|
{ offset: 1, color: 'rgba(58, 160, 255, 0.15)' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
unit: {
|
|
|
|
width: 10,
|
|
|
|
padding: [8, 10, 8, 0],
|
|
|
|
textAlign: 'left',
|
|
|
|
color: '#999',
|
|
|
|
backgroundColor: {
|
|
|
|
type: 'linear',
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
x2: 0,
|
|
|
|
y2: 1,
|
|
|
|
colorStops: [
|
|
|
|
{ offset: 0, color: 'rgba(58, 160, 255, 0)' },
|
|
|
|
{ offset: 0.5, color: 'rgba(58, 160, 255, 0.05)' },
|
|
|
|
{ offset: 1, color: 'rgba(58, 160, 255, 0.15)' }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-08-28 10:33:55 +08:00
|
|
|
const totalValue = props.chartConfig.option.dataset.source.reduce((total: number, item: any) => {
|
2025-08-26 15:43:53 +08:00
|
|
|
return total + (item.value || 0)
|
|
|
|
}, 0)
|
2025-08-28 10:33:55 +08:00
|
|
|
props.chartConfig.option.series[props.chartConfig.option.series.length - 1].label.formatter = `{a|${totalValue}}\n\n{b|总数}`
|
2025-08-26 15:43:53 +08:00
|
|
|
}
|
|
|
|
|
2025-08-28 10:33:55 +08:00
|
|
|
// watch 函数在数据源变化时更新图表,这里保持不变
|
2025-08-26 15:43:53 +08:00
|
|
|
watch(
|
|
|
|
() => props.chartConfig.option.dataset,
|
|
|
|
newData => {
|
|
|
|
if (newData) {
|
|
|
|
updateChartData(newData)
|
|
|
|
}
|
|
|
|
},
|
2025-08-27 21:52:06 +08:00
|
|
|
{ deep: true, immediate: true }
|
2025-08-26 15:43:53 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
|
|
|
|
updateChartData(newData)
|
|
|
|
})
|
|
|
|
|
2025-08-28 10:33:55 +08:00
|
|
|
/**
|
|
|
|
* 格式化日期时间为 ISO 8601 字符串 (YYYY-MM-DDTHH:mm:ss.SSS)
|
|
|
|
* JavaScript Date 对象只支持毫秒精度
|
|
|
|
*/
|
2025-08-27 21:52:06 +08:00
|
|
|
function formatDateTime(date: Date): string {
|
|
|
|
const year = date.getFullYear();
|
|
|
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
|
|
const day = date.getDate().toString().padStart(2, '0');
|
|
|
|
const hours = date.getHours().toString().padStart(2, '0');
|
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
|
|
const seconds = date.getSeconds().toString().padStart(2, '0');
|
2025-08-28 10:33:55 +08:00
|
|
|
const milliseconds = date.getMilliseconds().toString().padStart(3, '0'); // 添加毫秒
|
|
|
|
|
|
|
|
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}`; // 使用 'T' 分隔符和毫秒
|
2025-08-27 21:52:06 +08:00
|
|
|
}
|
2025-08-28 10:33:55 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 根据时间段生成开始和结束时间范围
|
|
|
|
* 结束时间设置为该时间段的最后一天的 23:59:59.999
|
|
|
|
*/
|
2025-08-27 21:52:06 +08:00
|
|
|
function generateTimeRange(period: string): { startTime: string; endTime: string } {
|
2025-08-28 10:33:55 +08:00
|
|
|
const now = new Date(); // 当前时间
|
|
|
|
|
|
|
|
let startTimeDate: Date;
|
|
|
|
let endTimeDate: Date;
|
|
|
|
|
2025-08-27 21:52:06 +08:00
|
|
|
switch (period) {
|
|
|
|
case 'day':
|
2025-08-28 10:33:55 +08:00
|
|
|
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0); // 当天 00:00:00.000
|
|
|
|
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999); // 当天 23:59:59.999
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
|
|
|
case 'week':
|
2025-09-02 19:55:30 +08:00
|
|
|
// 计算本周一到今天的起始和结束时间
|
|
|
|
const dayOfWeek = now.getDay(); // 0(周日) to 6(周六)
|
|
|
|
const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // 周日需要调整为上周一
|
|
|
|
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + diffToMonday, 0, 0, 0, 0);
|
2025-08-28 10:33:55 +08:00
|
|
|
// 结束时间为今天 23:59:59.999
|
|
|
|
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
|
|
|
case 'month':
|
2025-08-28 10:33:55 +08:00
|
|
|
startTimeDate = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0); // 当月第一天 00:00:00.000
|
|
|
|
// 获取当月最后一天 23:59:59.999
|
|
|
|
endTimeDate = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
|
|
|
case 'quarter':
|
|
|
|
const currentMonth = now.getMonth();
|
|
|
|
const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
|
2025-08-28 10:33:55 +08:00
|
|
|
startTimeDate = new Date(now.getFullYear(), quarterStartMonth, 1, 0, 0, 0, 0); // 当季度第一天 00:00:00.000
|
|
|
|
// 获取当季度最后一天 23:59:59.999
|
|
|
|
endTimeDate = new Date(now.getFullYear(), quarterStartMonth + 3, 0, 23, 59, 59, 999);
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
|
|
|
case 'year':
|
2025-08-28 10:33:55 +08:00
|
|
|
startTimeDate = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0); // 当年第一天 00:00:00.000
|
|
|
|
// 获取当年最后一天 23:59:59.999 (即下一年第一天的前一毫秒)
|
|
|
|
endTimeDate = new Date(now.getFullYear(), 12, 0, 23, 59, 59, 999);
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
2025-08-28 10:33:55 +08:00
|
|
|
default: // 默认按 'day' 处理
|
|
|
|
startTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
|
|
endTimeDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
2025-08-27 21:52:06 +08:00
|
|
|
break;
|
|
|
|
}
|
2025-08-28 10:33:55 +08:00
|
|
|
|
2025-08-27 21:52:06 +08:00
|
|
|
return {
|
2025-08-28 10:33:55 +08:00
|
|
|
startTime: formatDateTime(startTimeDate),
|
|
|
|
endTime: formatDateTime(endTimeDate),
|
2025-08-27 21:52:06 +08:00
|
|
|
};
|
2025-08-26 15:43:53 +08:00
|
|
|
}
|
2025-08-28 10:33:55 +08:00
|
|
|
|
|
|
|
// 异步获取报警数据
|
2025-08-27 21:52:06 +08:00
|
|
|
async function fetchAlarmData({ startTime, endTime }: { startTime: string; endTime: string }) {
|
|
|
|
try {
|
2025-09-02 16:28:29 +08:00
|
|
|
const res: any = await axiosInstance.get(
|
2025-09-04 09:45:21 +08:00
|
|
|
`/awjt/space/getNumberByAlarmLevel/${props.chartConfig.option.sceneCode}`,
|
2025-08-28 10:33:55 +08:00
|
|
|
{
|
|
|
|
params: { startTime, endTime }, // 请求参数
|
|
|
|
responseType: 'json',
|
2025-09-02 16:28:29 +08:00
|
|
|
baseURL: ''
|
2025-08-28 10:33:55 +08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2025-09-02 16:28:29 +08:00
|
|
|
if (res.state === true && res.value) {
|
2025-09-02 14:41:58 +08:00
|
|
|
console.log("API 响应数据:", res);
|
2025-08-27 21:52:06 +08:00
|
|
|
// 转换为图表需要的数据结构
|
|
|
|
return {
|
|
|
|
dimensions: ['name', 'value'],
|
2025-09-02 16:28:29 +08:00
|
|
|
source: res.value.map((item: any) => ({
|
2025-08-27 21:52:06 +08:00
|
|
|
name: item.alarmLevel,
|
2025-09-02 16:28:29 +08:00
|
|
|
value: item.alarm_count
|
2025-08-27 21:52:06 +08:00
|
|
|
}))
|
|
|
|
};
|
|
|
|
} else {
|
2025-08-28 10:33:55 +08:00
|
|
|
console.warn('API 返回非预期数据结构或空数据:', res);
|
2025-08-27 21:52:06 +08:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2025-08-28 10:33:55 +08:00
|
|
|
console.error('获取报警数据失败:', error);
|
2025-08-27 21:52:06 +08:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
2025-08-28 10:33:55 +08:00
|
|
|
|
|
|
|
// 组件挂载时初始化数据
|
2025-08-27 21:52:06 +08:00
|
|
|
onMounted(async () => {
|
|
|
|
const timeRange = generateTimeRange(props.chartConfig.option.dateTime.selectValue);
|
2025-08-28 10:33:55 +08:00
|
|
|
console.log("onMounted - 请求参数:", timeRange); // 打印初始请求参数
|
2025-08-27 21:52:06 +08:00
|
|
|
const fetchedData = await fetchAlarmData(timeRange);
|
2025-08-28 10:33:55 +08:00
|
|
|
updateChartData(fetchedData); // 直接调用 updateChartData
|
2025-08-27 21:52:06 +08:00
|
|
|
});
|
2025-08-28 10:33:55 +08:00
|
|
|
|
|
|
|
// 处理下拉选择器变化
|
|
|
|
const handleSelectChange = async (value: string) => {
|
|
|
|
props.chartConfig.option.dateTime.selectValue = value; // 更新选中值
|
|
|
|
const timeRange = generateTimeRange(value);
|
|
|
|
console.log("handleSelectChange - 请求参数:", timeRange); // 打印新的请求参数
|
|
|
|
const fetchedData = await fetchAlarmData(timeRange);
|
|
|
|
updateChartData(fetchedData); // 直接调用 updateChartData
|
|
|
|
};
|
2025-08-26 15:43:53 +08:00
|
|
|
</script>
|
|
|
|
|
2025-09-08 15:04:00 +08:00
|
|
|
<style lang="scss" scoped></style>
|