mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-25 16:10:18 +08:00
feat: 样式优化
This commit is contained in:
@@ -13,8 +13,9 @@ defineProps<{
|
||||
// 子 Tab 配置
|
||||
const subTabs = [
|
||||
{ id: 'chat-explorer', label: '对话式探索', icon: 'i-heroicons-chat-bubble-left-ellipsis' },
|
||||
{ id: 'lab', label: '实验室', icon: 'i-heroicons-beaker' },
|
||||
{ id: 'manual', label: '手动分析', icon: 'i-heroicons-adjustments-horizontal' },
|
||||
{ id: 'manual', label: '筛选分析', icon: 'i-heroicons-adjustments-horizontal' },
|
||||
{ id: 'sql', label: 'AI SQL', icon: 'i-heroicons-beaker' },
|
||||
{ id: 'mbti', label: 'MBTI检测仪', icon: 'i-heroicons-heart' },
|
||||
]
|
||||
|
||||
const activeSubTab = ref('chat-explorer')
|
||||
@@ -50,14 +51,38 @@ const activeSubTab = ref('chat-explorer')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手动分析 - 暂未实现 -->
|
||||
<!-- 筛选分析 - 暂未实现 -->
|
||||
<div v-else-if="activeSubTab === 'manual'" class="flex h-full items-center justify-center p-6">
|
||||
<div
|
||||
class="flex h-full w-full items-center justify-center rounded-xl border-2 border-dashed border-gray-300 bg-gray-50 dark:border-gray-700 dark:bg-gray-900/50"
|
||||
>
|
||||
<div class="text-center">
|
||||
<UIcon name="i-heroicons-adjustments-horizontal" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<p class="mt-3 text-sm font-medium text-gray-600 dark:text-gray-400">手动分析功能开发中</p>
|
||||
<p class="mt-3 text-sm font-medium text-gray-600 dark:text-gray-400">筛选分析功能开发中</p>
|
||||
<p class="mt-1 text-xs text-gray-400">敬请期待...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- AI SQL -->
|
||||
<div v-else-if="activeSubTab === 'sql'" class="flex h-full items-center justify-center p-6">
|
||||
<div
|
||||
class="flex h-full w-full items-center justify-center rounded-xl border-2 border-dashed border-gray-300 bg-gray-50 dark:border-gray-700 dark:bg-gray-900/50"
|
||||
>
|
||||
<div class="text-center">
|
||||
<UIcon name="i-heroicons-beaker" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<p class="mt-3 text-sm font-medium text-gray-600 dark:text-gray-400">开发中</p>
|
||||
<p class="mt-1 text-xs text-gray-400">敬请期待...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MBTI检测仪 -->
|
||||
<div v-else-if="activeSubTab === 'mbti'" class="flex h-full items-center justify-center p-6">
|
||||
<div
|
||||
class="flex h-full w-full items-center justify-center rounded-xl border-2 border-dashed border-gray-300 bg-gray-50 dark:border-gray-700 dark:bg-gray-900/50"
|
||||
>
|
||||
<div class="text-center">
|
||||
<UIcon name="i-heroicons-beaker" class="mx-auto h-12 w-12 text-gray-400" />
|
||||
<p class="mt-3 text-sm font-medium text-gray-600 dark:text-gray-400">开发中</p>
|
||||
<p class="mt-1 text-xs text-gray-400">敬请期待...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -71,9 +71,9 @@ function generateWelcomeMessage() {
|
||||
return `👋 你好!我是 AI 助手,可以帮你探索「${props.sessionName}」的聊天记录。
|
||||
|
||||
你可以这样问我:
|
||||
- "帮我找一下群里讨论买房的记录"
|
||||
- "大家最近聊了什么有趣的话题"
|
||||
- "谁是群里最活跃的人"
|
||||
- 大家最近聊了什么有趣的话题
|
||||
- 谁是群里最活跃的人
|
||||
- 帮我找一下群里讨论买房的记录
|
||||
|
||||
${configHint}`
|
||||
}
|
||||
@@ -152,9 +152,9 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="relative flex h-full overflow-hidden px-4">
|
||||
<div class="flex h-full overflow-hidden">
|
||||
<!-- 左侧:对话记录列表 -->
|
||||
<div class="absolute left-0 top-0 h-full w-64 p-4">
|
||||
<div class="w-64 shrink-0 border-r border-gray-200 bg-gray-50/50 dark:border-gray-800 dark:bg-gray-900/50">
|
||||
<ConversationList
|
||||
ref="conversationListRef"
|
||||
:session-id="sessionId"
|
||||
@@ -167,45 +167,11 @@ watch(
|
||||
</div>
|
||||
|
||||
<!-- 中间:对话区域 -->
|
||||
<div class="flex h-full flex-1 justify-center pl-64 pr-80">
|
||||
<div
|
||||
class="flex min-w-0 flex-1 max-w-3xl flex-col overflow-hidden rounded-xl bg-white shadow-sm dark:bg-gray-900"
|
||||
>
|
||||
<!-- 对话区域头部 -->
|
||||
<div class="shrink-0 flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-sparkles" class="h-5 w-5 text-violet-500" />
|
||||
<span class="font-medium text-gray-900 dark:text-white">AI 对话探索</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- 配置状态指示 -->
|
||||
<div
|
||||
v-if="!isCheckingConfig"
|
||||
class="flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs"
|
||||
:class="[
|
||||
hasLLMConfig
|
||||
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||
: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',
|
||||
]"
|
||||
>
|
||||
<span class="h-2 w-2 rounded-full" :class="[hasLLMConfig ? 'bg-green-500' : 'bg-amber-500']" />
|
||||
{{ hasLLMConfig ? '已配置' : '未配置' }}
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-heroicons-cog-6-tooth"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@click="showConfigModal = true"
|
||||
>
|
||||
配置
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex h-full flex-1">
|
||||
<div class="flex min-w-[480px] flex-1 flex-col overflow-hidden">
|
||||
<!-- 消息列表 -->
|
||||
<div ref="messagesContainer" class="min-h-0 flex-1 overflow-y-auto p-4">
|
||||
<div class="mx-auto space-y-4">
|
||||
<div class="mx-auto max-w-3xl space-y-4">
|
||||
<ChatMessage
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
@@ -238,18 +204,53 @@ watch(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<ChatInput
|
||||
:disabled="isAIThinking"
|
||||
:status="isAIThinking ? (isLoadingSource ? 'submitted' : 'streaming') : 'ready'"
|
||||
@send="handleSend"
|
||||
/>
|
||||
<!-- 输入框区域 -->
|
||||
<div class="p-4 pt-0">
|
||||
<div class="mx-auto max-w-3xl">
|
||||
<ChatInput
|
||||
:disabled="isAIThinking"
|
||||
:status="isAIThinking ? (isLoadingSource ? 'submitted' : 'streaming') : 'ready'"
|
||||
@send="handleSend"
|
||||
/>
|
||||
|
||||
<!-- 底部状态栏 -->
|
||||
<div class="mt-2 flex items-center justify-between px-1">
|
||||
<div class="flex items-center gap-2 text-xs text-gray-400">
|
||||
<UIcon name="i-heroicons-sparkles" class="h-3.5 w-3.5" />
|
||||
<span>AI 对话探索</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- 配置状态指示 -->
|
||||
<div
|
||||
v-if="!isCheckingConfig"
|
||||
class="flex items-center gap-1.5 text-xs transition-colors"
|
||||
:class="[hasLLMConfig ? 'text-gray-400' : 'text-amber-500 font-medium']"
|
||||
>
|
||||
<span class="h-1.5 w-1.5 rounded-full" :class="[hasLLMConfig ? 'bg-green-500' : 'bg-amber-500']" />
|
||||
{{ hasLLMConfig ? '服务已连接' : '未配置 AI 服务' }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="flex items-center gap-1 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
||||
@click="showConfigModal = true"
|
||||
>
|
||||
<UIcon name="i-heroicons-cog-6-tooth" class="h-3.5 w-3.5" />
|
||||
<span>配置</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:数据源面板(限制高度) -->
|
||||
<!-- 右侧:数据源面板 -->
|
||||
<Transition name="slide-fade">
|
||||
<div v-if="sourceMessages.length > 0 && !isSourcePanelCollapsed" class="absolute right-0 top-0 h-full w-80 p-4">
|
||||
<div
|
||||
v-if="sourceMessages.length > 0 && !isSourcePanelCollapsed"
|
||||
class="w-80 shrink-0 border-l border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-gray-900/50"
|
||||
>
|
||||
<DataSourcePanel
|
||||
:messages="sourceMessages"
|
||||
:keywords="currentKeywords"
|
||||
|
||||
@@ -113,38 +113,46 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full w-64 flex-col rounded-xl bg-white shadow-sm dark:bg-gray-900">
|
||||
<div class="flex h-full w-64 flex-col">
|
||||
<!-- 头部 -->
|
||||
<div class="flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-chat-bubble-left-right" class="h-5 w-5 text-gray-500" />
|
||||
<span class="font-medium text-gray-900 dark:text-white">对话记录</span>
|
||||
</div>
|
||||
<UButton icon="i-heroicons-plus" color="primary" variant="soft" size="xs" @click="emit('create')">新对话</UButton>
|
||||
<div class="flex items-center justify-between px-2 py-4">
|
||||
<span class="text-xs font-semibold tracking-wider text-gray-400 uppercase">History</span>
|
||||
<UButton
|
||||
icon="i-heroicons-plus"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
class="text-gray-500 hover:text-gray-900 dark:hover:text-white"
|
||||
@click="emit('create')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 对话列表 -->
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<div class="flex-1 overflow-y-auto px-2">
|
||||
<!-- 加载中 -->
|
||||
<div v-if="isLoading" class="flex items-center justify-center py-8">
|
||||
<UIcon name="i-heroicons-arrow-path" class="h-6 w-6 animate-spin text-gray-400" />
|
||||
<UIcon name="i-heroicons-arrow-path" class="h-5 w-5 animate-spin text-gray-400" />
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else-if="conversations.length === 0" class="flex flex-col items-center justify-center py-8 text-center">
|
||||
<UIcon name="i-heroicons-chat-bubble-left-ellipsis" class="h-10 w-10 text-gray-300 dark:text-gray-600" />
|
||||
<p class="mt-2 text-sm text-gray-500">暂无对话</p>
|
||||
<p class="text-xs text-gray-400">点击"新对话"开始</p>
|
||||
<div v-else-if="conversations.length === 0" class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div class="flex h-12 w-12 items-center justify-center rounded-full bg-gray-50 dark:bg-gray-800">
|
||||
<UIcon name="i-heroicons-chat-bubble-left" class="h-6 w-6 text-gray-300 dark:text-gray-600" />
|
||||
</div>
|
||||
<p class="mt-3 text-xs text-gray-400">暂无历史记录</p>
|
||||
<UButton class="mt-2" size="xs" variant="link" color="primary" @click="emit('create')">开始新对话</UButton>
|
||||
</div>
|
||||
|
||||
<!-- 对话列表 -->
|
||||
<div v-else class="p-2 space-y-1">
|
||||
<div v-else class="space-y-0.5">
|
||||
<div
|
||||
v-for="conv in conversations"
|
||||
:key="conv.id"
|
||||
class="group relative rounded-lg px-3 py-2.5 transition-colors cursor-pointer"
|
||||
class="group relative rounded-lg px-3 py-2.5 transition-all cursor-pointer"
|
||||
:class="[
|
||||
activeId === conv.id ? 'bg-violet-50 dark:bg-violet-900/20' : 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
|
||||
activeId === conv.id
|
||||
? 'bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-white'
|
||||
: 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-800/50',
|
||||
]"
|
||||
@click="emit('select', conv.id)"
|
||||
>
|
||||
@@ -152,8 +160,9 @@ defineExpose({
|
||||
<template v-if="editingId === conv.id">
|
||||
<input
|
||||
v-model="editingTitle"
|
||||
class="w-full rounded border border-gray-300 px-2 py-1 text-sm dark:border-gray-600 dark:bg-gray-800"
|
||||
class="w-full rounded border-none bg-white px-2 py-1 text-sm shadow-sm ring-1 ring-gray-200 focus:ring-2 focus:ring-primary-500 dark:bg-gray-900 dark:ring-gray-700"
|
||||
placeholder="输入标题..."
|
||||
autoFocus
|
||||
@blur="saveTitle(conv.id)"
|
||||
@keyup.enter="saveTitle(conv.id)"
|
||||
@click.stop
|
||||
@@ -164,22 +173,18 @@ defineExpose({
|
||||
<template v-else>
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p
|
||||
class="truncate text-sm font-medium"
|
||||
:class="[
|
||||
activeId === conv.id ? 'text-violet-700 dark:text-violet-400' : 'text-gray-900 dark:text-white',
|
||||
]"
|
||||
>
|
||||
<p class="truncate text-sm font-medium">
|
||||
{{ getTitle(conv) }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs text-gray-400">
|
||||
<p class="mt-1 text-[10px] text-gray-400 opacity-80">
|
||||
{{ formatTime(conv.updatedAt) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div
|
||||
class="flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100"
|
||||
class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100"
|
||||
:class="{ 'opacity-100': activeId === conv.id }"
|
||||
@click.stop
|
||||
>
|
||||
<UButton
|
||||
@@ -187,13 +192,15 @@ defineExpose({
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="2xs"
|
||||
class="text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
|
||||
@click="startEditing(conv)"
|
||||
/>
|
||||
<UButton
|
||||
icon="i-heroicons-trash"
|
||||
color="red"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="2xs"
|
||||
class="text-gray-400 hover:text-red-500 dark:hover:text-red-400"
|
||||
@click="handleDelete(conv.id)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ const tabs = [
|
||||
{ id: 'quotes', label: '群语录', icon: 'i-heroicons-chat-bubble-bottom-center-text' },
|
||||
{ id: 'relationships', label: '群关系', icon: 'i-heroicons-heart' },
|
||||
{ id: 'timeline', label: '群趋势', icon: 'i-heroicons-chart-bar' },
|
||||
{ id: 'ai', label: 'AI', icon: 'i-heroicons-sparkles' },
|
||||
{ id: 'ai', label: 'AI实验室', icon: 'i-heroicons-sparkles' },
|
||||
]
|
||||
|
||||
const activeTab = ref((route.query.tab as string) || 'overview')
|
||||
|
||||
Reference in New Issue
Block a user