✨ feat(im): 修一批管理端统计与成员选择器细节
- 消息趋势 / 用户趋势图表加 loading 态(接口错误由全局拦截器统一提示) - 群成员选择器 grid 模式补右上角 × 移除按钮 - 统计接口 6 个 API 补全返回值泛型im
parent
38ecc4f40c
commit
72d8c499a4
|
|
@ -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' })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue