mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-09 23:32:12 +08:00
feat: 抽象Header组件
This commit is contained in:
@@ -325,7 +325,7 @@ watch(
|
||||
</div>
|
||||
|
||||
<!-- 输入框区域 -->
|
||||
<div class="p-4 pt-0">
|
||||
<div class="px-4 pb-2">
|
||||
<div class="mx-auto max-w-3xl">
|
||||
<ChatInput
|
||||
:disabled="isAIThinking"
|
||||
@@ -335,7 +335,7 @@ watch(
|
||||
/>
|
||||
|
||||
<!-- 底部状态栏 -->
|
||||
<div class="mt-2 flex items-center justify-between px-1">
|
||||
<div class="flex items-center justify-between px-1">
|
||||
<!-- 左侧:预设选择器 -->
|
||||
<UPopover v-model:open="isPresetPopoverOpen" :ui="{ content: 'p-0' }">
|
||||
<button
|
||||
@@ -384,7 +384,7 @@ watch(
|
||||
: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 服务' }}
|
||||
{{ hasLLMConfig ? 'AI 已连接' : '请在全局设置中配置 AI 服务' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ function handleStop() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="shrink-0 border-t border-gray-200 py-4 dark:border-gray-800">
|
||||
<div class="shrink-0 border-t border-gray-200 pt-4 pb-2 dark:border-gray-800">
|
||||
<div class="w-full">
|
||||
<UChatPrompt
|
||||
v-model="inputValue"
|
||||
|
||||
@@ -176,7 +176,7 @@ function isPrivateChat(session: AnalysisSession): boolean {
|
||||
variant="ghost"
|
||||
@click="router.push({ name: 'tools' })"
|
||||
>
|
||||
<UIcon name="i-heroicons-squares-2x2" class="h-5 w-5 shrink-0" :class="[isCollapsed ? '' : 'mr-2']" />
|
||||
<UIcon name="i-heroicons-wrench-screwdriver" class="h-4 w-4 shrink-0" :class="[isCollapsed ? '' : 'mr-2']" />
|
||||
<span v-if="!isCollapsed" class="truncate">实用工具</span>
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
@@ -231,9 +231,8 @@ function isPrivateChat(session: AnalysisSession): boolean {
|
||||
isCollapsed ? '' : 'mr-3',
|
||||
]"
|
||||
>
|
||||
<!-- 私聊显示用户图标,群聊显示首字母 -->
|
||||
<UIcon v-if="isPrivateChat(session)" name="i-heroicons-user" class="h-4 w-4" />
|
||||
<template v-else>{{ session.name ? session.name.charAt(0) : '?' }}</template>
|
||||
<!-- 私聊和群聊都显示名字首字母 -->
|
||||
{{ session.name ? session.name.charAt(0) : '?' }}
|
||||
</div>
|
||||
|
||||
<!-- Session Info -->
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 页面 Header 通用组件
|
||||
* 包含标题、描述、可选图标,以及默认 slot 用于额外内容
|
||||
*/
|
||||
|
||||
defineProps<{
|
||||
title: string
|
||||
description?: string
|
||||
icon?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-800 dark:bg-gray-900">
|
||||
<!-- 标题区域 -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- 可选图标 -->
|
||||
<div
|
||||
v-if="icon"
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-pink-400 to-pink-600"
|
||||
>
|
||||
<UIcon :name="icon" class="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p v-if="description" class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 额外内容 slot(如 Tabs) -->
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -12,6 +12,7 @@ import RankingTab from './components/RankingTab.vue'
|
||||
import QuotesTab from './components/QuotesTab.vue'
|
||||
import RelationshipsTab from './components/RelationshipsTab.vue'
|
||||
import MemberTab from './components/MemberTab.vue'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -239,30 +240,14 @@ onMounted(() => {
|
||||
<!-- Content -->
|
||||
<template v-else-if="session">
|
||||
<!-- Header -->
|
||||
<div class="border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Session Info -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-pink-400 to-pink-600"
|
||||
>
|
||||
<UIcon name="i-heroicons-chat-bubble-left-right" class="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ session.name }}
|
||||
</h1>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ dateRangeText }},{{ selectedYear ? filteredMemberCount : session.memberCount }} 位成员共聊了
|
||||
{{ selectedYear ? filteredMessageCount : session.messageCount }} 条消息
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PageHeader
|
||||
:title="session.name"
|
||||
:description="`${dateRangeText},${selectedYear ? filteredMemberCount : session.memberCount} 位成员共聊了 ${selectedYear ? filteredMessageCount : session.messageCount} 条消息`"
|
||||
icon="i-heroicons-chat-bubble-left-right"
|
||||
>
|
||||
<!-- Tabs -->
|
||||
<div class="mt-4 flex items-center justify-between gap-4">
|
||||
<div class="flex flex-shrink-0 items-center gap-1 overflow-x-auto scrollbar-hide">
|
||||
<div class="flex shrink-0 items-center gap-1 overflow-x-auto scrollbar-hide">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@@ -284,10 +269,10 @@ onMounted(() => {
|
||||
v-model="selectedYear"
|
||||
:items="yearOptions"
|
||||
size="sm"
|
||||
class="min-w-0 flex-shrink"
|
||||
class="min-w-0 shrink"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="relative flex-1 overflow-y-auto">
|
||||
|
||||
@@ -10,6 +10,7 @@ import AITab from '@/components/analysis/AITab.vue'
|
||||
import OverviewTab from './components/OverviewTab.vue'
|
||||
import QuotesTab from './components/QuotesTab.vue'
|
||||
import MemberTab from './components/MemberTab.vue'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -217,29 +218,14 @@ onMounted(() => {
|
||||
<!-- Content -->
|
||||
<template v-else-if="session">
|
||||
<!-- Header -->
|
||||
<div class="border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-800 dark:bg-gray-900">
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Session Info -->
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-pink-400 to-pink-600"
|
||||
>
|
||||
<UIcon name="i-heroicons-user" class="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{{ session.name }}
|
||||
</h1>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ dateRangeText }},共 {{ selectedYear ? filteredMessageCount : session.messageCount }} 条消息
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PageHeader
|
||||
:title="session.name"
|
||||
:description="`${dateRangeText},共 ${selectedYear ? filteredMessageCount : session.messageCount} 条消息`"
|
||||
icon="i-heroicons-user"
|
||||
>
|
||||
<!-- Tabs -->
|
||||
<div class="mt-4 flex items-center justify-between gap-4">
|
||||
<div class="flex flex-shrink-0 items-center gap-1 overflow-x-auto scrollbar-hide">
|
||||
<div class="flex shrink-0 items-center gap-1 overflow-x-auto scrollbar-hide">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@@ -261,10 +247,10 @@ onMounted(() => {
|
||||
v-model="selectedYear"
|
||||
:items="yearOptions"
|
||||
size="sm"
|
||||
class="min-w-0 flex-shrink"
|
||||
class="min-w-0 shrink"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="relative flex-1 overflow-y-auto">
|
||||
|
||||
+2
-4
@@ -2,6 +2,7 @@
|
||||
import { ref } from 'vue'
|
||||
import { SubTabs } from '@/components/UI'
|
||||
import MergeTab from '@/components/tools/MergeTab.vue'
|
||||
import PageHeader from '@/components/layout/PageHeader.vue'
|
||||
|
||||
// Tab 配置
|
||||
const tabs = [{ id: 'merge', label: '合并聊天记录', icon: 'i-heroicons-document-duplicate' }]
|
||||
@@ -12,10 +13,7 @@ const activeTab = ref('merge')
|
||||
<template>
|
||||
<div class="flex h-full flex-col bg-gray-50 dark:bg-gray-950">
|
||||
<!-- Header -->
|
||||
<div class="border-b border-gray-200 bg-white px-6 py-4 dark:border-gray-800 dark:bg-gray-900">
|
||||
<h1 class="text-xl font-semibold text-gray-900 dark:text-white">实用工具</h1>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">提供聊天记录处理的实用工具</p>
|
||||
</div>
|
||||
<PageHeader title="实用工具" description="提供聊天记录处理的实用工具" icon="i-heroicons-wrench-screwdriver" />
|
||||
|
||||
<!-- Tabs -->
|
||||
<SubTabs v-model="activeTab" :items="tabs" />
|
||||
|
||||
Reference in New Issue
Block a user