Showing
4 changed files
with
131 additions
and
141 deletions
| 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> |