Commit e116f65d5a10c40e42b7a74af47fc31d90771525

Authored by fengtao
1 parent 67f701e6

perf: 优化接口调用图表绘制

1 -import { ApplicationRecordPageParams, CallStatisticsItemType } from './model/record'; 1 +import {
  2 + ApplicationRecordPageParams,
  3 + CallStatisticsItemType,
  4 + ClassifyItemType,
  5 +} from './model/record';
2 import { PaginationResult } from '/#/axios'; 6 import { PaginationResult } from '/#/axios';
3 import { defHttp } from '/@/utils/http/axios'; 7 import { defHttp } from '/@/utils/http/axios';
4 8
@@ -40,7 +44,7 @@ export const getApplicationRecordTop = () => { @@ -40,7 +44,7 @@ export const getApplicationRecordTop = () => {
40 44
41 export const getApplicationRecordClassify = (type?: string) => { 45 export const getApplicationRecordClassify = (type?: string) => {
42 const joinUrlParams = type ? `?type=${type}` : ''; 46 const joinUrlParams = type ? `?type=${type}` : '';
43 - return defHttp.get({ 47 + return defHttp.get<ClassifyItemType[]>({
44 url: `${ApplicationRecordManageApi.OPEN_API_RECORD}/getClassify${joinUrlParams}`, 48 url: `${ApplicationRecordManageApi.OPEN_API_RECORD}/getClassify${joinUrlParams}`,
45 }); 49 });
46 }; 50 };
@@ -4,23 +4,20 @@ @@ -4,23 +4,20 @@
4 import { useAppStore } from '/@/store/modules/app'; 4 import { useAppStore } from '/@/store/modules/app';
5 import { useI18n } from '/@/hooks/web/useI18n'; 5 import { useI18n } from '/@/hooks/web/useI18n';
6 import { Empty } from 'ant-design-vue'; 6 import { Empty } from 'ant-design-vue';
  7 + import { EChartsOption } from 'echarts';
7 8
8 const { t } = useI18n(); 9 const { t } = useI18n();
9 10
10 - const props = defineProps({  
11 - seriesData: {  
12 - type: Array,  
13 - default: () => [],  
14 - },  
15 - timeLineXAxisData: {  
16 - type: Array,  
17 - default: () => [],  
18 - },  
19 - timeType: {  
20 - type: String,  
21 - default: 'week',  
22 - },  
23 - }); 11 + const props = withDefaults(
  12 + defineProps<{
  13 + timeType: string;
  14 + chartsData?: Partial<EChartsOption>;
  15 + }>(),
  16 + {
  17 + timeType: 'week',
  18 + chartsData: () => ({}),
  19 + }
  20 + );
24 21
25 const emits = defineEmits(['emitTimeRange']); 22 const emits = defineEmits(['emitTimeRange']);
26 23
@@ -51,7 +48,8 @@ @@ -51,7 +48,8 @@
51 48
52 const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>); 49 const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>);
53 50
54 - const getOptions: any = () => { 51 + const getOptions = (): EChartsOption => {
  52 + const { xAxis, series } = props.chartsData || {};
55 return { 53 return {
56 backgroundColor: skinName.value, 54 backgroundColor: skinName.value,
57 tooltip: { 55 tooltip: {
@@ -63,31 +61,12 @@ @@ -63,31 +61,12 @@
63 bottom: '1%', 61 bottom: '1%',
64 containLabel: true, 62 containLabel: true,
65 }, 63 },
66 - xAxis: {  
67 - type: 'time',  
68 - splitLine: { show: false },  
69 - lineStyle: {  
70 - width: 2,  
71 - },  
72 - axisTick: {  
73 - show: false,  
74 - },  
75 - axisLabel: {  
76 - formatter:  
77 - props.timeType === 'hour'  
78 - ? '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'  
79 - : props.timeType === 'day'  
80 - ? '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'  
81 - : '{yyyy}-{MM}-{dd}',  
82 - showMinLabel: true,  
83 - showMaxLabel: true, // 固定显示X轴的最后一条数据  
84 - },  
85 - }, 64 + xAxis,
86 yAxis: { 65 yAxis: {
87 type: 'value', 66 type: 'value',
88 axisLabel: { formatter: '{value}' }, 67 axisLabel: { formatter: '{value}' },
89 }, 68 },
90 - series: props.seriesData, 69 + series,
91 }; 70 };
92 }; 71 };
93 72
@@ -104,13 +83,12 @@ @@ -104,13 +83,12 @@
104 ); 83 );
105 84
106 watch( 85 watch(
107 - () => props.seriesData, 86 + () => props.chartsData,
108 () => { 87 () => {
109 setOptions(getOptions()); 88 setOptions(getOptions());
110 }, 89 },
111 { 90 {
112 deep: true, 91 deep: true,
113 - immediate: true,  
114 } 92 }
115 ); 93 );
116 94
@@ -141,8 +119,8 @@ @@ -141,8 +119,8 @@
141 </template> 119 </template>
142 </a-radio-group> 120 </a-radio-group>
143 </template> 121 </template>
144 - <div v-show="seriesData.length" ref="chartRef" class="w-full h-80"></div>  
145 - <div v-show="!seriesData.length" class="w-full h-72 flex justify-center items-center" 122 + <div v-show="chartsData?.series?.length" ref="chartRef" class="w-full h-80"></div>
  123 + <div v-show="!chartsData?.series?.length" class="w-full h-72 flex justify-center items-center"
146 ><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" 124 ><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE"
147 /></div> 125 /></div>
148 </a-card> 126 </a-card>
  1 +import { EChartsOption } from 'echarts';
  2 +import { formatClassifyText } from '../../api/config';
  3 +import { ClassifyDtsItemType, ClassifyItemType } from '/@/api/application/model/record';
1 import { DescItem } from '/@/components/Description'; 4 import { DescItem } from '/@/components/Description';
2 import { BasicColumn } from '/@/components/Table'; 5 import { BasicColumn } from '/@/components/Table';
3 import { useI18n } from '/@/hooks/web/useI18n'; 6 import { useI18n } from '/@/hooks/web/useI18n';
  7 +import { dateUtil } from '/@/utils/dateUtil';
  8 +
  9 +export type DateInterval = 'hour' | 'day' | 'week' | 'month';
4 10
5 const { t } = useI18n(); 11 const { t } = useI18n();
6 12
@@ -45,3 +51,85 @@ export const formSchema: DescItem[] = [ @@ -45,3 +51,85 @@ export const formSchema: DescItem[] = [
45 label: t('application.record.text.numberOfFailedAttempts'), 51 label: t('application.record.text.numberOfFailedAttempts'),
46 }, 52 },
47 ]; 53 ];
  54 +
  55 +function generateFullXAxis(type: DateInterval) {
  56 + if (type === 'hour') {
  57 + return Array.from({ length: 60 / 5 }, (_, index) =>
  58 + dateUtil()
  59 + .subtract(index * 5, 'minute')
  60 + .startOf('minute')
  61 + .valueOf()
  62 + );
  63 + } else if (type === 'day') {
  64 + return Array.from({ length: 24 / 2 }, (_, index) =>
  65 + dateUtil()
  66 + .subtract(index * 2, 'hour')
  67 + .startOf('hour')
  68 + .valueOf()
  69 + );
  70 + } else if (type === 'week') {
  71 + return Array.from({ length: 7 }, (_, index) =>
  72 + dateUtil().subtract(index, 'day').startOf('day').valueOf()
  73 + );
  74 + } else {
  75 + return Array.from({ length: 30 }, (_, index) =>
  76 + dateUtil().subtract(index, 'day').startOf('day').valueOf()
  77 + );
  78 + }
  79 +}
  80 +
  81 +export function formatDateFromType(date: number, type: DateInterval) {
  82 + if (type === 'hour') {
  83 + return dateUtil(date).format('YYYY-MM-DD HH:mm');
  84 + } else if (type === 'day') {
  85 + return dateUtil(date).format('YYYY-MM-DD HH:mm');
  86 + } else {
  87 + return dateUtil(date).format('YYYY-MM-DD');
  88 + }
  89 +}
  90 +
  91 +export function transformClassifyItem(data: ClassifyItemType[], type: DateInterval) {
  92 + const res: ClassifyItemType[] = [];
  93 + const fullXAxis = generateFullXAxis(type);
  94 +
  95 + for (const { dts, classify } of data) {
  96 + const valueMap = dts.reduce((prev, next) => {
  97 + const key = dateUtil(next.time).valueOf();
  98 +
  99 + return { ...prev, [key]: next };
  100 + }, {} as Record<number, ClassifyDtsItemType>);
  101 +
  102 + const transformDts: ClassifyDtsItemType[] = fullXAxis.map((timeSpan) => {
  103 + const exist = valueMap[timeSpan];
  104 +
  105 + return exist || { time: formatDateFromType(timeSpan, type), count: 0, classify };
  106 + });
  107 +
  108 + res.push({ classify, dts: transformDts });
  109 + }
  110 +
  111 + return res;
  112 +}
  113 +
  114 +export function transformChartsOptionsByClassifyRecord(
  115 + data: ClassifyItemType[],
  116 + type: DateInterval
  117 +): Partial<EChartsOption> {
  118 + const series: EChartsOption['series'] = data.map(({ classify, dts }) => {
  119 + return {
  120 + type: 'line',
  121 + name: formatClassifyText[classify],
  122 + data: dts.map(({ count }) => count),
  123 + };
  124 + });
  125 +
  126 + const xAxisData = generateFullXAxis(type).map((timeSpan) => formatDateFromType(timeSpan, type));
  127 +
  128 + return {
  129 + series: series.reverse(),
  130 + xAxis: {
  131 + type: 'category',
  132 + data: xAxisData.reverse(),
  133 + },
  134 + };
  135 +}
@@ -8,35 +8,32 @@ @@ -8,35 +8,32 @@
8 import { useI18n } from '/@/hooks/web/useI18n'; 8 import { useI18n } from '/@/hooks/web/useI18n';
9 import { onMounted, reactive } from 'vue'; 9 import { onMounted, reactive } from 'vue';
10 import { getApplicationRecordClassify, getApplicationRecordTop } from '/@/api/application/record'; 10 import { getApplicationRecordClassify, getApplicationRecordTop } from '/@/api/application/record';
11 - import {  
12 - ApplicationRecordTopItemType,  
13 - ClassifyDtsItemType,  
14 - ClassifyItemType,  
15 - TopItemType,  
16 - } from '/@/api/application/model/record'; 11 + import { ApplicationRecordTopItemType, TopItemType } from '/@/api/application/model/record';
17 import { Empty } from 'ant-design-vue'; 12 import { Empty } from 'ant-design-vue';
18 - import moment from 'moment';  
19 - import { formatClassifyText } from '../api/config';  
20 import total1 from '/@/assets/images/total1.png'; 13 import total1 from '/@/assets/images/total1.png';
21 import total2 from '/@/assets/images/total2.png'; 14 import total2 from '/@/assets/images/total2.png';
22 import total3 from '/@/assets/images/total3.png'; 15 import total3 from '/@/assets/images/total3.png';
  16 + import {
  17 + DateInterval,
  18 + transformClassifyItem,
  19 + transformChartsOptionsByClassifyRecord,
  20 + } from './config';
  21 + import { EChartsOption } from 'echarts';
23 22
24 const { t } = useI18n(); 23 const { t } = useI18n();
25 24
26 interface ChartData { 25 interface ChartData {
27 lineChartData: Recordable[]; 26 lineChartData: Recordable[];
28 pieChartData: Recordable[]; 27 pieChartData: Recordable[];
29 - timeLineChartData: Recordable[];  
30 totalData: Recordable[]; 28 totalData: Recordable[];
31 - timeLineXAxisData?: string[]; 29 + timeLineChartOptions?: Partial<EChartsOption>;
32 } 30 }
33 31
34 const chartData = reactive<ChartData>({ 32 const chartData = reactive<ChartData>({
35 lineChartData: [], 33 lineChartData: [],
36 pieChartData: [], 34 pieChartData: [],
37 - timeLineChartData: [],  
38 totalData: [], 35 totalData: [],
39 - timeLineXAxisData: [], 36 + timeLineChartOptions: {},
40 }); 37 });
41 38
42 // API统计 39 // API统计
@@ -88,96 +85,20 @@ @@ -88,96 +85,20 @@
88 } 85 }
89 }; 86 };
90 87
91 - //1小时->5分钟间隔 1天->2小时间隔 7天->1天间隔 30天->1天间隔  
92 - const timeFormat = (type, config) => {  
93 - let startT: any = null;  
94 - let endT: any = null;  
95 - let interval: any = null; //分割间隔  
96 - if (type == 'hour') {  
97 - interval = 5;  
98 - startT = moment().subtract(1, config.text);  
99 - endT = moment().format('HH:mm');  
100 - } else if (type == 'day') {  
101 - interval = 2;  
102 - startT = moment().subtract(1, config.text);  
103 - endT = moment().format('HH:mm');  
104 - } else if (type == 'week') {  
105 - interval = 1;  
106 - startT = moment().subtract(1, config.text);  
107 - endT = moment().format('HH:mm');  
108 - } else if (type == 'month') {  
109 - interval = 1;  
110 - startT = moment().subtract(1, config.text);  
111 - endT = moment().format('HH:mm');  
112 - }  
113 - let starTime = moment(startT, 'HH:mm');  
114 - let endTime = moment(endT, 'HH:mm');  
115 - let diff = endTime.diff(starTime, config.diffText);  
116 - let num = Math.ceil(diff / interval);  
117 - let times: any = [];  
118 - for (let i = 1; i <= num; i++) {  
119 - let timeFrom = starTime.clone().add((i - 1) * interval, config.timeText) as any;  
120 - times.push(timeFrom.format('YYYY-MM-DD HH:mm'));  
121 - }  
122 - return times;  
123 - };  
124 -  
125 - const configTime = [  
126 - {  
127 - name: 'hour',  
128 - value: {  
129 - text: 'hours',  
130 - diffText: 'minutes',  
131 - timeText: 'minutes',  
132 - },  
133 - },  
134 - {  
135 - name: 'day',  
136 - value: {  
137 - text: 'days',  
138 - diffText: 'hours',  
139 - timeText: 'hours',  
140 - },  
141 - },  
142 - {  
143 - name: 'week',  
144 - value: {  
145 - text: 'weeks',  
146 - diffText: 'days',  
147 - timeText: 'days',  
148 - },  
149 - },  
150 - {  
151 - name: 'month',  
152 - value: {  
153 - text: 'months',  
154 - diffText: 'days',  
155 - timeText: 'days',  
156 - },  
157 - },  
158 - ];  
159 - //代码保留  
160 -  
161 const timeType = ref(''); 88 const timeType = ref('');
162 89
163 // 接口调用历史 90 // 接口调用历史
164 - const getClassify = async (type?: string) => {  
165 - timeType.value = type as string;  
166 - const findValue = configTime.find((configItem) => configItem.name === type)?.value;  
167 - chartData.timeLineXAxisData = timeFormat(type, findValue);  
168 - const res = (await getApplicationRecordClassify(type)) as any as ClassifyItemType[];  
169 - chartData.timeLineChartData = res?.map((classifyItem: ClassifyItemType) => ({  
170 - type: 'line',  
171 - name: formatClassifyText[classifyItem.classify],  
172 - data: classifyItem?.dts?.map((dtsItem: ClassifyDtsItemType) => [  
173 - moment(dtsItem.time).format('YYYY-MM-DD HH:mm:ss'),  
174 - dtsItem.count,  
175 - ]),  
176 - connectNulls: true,  
177 - })); 91 + const getClassify = async (type: DateInterval) => {
  92 + timeType.value = type;
  93 + // const findValue = configTime.find((configItem) => configItem.name === type)?.value;
  94 + // chartData.timeLineXAxisData = timeFormat(type, findValue);
  95 + const res = await getApplicationRecordClassify(type);
  96 +
  97 + const transformData = transformClassifyItem(res, type);
  98 + chartData.timeLineChartOptions = transformChartsOptionsByClassifyRecord(transformData, type);
178 }; 99 };
179 100
180 - const handleReceiveTimeRange = (value: string) => { 101 + const handleReceiveTimeRange = (value: DateInterval) => {
181 getClassify(value); 102 getClassify(value);
182 }; 103 };
183 104
@@ -206,8 +127,7 @@ @@ -206,8 +127,7 @@
206 <div> 127 <div>
207 <TimeLineChart 128 <TimeLineChart
208 @emitTimeRange="handleReceiveTimeRange" 129 @emitTimeRange="handleReceiveTimeRange"
209 - :seriesData="chartData.timeLineChartData"  
210 - :timeLineXAxisData="chartData.timeLineXAxisData" 130 + :charts-data="chartData.timeLineChartOptions"
211 :timeType="timeType" 131 :timeType="timeType"
212 /> 132 />
213 </div> 133 </div>