mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-17 03:50:42 +08:00
feat: 子Tab支持路由状态缓存
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, watch, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
/**
|
||||
* 轻量级子标签页组件
|
||||
* 适用于页面内部的二级导航,使用原生样式
|
||||
* 支持通过 persistKey 将选中状态同步到 URL 查询参数
|
||||
*/
|
||||
interface TabItem {
|
||||
id: string
|
||||
@@ -14,6 +16,8 @@ interface TabItem {
|
||||
interface Props {
|
||||
modelValue: string
|
||||
items: TabItem[]
|
||||
/** 持久化 key,设置后会将当前 tab 状态同步到 URL 查询参数 */
|
||||
persistKey?: string
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -24,6 +28,9 @@ interface Emits {
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 计算内部值
|
||||
const activeTab = computed({
|
||||
get: () => props.modelValue,
|
||||
@@ -37,6 +44,33 @@ const activeTab = computed({
|
||||
const handleTabClick = (tabId: string) => {
|
||||
activeTab.value = tabId
|
||||
}
|
||||
|
||||
// 从 URL 查询参数恢复 tab 状态
|
||||
onMounted(() => {
|
||||
if (props.persistKey) {
|
||||
const savedTab = route.query[props.persistKey] as string
|
||||
// 验证 savedTab 是否在 items 中存在
|
||||
if (savedTab && props.items.some((item) => item.id === savedTab)) {
|
||||
activeTab.value = savedTab
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 监听 tab 变化,同步到 URL 查询参数
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (props.persistKey && newValue) {
|
||||
// 使用 replace 而不是 push,避免产生大量历史记录
|
||||
router.replace({
|
||||
query: {
|
||||
...route.query,
|
||||
[props.persistKey]: newValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { SubTabs } from '@/components/UI'
|
||||
import ChatExplorer from './ai/ChatExplorer.vue'
|
||||
import SQLLabTab from './SQLLabTab.vue'
|
||||
@@ -12,8 +13,16 @@ const props = defineProps<{
|
||||
chatType?: 'group' | 'private'
|
||||
}>()
|
||||
|
||||
// 子 Tab 配置
|
||||
const subTabs = [
|
||||
const route = useRoute()
|
||||
|
||||
// 判断是否为群聊(通过路由名称判断)
|
||||
const isGroupChat = computed(() => route.name === 'group-chat')
|
||||
|
||||
// 仅群聊显示的功能 ID
|
||||
const groupOnlyTabs = ['mbti', 'cyber-friend', 'campus']
|
||||
|
||||
// 所有子 Tab 配置
|
||||
const allSubTabs = [
|
||||
{ id: 'chat-explorer', label: '对话式探索', icon: 'i-heroicons-chat-bubble-left-ellipsis' },
|
||||
{ id: 'sql-lab', label: 'SQL实验室', icon: 'i-heroicons-command-line' },
|
||||
{
|
||||
@@ -42,6 +51,16 @@ const subTabs = [
|
||||
},
|
||||
]
|
||||
|
||||
// 根据聊天类型过滤显示的子 Tab
|
||||
const subTabs = computed(() => {
|
||||
if (isGroupChat.value) {
|
||||
// 群聊显示所有 Tab
|
||||
return allSubTabs
|
||||
}
|
||||
// 私聊过滤掉群聊专属功能
|
||||
return allSubTabs.filter((tab) => !groupOnlyTabs.includes(tab.id))
|
||||
})
|
||||
|
||||
const activeSubTab = ref('chat-explorer')
|
||||
|
||||
// ChatExplorer 组件引用
|
||||
@@ -61,7 +80,7 @@ defineExpose({
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<!-- 子 Tab 导航 -->
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" />
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" persist-key="aiTab" />
|
||||
|
||||
<!-- 子 Tab 内容 -->
|
||||
<div class="flex-1 min-h-0 overflow-hidden">
|
||||
|
||||
@@ -196,23 +196,24 @@ function getSessionAvatarText(session: AnalysisSession): string {
|
||||
</div>
|
||||
|
||||
<!-- Session List -->
|
||||
<div class="flex-1 relative min-h-0">
|
||||
<div class="h-full overflow-y-auto px-3">
|
||||
<div class="flex-1 relative min-h-0 px-4 flex flex-col">
|
||||
<!-- 聊天记录标题 - 固定在顶部,不随列表滚动 -->
|
||||
<UTooltip
|
||||
v-if="!isCollapsed && sessions.length > 0"
|
||||
text="右键可删除或重命名聊天记录"
|
||||
:popper="{ placement: 'right' }"
|
||||
>
|
||||
<div class="px-3 mb-2 flex items-center gap-1">
|
||||
<div class="text-sm font-medium text-gray-500">聊天记录</div>
|
||||
<UIcon name="i-heroicons-question-mark-circle" class="size-3.5 text-gray-400" />
|
||||
</div>
|
||||
</UTooltip>
|
||||
|
||||
<!-- 聊天记录列表 - 可滚动区域 -->
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div v-if="sessions.length === 0 && !isCollapsed" class="py-8 text-center text-sm text-gray-500">暂无记录</div>
|
||||
|
||||
<div class="space-y-1 pb-8">
|
||||
<!-- Session List Header - Sticky -->
|
||||
<UTooltip
|
||||
v-if="!isCollapsed && sessions.length > 0"
|
||||
text="右键可删除或重命名聊天记录"
|
||||
:popper="{ placement: 'right' }"
|
||||
>
|
||||
<div class="sticky top-0 bg-gray-50 dark:bg-gray-900 mb-4 px-2 flex items-center gap-1 z-1">
|
||||
<div class="text-sm font-medium text-gray-500">聊天记录</div>
|
||||
<UIcon name="i-heroicons-question-mark-circle" class="size-3.5 text-gray-400" />
|
||||
</div>
|
||||
</UTooltip>
|
||||
|
||||
<UTooltip
|
||||
v-for="session in sessions"
|
||||
:key="session.id"
|
||||
|
||||
@@ -26,7 +26,7 @@ const activeSubTab = ref('catchphrase')
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<!-- 子 Tab 导航 -->
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" />
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" persist-key="quotesTab" />
|
||||
|
||||
<!-- 子 Tab 内容 -->
|
||||
<div class="flex-1 min-h-0 overflow-auto">
|
||||
@@ -65,4 +65,3 @@ const activeSubTab = ref('catchphrase')
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -246,11 +246,6 @@ function getProgressDetail(): string {
|
||||
</template>
|
||||
</FileDropZone>
|
||||
|
||||
<!-- Supported Formats Text -->
|
||||
<p class="text-sm text-gray-400 dark:text-gray-500">
|
||||
支持 QQ、微信、Discord、Snapchat、Reddit、TikTok 等聊天记录
|
||||
</p>
|
||||
|
||||
<!-- Error Message -->
|
||||
<div
|
||||
v-if="importError"
|
||||
|
||||
@@ -25,7 +25,7 @@ const activeSubTab = ref('catchphrase')
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<!-- 子 Tab 导航 -->
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" />
|
||||
<SubTabs v-model="activeSubTab" :items="subTabs" persist-key="quotesTab" />
|
||||
|
||||
<!-- 子 Tab 内容 -->
|
||||
<div class="flex-1 min-h-0 overflow-auto">
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ const activeTab = ref('merge')
|
||||
<PageHeader title="实用工具" description="提供聊天记录处理的实用工具" icon="i-heroicons-wrench-screwdriver" />
|
||||
|
||||
<!-- Tabs -->
|
||||
<SubTabs v-model="activeTab" :items="tabs" />
|
||||
<SubTabs v-model="activeTab" :items="tabs" persist-key="toolTab" />
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="flex-1 overflow-auto p-6">
|
||||
|
||||
Reference in New Issue
Block a user