✨ feat(im): 修一批管理端统计与成员选择器细节
- 消息趋势 / 用户趋势图表加 loading 态(接口错误由全局拦截器统一提示) - 群成员选择器 grid 模式补右上角 × 移除按钮 - 统计接口 6 个 API 补全返回值泛型im
parent
38ecc4f40c
commit
72d8c499a4
|
|
@ -37,30 +37,34 @@ export interface ImStatisticsTopSenderVO {
|
||||||
|
|
||||||
// 获得 KPI 概览
|
// 获得 KPI 概览
|
||||||
export const getStatisticsOverview = (): Promise<ImStatisticsOverviewVO> => {
|
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> => {
|
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> => {
|
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 天)
|
// 获得消息类型分布(最近 30 天)
|
||||||
export const getMessageTypeDistribution = (): Promise<ImStatisticsMessageTypeVO[]> => {
|
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[]> => {
|
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 天)
|
// 获得消息 TOP 发送者(最近 30 天)
|
||||||
export const getTopSenders = (): Promise<ImStatisticsTopSenderVO[]> => {
|
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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- grid 形态:宫格预览(管理员设置等场景沿用) -->
|
<!-- grid 形态:宫格预览;非 locked 成员右上角叠加 × 移除(locked 不渲染) -->
|
||||||
<div v-else class="flex flex-wrap p-2.5">
|
<div v-else class="flex flex-wrap p-2.5">
|
||||||
<GroupMemberGrid
|
<GroupMemberGrid
|
||||||
v-for="member in selectedMembers"
|
v-for="member in selectedMembers"
|
||||||
:key="member.userId"
|
:key="member.userId"
|
||||||
:member="member"
|
: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>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div ref="chartRef" style="width: 100%; height: 320px"></div>
|
<div ref="chartRef" v-loading="loading" style="width: 100%; height: 320px"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@ defineOptions({ name: 'ImStatisticsMessageTrendChart' })
|
||||||
|
|
||||||
const chartRef = ref<HTMLElement>()
|
const chartRef = ref<HTMLElement>()
|
||||||
const days = ref(7)
|
const days = ref(7)
|
||||||
|
const loading = ref(false)
|
||||||
let chart: echarts.ECharts | null = null
|
let chart: echarts.ECharts | null = null
|
||||||
|
|
||||||
const buildOption = (dates: string[], priv: number[], grp: number[]): echarts.EChartsCoreOption => ({
|
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 loadData = async () => {
|
||||||
const data = await StatisticsApi.getMessageTrend(days.value)
|
loading.value = true
|
||||||
chart?.setOption(buildOption(data.dates, data.series.private || [], data.series.group || []))
|
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 () => {
|
onMounted(async () => {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div ref="chartRef" style="width: 100%; height: 320px"></div>
|
<div ref="chartRef" v-loading="loading" style="width: 100%; height: 320px"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@ defineOptions({ name: 'ImStatisticsUserTrendChart' })
|
||||||
|
|
||||||
const chartRef = ref<HTMLElement>()
|
const chartRef = ref<HTMLElement>()
|
||||||
const days = ref(7)
|
const days = ref(7)
|
||||||
|
const loading = ref(false)
|
||||||
let chart: echarts.ECharts | null = null
|
let chart: echarts.ECharts | null = null
|
||||||
|
|
||||||
const buildOption = (dates: string[], reg: number[], act: number[]): echarts.EChartsCoreOption => ({
|
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 loadData = async () => {
|
||||||
const data = await StatisticsApi.getUserTrend(days.value)
|
loading.value = true
|
||||||
chart?.setOption(buildOption(data.dates, data.series.register || [], data.series.active || []))
|
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 () => {
|
onMounted(async () => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue