mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-24 15:40:19 +08:00
feat: 迁移成员Tab内的模块到其他Tab
This commit is contained in:
@@ -1,70 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { SubTabs } from '@/components/UI'
|
||||
import NicknameHistory from './member/NicknameHistory.vue'
|
||||
import Relationships from './member/Relationships.vue'
|
||||
import ClusterView from '@openchatlab/chart-cluster/ClusterView.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface TimeFilter {
|
||||
startTs?: number
|
||||
endTs?: number
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
timeFilter?: TimeFilter
|
||||
}>()
|
||||
|
||||
// 子 Tab 配置
|
||||
const subTabs = computed(() => [
|
||||
{ id: 'relationships', label: t('analysis.subTabs.member.relationships'), icon: 'i-heroicons-heart' },
|
||||
{ id: 'cluster', label: t('analysis.subTabs.member.cluster'), icon: 'i-heroicons-user-group' },
|
||||
{ id: 'history', label: t('analysis.subTabs.member.nicknameHistory'), icon: 'i-heroicons-clock' },
|
||||
])
|
||||
|
||||
const activeSubTab = ref('relationships')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<!-- 子 Tab 导航 -->
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" persist-key="memberTab" />
|
||||
|
||||
<!-- 子 Tab 内容 -->
|
||||
<div class="flex-1 min-h-0 overflow-auto">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<!-- 群关系 -->
|
||||
<Relationships
|
||||
v-if="activeSubTab === 'relationships'"
|
||||
:session-id="props.sessionId"
|
||||
:time-filter="props.timeFilter"
|
||||
/>
|
||||
|
||||
<!-- 小团体 -->
|
||||
<ClusterView
|
||||
v-else-if="activeSubTab === 'cluster'"
|
||||
:session-id="props.sessionId"
|
||||
:time-filter="props.timeFilter"
|
||||
/>
|
||||
|
||||
<!-- 昵称变更记录 -->
|
||||
<NicknameHistory v-else-if="activeSubTab === 'history'" :session-id="props.sessionId" />
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,8 @@ import UserSelect from '@/components/common/UserSelect.vue'
|
||||
import MessageView from '@openchatlab/chart-message/MessageView.vue'
|
||||
import InteractionView from '@openchatlab/chart-interaction/InteractionView.vue'
|
||||
import RankingView from '@openchatlab/chart-ranking/RankingView.vue'
|
||||
import Relationships from './view/Relationships.vue'
|
||||
import ClusterView from '@openchatlab/chart-cluster/ClusterView.vue'
|
||||
import { isFeatureSupported, type LocaleType } from '@/i18n'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
@@ -25,10 +27,12 @@ const subTabs = computed(() => {
|
||||
const tabs = [
|
||||
{ id: 'message', label: t('analysis.subTabs.view.message'), icon: 'i-heroicons-chat-bubble-left-right' },
|
||||
{ id: 'interaction', label: t('analysis.subTabs.view.interaction'), icon: 'i-heroicons-arrows-right-left' },
|
||||
{ id: 'relationships', label: t('analysis.subTabs.member.relationships'), icon: 'i-heroicons-heart' },
|
||||
{ id: 'cluster', label: t('analysis.subTabs.member.cluster'), icon: 'i-heroicons-user-group' },
|
||||
]
|
||||
// 榜单仅在中文下显示
|
||||
if (isFeatureSupported('groupRanking', locale.value as LocaleType)) {
|
||||
tabs.push({ id: 'ranking', label: t('analysis.subTabs.view.ranking'), icon: 'i-heroicons-trophy' })
|
||||
tabs.splice(1, 0, { id: 'ranking', label: t('analysis.subTabs.view.ranking'), icon: 'i-heroicons-trophy' })
|
||||
}
|
||||
return tabs
|
||||
})
|
||||
@@ -63,6 +67,12 @@ const viewTimeFilter = computed(() => ({
|
||||
:session-id="props.sessionId"
|
||||
:time-filter="viewTimeFilter"
|
||||
/>
|
||||
<Relationships
|
||||
v-else-if="activeSubTab === 'relationships'"
|
||||
:session-id="props.sessionId"
|
||||
:time-filter="viewTimeFilter"
|
||||
/>
|
||||
<ClusterView v-else-if="activeSubTab === 'cluster'" :session-id="props.sessionId" :time-filter="viewTimeFilter" />
|
||||
<RankingView
|
||||
v-else-if="activeSubTab === 'ranking'"
|
||||
:session-id="props.sessionId"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import NicknameHistory from './NicknameHistory.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
sessionId: string
|
||||
}>()
|
||||
|
||||
const isOpen = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UButton variant="ghost" icon="i-heroicons-clock" size="sm" @click="isOpen = true">
|
||||
{{ t('analysis.subTabs.member.nicknameHistory') }}
|
||||
</UButton>
|
||||
|
||||
<UModal v-if="props.sessionId" v-model:open="isOpen" :ui="{ content: 'max-w-6xl h-[85vh]' }">
|
||||
<template #content>
|
||||
<div class="flex h-full flex-col overflow-hidden bg-white dark:bg-gray-900">
|
||||
<div class="flex flex-none items-center justify-between border-b border-gray-200 px-5 py-3 dark:border-gray-700">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ t('analysis.subTabs.member.nicknameHistory') }}
|
||||
</h2>
|
||||
<UButton variant="ghost" icon="i-heroicons-x-mark" size="sm" @click="isOpen = false" />
|
||||
</div>
|
||||
<div class="flex-1 overflow-auto">
|
||||
<NicknameHistory :session-id="props.sessionId" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</template>
|
||||
@@ -12,8 +12,8 @@ import { ChatExplorer } from '@/components/AIChat'
|
||||
import OverviewTab from './components/OverviewTab.vue'
|
||||
import ViewTab from './components/ViewTab.vue'
|
||||
import QuotesTab from './components/QuotesTab.vue'
|
||||
import MemberTab from './components/MemberTab.vue'
|
||||
import MemberList from '@/components/common/member/MemberList.vue'
|
||||
import NicknameHistoryEntry from './components/member/NicknameHistoryEntry.vue'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
import SessionIndexModal from '@/components/analysis/SessionIndexModal.vue'
|
||||
import IncrementalImportModal from '@/components/analysis/IncrementalImportModal.vue'
|
||||
@@ -64,7 +64,6 @@ const allTabs = [
|
||||
{ id: 'overview', labelKey: 'analysis.tabs.overview', icon: 'i-heroicons-chart-pie' },
|
||||
{ id: 'view', labelKey: 'analysis.tabs.view', icon: 'i-heroicons-presentation-chart-bar' },
|
||||
{ id: 'quotes', labelKey: 'analysis.tabs.groupQuotes', icon: 'i-heroicons-chat-bubble-bottom-center-text' },
|
||||
{ id: 'members', labelKey: 'analysis.tabs.members', icon: 'i-heroicons-user-group' },
|
||||
{ id: 'ai-chat', labelKey: 'analysis.tabs.aiChat', icon: 'i-heroicons-chat-bubble-left-ellipsis' },
|
||||
{ id: 'lab', labelKey: 'analysis.tabs.lab', icon: 'i-heroicons-beaker' },
|
||||
]
|
||||
@@ -247,11 +246,11 @@ onMounted(() => {
|
||||
<span class="whitespace-nowrap">{{ t(tab.labelKey) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- AI 对话、实验室和成员页都不使用这里的时间范围筛选,因此在这些一级 Tab 下隐藏。 -->
|
||||
<!-- AI 对话和实验室都不使用这里的时间范围筛选,因此在这些一级 Tab 下隐藏。 -->
|
||||
<TimeSelect
|
||||
v-model="timeRangeValue"
|
||||
:session-id="currentSessionId ?? undefined"
|
||||
:visible="activeTab !== 'ai-chat' && activeTab !== 'lab' && activeTab !== 'members'"
|
||||
:visible="activeTab !== 'ai-chat' && activeTab !== 'lab'"
|
||||
:initial-state="initialTimeState"
|
||||
@update:full-range="fullTimeRange = $event"
|
||||
@update:available-years="availableYears = $event"
|
||||
@@ -298,12 +297,6 @@ onMounted(() => {
|
||||
:session-id="currentSessionId!"
|
||||
:time-filter="timeFilter"
|
||||
/>
|
||||
<MemberTab
|
||||
v-else-if="activeTab === 'members'"
|
||||
:key="'members-' + currentSessionId"
|
||||
:session-id="currentSessionId!"
|
||||
:time-filter="timeFilter"
|
||||
/>
|
||||
<ChatExplorer
|
||||
v-else-if="activeTab === 'ai-chat'"
|
||||
:key="'ai-chat-' + currentSessionId"
|
||||
@@ -359,7 +352,10 @@ onMounted(() => {
|
||||
{{ t('members.list.description', { count: session?.memberCount ?? 0 }) }}
|
||||
</p>
|
||||
</div>
|
||||
<UButton variant="ghost" icon="i-heroicons-x-mark" size="sm" @click="showMemberManagementModal = false" />
|
||||
<div class="flex items-center gap-2">
|
||||
<NicknameHistoryEntry :session-id="currentSessionId" />
|
||||
<UButton variant="ghost" icon="i-heroicons-x-mark" size="sm" @click="showMemberManagementModal = false" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<MemberList :session-id="currentSessionId" :show-header="false" @data-changed="loadData" />
|
||||
|
||||
Reference in New Issue
Block a user