feat(im): 修一批管理端统计与成员选择器细节

- 消息趋势 / 用户趋势图表加 loading 态(接口错误由全局拦截器统一提示)
- 群成员选择器 grid 模式补右上角 × 移除按钮
- 统计接口 6 个 API 补全返回值泛型
im
YunaiV 2026-05-22 20:15:15 +08:00
parent 38ecc4f40c
commit 72d8c499a4
4 changed files with 38 additions and 14 deletions

View File

@ -37,30 +37,34 @@ export interface ImStatisticsTopSenderVO {
// 获得 KPI 概览
export const getStatisticsOverview = (): Promise<ImStatisticsOverviewVO> => {
return request.get({ url: '/im/manager/statistics/overview' })
return request.get<ImStatisticsOverviewVO>({ url: '/im/manager/statistics/overview' })
}
// 获得消息趋势(私聊 + 群聊双线)
export const getMessageTrend = (days: number): Promise<ImStatisticsTrendVO> => {
return request.get({ url: '/im/manager/statistics/message-trend', params: { days } })
return request.get<ImStatisticsTrendVO>({ url: '/im/manager/statistics/message-trend', params: { days } })
}
// 获得用户趋势(新增注册 + 日活双线)
export const getUserTrend = (days: number): Promise<ImStatisticsTrendVO> => {
return request.get({ url: '/im/manager/statistics/user-trend', params: { days } })
return request.get<ImStatisticsTrendVO>({ url: '/im/manager/statistics/user-trend', params: { days } })
}
// 获得消息类型分布(最近 30 天)
export const getMessageTypeDistribution = (): Promise<ImStatisticsMessageTypeVO[]> => {
return request.get({ url: '/im/manager/statistics/message-type-distribution' })
return request.get<ImStatisticsMessageTypeVO[]>({
url: '/im/manager/statistics/message-type-distribution'
})
}
// 获得群规模分布
export const getGroupSizeDistribution = (): Promise<ImStatisticsGroupSizeVO[]> => {
return request.get({ url: '/im/manager/statistics/group-size-distribution' })
return request.get<ImStatisticsGroupSizeVO[]>({
url: '/im/manager/statistics/group-size-distribution'
})
}
// 获得消息 TOP 发送者(最近 30 天)
export const getTopSenders = (): Promise<ImStatisticsTopSenderVO[]> => {
return request.get({ url: '/im/manager/statistics/top-senders' })
return request.get<ImStatisticsTopSenderVO[]>({ url: '/im/manager/statistics/top-senders' })
}

View File

@ -84,13 +84,21 @@
</div>
</template>
<!-- grid 形态宫格预览管理员设置等场景沿用 -->
<!-- grid 形态宫格预览 locked 成员右上角叠加 × 移除locked 不渲染 -->
<div v-else class="flex flex-wrap p-2.5">
<GroupMemberGrid
v-for="member in selectedMembers"
:key="member.userId"
:member="member"
/>
>
<Icon
v-if="!isLocked(member)"
icon="ant-design:close-circle-filled"
:size="16"
class="absolute top-0 right-0 cursor-pointer transition-colors text-[var(--el-text-color-placeholder)] hover:text-[var(--el-color-danger)]"
@click="handleToggle(member)"
/>
</GroupMemberGrid>
</div>
<div

View File

@ -10,7 +10,7 @@
</el-select>
</div>
</template>
<div ref="chartRef" style="width: 100%; height: 320px"></div>
<div ref="chartRef" v-loading="loading" style="width: 100%; height: 320px"></div>
</el-card>
</template>
@ -22,6 +22,7 @@ defineOptions({ name: 'ImStatisticsMessageTrendChart' })
const chartRef = ref<HTMLElement>()
const days = ref(7)
const loading = ref(false)
let chart: echarts.ECharts | null = null
const buildOption = (dates: string[], priv: number[], grp: number[]): echarts.EChartsCoreOption => ({
@ -71,8 +72,13 @@ const buildOption = (dates: string[], priv: number[], grp: number[]): echarts.EC
})
const loadData = async () => {
const data = await StatisticsApi.getMessageTrend(days.value)
chart?.setOption(buildOption(data.dates, data.series.private || [], data.series.group || []))
loading.value = true
try {
const data = await StatisticsApi.getMessageTrend(days.value)
chart?.setOption(buildOption(data.dates, data.series.private || [], data.series.group || []))
} finally {
loading.value = false
}
}
onMounted(async () => {

View File

@ -10,7 +10,7 @@
</el-select>
</div>
</template>
<div ref="chartRef" style="width: 100%; height: 320px"></div>
<div ref="chartRef" v-loading="loading" style="width: 100%; height: 320px"></div>
</el-card>
</template>
@ -22,6 +22,7 @@ defineOptions({ name: 'ImStatisticsUserTrendChart' })
const chartRef = ref<HTMLElement>()
const days = ref(7)
const loading = ref(false)
let chart: echarts.ECharts | null = null
const buildOption = (dates: string[], reg: number[], act: number[]): echarts.EChartsCoreOption => ({
@ -50,8 +51,13 @@ const buildOption = (dates: string[], reg: number[], act: number[]): echarts.ECh
})
const loadData = async () => {
const data = await StatisticsApi.getUserTrend(days.value)
chart?.setOption(buildOption(data.dates, data.series.register || [], data.series.active || []))
loading.value = true
try {
const data = await StatisticsApi.getUserTrend(days.value)
chart?.setOption(buildOption(data.dates, data.series.register || [], data.series.active || []))
} finally {
loading.value = false
}
}
onMounted(async () => {