diff --git a/electron/main/database/analysis.ts b/electron/main/database/analysis.ts index 3c030ae..203d412 100644 --- a/electron/main/database/analysis.ts +++ b/electron/main/database/analysis.ts @@ -25,9 +25,6 @@ import type { DragonKingRankItem, DivingAnalysis, DivingRankItem, - MonologueAnalysis, - MonologueRankItem, - MaxComboRecord, } from '../../../src/types/analysis' import { openDatabase } from './core' @@ -1154,189 +1151,3 @@ export function getDivingAnalysis(sessionId: string, filter?: TimeFilter): Divin db.close() } } - -/** - * 获取自言自语分析 - * 定义:一个人连续发言超过三次(间隔<=5分钟) - * 分类:3-4句(加特林)、5-9句(小作文)、10+句(无人区广播) - */ -export function getMonologueAnalysis(sessionId: string, filter?: TimeFilter): MonologueAnalysis { - const db = openDatabase(sessionId) - const emptyResult: MonologueAnalysis = { - rank: [], - maxComboRecord: null, - } - - if (!db) return emptyResult - - try { - const { clause, params } = buildTimeFilter(filter) - - // 构建 WHERE 子句:只统计文本消息 - let whereClause = clause - if (whereClause.includes('WHERE')) { - whereClause += " AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0" - } else { - whereClause = " WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0" - } - - // 获取所有文本消息,按时间排序 - const messages = db - .prepare( - ` - SELECT - msg.id, - msg.sender_id as senderId, - msg.ts, - m.platform_id as platformId, - COALESCE(m.group_nickname, m.account_name, m.platform_id) as name - FROM message msg - JOIN member m ON msg.sender_id = m.id - ${whereClause} - ORDER BY msg.ts ASC - ` - ) - .all(...params) as Array<{ - id: number - senderId: number - ts: number - platformId: string - name: string - }> - - if (messages.length === 0) return emptyResult - - // 成员信息映射 - const memberInfo = new Map() - - // 连击统计:memberId -> { totalStreaks, maxCombo, lowStreak, midStreak, highStreak } - const memberStats = new Map< - number, - { - totalStreaks: number - maxCombo: number - lowStreak: number - midStreak: number - highStreak: number - } - >() - - // 全群最高纪录 - let globalMaxCombo: { memberId: number; comboLength: number; startTs: number } | null = null - - // 连击间隔限制:5分钟 = 300秒 - const MAX_INTERVAL = 300 - - // 当前连击状态 - let currentStreak = { - senderId: -1, - count: 0, - startTs: 0, - lastTs: 0, - } - - // 处理一个连击结束 - const finishStreak = () => { - if (currentStreak.count >= 3) { - const memberId = currentStreak.senderId - - // 初始化成员统计 - if (!memberStats.has(memberId)) { - memberStats.set(memberId, { - totalStreaks: 0, - maxCombo: 0, - lowStreak: 0, - midStreak: 0, - highStreak: 0, - }) - } - - const stats = memberStats.get(memberId)! - stats.totalStreaks++ - stats.maxCombo = Math.max(stats.maxCombo, currentStreak.count) - - // 分类统计:3-4句、5-9句、10+句 - if (currentStreak.count >= 10) { - stats.highStreak++ - } else if (currentStreak.count >= 5) { - stats.midStreak++ - } else { - stats.lowStreak++ // 3-4句 - } - - // 更新全群最高纪录 - if (!globalMaxCombo || currentStreak.count > globalMaxCombo.comboLength) { - globalMaxCombo = { - memberId, - comboLength: currentStreak.count, - startTs: currentStreak.startTs, - } - } - } - } - - // 遍历所有消息 - for (const msg of messages) { - // 记录成员信息 - if (!memberInfo.has(msg.senderId)) { - memberInfo.set(msg.senderId, { platformId: msg.platformId, name: msg.name }) - } - - // 检查是否延续当前连击 - const isSameSender = msg.senderId === currentStreak.senderId - const isWithinInterval = msg.ts - currentStreak.lastTs <= MAX_INTERVAL - - if (isSameSender && isWithinInterval) { - // 延续连击 - currentStreak.count++ - currentStreak.lastTs = msg.ts - } else { - // 结束当前连击,开始新的 - finishStreak() - currentStreak = { - senderId: msg.senderId, - count: 1, - startTs: msg.ts, - lastTs: msg.ts, - } - } - } - - // 处理最后一个连击 - finishStreak() - - // 构建排行榜(按总连击次数排序) - const rank: MonologueRankItem[] = [] - for (const [memberId, stats] of memberStats.entries()) { - const info = memberInfo.get(memberId)! - rank.push({ - memberId, - platformId: info.platformId, - name: info.name, - totalStreaks: stats.totalStreaks, - maxCombo: stats.maxCombo, - lowStreak: stats.lowStreak, - midStreak: stats.midStreak, - highStreak: stats.highStreak, - }) - } - rank.sort((a, b) => b.totalStreaks - a.totalStreaks) - - // 构建全群最高纪录 - let maxComboRecord: MaxComboRecord | null = null - if (globalMaxCombo) { - const info = memberInfo.get(globalMaxCombo.memberId)! - maxComboRecord = { - memberId: globalMaxCombo.memberId, - platformId: info.platformId, - memberName: info.name, - comboLength: globalMaxCombo.comboLength, - startTs: globalMaxCombo.startTs, - } - } - - return { rank, maxComboRecord } - } finally { - db.close() - } -} diff --git a/electron/main/ipc/chat.ts b/electron/main/ipc/chat.ts index 0542a52..01716ab 100644 --- a/electron/main/ipc/chat.ts +++ b/electron/main/ipc/chat.ts @@ -483,21 +483,6 @@ export function registerChatHandlers(ctx: IpcContext): void { } ) - /** - * 获取自言自语分析数据 - */ - ipcMain.handle( - 'chat:getMonologueAnalysis', - async (_, sessionId: string, filter?: { startTs?: number; endTs?: number }) => { - try { - return await worker.getMonologueAnalysis(sessionId, filter) - } catch (error) { - console.error('获取自言自语分析失败:', error) - return { rank: [], maxComboRecord: null } - } - } - ) - /** * 获取 @ 互动分析数据 */ diff --git a/electron/main/worker/dbWorker.ts b/electron/main/worker/dbWorker.ts index b98ec90..54ab3f4 100644 --- a/electron/main/worker/dbWorker.ts +++ b/electron/main/worker/dbWorker.ts @@ -30,7 +30,6 @@ import { getNightOwlAnalysis, getDragonKingAnalysis, getDivingAnalysis, - getMonologueAnalysis, getMentionAnalysis, getMentionGraph, getLaughAnalysis, @@ -120,7 +119,6 @@ const syncHandlers: Record any> = { getNightOwlAnalysis: (p) => getNightOwlAnalysis(p.sessionId, p.filter), getDragonKingAnalysis: (p) => getDragonKingAnalysis(p.sessionId, p.filter), getDivingAnalysis: (p) => getDivingAnalysis(p.sessionId, p.filter), - getMonologueAnalysis: (p) => getMonologueAnalysis(p.sessionId, p.filter), getMentionAnalysis: (p) => getMentionAnalysis(p.sessionId, p.filter), getMentionGraph: (p) => getMentionGraph(p.sessionId, p.filter), getLaughAnalysis: (p) => getLaughAnalysis(p.sessionId, p.filter, p.keywords), diff --git a/electron/main/worker/query/advanced/behavior.ts b/electron/main/worker/query/advanced/behavior.ts index 9f4e038..4bed190 100644 --- a/electron/main/worker/query/advanced/behavior.ts +++ b/electron/main/worker/query/advanced/behavior.ts @@ -1,168 +1,10 @@ /** * 行为分析模块 - * 包含:自言自语分析、斗图分析 + * 包含:斗图分析 */ import { openDatabase, buildTimeFilter, type TimeFilter } from '../../core' -// ==================== 自言自语分析 ==================== - -/** - * 获取自言自语分析 - */ -export function getMonologueAnalysis(sessionId: string, filter?: TimeFilter): any { - const db = openDatabase(sessionId) - const emptyResult = { rank: [], maxComboRecord: null } - - if (!db) return emptyResult - - const { clause, params } = buildTimeFilter(filter) - - let whereClause = clause - if (whereClause.includes('WHERE')) { - whereClause += " AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0" - } else { - whereClause = " WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0" - } - - const messages = db - .prepare( - ` - SELECT - msg.id, - msg.sender_id as senderId, - msg.ts, - m.platform_id as platformId, - COALESCE(m.group_nickname, m.account_name, m.platform_id) as name - FROM message msg - JOIN member m ON msg.sender_id = m.id - ${whereClause} - ORDER BY msg.ts ASC - ` - ) - .all(...params) as Array<{ - id: number - senderId: number - ts: number - platformId: string - name: string - }> - - if (messages.length === 0) return emptyResult - - const memberInfo = new Map() - const memberStats = new Map< - number, - { - totalStreaks: number - maxCombo: number - lowStreak: number - midStreak: number - highStreak: number - } - >() - - let globalMaxCombo: { memberId: number; comboLength: number; startTs: number } | null = null - const MAX_INTERVAL = 300 - - let currentStreak = { - senderId: -1, - count: 0, - startTs: 0, - lastTs: 0, - } - - const finishStreak = () => { - if (currentStreak.count >= 3) { - const memberId = currentStreak.senderId - - if (!memberStats.has(memberId)) { - memberStats.set(memberId, { - totalStreaks: 0, - maxCombo: 0, - lowStreak: 0, - midStreak: 0, - highStreak: 0, - }) - } - - const stats = memberStats.get(memberId)! - stats.totalStreaks++ - stats.maxCombo = Math.max(stats.maxCombo, currentStreak.count) - - if (currentStreak.count >= 10) { - stats.highStreak++ - } else if (currentStreak.count >= 5) { - stats.midStreak++ - } else { - stats.lowStreak++ - } - - if (!globalMaxCombo || currentStreak.count > globalMaxCombo.comboLength) { - globalMaxCombo = { - memberId, - comboLength: currentStreak.count, - startTs: currentStreak.startTs, - } - } - } - } - - for (const msg of messages) { - if (!memberInfo.has(msg.senderId)) { - memberInfo.set(msg.senderId, { platformId: msg.platformId, name: msg.name }) - } - - const isSameSender = msg.senderId === currentStreak.senderId - const isWithinInterval = msg.ts - currentStreak.lastTs <= MAX_INTERVAL - - if (isSameSender && isWithinInterval) { - currentStreak.count++ - currentStreak.lastTs = msg.ts - } else { - finishStreak() - currentStreak = { - senderId: msg.senderId, - count: 1, - startTs: msg.ts, - lastTs: msg.ts, - } - } - } - - finishStreak() - - const rank: any[] = [] - for (const [memberId, stats] of memberStats.entries()) { - const info = memberInfo.get(memberId)! - rank.push({ - memberId, - platformId: info.platformId, - name: info.name, - totalStreaks: stats.totalStreaks, - maxCombo: stats.maxCombo, - lowStreak: stats.lowStreak, - midStreak: stats.midStreak, - highStreak: stats.highStreak, - }) - } - rank.sort((a, b) => b.totalStreaks - a.totalStreaks) - - let maxComboRecord: any = null - if (globalMaxCombo) { - const info = memberInfo.get(globalMaxCombo.memberId)! - maxComboRecord = { - memberId: globalMaxCombo.memberId, - platformId: info.platformId, - memberName: info.name, - comboLength: globalMaxCombo.comboLength, - startTs: globalMaxCombo.startTs, - } - } - - return { rank, maxComboRecord } -} - // ==================== 斗图分析 ==================== /** diff --git a/electron/main/worker/query/advanced/index.ts b/electron/main/worker/query/advanced/index.ts index f343dce..3b323e1 100644 --- a/electron/main/worker/query/advanced/index.ts +++ b/electron/main/worker/query/advanced/index.ts @@ -9,8 +9,8 @@ export { getRepeatAnalysis, getCatchphraseAnalysis } from './repeat' // 活跃度分析:夜猫、龙王、潜水、打卡 export { getNightOwlAnalysis, getDragonKingAnalysis, getDivingAnalysis, getCheckInAnalysis } from './activity' -// 行为分析:自言自语、斗图 -export { getMonologueAnalysis, getMemeBattleAnalysis } from './behavior' +// 行为分析:斗图 +export { getMemeBattleAnalysis } from './behavior' // 社交分析:@ 互动、含笑量 export { getMentionAnalysis, getMentionGraph, getLaughAnalysis } from './social' diff --git a/electron/main/worker/query/index.ts b/electron/main/worker/query/index.ts index a934035..812d018 100644 --- a/electron/main/worker/query/index.ts +++ b/electron/main/worker/query/index.ts @@ -36,7 +36,6 @@ export { getDragonKingAnalysis, getDivingAnalysis, getCheckInAnalysis, - getMonologueAnalysis, getMemeBattleAnalysis, getMentionAnalysis, getMentionGraph, diff --git a/electron/main/worker/workerManager.ts b/electron/main/worker/workerManager.ts index 2884823..4f4e154 100644 --- a/electron/main/worker/workerManager.ts +++ b/electron/main/worker/workerManager.ts @@ -294,10 +294,6 @@ export async function getDivingAnalysis(sessionId: string, filter?: any): Promis return sendToWorker('getDivingAnalysis', { sessionId, filter }) } -export async function getMonologueAnalysis(sessionId: string, filter?: any): Promise { - return sendToWorker('getMonologueAnalysis', { sessionId, filter }) -} - export async function getMentionAnalysis(sessionId: string, filter?: any): Promise { return sendToWorker('getMentionAnalysis', { sessionId, filter }) } diff --git a/electron/preload/apis/chat.ts b/electron/preload/apis/chat.ts index 27376b5..92025b6 100644 --- a/electron/preload/apis/chat.ts +++ b/electron/preload/apis/chat.ts @@ -15,7 +15,6 @@ import type { NightOwlAnalysis, DragonKingAnalysis, DivingAnalysis, - MonologueAnalysis, MentionAnalysis, LaughAnalysis, CheckInAnalysis, @@ -257,16 +256,6 @@ export const chatApi = { return ipcRenderer.invoke('chat:getDivingAnalysis', sessionId, filter) }, - /** - * 获取自言自语分析数据 - */ - getMonologueAnalysis: ( - sessionId: string, - filter?: { startTs?: number; endTs?: number } - ): Promise => { - return ipcRenderer.invoke('chat:getMonologueAnalysis', sessionId, filter) - }, - /** * 获取 @ 互动分析数据 */ diff --git a/electron/preload/index.d.ts b/electron/preload/index.d.ts index ecfa055..b9e35f0 100644 --- a/electron/preload/index.d.ts +++ b/electron/preload/index.d.ts @@ -12,7 +12,6 @@ import type { NightOwlAnalysis, DragonKingAnalysis, DivingAnalysis, - MonologueAnalysis, MentionAnalysis, LaughAnalysis, MemeBattleAnalysis, @@ -130,7 +129,6 @@ interface ChatApi { getNightOwlAnalysis: (sessionId: string, filter?: TimeFilter) => Promise getDragonKingAnalysis: (sessionId: string, filter?: TimeFilter) => Promise getDivingAnalysis: (sessionId: string, filter?: TimeFilter) => Promise - getMonologueAnalysis: (sessionId: string, filter?: TimeFilter) => Promise getMentionAnalysis: (sessionId: string, filter?: TimeFilter) => Promise getMentionGraph: (sessionId: string, filter?: TimeFilter) => Promise getLaughAnalysis: (sessionId: string, filter?: TimeFilter, keywords?: string[]) => Promise diff --git a/src/components/UI/PageAnchorsNav.vue b/src/components/UI/PageAnchorsNav.vue index 0e84dd9..d3b7040 100644 --- a/src/components/UI/PageAnchorsNav.vue +++ b/src/components/UI/PageAnchorsNav.vue @@ -30,22 +30,26 @@ function handleClick(id: string) { diff --git a/src/components/UI/ScrollableChart.vue b/src/components/UI/ScrollableChart.vue new file mode 100644 index 0000000..71f4951 --- /dev/null +++ b/src/components/UI/ScrollableChart.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/UI/SectionCard.vue b/src/components/UI/SectionCard.vue index 49ebadf..debc312 100644 --- a/src/components/UI/SectionCard.vue +++ b/src/components/UI/SectionCard.vue @@ -3,17 +3,36 @@ * 带标题的卡片容器组件 * 统一的分析模块卡片样式 */ -defineProps<{ - /** 卡片标题 */ - title: string - /** 可选的描述文字 */ - description?: string - /** 是否显示边框分隔线(默认 true) */ - showDivider?: boolean -}>() +import { computed } from 'vue' -// 设置默认值 -const showDivider = defineModel('showDivider', { default: true }) +const props = withDefaults( + defineProps<{ + /** 卡片标题 */ + title: string + /** 可选的描述文字 */ + description?: string + /** 是否显示边框分隔线(默认 true) */ + showDivider?: boolean + /** 是否启用内容滚动 */ + scrollable?: boolean + /** 最大高度(vh 单位),默认 60vh */ + maxHeightVh?: number + }>(), + { + showDivider: true, + scrollable: false, + maxHeightVh: 60, + } +) + +// 内容区域样式 +const contentStyle = computed(() => { + if (!props.scrollable) return undefined + return { + maxHeight: `${props.maxHeightVh}vh`, + overflowY: 'auto' as const, + } +}) diff --git a/src/components/UI/TopNSelect.vue b/src/components/UI/TopNSelect.vue new file mode 100644 index 0000000..8c06e85 --- /dev/null +++ b/src/components/UI/TopNSelect.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/components/UI/index.ts b/src/components/UI/index.ts index 608a3e4..ec8a8bf 100644 --- a/src/components/UI/index.ts +++ b/src/components/UI/index.ts @@ -11,3 +11,5 @@ export { default as SubTabs } from './SubTabs.vue' export { default as PageAnchorsNav } from './PageAnchorsNav.vue' export { default as FileDropZone } from './FileDropZone.vue' export { default as DatePicker } from './DatePicker.vue' +export { default as TopNSelect } from './TopNSelect.vue' +export { default as ScrollableChart } from './ScrollableChart.vue' diff --git a/src/components/charts/EChart.vue b/src/components/charts/EChart.vue index 701c0a1..1b3a0a3 100644 --- a/src/components/charts/EChart.vue +++ b/src/components/charts/EChart.vue @@ -93,6 +93,17 @@ function handleResize() { // 监听 option 变化 watch(() => props.option, updateChart, { deep: true }) +// 监听高度变化 +watch( + () => props.height, + () => { + // 使用 nextTick 确保 DOM 更新后再调整大小 + setTimeout(() => { + chartInstance?.resize() + }, 0) + } +) + // 监听主题变化 watch(isDark, () => { initChart() diff --git a/src/components/charts/EChartRank.vue b/src/components/charts/EChartRank.vue new file mode 100644 index 0000000..7a16be0 --- /dev/null +++ b/src/components/charts/EChartRank.vue @@ -0,0 +1,190 @@ + + + diff --git a/src/components/charts/index.ts b/src/components/charts/index.ts index ce48b60..433fb4e 100644 --- a/src/components/charts/index.ts +++ b/src/components/charts/index.ts @@ -9,6 +9,7 @@ export { default as EChartHeatmap } from './EChartHeatmap.vue' export { default as EChartCalendar } from './EChartCalendar.vue' export { default as EChartGraph } from './EChartGraph.vue' export { default as EChartWordcloud } from './EChartWordcloud.vue' +export { default as EChartRank } from './EChartRank.vue' // 其他组件 export { default as RankList } from './RankList.vue' diff --git a/src/pages/group-chat/components/RankingTab.vue b/src/pages/group-chat/components/RankingTab.vue index 0575b27..a487163 100644 --- a/src/pages/group-chat/components/RankingTab.vue +++ b/src/pages/group-chat/components/RankingTab.vue @@ -1,14 +1,11 @@ diff --git a/src/pages/group-chat/components/ViewTab.vue b/src/pages/group-chat/components/ViewTab.vue index dd8b6f7..94961f0 100644 --- a/src/pages/group-chat/components/ViewTab.vue +++ b/src/pages/group-chat/components/ViewTab.vue @@ -54,7 +54,7 @@ const selectedMemberId = ref(null) -
+
+import { computed, ref, watch } from 'vue' +import type { DragonKingAnalysis, CheckInAnalysis, MemberActivity } from '@/types/analysis' +import { EChartRank } from '@/components/charts' +import type { RankItem } from '@/components/charts' +import { SectionCard, LoadingState, Tabs, TopNSelect } from '@/components/UI' + +interface TimeFilter { + startTs?: number + endTs?: number +} + +const props = withDefaults( + defineProps<{ + sessionId: string + memberActivity: MemberActivity[] + timeFilter?: TimeFilter + /** 是否显示 TopN 选择器 */ + showTopNSelect?: boolean + /** 全局 TopN 控制(变化时强制同步) */ + globalTopN?: number + }>(), + { + showTopNSelect: true, + } +) + +const dragonKingAnalysis = ref(null) +const checkInAnalysis = ref(null) +const isLoading = ref(false) +const activeTab = ref<'activity' | 'dragon' | 'loyalty'>('activity') // 默认发言数量 +const topN = ref(props.globalTopN ?? 10) + +// 监听全局 TopN 变化,强制同步 +watch( + () => props.globalTopN, + (newVal) => { + if (newVal !== undefined) { + topN.value = newVal + } + } +) + +async function loadData() { + if (!props.sessionId) return + isLoading.value = true + try { + const [dragonKing, checkIn] = await Promise.all([ + window.chatApi.getDragonKingAnalysis(props.sessionId, props.timeFilter), + window.chatApi.getCheckInAnalysis(props.sessionId, props.timeFilter), + ]) + dragonKingAnalysis.value = dragonKing + checkInAnalysis.value = checkIn + } catch (error) { + console.error('加载活跃数据失败:', error) + } finally { + isLoading.value = false + } +} + +// 发言数量数据 +const memberRankData = computed(() => { + return props.memberActivity.map((m) => ({ + id: m.memberId.toString(), + name: m.name, + value: m.messageCount, + percentage: m.percentage, + })) +}) + +// 龙王榜数据 +const dragonKingRankData = computed(() => { + if (!dragonKingAnalysis.value) return [] + return dragonKingAnalysis.value.rank.map((m) => ({ + id: m.memberId.toString(), + name: m.name, + value: m.count, + percentage: m.percentage, + })) +}) + +// 忠臣榜(累计发言)数据 +const loyaltyRankData = computed(() => { + if (!checkInAnalysis.value) return [] + return checkInAnalysis.value.loyaltyRank.map((m) => ({ + id: m.memberId.toString(), + name: m.name, + value: m.totalDays, + percentage: m.percentage, + })) +}) + +const currentRankData = computed(() => { + switch (activeTab.value) { + case 'activity': + return memberRankData.value + case 'dragon': + return dragonKingRankData.value + case 'loyalty': + return loyaltyRankData.value + default: + return memberRankData.value + } +}) + +const rankUnit = computed(() => { + switch (activeTab.value) { + case 'activity': + return '条' + case 'dragon': + case 'loyalty': + return '天' + default: + return '条' + } +}) + +const cardTitle = computed(() => { + switch (activeTab.value) { + case 'activity': + return '🏆 活跃榜 - 发言数量' + case 'dragon': + return '🏆 活跃榜 - 龙王' + case 'loyalty': + return '🏆 活跃榜 - 累计发言' + default: + return '🏆 活跃榜' + } +}) + +const cardDescription = computed(() => { + switch (activeTab.value) { + case 'activity': + return '按消息发送数量排名' + case 'dragon': { + const totalDays = dragonKingAnalysis.value?.totalDays ?? 0 + return `每天发言最多的人+1(共 ${totalDays} 天)` + } + case 'loyalty': { + const totalDays = checkInAnalysis.value?.totalDays ?? 0 + return `累计发言天数排名(共 ${totalDays} 天)` + } + default: + return '' + } +}) + +watch( + () => [props.sessionId, props.timeFilter], + () => loadData(), + { immediate: true, deep: true } +) + + + diff --git a/src/pages/group-chat/components/ranking/CheckInRank.vue b/src/pages/group-chat/components/ranking/CheckInRank.vue index cd6f0c9..4bc8889 100644 --- a/src/pages/group-chat/components/ranking/CheckInRank.vue +++ b/src/pages/group-chat/components/ranking/CheckInRank.vue @@ -1,23 +1,54 @@ diff --git a/src/pages/group-chat/components/ranking/DivingRank.vue b/src/pages/group-chat/components/ranking/DivingRank.vue index e13ac7b..3f378b6 100644 --- a/src/pages/group-chat/components/ranking/DivingRank.vue +++ b/src/pages/group-chat/components/ranking/DivingRank.vue @@ -1,9 +1,8 @@ - - diff --git a/src/pages/group-chat/components/ranking/MemeBattleRank.vue b/src/pages/group-chat/components/ranking/MemeBattleRank.vue index 7363c16..6630011 100644 --- a/src/pages/group-chat/components/ranking/MemeBattleRank.vue +++ b/src/pages/group-chat/components/ranking/MemeBattleRank.vue @@ -1,24 +1,44 @@