| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <template>
- <div data-theme="dark" class="flex flex-col h-screen">
- <!-- 顶部栏 -->
- <header class="navbar bg-base-200 min-h-0 px-4 py-2 shrink-0">
- <!-- Tab 切换 -->
- <div class="tabs tabs-boxed bg-base-300">
- <a
- class="tab tab-sm"
- :class="{ 'tab-active': activeTab === 'persona' }"
- @click="switchTab('persona')"
- >人设</a>
- <a
- class="tab tab-sm"
- :class="{ 'tab-active': activeTab === 'match' }"
- @click="switchTab('match')"
- >待解构帖子</a>
- </div>
- <!-- 图例 -->
- <div class="flex gap-3 text-xs text-base-content/60 ml-6 border-l border-base-content/20 pl-4">
- <span class="text-base-content font-medium">颜色:</span>
- <div class="flex items-center gap-1">
- <span class="w-2.5 h-2.5 rounded-full bg-dim-persona"></span>人设
- </div>
- <div class="flex items-center gap-1">
- <span class="w-2.5 h-2.5 rounded-full bg-dim-inspiration"></span>灵感点
- </div>
- <div class="flex items-center gap-1">
- <span class="w-2.5 h-2.5 rounded-full bg-dim-purpose"></span>目的点
- </div>
- <div class="flex items-center gap-1">
- <span class="w-2.5 h-2.5 rounded-full bg-dim-key"></span>关键点
- </div>
- </div>
- <div class="flex gap-3 text-xs text-base-content/60 ml-4 border-l border-base-content/20 pl-4">
- <span class="text-base-content font-medium">形状:</span>
- <div class="flex items-center gap-1">○ 标签</div>
- <div class="flex items-center gap-1">□ 分类/点</div>
- </div>
- <div class="flex gap-3 text-xs text-base-content/60 ml-4 border-l border-base-content/20 pl-4">
- <span class="text-base-content font-medium">填充:</span>
- <div class="flex items-center gap-1">◯ 帖子</div>
- <div class="flex items-center gap-1">● 人设</div>
- </div>
- <!-- 边图例 -->
- <div class="flex gap-3 text-xs text-base-content/60 ml-4 border-l border-base-content/20 pl-4">
- <span class="text-base-content font-medium">边:</span>
- <div v-for="(color, type) in edgeTypeColors" :key="type" class="flex items-center gap-1">
- <span class="w-4 h-0.5" :style="{ backgroundColor: color }"></span>
- <span>{{ type }}</span>
- </div>
- </div>
- </header>
- <!-- 主内容区 - 人设图谱 Tab -->
- <main v-if="activeTab === 'persona'" class="flex flex-1 overflow-hidden">
- <TreeView class="w-[420px] shrink-0 bg-base-200 border-r border-base-300" />
- <GraphView class="flex-1" />
- <!-- 详情侧边栏 -->
- <DetailPanel class="w-56 shrink-0 transition-all duration-200" :class="{ 'w-0 overflow-hidden': !hasSelection }" />
- </main>
- <!-- 主内容区 - 帖子匹配 Tab -->
- <main v-else-if="activeTab === 'match'" class="flex flex-1 overflow-hidden">
- <!-- 左侧:人设树 + 相关图(上下布局) -->
- <div
- class="shrink-0 bg-base-200 border-r border-base-300 flex flex-col transition-all duration-200"
- :class="getLeftPanelClass()"
- >
- <!-- 人设树(上部,占50%) -->
- <div
- class="flex flex-col transition-all duration-200 h-1/2 shrink-0"
- :class="{ 'h-full': store.expandedPanel === 'persona-tree', 'h-10': store.expandedPanel === 'graph' }"
- >
- <div class="flex items-center justify-between px-4 py-1 bg-base-300 text-xs text-base-content/60 shrink-0 border-b border-base-300">
- <span>人设树</span>
- <div class="flex gap-1">
- <button
- v-if="store.expandedPanel !== 'persona-tree'"
- @click="store.expandPanel('persona-tree')"
- class="btn btn-ghost btn-xs"
- title="放大"
- >⤢</button>
- <button
- v-if="store.expandedPanel !== 'default'"
- @click="store.resetLayout()"
- class="btn btn-ghost btn-xs"
- title="恢复"
- >⊡</button>
- </div>
- </div>
- <TreeView class="flex-1 min-h-0 overflow-auto" :hide-header="true" />
- </div>
- <!-- 相关图(下部,占50%) -->
- <div
- v-show="store.expandedPanel !== 'persona-tree'"
- class="flex-1 border-t border-base-300"
- :class="{ 'h-full': store.expandedPanel === 'graph' }"
- >
- <GraphView class="h-full" :show-expand="true" />
- </div>
- </div>
- <!-- 中间:推导图谱 -->
- <div
- class="shrink-0 bg-base-200 border-l border-base-300 transition-all duration-200"
- :class="getDerivationPanelClass()"
- >
- <DerivationView class="h-full" />
- </div>
- <!-- 右侧:帖子树(含匹配列表+详情) -->
- <div
- class="bg-base-200 border-l border-base-300 flex flex-col transition-all duration-200"
- :class="getPostTreeClass()"
- >
- <PostTreeView class="flex-1" :show-expand="true" />
- </div>
- </main>
- </div>
- </template>
- <script setup>
- import { ref, computed } from 'vue'
- import TreeView from './components/TreeView.vue'
- import GraphView from './components/GraphView.vue'
- import PostTreeView from './components/PostTreeView.vue'
- import DetailPanel from './components/DetailPanel.vue'
- import DerivationView from './components/DerivationView.vue'
- import { useGraphStore } from './stores/graph'
- import { edgeTypeColors } from './config/edgeStyle'
- const store = useGraphStore()
- // 当前激活的 Tab
- const activeTab = ref('match')
- // 是否有选中内容(用于控制侧边栏显示)
- const hasSelection = computed(() => store.selectedNode || store.selectedEdge)
- // 切换 Tab 时清除选中状态,避免干扰
- function switchTab(tab) {
- if (activeTab.value !== tab) {
- store.clearSelection()
- store.resetLayout()
- activeTab.value = tab
- }
- }
- // ==================== 布局类计算 ====================
- function getLeftPanelClass() {
- const panel = store.expandedPanel
- if (panel === 'post-tree') return 'w-0 opacity-0 overflow-hidden'
- if (panel === 'persona-tree' || panel === 'graph') return 'flex-1'
- return 'w-[360px]'
- }
- function getPersonaTreeClass() {
- const panel = store.expandedPanel
- if (panel === 'persona-tree') return 'flex-1'
- if (panel === 'graph') return 'h-10 shrink-0'
- // 默认:根据相关图状态调整
- if (store.selectedNodeId) return 'flex-1'
- return 'flex-1'
- }
- function getGraphClass() {
- const panel = store.expandedPanel
- if (panel === 'graph') return 'flex-1'
- if (store.selectedNodeId) return 'h-[280px] shrink-0'
- return 'h-10 shrink-0'
- }
- function getPostTreeClass() {
- const panel = store.expandedPanel
- if (panel === 'post-tree') return 'flex-1'
- if (panel === 'persona-tree' || panel === 'graph' || panel === 'derivation') return 'w-0 opacity-0 overflow-hidden'
- return 'flex-1'
- }
- function getDerivationPanelClass() {
- const panel = store.expandedPanel
- if (panel === 'derivation') return 'flex-1'
- if (panel === 'persona-tree' || panel === 'graph' || panel === 'post-tree') return 'w-0 opacity-0 overflow-hidden'
- return 'w-[400px]'
- }
- </script>
|