Commit 32f333ea72b0041c975c024828c62dbb1dd60e48

Authored by fengtao
Committed by xp.Huang
1 parent 14446abd

perf: 优化接口调用历史折线图显示

@@ -3,6 +3,7 @@ import { findDictItemByCode } from '/@/api/system/dict'; @@ -3,6 +3,7 @@ import { findDictItemByCode } from '/@/api/system/dict';
3 import { FormSchema } from '/@/components/Form'; 3 import { FormSchema } from '/@/components/Form';
4 import { DictEnum } from '/@/enums/dictEnum'; 4 import { DictEnum } from '/@/enums/dictEnum';
5 import { useI18n } from '/@/hooks/web/useI18n'; 5 import { useI18n } from '/@/hooks/web/useI18n';
  6 +import HelpMessage from '../HelpMessage.vue';
6 7
7 export enum FormFieldsEnum { 8 export enum FormFieldsEnum {
8 NAME = 'name', 9 NAME = 'name',
@@ -62,23 +63,9 @@ export const formSchema: FormSchema[] = [ @@ -62,23 +63,9 @@ export const formSchema: FormSchema[] = [
62 }, 63 },
63 { 64 {
64 field: FormFieldsEnum.INTERFACE_ADDRESS, 65 field: FormFieldsEnum.INTERFACE_ADDRESS,
65 - label: t('application.api.text.interfaceAddress'), 66 + label: h(HelpMessage) as unknown as string,
66 component: 'Input', 67 component: 'Input',
67 required: true, 68 required: true,
68 - helpMessage: [  
69 - h(  
70 - 'span',  
71 - {  
72 - style: {  
73 - cursor: 'pointer',  
74 - },  
75 - onClick: () => {  
76 - window.open('https://yunteng.yuque.com/avshoi/open_api');  
77 - },  
78 - },  
79 - t('common.documentUrl')  
80 - ) as any as string,  
81 - ],  
82 componentProps: { 69 componentProps: {
83 maxLength: 255, 70 maxLength: 255,
84 placeholder: t('application.api.search.interfaceAddressPlaceholder'), 71 placeholder: t('application.api.search.interfaceAddressPlaceholder'),
  1 +<script lang="ts" setup>
  2 + import { BasicHelp } from '/@/components/Basic';
  3 + import { useI18n } from '/@/hooks/web/useI18n';
  4 +
  5 + function handleLinkOpenApiDoc() {
  6 + window.open('https://yunteng.yuque.com/avshoi/open_api');
  7 + }
  8 +
  9 + const { t } = useI18n();
  10 +</script>
  11 +
  12 +<template>
  13 + <div>{{ t('application.api.text.interfaceAddress') }}</div>
  14 +
  15 + <BasicHelp
  16 + placement="top"
  17 + @click="handleLinkOpenApiDoc"
  18 + class="mx-1"
  19 + :text="t('common.documentUrl')"
  20 + />
  21 +</template>
1 -<script setup lang="ts">  
2 - import { Descriptions, Progress, Skeleton, Tooltip, DescriptionsItem } from 'ant-design-vue';  
3 -  
4 - defineProps({  
5 - seriesData: {  
6 - type: Array as PropType<Recordable[]>,  
7 - default: () => [],  
8 - },  
9 - });  
10 -</script>  
11 -<template>  
12 - <div class="m-16">  
13 - <Skeleton active :paragraph="{ rows: 10 }" :loading="!seriesData">  
14 - <Descriptions :column="1">  
15 - <template v-for="(item, index) in seriesData" :key="item.label">  
16 - <DescriptionsItem>  
17 - <span  
18 - class="mr-2 item-span"  
19 - :style="{  
20 - color:  
21 - index === 0  
22 - ? '#f0a16e'  
23 - : index === 1  
24 - ? '#868585'  
25 - : index === 2  
26 - ? '#e78739'  
27 - : '#4e84f5',  
28 - backgroundColor:  
29 - index === 0 ? '#FAD672' : index === 1 ? '#CBCAC9' : index === 2 ? '#F1B889' : '',  
30 - borderColor:  
31 - index === 0  
32 - ? '#fdee7d'  
33 - : index === 1  
34 - ? '#e6e6e5'  
35 - : index === 2  
36 - ? '#f8c296'  
37 - : '#0b55f1',  
38 - }"  
39 - >{{ index + 1 }}</span  
40 - >  
41 - <div class="flex justify-between" style="width: 100%">  
42 - <div class="label-span">  
43 - <Tooltip :title="item.label">  
44 - {{ item.label }}  
45 - </Tooltip>  
46 - </div>  
47 - <div class="flex w-7/10">  
48 - <Progress  
49 - :showInfo="false"  
50 - size="small"  
51 - style="width: 70%"  
52 - :percent="(item.value / seriesData[0].value) * 100"  
53 - :strokeWidth="12"  
54 - :strokeColor="  
55 - index === 0  
56 - ? '#ea5b42'  
57 - : index === 1  
58 - ? '#666'  
59 - : index === 2  
60 - ? '#e4751a'  
61 - : '#b5b6b6'  
62 - "  
63 - />  
64 - <span  
65 - class="ml-2"  
66 - :style="{ color: index === 0 ? '#EA5B42' : index === 2 ? '#E4751A' : '#666' }"  
67 - >  
68 - {{ item.value }}  
69 - </span>  
70 - </div>  
71 - </div>  
72 - </DescriptionsItem>  
73 - </template>  
74 - </Descriptions>  
75 - </Skeleton>  
76 - </div>  
77 -</template>  
78 -  
79 -<style scoped lang="less">  
80 - .item-span {  
81 - width: 1.4rem;  
82 - height: 1.25rem;  
83 - border: 1px solid;  
84 - color: #0b55f1;  
85 - border-radius: 50%;  
86 - display: flex;  
87 - align-items: center;  
88 - justify-content: center;  
89 - }  
90 -  
91 - .label-span {  
92 - width: 10rem;  
93 - white-space: nowrap;  
94 - text-overflow: ellipsis;  
95 - overflow: hidden;  
96 - word-break: break-all;  
97 - }  
98 -</style>  
@@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
37 }, 37 },
38 legend: { 38 legend: {
39 top: 'bottom', 39 top: 'bottom',
40 - padding: 5, 40 + type: 'scroll',
41 }, 41 },
42 title: [ 42 title: [
43 { 43 {
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 import { useECharts } from '/@/hooks/web/useECharts'; 3 import { useECharts } from '/@/hooks/web/useECharts';
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 7
7 const { t } = useI18n(); 8 const { t } = useI18n();
8 9
@@ -11,6 +12,14 @@ @@ -11,6 +12,14 @@
11 type: Array, 12 type: Array,
12 default: () => [], 13 default: () => [],
13 }, 14 },
  15 + timeLineXAxisData: {
  16 + type: Array,
  17 + default: () => [],
  18 + },
  19 + timeType: {
  20 + type: String,
  21 + default: 'week',
  22 + },
14 }); 23 });
15 24
16 const emits = defineEmits(['emitTimeRange']); 25 const emits = defineEmits(['emitTimeRange']);
@@ -21,10 +30,11 @@ @@ -21,10 +30,11 @@
21 30
22 const timeRangeList = ref([ 31 const timeRangeList = ref([
23 { label: `1${t('home.index.timeUnit.hour')}`, value: 'hour', interval: 1000 * 60 * 5 }, 32 { label: `1${t('home.index.timeUnit.hour')}`, value: 'hour', interval: 1000 * 60 * 5 },
  33 + { label: `1${t('home.index.timeUnit.day')}`, value: 'day', interval: 1000 * 60 * 60 * 1 },
24 { 34 {
25 label: `7${t('home.index.timeUnit.day')}`, 35 label: `7${t('home.index.timeUnit.day')}`,
26 value: 'week', 36 value: 'week',
27 - interval: 1000 * 60 * 60 * 2, 37 + interval: 1000 * 60 * 60 * 1,
28 }, 38 },
29 { 39 {
30 label: `30${t('home.index.timeUnit.day')}`, 40 label: `30${t('home.index.timeUnit.day')}`,
@@ -63,7 +73,12 @@ @@ -63,7 +73,12 @@
63 show: false, 73 show: false,
64 }, 74 },
65 axisLabel: { 75 axisLabel: {
66 - formatter: '{yyyy}-{MM}-{dd}', 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}',
67 showMinLabel: true, 82 showMinLabel: true,
68 showMaxLabel: true, // 固定显示X轴的最后一条数据 83 showMaxLabel: true, // 固定显示X轴的最后一条数据
69 }, 84 },
@@ -95,13 +110,17 @@ @@ -95,13 +110,17 @@
95 }, 110 },
96 { 111 {
97 deep: true, 112 deep: true,
  113 + immediate: true,
98 } 114 }
99 ); 115 );
100 116
101 onMounted(() => { 117 onMounted(() => {
102 setOptions(getOptions()); 118 setOptions(getOptions());
103 //自适应 119 //自适应
104 - window.addEventListener('resize', () => resize()); 120 + window.addEventListener('resize', () => {
  121 + resize();
  122 + setOptions(getOptions());
  123 + });
105 }); 124 });
106 125
107 const handleTimeRangeChange = (e: any) => { 126 const handleTimeRangeChange = (e: any) => {
@@ -122,7 +141,10 @@ @@ -122,7 +141,10 @@
122 </template> 141 </template>
123 </a-radio-group> 142 </a-radio-group>
124 </template> 143 </template>
125 - <div ref="chartRef" class="w-full h-80"></div> 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"
  146 + ><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE"
  147 + /></div>
126 </a-card> 148 </a-card>
127 </template> 149 </template>
128 150
1 <script setup lang="ts"> 1 <script setup lang="ts">
  2 + import { ref } from 'vue';
2 import LineChart from './components/LineChart.vue'; 3 import LineChart from './components/LineChart.vue';
3 import PieChart from './components/PieChart.vue'; 4 import PieChart from './components/PieChart.vue';
4 import Table from './components/Table.vue'; 5 import Table from './components/Table.vue';
@@ -27,6 +28,7 @@ @@ -27,6 +28,7 @@
27 pieChartData: Recordable[]; 28 pieChartData: Recordable[];
28 timeLineChartData: Recordable[]; 29 timeLineChartData: Recordable[];
29 totalData: Recordable[]; 30 totalData: Recordable[];
  31 + timeLineXAxisData?: string[];
30 } 32 }
31 33
32 const chartData = reactive<ChartData>({ 34 const chartData = reactive<ChartData>({
@@ -34,6 +36,7 @@ @@ -34,6 +36,7 @@
34 pieChartData: [], 36 pieChartData: [],
35 timeLineChartData: [], 37 timeLineChartData: [],
36 totalData: [], 38 totalData: [],
  39 + timeLineXAxisData: [],
37 }); 40 });
38 41
39 // API统计 42 // API统计
@@ -76,15 +79,92 @@ @@ -76,15 +79,92 @@
76 ?.map((topItem) => topItem.value) 79 ?.map((topItem) => topItem.value)
77 ?.reduce((sum, curr) => sum + curr); 80 ?.reduce((sum, curr) => sum + curr);
78 // 相加总共 81 // 相加总共
79 - chartData.pieChartData.push({  
80 - name: '其他应用',  
81 - value: res.tops.length <= 5 ? 0 : otherApplicationCount,  
82 - }); 82 + if (res.tops && res.tops.length > 5) {
  83 + chartData.pieChartData.push({
  84 + name: '其他应用',
  85 + value: otherApplicationCount,
  86 + });
  87 + }
83 } 88 }
84 }; 89 };
85 90
  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('');
  162 +
86 // 接口调用历史 163 // 接口调用历史
87 const getClassify = async (type?: string) => { 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);
88 const res = (await getApplicationRecordClassify(type)) as any as ClassifyItemType[]; 168 const res = (await getApplicationRecordClassify(type)) as any as ClassifyItemType[];
89 chartData.timeLineChartData = res?.map((classifyItem: ClassifyItemType) => ({ 169 chartData.timeLineChartData = res?.map((classifyItem: ClassifyItemType) => ({
90 type: 'line', 170 type: 'line',
@@ -125,15 +205,11 @@ @@ -125,15 +205,11 @@
125 </div> 205 </div>
126 <div> 206 <div>
127 <TimeLineChart 207 <TimeLineChart
128 - v-if="chartData.timeLineChartData.length"  
129 @emitTimeRange="handleReceiveTimeRange" 208 @emitTimeRange="handleReceiveTimeRange"
130 :seriesData="chartData.timeLineChartData" 209 :seriesData="chartData.timeLineChartData"
  210 + :timeLineXAxisData="chartData.timeLineXAxisData"
  211 + :timeType="timeType"
131 /> 212 />
132 - <a-card v-else :title="t('application.record.text.timeLineTitle')" class="w-full h-120">  
133 - <div class="w-full h-72 flex justify-center items-center"  
134 - ><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE"  
135 - /></div>  
136 - </a-card>  
137 </div> 213 </div>
138 <div> 214 <div>
139 <a-card class="w-full h-140"> 215 <a-card class="w-full h-140">