feat(Charts): 更新报警处置情况图表数据展示逻辑
重构报警处置情况图表组件,使用新的mock数据格式展示报警总数、未处置报警数和平均处置时长 添加时间范围选择功能,支持按日、周、月等不同维度展示数据 优化数据转换逻辑,从原始报警数据生成图表所需格式
This commit is contained in:
parent
f32a08ea99
commit
5a62df9233
@ -5,7 +5,7 @@ export const LineGraph01: ConfigType = {
|
||||
key: 'LineGraph01Haz',
|
||||
chartKey: 'VLineGraph01Haz',
|
||||
conKey: 'VCLineGraph01Haz',
|
||||
title: '曲线图(带边框,标题)',
|
||||
title: '报警处置情况',
|
||||
category: 'HazardousChemicalsSpace',
|
||||
categoryName: '危化品场景',
|
||||
package: 'Charts',
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
<img src="./assets/title.svg" class="svg" />
|
||||
<div class="buttonContent">
|
||||
<span class="title">{{ configData.title }}</span>
|
||||
<span class="title">报警处置情况</span>
|
||||
</div>
|
||||
|
||||
<!-- 使用新的下拉选择器组件 -->
|
||||
@ -15,25 +15,22 @@
|
||||
|
||||
<div class="textContent">
|
||||
<div class="textInContent">
|
||||
<span class="smallText">{{ configData.names[0] }} </span>
|
||||
<span class="bigText">{{ configData.dataSource[selectedIndex].values[0]?.toLocaleString('en-US')
|
||||
}}</span>
|
||||
<span class="smallText">报警总数 </span>
|
||||
<span class="bigText">{{ currentData.alertTotal?.toLocaleString('en-US') || '0' }}</span>
|
||||
</div>
|
||||
<div class="textInContent">
|
||||
<span class="smallText">{{ configData.names[1] }}</span>
|
||||
<span class="bigText">{{ configData.dataSource[selectedIndex].values[1]?.toLocaleString('en-US')
|
||||
}}</span>
|
||||
<span class="smallText">未处置报警数</span>
|
||||
<span class="bigText">{{ currentData.unprocessedAlert?.toLocaleString('en-US') || '0' }}</span>
|
||||
</div>
|
||||
<div class="textInContent">
|
||||
<span class="smallText">{{ configData.names[2] }}</span>
|
||||
<span class="bigText">{{ configData.dataSource[selectedIndex].values[2]?.toLocaleString('en-US')
|
||||
}}</span>
|
||||
<span class="smallText">平均处置时长</span>
|
||||
<span class="bigText">{{ currentData.averageResolutionTime?.toLocaleString('en-US') || '0' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chartsContent">
|
||||
<span class="smallText Tips">
|
||||
<img class="icon" src="./icon01.png" alt="">
|
||||
{{ configData.tip }}</span>
|
||||
数据统计图表</span>
|
||||
<v-chart ref="vChartRef" :init-options="initOptions" :theme="themeColor" :option="chartOption" :update-options="{
|
||||
replaceMerge: replaceMergeArr
|
||||
}" autoresize>
|
||||
@ -42,20 +39,20 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { PropType, computed, watch, ref, nextTick, onMounted, onUnmounted } from 'vue'
|
||||
import { PropType, computed, watch, ref, nextTick } from 'vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { useCanvasInitOptions } from '@/hooks/useCanvasInitOptions.hook'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import Config, { DatasType, includes, seriesItem } from './config'
|
||||
import Config, { includes, seriesItem } from './config'
|
||||
import { mergeTheme } from '@/packages/public/chart'
|
||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
import { useChartDataFetch } from '@/hooks'
|
||||
import { DatasetComponent, GridComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||
import isObject from 'lodash/isObject'
|
||||
import CustomSelect from './select.vue'
|
||||
|
||||
import mockData1 from './mock1.json'
|
||||
|
||||
const props = defineProps({
|
||||
themeSetting: {
|
||||
@ -82,105 +79,118 @@ use([DatasetComponent, CanvasRenderer, LineChart, GridComponent, TooltipComponen
|
||||
|
||||
const replaceMergeArr = ref<string[]>()
|
||||
|
||||
// 生成选项数据
|
||||
const selectOptions = computed(() => {
|
||||
return configData.value.dataSource.map((item, index) => ({
|
||||
label: item.dataname,
|
||||
value: index
|
||||
}))
|
||||
// 当前选中的时间范围
|
||||
const selectedTimeRange = ref('day')
|
||||
|
||||
// 数据转换函数:将mock1.json格式转换为mock.json格式
|
||||
const convertMock1ToMockFormat = (mock1Data: any) => {
|
||||
const convertedData: any = {}
|
||||
|
||||
Object.keys(mock1Data).forEach(timeRange => {
|
||||
const dataArray = mock1Data[timeRange]
|
||||
|
||||
if (!Array.isArray(dataArray) || dataArray.length === 0) {
|
||||
convertedData[timeRange] = {
|
||||
alertTotal: 0,
|
||||
unprocessedAlert: 0,
|
||||
averageResolutionTime: 0,
|
||||
datavalues: [['时间', '数值']]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 计算聚合数据
|
||||
const alertTotal = dataArray.reduce((sum, item) => sum + (item.alarm_count || 0), 0)
|
||||
const unprocessedAlert = dataArray.reduce((sum, item) => sum + (item.un_alarm_count || 0), 0)
|
||||
const avgHandleTimeSum = dataArray.reduce((sum, item) => sum + (item.avg_handle_time_seconds || 0), 0)
|
||||
const averageResolutionTime = Math.round(avgHandleTimeSum / dataArray.length)
|
||||
|
||||
// 根据不同时间范围生成datavalues
|
||||
let datavalues: (string | number)[][] = [['时间', '数值']]
|
||||
|
||||
switch (timeRange) {
|
||||
case 'day':
|
||||
datavalues = [['时间', '数值'], ...dataArray.map(item => [
|
||||
`${item.alarm_hour}:00`,
|
||||
item.avg_handle_time_seconds || 0
|
||||
])]
|
||||
break
|
||||
case 'week':
|
||||
datavalues = [['时间', '数值'], ...dataArray.map(item => [
|
||||
item.week_day_name || '',
|
||||
item.avg_handle_time_seconds || 0
|
||||
])]
|
||||
break
|
||||
case 'month':
|
||||
datavalues = [['时间', '数值'], ...dataArray.map(item => [
|
||||
item.alarm_date || '',
|
||||
item.avg_handle_time_seconds || 0
|
||||
])]
|
||||
break
|
||||
case 'quarter':
|
||||
datavalues = [['时间', '数值'], ...dataArray.map(item => [
|
||||
`${item.month}月`,
|
||||
item.avg_handle_time_seconds || 0
|
||||
])]
|
||||
break
|
||||
case 'year':
|
||||
datavalues = [['时间', '数值'], ...dataArray.map(item => [
|
||||
`${item.month}月`,
|
||||
item.avg_handle_time_seconds || 0
|
||||
])]
|
||||
break
|
||||
default:
|
||||
datavalues = [['时间', '数值']]
|
||||
}
|
||||
|
||||
convertedData[timeRange] = {
|
||||
alertTotal,
|
||||
unprocessedAlert,
|
||||
averageResolutionTime,
|
||||
datavalues
|
||||
}
|
||||
})
|
||||
|
||||
const selectedIndex = ref(0); // 当前选中的数据源索引
|
||||
|
||||
|
||||
// 辅助函数:解析 mockData
|
||||
const parseMockData = (mockData: any): DatasType => {
|
||||
const DEFAULT_MOCK_DATA: DatasType = {
|
||||
title: '默认标题',
|
||||
names: ['项1', '项2', '项3'],
|
||||
tip: "默认提示",
|
||||
dataIndex: 0,
|
||||
dataSource: [{
|
||||
dataname: '默认数据',
|
||||
values: [0, 0, 0],
|
||||
datavalues: [
|
||||
['维度', '数值'],
|
||||
['0', 0],
|
||||
['1', 0],
|
||||
['2', 0]
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
let parsedData: DatasType;
|
||||
if (typeof mockData === 'string') {
|
||||
try {
|
||||
const tempParsed = JSON.parse(mockData);
|
||||
parsedData = (typeof tempParsed === 'object' && tempParsed !== null) ? tempParsed : DEFAULT_MOCK_DATA;
|
||||
} catch (e) {
|
||||
console.error('解析 mockData 字符串失败:', e);
|
||||
parsedData = DEFAULT_MOCK_DATA;
|
||||
}
|
||||
} else if (typeof mockData === 'object' && mockData !== null) {
|
||||
parsedData = mockData;
|
||||
} else {
|
||||
parsedData = DEFAULT_MOCK_DATA;
|
||||
return convertedData
|
||||
}
|
||||
|
||||
if (parsedData.dataIndex === undefined || parsedData.dataIndex < 0 || parsedData.dataIndex >= parsedData.dataSource.length) {
|
||||
parsedData.dataIndex = 0;
|
||||
// 转换mock1数据为mock格式
|
||||
const convertedMockData = convertMock1ToMockFormat(mockData1)
|
||||
|
||||
// 数据获取函数
|
||||
const getMockDataByTimeRange = (timeRange: string) => {
|
||||
const defaultData = {
|
||||
alertTotal: 0,
|
||||
unprocessedAlert: 0,
|
||||
averageResolutionTime: 0,
|
||||
datavalues: [['时间', '数值']]
|
||||
}
|
||||
|
||||
if (!parsedData.dataSource || parsedData.dataSource.length === 0) {
|
||||
parsedData.dataSource = DEFAULT_MOCK_DATA.dataSource;
|
||||
if (convertedMockData && convertedMockData[timeRange]) {
|
||||
return convertedMockData[timeRange]
|
||||
}
|
||||
|
||||
if (!parsedData.names || parsedData.names.length === 0) parsedData.names = DEFAULT_MOCK_DATA.names;
|
||||
|
||||
|
||||
return parsedData;
|
||||
};
|
||||
|
||||
|
||||
const configData = computed<DatasType>(() => {
|
||||
return parseMockData(props.chartConfig.mockData);
|
||||
});
|
||||
|
||||
// 监听 mockData 变化,并初始化 selectedIndex
|
||||
watch(
|
||||
() => props.chartConfig.mockData,
|
||||
(newMockData) => {
|
||||
const parsedData = parseMockData(newMockData);
|
||||
selectedIndex.value = parsedData.dataIndex;
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
return defaultData
|
||||
}
|
||||
|
||||
// 当前数据
|
||||
const currentData = computed(() => {
|
||||
return getMockDataByTimeRange(selectedTimeRange.value)
|
||||
})
|
||||
|
||||
// 图表配置
|
||||
const chartOption = computed(() => {
|
||||
const mergedOption = mergeTheme(props.chartConfig.option, props.themeSetting, includes);
|
||||
|
||||
|
||||
const currentDataSourceItem = configData.value.dataSource[selectedIndex.value];
|
||||
const currentDatavalues = currentDataSourceItem ? currentDataSourceItem.datavalues : [];
|
||||
|
||||
const mergedOption = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
|
||||
|
||||
if (!mergedOption.dataset) {
|
||||
mergedOption.dataset = {};
|
||||
mergedOption.dataset = {}
|
||||
}
|
||||
mergedOption.dataset.source = currentDatavalues;
|
||||
mergedOption.dataset.source = currentData.value.datavalues
|
||||
|
||||
return mergedOption;
|
||||
});
|
||||
return mergedOption
|
||||
})
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
width: props.chartConfig.attr.w + 'px',
|
||||
height: "16%"
|
||||
}))
|
||||
|
||||
|
||||
// dataset 无法变更条数的补丁 (保持不变)
|
||||
// dataset 无法变更条数的补丁
|
||||
watch(
|
||||
() => props.chartConfig.option.dataset,
|
||||
(newData: { dimensions: any }, oldData) => {
|
||||
@ -207,15 +217,17 @@ watch(
|
||||
)
|
||||
|
||||
const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
|
||||
|
||||
});
|
||||
|
||||
// 数据获取回调
|
||||
})
|
||||
|
||||
// 处理下拉选择器变化
|
||||
const handleSelectChange = (value: any) => {
|
||||
props.chartConfig.option.dateTime.selectValue = value
|
||||
// selectedIndex.value = value
|
||||
selectedTimeRange.value = value
|
||||
}
|
||||
|
||||
// 初始化时设置默认值
|
||||
selectedTimeRange.value = props.chartConfig.option.dateTime.selectValue || 'day'
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@include go(border-box) {
|
||||
|
@ -0,0 +1,197 @@
|
||||
{
|
||||
"day": {
|
||||
"alertTotal": 2623,
|
||||
"unprocessedAlert ": 2624,
|
||||
"averageResolutionTime": 2625,
|
||||
"datavalues": [
|
||||
[
|
||||
"时间",
|
||||
"数值"
|
||||
],
|
||||
[
|
||||
"13:00",
|
||||
99
|
||||
],
|
||||
[
|
||||
"14:00",
|
||||
19
|
||||
],
|
||||
[
|
||||
"15:00",
|
||||
75
|
||||
],
|
||||
[
|
||||
"16:00",
|
||||
26
|
||||
],
|
||||
[
|
||||
"17:00",
|
||||
35
|
||||
],
|
||||
[
|
||||
"18:00",
|
||||
20
|
||||
],
|
||||
[
|
||||
"19:00",
|
||||
10
|
||||
]
|
||||
]
|
||||
},
|
||||
"week": {
|
||||
"alertTotal": 12623,
|
||||
"unprocessedAlert ": 12624,
|
||||
"averageResolutionTime": 12625,
|
||||
"datavalues": [
|
||||
[
|
||||
"时间",
|
||||
"数值"
|
||||
],
|
||||
[
|
||||
"13:00",
|
||||
93
|
||||
],
|
||||
[
|
||||
"14:00",
|
||||
20
|
||||
],
|
||||
[
|
||||
"15:00",
|
||||
88
|
||||
],
|
||||
[
|
||||
"16:00",
|
||||
23
|
||||
],
|
||||
[
|
||||
"17:00",
|
||||
90
|
||||
],
|
||||
[
|
||||
"18:00",
|
||||
1
|
||||
],
|
||||
[
|
||||
"19:00",
|
||||
29
|
||||
]
|
||||
]
|
||||
},
|
||||
"month": {
|
||||
"alertTotal": 112623,
|
||||
"unprocessedAlert ": 112624,
|
||||
"averageResolutionTime": 625,
|
||||
"datavalues": [
|
||||
[
|
||||
"时间",
|
||||
"数值"
|
||||
],
|
||||
[
|
||||
"13:00",
|
||||
73
|
||||
],
|
||||
[
|
||||
"14:00",
|
||||
12
|
||||
],
|
||||
[
|
||||
"15:00",
|
||||
32
|
||||
],
|
||||
[
|
||||
"16:00",
|
||||
44
|
||||
],
|
||||
[
|
||||
"17:00",
|
||||
33
|
||||
],
|
||||
[
|
||||
"18:00",
|
||||
89
|
||||
],
|
||||
[
|
||||
"19:00",
|
||||
83
|
||||
]
|
||||
]
|
||||
},
|
||||
"quarter": {
|
||||
"alertTotal": 312623,
|
||||
"unprocessedAlert ": 312624,
|
||||
"averageResolutionTime": 312625,
|
||||
"datavalues": [
|
||||
[
|
||||
"时间",
|
||||
"数值"
|
||||
],
|
||||
[
|
||||
"13:00",
|
||||
93
|
||||
],
|
||||
[
|
||||
"14:00",
|
||||
33
|
||||
],
|
||||
[
|
||||
"15:00",
|
||||
22
|
||||
],
|
||||
[
|
||||
"16:00",
|
||||
88
|
||||
],
|
||||
[
|
||||
"17:00",
|
||||
78
|
||||
],
|
||||
[
|
||||
"18:00",
|
||||
42
|
||||
],
|
||||
[
|
||||
"19:00",
|
||||
77
|
||||
]
|
||||
]
|
||||
},
|
||||
"year": {
|
||||
"alertTotal": 1112623,
|
||||
"unprocessedAlert ": 1112624,
|
||||
"averageResolutionTime": 3625,
|
||||
"datavalues": [
|
||||
[
|
||||
"时间",
|
||||
"数值"
|
||||
],
|
||||
[
|
||||
"13:00",
|
||||
33
|
||||
],
|
||||
[
|
||||
"14:00",
|
||||
22
|
||||
],
|
||||
[
|
||||
"15:00",
|
||||
54
|
||||
],
|
||||
[
|
||||
"16:00",
|
||||
99
|
||||
],
|
||||
[
|
||||
"17:00",
|
||||
44
|
||||
],
|
||||
[
|
||||
"18:00",
|
||||
32
|
||||
],
|
||||
[
|
||||
"19:00",
|
||||
54
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
{
|
||||
"day": [
|
||||
{
|
||||
"alarm_count": 79,
|
||||
"un_alarm_count": 45,
|
||||
"alarm_hour": 12,
|
||||
"avg_handle_time_seconds": 9307.5949
|
||||
},
|
||||
{
|
||||
"alarm_count": 71,
|
||||
"un_alarm_count": 45,
|
||||
"alarm_hour": 13,
|
||||
"avg_handle_time_seconds": 5568.1831
|
||||
}
|
||||
],
|
||||
"week": [
|
||||
{
|
||||
"alarm_count": 10,
|
||||
"un_alarm_count": 45,
|
||||
"week_day_name": "星期一",
|
||||
"avg_handle_time_seconds": 6360,
|
||||
"day_of_week": 2
|
||||
},
|
||||
{
|
||||
"alarm_count": 15,
|
||||
"un_alarm_count": 45,
|
||||
"week_day_name": "星期二",
|
||||
"avg_handle_time_seconds": 11996,
|
||||
"day_of_week": 3
|
||||
},
|
||||
{
|
||||
"alarm_count": 150,
|
||||
"un_alarm_count": 45,
|
||||
"week_day_name": "星期三",
|
||||
"avg_handle_time_seconds": 7537.6067,
|
||||
"day_of_week": 4
|
||||
}
|
||||
],
|
||||
"month": [
|
||||
{
|
||||
"alarm_count": 10,
|
||||
"un_alarm_count": 45,
|
||||
"alarm_date": "2025-08-25",
|
||||
"avg_handle_time_seconds": 6360,
|
||||
"day_of_month": 25
|
||||
},
|
||||
{
|
||||
"alarm_count": 15,
|
||||
"un_alarm_count": 45,
|
||||
"alarm_date": "2025-08-26",
|
||||
"avg_handle_time_seconds": 11996,
|
||||
"day_of_month": 26
|
||||
},
|
||||
{
|
||||
"alarm_count": 150,
|
||||
"un_alarm_count": 45,
|
||||
"alarm_date": "2025-08-27",
|
||||
"avg_handle_time_seconds": 7537.6067,
|
||||
"day_of_month": 27
|
||||
}
|
||||
],
|
||||
"quarter": [
|
||||
{
|
||||
"alarm_count": 175,
|
||||
"un_alarm_count": 45,
|
||||
"month": 7,
|
||||
"avg_handle_time_seconds": 7852.4629,
|
||||
"quarter": 3
|
||||
},
|
||||
{
|
||||
"alarm_count": 175,
|
||||
"un_alarm_count": 45,
|
||||
"month": 8,
|
||||
"avg_handle_time_seconds": 7852.4629,
|
||||
"quarter": 3
|
||||
}
|
||||
],
|
||||
"year": [
|
||||
{
|
||||
"alarm_count": 175,
|
||||
"un_alarm_count": 45,
|
||||
"month": 7,
|
||||
"year": 2025,
|
||||
"avg_handle_time_seconds": 7852.4629
|
||||
},
|
||||
{
|
||||
"alarm_count": 175,
|
||||
"un_alarm_count": 45,
|
||||
"month": 8,
|
||||
"year": 2025,
|
||||
"avg_handle_time_seconds": 7852.4629
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user