mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
feat(ui): optimize page header and toolbar layout
This commit is contained in:
1 parent
066564c64e
commit
d1637b41b8
14 files changed
+197
-160
No files matched your search
@@ -26,10 +26,13 @@ const props = withDefaults(
|
||||
targetSelector?: string
|
||||
/** 是否应用 Markdown 列表渲染兼容修复(仅截取 Markdown 内容时传 true) */
|
||||
markdownFix?: boolean
|
||||
/** 按钮颜色,默认 primary */
|
||||
color?: string
|
||||
}>(),
|
||||
{
|
||||
size: 'sm',
|
||||
type: 'page',
|
||||
color: 'primary',
|
||||
}
|
||||
)
|
||||
|
||||
@@ -77,7 +80,8 @@ async function handleCapture(event: Event) {
|
||||
:id="buttonId"
|
||||
icon="i-heroicons-camera"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
:color="color"
|
||||
:class="color !== 'primary' ? 'hover:bg-gray-100 dark:hover:bg-gray-800' : ''"
|
||||
:size="size"
|
||||
:loading="isCapturing"
|
||||
@click="handleCapture"
|
||||
|
||||
@@ -240,7 +240,7 @@ function getAvatarColorClass(session: AnalysisSession, isActive: boolean) {
|
||||
class="flex h-full flex-col border-r border-gray-200/50 transition-all duration-300 ease-in-out dark:border-gray-800/50"
|
||||
:class="[isCollapsed ? 'w-20' : 'w-72', isHomePage ? '' : 'bg-gray-50 dark:bg-gray-900']"
|
||||
>
|
||||
<div class="flex flex-col p-4 pt-5">
|
||||
<div class="flex flex-col pt-5" :class="[isCollapsed ? 'px-3 pb-4' : 'p-4']">
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="mb-2 flex items-center"
|
||||
@@ -256,12 +256,12 @@ function getAvatarColorClass(session: AnalysisSession, isActive: boolean) {
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="group relative flex h-12 w-12 cursor-pointer items-center justify-center rounded-full hover:bg-gray-200/60 dark:hover:bg-gray-800"
|
||||
class="group relative flex h-9 w-9 cursor-pointer items-center justify-center rounded-full hover:bg-gray-200/60 dark:hover:bg-gray-800"
|
||||
style="-webkit-app-region: no-drag"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<img :src="logoSvg" alt="ChatLab" class="size-6 select-none pointer-events-none group-hover:hidden" />
|
||||
<UIcon name="i-lucide-panel-right-open" class="size-5 hidden group-hover:block scale-x-[-1]" />
|
||||
<img :src="logoSvg" alt="ChatLab" class="size-5 select-none pointer-events-none group-hover:hidden" />
|
||||
<UIcon name="i-lucide-panel-right-open" class="size-4 hidden group-hover:block scale-x-[-1]" />
|
||||
</div>
|
||||
<UTooltip
|
||||
v-if="!isCollapsed"
|
||||
@@ -272,18 +272,23 @@ function getAvatarColorClass(session: AnalysisSession, isActive: boolean) {
|
||||
<UButton
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="md"
|
||||
class="group flex h-12 w-12 cursor-pointer items-center justify-center rounded-full hover:bg-gray-200/60 dark:hover:bg-gray-800"
|
||||
size="sm"
|
||||
class="group flex h-9 w-9 cursor-pointer items-center justify-center rounded-full hover:bg-gray-200/60 dark:hover:bg-gray-800"
|
||||
@click="toggleSidebar"
|
||||
>
|
||||
<UIcon name="i-lucide-panel-right" class="size-5 group-hover:hidden scale-x-[-1]" />
|
||||
<UIcon name="i-lucide-panel-right-close" class="size-5 hidden group-hover:block scale-x-[-1]" />
|
||||
<UIcon name="i-lucide-panel-right" class="size-4 group-hover:hidden scale-x-[-1]" />
|
||||
<UIcon name="i-lucide-panel-right-close" class="size-4 hidden group-hover:block scale-x-[-1]" />
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
</div>
|
||||
|
||||
<!-- 新建分析 -->
|
||||
<SidebarButton icon="i-heroicons-plus" :title="t('layout.newAnalysis')" @click="handleImport" />
|
||||
<SidebarButton
|
||||
icon="i-heroicons-plus"
|
||||
:title="t('layout.newAnalysis')"
|
||||
:active="isHomePage"
|
||||
@click="handleImport"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Session List -->
|
||||
|
||||
@@ -82,7 +82,7 @@ function close() {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
z-index: 30;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,23 @@ const { isSidebarCollapsed: isCollapsed } = storeToRefs(layoutStore)
|
||||
|
||||
<template>
|
||||
<UTooltip :text="isCollapsed ? tooltip || title : ''" :popper="{ placement: 'right' }">
|
||||
<!-- 收起状态:用原生 div 承载背景和尺寸,与会话列表项结构一致,避免 UButton 内部撑大 -->
|
||||
<div
|
||||
v-if="isCollapsed"
|
||||
class="flex h-9 w-9 cursor-pointer items-center justify-center rounded-xl mx-auto transition-all duration-200"
|
||||
:class="[
|
||||
active
|
||||
? 'bg-gray-200/50 dark:bg-gray-800/80 text-gray-900 dark:text-white'
|
||||
: 'text-gray-600 dark:text-gray-300 hover:bg-gray-200/40 dark:hover:bg-gray-800/40',
|
||||
]"
|
||||
>
|
||||
<UIcon :name="icon" class="h-5 w-5 shrink-0" />
|
||||
</div>
|
||||
<!-- 展开状态:保持原有 UButton 样式 -->
|
||||
<UButton
|
||||
:block="!isCollapsed"
|
||||
class="transition-all duration-200 rounded-xl hover:bg-gray-200/40 dark:hover:bg-gray-800/40 h-10 cursor-pointer"
|
||||
v-else
|
||||
class="transition-all duration-200 rounded-xl hover:bg-gray-200/40 dark:hover:bg-gray-800/40 h-10 cursor-pointer justify-start pl-1.5 w-[calc(100%-8px)]"
|
||||
:class="[
|
||||
isCollapsed ? 'flex w-10 items-center justify-center px-0 mx-auto' : 'justify-start pl-1.5',
|
||||
active
|
||||
? 'bg-gray-200/50 dark:bg-gray-800/80 text-gray-900 dark:text-white font-medium'
|
||||
: 'text-gray-600 dark:text-gray-300',
|
||||
@@ -32,8 +44,8 @@ const { isSidebarCollapsed: isCollapsed } = storeToRefs(layoutStore)
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
>
|
||||
<UIcon :name="icon" class="h-5 w-5 shrink-0" :class="[isCollapsed ? '' : 'mr-2.5']" />
|
||||
<span v-if="!isCollapsed" class="truncate text-xs font-medium">{{ title }}</span>
|
||||
<UIcon :name="icon" class="mr-2.5 h-5 w-5 shrink-0" />
|
||||
<span class="truncate text-xs font-medium">{{ title }}</span>
|
||||
</UButton>
|
||||
</UTooltip>
|
||||
</template>
|
||||
@@ -1,50 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { computed, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
|
||||
const { t } = useI18n()
|
||||
const layoutStore = useLayoutStore()
|
||||
const { isToolsPanelLocked, isToolsPanelMini, toolsPanelPosition, isToolsPanelOpen } = storeToRefs(layoutStore)
|
||||
const { isToolsPanelMini, toolsPanelPosition, isToolsPanelOpen } = storeToRefs(layoutStore)
|
||||
|
||||
const isHeaderMode = computed(() => toolsPanelPosition.value === 'header')
|
||||
|
||||
const isHovered = ref(false)
|
||||
let hideTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
const isVisible = computed(() => {
|
||||
if (isHeaderMode.value) return isToolsPanelOpen.value
|
||||
return isToolsPanelLocked.value || isHovered.value
|
||||
})
|
||||
|
||||
function onMouseEnter() {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer)
|
||||
hideTimer = null
|
||||
}
|
||||
if (!isHeaderMode.value) {
|
||||
isHovered.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
if (isHeaderMode.value) {
|
||||
hideTimer = setTimeout(() => {
|
||||
isToolsPanelOpen.value = false
|
||||
}, 200)
|
||||
return
|
||||
}
|
||||
if (isToolsPanelLocked.value) return
|
||||
hideTimer = setTimeout(() => {
|
||||
isHovered.value = false
|
||||
}, 200)
|
||||
}
|
||||
|
||||
watch(toolsPanelPosition, () => {
|
||||
isToolsPanelOpen.value = false
|
||||
isHovered.value = false
|
||||
})
|
||||
const isPanelVisible = computed(() => !isHeaderMode.value || isToolsPanelOpen.value)
|
||||
|
||||
type ToolEvent =
|
||||
| 'openIncrementalImport'
|
||||
@@ -61,8 +26,6 @@ function handleToolClick(event: ToolEvent) {
|
||||
emit(event)
|
||||
if (isHeaderMode.value) {
|
||||
isToolsPanelOpen.value = false
|
||||
} else if (!isToolsPanelLocked.value) {
|
||||
isHovered.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +66,29 @@ const tools = [
|
||||
labelKey: 'analysis.messageExport.title',
|
||||
},
|
||||
]
|
||||
|
||||
const headerTools = [
|
||||
{
|
||||
event: 'openSessionIndex' as const,
|
||||
icon: 'i-heroicons-clock',
|
||||
hoverColor: 'group-hover:text-blue-500',
|
||||
miniHoverBg: 'hover:text-blue-500',
|
||||
labelKey: 'analysis.tooltip.sessionIndex',
|
||||
},
|
||||
{
|
||||
event: 'openMessageExport' as const,
|
||||
icon: 'i-heroicons-document-arrow-down',
|
||||
hoverColor: 'group-hover:text-green-500',
|
||||
miniHoverBg: 'hover:text-green-500',
|
||||
labelKey: 'analysis.messageExport.title',
|
||||
},
|
||||
]
|
||||
|
||||
const visibleTools = computed(() => (isHeaderMode.value ? headerTools : tools))
|
||||
|
||||
watch(toolsPanelPosition, () => {
|
||||
isToolsPanelOpen.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -136,27 +122,11 @@ const tools = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 普通/header 模式:面板从右侧滑入 -->
|
||||
<div
|
||||
v-else
|
||||
class="fixed right-0 z-40"
|
||||
:class="isHeaderMode ? 'top-14' : 'top-1/3'"
|
||||
@mouseenter="onMouseEnter"
|
||||
@mouseleave="onMouseLeave"
|
||||
>
|
||||
<!-- Trigger 标签(仅 side 模式) -->
|
||||
<div
|
||||
v-if="!isHeaderMode"
|
||||
class="h-10 w-6 cursor-pointer items-center justify-center rounded-l-lg border border-r-0 border-primary-200 bg-primary-50 text-primary-500 shadow-sm transition-opacity duration-200 hover:bg-primary-100 hover:text-primary-600 dark:border-primary-800 dark:bg-primary-950 dark:text-primary-400 dark:hover:bg-primary-900 dark:hover:text-primary-300"
|
||||
:class="isVisible ? 'pointer-events-none flex opacity-0' : 'flex opacity-100'"
|
||||
>
|
||||
<UIcon name="i-heroicons-wrench-screwdriver" class="h-3.5 w-3.5" />
|
||||
</div>
|
||||
|
||||
<!-- 面板(从右侧滑入) -->
|
||||
<!-- header 模式:点击更多后从右侧滑入;side 模式:面板常驻 -->
|
||||
<div v-else class="fixed right-0 z-40" :class="isHeaderMode ? 'top-14' : 'top-1/3'">
|
||||
<div
|
||||
class="absolute right-0 top-0 transition-all duration-250 ease-in-out"
|
||||
:class="isVisible ? 'translate-x-0 opacity-100' : 'pointer-events-none translate-x-full opacity-0'"
|
||||
:class="isPanelVisible ? 'translate-x-0 opacity-100' : 'pointer-events-none translate-x-full opacity-0'"
|
||||
>
|
||||
<div
|
||||
class="no-capture flex w-40 flex-col rounded-l-xl border border-r-0 border-gray-200/60 bg-white p-3 shadow-lg dark:border-white/5 dark:bg-gray-900"
|
||||
@@ -165,42 +135,20 @@ const tools = [
|
||||
<span class="px-0.5 text-[9px] font-bold uppercase tracking-widest text-gray-400 dark:text-gray-500">
|
||||
{{ t('analysis.overview.tools') }}
|
||||
</span>
|
||||
<div v-if="!isHeaderMode" class="flex items-center gap-0.5">
|
||||
<!-- Mini 模式按钮 -->
|
||||
<UTooltip :text="t('analysis.toolsPanel.miniMode')" :popper="{ placement: 'left' }">
|
||||
<button
|
||||
class="flex h-5 w-5 items-center justify-center rounded text-gray-300 transition-colors hover:text-gray-500 dark:text-gray-600 dark:hover:text-gray-400"
|
||||
@click="layoutStore.toggleToolsPanelMini()"
|
||||
>
|
||||
<UIcon name="i-heroicons-arrows-pointing-in" class="h-3 w-3" />
|
||||
</button>
|
||||
</UTooltip>
|
||||
<!-- 锁定按钮 -->
|
||||
<UTooltip
|
||||
:text="isToolsPanelLocked ? t('analysis.toolsPanel.unlock') : t('analysis.toolsPanel.lock')"
|
||||
:popper="{ placement: 'left' }"
|
||||
<!-- Mini 模式按钮(仅 side 模式) -->
|
||||
<UTooltip v-if="!isHeaderMode" :text="t('analysis.toolsPanel.miniMode')" :popper="{ placement: 'left' }">
|
||||
<button
|
||||
class="flex h-5 w-5 items-center justify-center rounded text-gray-300 transition-colors hover:text-gray-500 dark:text-gray-600 dark:hover:text-gray-400"
|
||||
@click="layoutStore.toggleToolsPanelMini()"
|
||||
>
|
||||
<button
|
||||
class="flex h-5 w-5 items-center justify-center rounded transition-colors"
|
||||
:class="
|
||||
isToolsPanelLocked
|
||||
? 'text-pink-500 hover:text-pink-600 dark:text-pink-400'
|
||||
: 'text-gray-300 hover:text-gray-500 dark:text-gray-600 dark:hover:text-gray-400'
|
||||
"
|
||||
@click="layoutStore.toggleToolsPanelLock()"
|
||||
>
|
||||
<UIcon
|
||||
:name="isToolsPanelLocked ? 'i-heroicons-lock-closed-solid' : 'i-heroicons-lock-open'"
|
||||
class="h-3 w-3"
|
||||
/>
|
||||
</button>
|
||||
</UTooltip>
|
||||
</div>
|
||||
<UIcon name="i-heroicons-arrows-pointing-in" class="h-3 w-3" />
|
||||
</button>
|
||||
</UTooltip>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<button
|
||||
v-for="tool in tools"
|
||||
v-for="tool in visibleTools"
|
||||
:key="tool.event"
|
||||
class="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs font-medium text-gray-600 transition-all hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
@click="handleToolClick(tool.event)"
|
||||
|
||||
@@ -37,17 +37,17 @@ const props = withDefaults(
|
||||
:src="avatar"
|
||||
:alt="title"
|
||||
class="object-cover"
|
||||
:class="props.size === 'compact' ? 'h-8 w-8 rounded-lg' : 'h-10 w-10 rounded-xl'"
|
||||
:class="props.size === 'compact' ? 'h-6 w-6 rounded-md' : 'h-10 w-10 rounded-xl'"
|
||||
/>
|
||||
<!-- 可选图标(fallback) -->
|
||||
<div
|
||||
v-else-if="icon"
|
||||
class="flex items-center justify-center"
|
||||
:class="[iconClass, props.size === 'compact' ? 'h-8 w-8 rounded-lg' : 'h-10 w-10 rounded-xl']"
|
||||
:class="[iconClass, props.size === 'compact' ? 'h-6 w-6 rounded-md' : 'h-10 w-10 rounded-xl']"
|
||||
>
|
||||
<UIcon :name="icon" class="text-white" :class="props.size === 'compact' ? 'h-4 w-4' : 'h-5 w-5'" />
|
||||
<UIcon :name="icon" class="text-white" :class="props.size === 'compact' ? 'h-3 w-3' : 'h-5 w-5'" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="group/title flex items-baseline gap-2">
|
||||
<h1
|
||||
class="font-semibold text-gray-900 dark:text-white"
|
||||
:class="props.size === 'compact' ? 'text-base' : 'text-lg'"
|
||||
@@ -56,7 +56,7 @@ const props = withDefaults(
|
||||
</h1>
|
||||
<p
|
||||
v-if="description"
|
||||
class="text-gray-500 dark:text-gray-400"
|
||||
class="pointer-events-none whitespace-nowrap text-gray-500 opacity-0 transition-opacity duration-200 group-hover/title:opacity-100 dark:text-gray-400"
|
||||
:class="props.size === 'compact' ? 'text-[11px]' : 'text-xs'"
|
||||
>
|
||||
{{ description }}
|
||||
@@ -68,7 +68,7 @@ const props = withDefaults(
|
||||
<div class="flex-1 self-stretch mx-4" style="-webkit-app-region: drag" />
|
||||
|
||||
<!-- 右侧操作区域 -->
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="header-actions relative z-[40] flex items-center gap-2">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -84,9 +84,22 @@ const props = withDefaults(
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
z-index: 30;
|
||||
top: calc(-1 * var(--titlebar-area-height));
|
||||
height: calc(var(--titlebar-area-height) + 1rem);
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.header-actions :deep(button),
|
||||
.header-actions :deep(button *),
|
||||
.header-actions :deep(a),
|
||||
.header-actions :deep(a *),
|
||||
.header-actions :deep([role='button']),
|
||||
.header-actions :deep([role='button'] *) {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
</style>
|
||||
@@ -66,7 +66,8 @@
|
||||
"viewChatRecord": "View Chat History",
|
||||
"sessionIndex": "Session Index",
|
||||
"incrementalImport": "Import More",
|
||||
"memberManagement": "Manage Member"
|
||||
"memberManagement": "Manage Member",
|
||||
"more": "More"
|
||||
},
|
||||
"incremental": {
|
||||
"title": "Append Chat Records",
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
"viewChatRecord": "チャット履歴を見る",
|
||||
"sessionIndex": "セッション索引",
|
||||
"incrementalImport": "追加インポート",
|
||||
"memberManagement": "メンバー管理"
|
||||
"memberManagement": "メンバー管理",
|
||||
"more": "その他"
|
||||
},
|
||||
"incremental": {
|
||||
"title": "チャット記録を追加",
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
"viewChatRecord": "查看聊天记录",
|
||||
"sessionIndex": "会话索引",
|
||||
"incrementalImport": "增量导入",
|
||||
"memberManagement": "成员管理"
|
||||
"memberManagement": "成员管理",
|
||||
"more": "更多"
|
||||
},
|
||||
"incremental": {
|
||||
"title": "追加聊天记录",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"brand": "ChatLab",
|
||||
"newAnalysis": "分析新聊天",
|
||||
"newAnalysis": "分析新对话",
|
||||
"tools": "实用工具",
|
||||
"chatHistory": "聊天对话",
|
||||
"manage": "管理",
|
||||
|
||||
@@ -66,7 +66,8 @@
|
||||
"viewChatRecord": "查看聊天紀錄",
|
||||
"sessionIndex": "會話索引",
|
||||
"incrementalImport": "追加匯入",
|
||||
"memberManagement": "成員管理"
|
||||
"memberManagement": "成員管理",
|
||||
"more": "更多"
|
||||
},
|
||||
"incremental": {
|
||||
"title": "新增聊天紀錄",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"brand": "ChatLab",
|
||||
"newAnalysis": "分析新聊天",
|
||||
"newAnalysis": "分析新對話",
|
||||
"tools": "實用工具",
|
||||
"chatHistory": "聊天紀錄",
|
||||
"manage": "管理",
|
||||
|
||||
@@ -23,7 +23,7 @@ import LoadingState from '@/components/UI/LoadingState.vue'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import { useSessionAnalysisPageBase, useSessionHeaderDescription } from '@/composables'
|
||||
import { useSessionAnalysisPageBase } from '@/composables'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -111,13 +111,6 @@ const filteredMessageCount = computed(() => {
|
||||
const filteredMemberCount = computed(() => {
|
||||
return memberActivity.value.filter((m) => m.messageCount > 0).length
|
||||
})
|
||||
|
||||
const { headerDescription } = useSessionHeaderDescription({
|
||||
session,
|
||||
fullTimeRange,
|
||||
timeRangeValue,
|
||||
descriptionKey: 'analysis.groupChat.description',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -130,25 +123,59 @@ const { headerDescription } = useSessionHeaderDescription({
|
||||
<!-- Header -->
|
||||
<PageHeader
|
||||
:title="session.name"
|
||||
:description="headerDescription"
|
||||
:avatar="session.groupAvatar"
|
||||
size="compact"
|
||||
icon="i-heroicons-chat-bubble-left-right"
|
||||
icon-class="bg-primary-600 text-white dark:bg-primary-500 dark:text-white"
|
||||
>
|
||||
<template #actions>
|
||||
<UTooltip v-if="layoutStore.toolsPanelPosition === 'header'" :text="t('analysis.overview.tools')">
|
||||
<UButton
|
||||
icon="i-heroicons-wrench-screwdriver"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
size="sm"
|
||||
@click="layoutStore.toggleToolsPanelOpen()"
|
||||
/>
|
||||
</UTooltip>
|
||||
<CaptureButton />
|
||||
<template v-if="layoutStore.toolsPanelPosition === 'header'">
|
||||
<UTooltip :text="t('analysis.tooltip.incrementalImport')">
|
||||
<UButton
|
||||
icon="i-heroicons-plus-circle"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="showIncrementalImportModal = true"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip :text="t('analysis.tooltip.memberManagement')">
|
||||
<UButton
|
||||
icon="i-heroicons-user-group"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="showMemberManagementModal = true"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip :text="t('analysis.tooltip.viewChatRecord')">
|
||||
<UButton
|
||||
icon="i-heroicons-chat-bubble-bottom-center-text"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="openChatRecordViewer"
|
||||
/>
|
||||
</UTooltip>
|
||||
<CaptureButton color="gray" />
|
||||
<UTooltip :text="t('analysis.tooltip.more')">
|
||||
<UButton
|
||||
icon="i-heroicons-ellipsis-horizontal"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="layoutStore.toggleToolsPanelOpen()"
|
||||
/>
|
||||
</UTooltip>
|
||||
</template>
|
||||
<CaptureButton v-else color="gray" />
|
||||
</template>
|
||||
<!-- Tabs -->
|
||||
<div class="mt-2 flex items-center justify-between gap-3">
|
||||
<div class="mt-3 flex items-center justify-between gap-3">
|
||||
<div class="flex shrink-0 items-center gap-0.5 overflow-x-auto scrollbar-hide">
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
@@ -238,7 +265,6 @@ const { headerDescription } = useSessionHeaderDescription({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧工具面板(fixed 定位,不占用页面空间) -->
|
||||
<ActionToolsPanel
|
||||
@open-incremental-import="showIncrementalImportModal = true"
|
||||
@open-session-index="showSessionIndexModal = true"
|
||||
|
||||
@@ -22,7 +22,7 @@ import LoadingState from '@/components/UI/LoadingState.vue'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
import { useLayoutStore } from '@/stores/layout'
|
||||
import { useSettingsStore } from '@/stores/settings'
|
||||
import { useSessionAnalysisPageBase, useSessionHeaderDescription } from '@/composables'
|
||||
import { useSessionAnalysisPageBase } from '@/composables'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -101,13 +101,6 @@ const filteredMemberCount = computed(() => {
|
||||
return memberActivity.value.filter((m) => m.messageCount > 0).length
|
||||
})
|
||||
|
||||
const { headerDescription } = useSessionHeaderDescription({
|
||||
session,
|
||||
fullTimeRange,
|
||||
timeRangeValue,
|
||||
descriptionKey: 'analysis.privateChat.description',
|
||||
})
|
||||
|
||||
// 获取对方头像
|
||||
const otherMemberAvatar = computed(() => {
|
||||
if (!session.value || memberActivity.value.length === 0) return null
|
||||
@@ -143,23 +136,56 @@ const otherMemberAvatar = computed(() => {
|
||||
<!-- Header -->
|
||||
<PageHeader
|
||||
:title="session.name"
|
||||
:description="headerDescription"
|
||||
:avatar="otherMemberAvatar"
|
||||
size="compact"
|
||||
icon="i-heroicons-user"
|
||||
icon-class="bg-pink-600 text-white dark:bg-pink-500 dark:text-white"
|
||||
>
|
||||
<template #actions>
|
||||
<UTooltip v-if="layoutStore.toolsPanelPosition === 'header'" :text="t('analysis.overview.tools')">
|
||||
<UButton
|
||||
icon="i-heroicons-wrench-screwdriver"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
size="sm"
|
||||
@click="layoutStore.toggleToolsPanelOpen()"
|
||||
/>
|
||||
</UTooltip>
|
||||
<CaptureButton />
|
||||
<template v-if="layoutStore.toolsPanelPosition === 'header'">
|
||||
<UTooltip :text="t('analysis.tooltip.incrementalImport')">
|
||||
<UButton
|
||||
icon="i-heroicons-plus-circle"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="showIncrementalImportModal = true"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip :text="t('analysis.tooltip.memberManagement')">
|
||||
<UButton
|
||||
icon="i-heroicons-user-group"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="showMemberManagementModal = true"
|
||||
/>
|
||||
</UTooltip>
|
||||
<UTooltip :text="t('analysis.tooltip.viewChatRecord')">
|
||||
<UButton
|
||||
icon="i-heroicons-chat-bubble-bottom-center-text"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="openChatRecordViewer"
|
||||
/>
|
||||
</UTooltip>
|
||||
<CaptureButton color="gray" />
|
||||
<UTooltip :text="t('analysis.tooltip.more')">
|
||||
<UButton
|
||||
icon="i-heroicons-ellipsis-horizontal"
|
||||
variant="ghost"
|
||||
color="gray"
|
||||
size="sm"
|
||||
class="hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
@click="layoutStore.toggleToolsPanelOpen()"
|
||||
/>
|
||||
</UTooltip>
|
||||
</template>
|
||||
<CaptureButton v-else color="gray" />
|
||||
</template>
|
||||
<!-- Tabs -->
|
||||
<div class="mt-3 flex items-center justify-between gap-3">
|
||||
@@ -249,7 +275,6 @@ const otherMemberAvatar = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧工具面板(fixed 定位,不占用页面空间) -->
|
||||
<ActionToolsPanel
|
||||
@open-incremental-import="showIncrementalImportModal = true"
|
||||
@open-session-index="showSessionIndexModal = true"
|
||||
|
||||
Reference in new issue
Block a user