| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { EchartsUIType } from '#/packages/effects/plugins/src/echarts'; |
| | | |
| | | import { nextTick, onMounted, ref } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictObj } from '#/packages/effects/hooks/src'; |
| | | import { EchartsUI, useEcharts } from '#/packages/effects/plugins/src/echarts'; |
| | | |
| | | import { Card } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getGroupSizeDistribution, |
| | | getMessageTypeDistribution, |
| | | getTopSenders, |
| | | } from '#/api/im/manager/statistics'; |
| | | |
| | | const props = defineProps<{ |
| | | type: 'groupSize' | 'messageType' | 'topSenders'; |
| | | }>(); |
| | | |
| | | const titleMap = { |
| | | groupSize: 'ç¾¤è§æ¨¡åå¸', |
| | | messageType: 'æ¶æ¯ç±»ååå¸', |
| | | topSenders: 'æ¶æ¯åé TOP 10', |
| | | }; |
| | | |
| | | const loading = ref(false); |
| | | const chartRef = ref<EchartsUIType>(); |
| | | const { renderEcharts } = useEcharts(chartRef); |
| | | |
| | | /** å è½½å¾è¡¨æ°æ® */ |
| | | async function loadData() { |
| | | loading.value = true; |
| | | try { |
| | | await nextTick(); |
| | | if (props.type === 'groupSize') { |
| | | const data = await getGroupSizeDistribution(); |
| | | await renderEcharts({ |
| | | tooltip: { trigger: 'axis' }, |
| | | grid: { left: 24, right: 24, bottom: 16, top: 28, containLabel: true }, |
| | | xAxis: { type: 'category', data: data.map((item) => item.range) }, |
| | | yAxis: { type: 'value', name: 'ç¾¤ç»æ°' }, |
| | | series: [ |
| | | { |
| | | type: 'bar', |
| | | data: data.map((item) => item.count), |
| | | itemStyle: { color: '#18a058', borderRadius: [4, 4, 0, 0] }, |
| | | barMaxWidth: 48, |
| | | }, |
| | | ], |
| | | }); |
| | | return; |
| | | } |
| | | if (props.type === 'messageType') { |
| | | const data = await getMessageTypeDistribution(); |
| | | const items = data.map((item) => ({ |
| | | name: |
| | | getDictObj(DICT_TYPE.IM_CONTENT_TYPE, String(item.type))?.label || |
| | | `æªç¥(${item.type})`, |
| | | value: item.value, |
| | | })); |
| | | await renderEcharts({ |
| | | tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, |
| | | legend: { orient: 'vertical', right: 8, top: 'middle' }, |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['42%', '70%'], |
| | | data: items, |
| | | itemStyle: { borderColor: '#fff', borderRadius: 4, borderWidth: 2 }, |
| | | label: { show: false }, |
| | | }, |
| | | ], |
| | | }); |
| | | return; |
| | | } |
| | | const data = await getTopSenders(); |
| | | const sorted = data.toSorted((a, b) => a.messageCount - b.messageCount); |
| | | await renderEcharts({ |
| | | tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, |
| | | grid: { left: 24, right: 24, bottom: 16, top: 20, containLabel: true }, |
| | | xAxis: { type: 'value', name: 'æ¶æ¯æ°' }, |
| | | yAxis: { |
| | | type: 'category', |
| | | data: sorted.map((item) => `${item.nickname || item.userId}(${item.userId})`), |
| | | axisLabel: { overflow: 'truncate', width: 110 }, |
| | | }, |
| | | series: [ |
| | | { |
| | | type: 'bar', |
| | | data: sorted.map((item) => item.messageCount), |
| | | itemStyle: { color: '#2f7df6', borderRadius: [0, 4, 4, 0] }, |
| | | barMaxWidth: 18, |
| | | }, |
| | | ], |
| | | }); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | onMounted(loadData); |
| | | </script> |
| | | |
| | | <template> |
| | | <Card :body-style="{ padding: '12px 16px 16px' }" :title="titleMap[type]"> |
| | | <div class="relative min-h-[320px]"> |
| | | <EchartsUI ref="chartRef" height="320px" /> |
| | | <div |
| | | v-if="loading" |
| | | class="absolute inset-0 flex items-center justify-center bg-card/70 text-sm text-muted-foreground" |
| | | > |
| | | å è½½ä¸ |
| | | </div> |
| | | </div> |
| | | </Card> |
| | | </template> |