feat(ui): 优化展现细节

This commit is contained in:
Wenjie Zhang 2026-06-11 21:47:15 +08:00
parent ac98684932
commit d59756c167
4 changed files with 19 additions and 143 deletions

View File

@ -8,7 +8,7 @@
}"
:style="{ '--file-panel-width': filePanelWidthStyle }"
>
<div class="chat-header">
<div class="chat-header" :class="{ 'has-active-thread': !!currentChatId }">
<div class="header__left">
<slot name="header-left"></slot>
<div
@ -2606,6 +2606,10 @@ watch(currentChatId, (threadId, oldThreadId) => {
flex-shrink: 0; /* Prevent header from shrinking */
transition: padding-right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
&.has-active-thread {
border-bottom: 1px solid var(--gray-150);
}
.header__left,
.header__right {
display: flex;
@ -3194,7 +3198,7 @@ watch(currentChatId, (threadId, oldThreadId) => {
align-items: center;
justify-content: space-between;
gap: 8px;
min-height: 44px;
min-height: var(--header-height);
padding: 4px 12px;
background: var(--gray-25);
border-bottom: 1px solid var(--gray-100);

View File

@ -709,7 +709,7 @@ watch(
justify-content: space-between;
gap: 8px;
padding: 4px 12px;
min-height: 44px;
min-height: var(--header-height);
background: var(--gray-25);
border-bottom: 1px solid var(--gray-100);
flex-shrink: 0;

View File

@ -31,28 +31,7 @@
<a-divider />
<!-- 图表区域拆分为两行 -->
<a-row :gutter="24" style="margin-bottom: 16px">
<!-- 数据库类型分布 -->
<a-col :span="24">
<div class="chart-container">
<div class="chart-header">
<h4>类型分布</h4>
<div class="legend" v-if="dbTypeLegend.length">
<div class="legend-item" v-for="(item, idx) in dbTypeLegend" :key="item.name">
<span
class="legend-color"
:style="{ backgroundColor: getLegendColorByIndex(idx) }"
></span>
<span class="legend-label">{{ item.name }}</span>
</div>
</div>
</div>
<div ref="dbTypeChartRef" class="chart chart--thin"></div>
</div>
</a-col>
</a-row>
<!-- 图表区域 -->
<a-row :gutter="24">
<!-- 文件类型分布 -->
<a-col :span="24">
@ -112,7 +91,7 @@
<script setup>
import { ref, onMounted, watch, nextTick, computed } from 'vue'
import * as echarts from 'echarts'
import { getColorByIndex, getColorPalette } from '@/utils/chartColors'
import { getColorPalette } from '@/utils/chartColors'
import { useThemeStore } from '@/stores/theme'
// CSS
@ -136,9 +115,7 @@ const props = defineProps({
})
// Chart refs
const dbTypeChartRef = ref(null)
const fileTypeChartRef = ref(null)
let dbTypeChart = null
let fileTypeChart = null
// File type chart data for carousel
@ -168,77 +145,6 @@ const formattedStorageSize = computed(() => {
// return nodes > 0 ? size / (nodes * 1024) : 0 // KB
// })
// 使
const getLegendColorByIndex = (index) => getColorByIndex(index)
// -
const dbTypeLegend = ref([])
const initDbTypeChart = () => {
if (!dbTypeChartRef.value || !props.knowledgeStats?.databases_by_type) return
const entries = Object.entries(props.knowledgeStats.databases_by_type)
.map(([type, count]) => ({ name: type || '未知', value: count }))
.filter((item) => item.value > 0)
// update legend data
dbTypeLegend.value = entries
if (!dbTypeChart) {
dbTypeChart = echarts.init(dbTypeChartRef.value)
}
const total = entries.reduce((sum, item) => sum + item.value, 0)
// Build stacked bar series, but render with neutral color and separators
const series = entries.map((item, idx) => ({
name: item.name,
type: 'bar',
stack: 'total',
barWidth: 28,
data: [item.value],
itemStyle: {
color: getLegendColorByIndex(idx),
borderColor: getCSSVariable('--gray-0'),
borderWidth: 2,
borderJoin: 'miter'
},
emphasis: {
itemStyle: {
color: getLegendColorByIndex(idx)
}
}
}))
const option = {
animation: false,
tooltip: {
trigger: 'item',
backgroundColor: getCSSVariable('--gray-0'),
borderColor: getCSSVariable('--gray-200'),
borderWidth: 1,
textStyle: { color: getCSSVariable('--gray-600') },
formatter: (params) => {
const value = params.value || 0
return `${params.seriesName}: ${value}/${total}`
}
},
grid: { left: 0, right: 0, top: 10, bottom: 10, containLabel: false },
xAxis: {
type: 'value',
show: false,
max: total > 0 ? total : undefined
},
yAxis: {
type: 'category',
show: false,
data: ['all']
},
series
}
dbTypeChart.setOption(option, true)
}
//
const initFileTypeChart = () => {
if (!fileTypeChartRef.value) return
@ -399,7 +305,6 @@ const stopCarousel = () => {
//
const updateCharts = () => {
nextTick(() => {
initDbTypeChart()
initFileTypeChart()
})
}
@ -415,7 +320,6 @@ watch(
//
const handleResize = () => {
if (dbTypeChart) dbTypeChart.resize()
if (fileTypeChart) fileTypeChart.resize()
}
@ -428,7 +332,7 @@ onMounted(() => {
watch(
() => themeStore.isDark,
() => {
if (props.knowledgeStats && (dbTypeChart || fileTypeChart)) {
if (props.knowledgeStats && fileTypeChart) {
nextTick(() => {
updateCharts()
})
@ -440,10 +344,6 @@ watch(
const cleanup = () => {
window.removeEventListener('resize', handleResize)
stopCarousel() //
if (dbTypeChart) {
dbTypeChart.dispose()
dbTypeChart = null
}
if (fileTypeChart) {
fileTypeChart.dispose()
fileTypeChart = null
@ -459,46 +359,11 @@ defineExpose({
<style scoped lang="less">
// KnowledgeStatsComponent
.chart-container {
.chart-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
h4 {
margin: 0;
}
.legend {
display: flex;
gap: 12px;
align-items: center;
.legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--gray-500);
}
.legend-color {
width: 10px;
height: 10px;
border-radius: 2px;
}
}
}
.chart {
height: 300px;
width: 100%;
}
.chart--thin {
height: 80px;
}
//
.donut-chart-container {
position: relative;

View File

@ -112,6 +112,9 @@ const route = useRoute()
const router = useRouter()
const activeTaskCount = computed(() => activeCountRef.value || 0)
const activeConversationThreadId = computed(() => {
return route.path.startsWith('/agent') ? currentThreadId.value : null
})
const organizationName = computed(() => {
return infoStore.organization.name || infoStore.branding.name || 'Yuxi'
})
@ -124,7 +127,8 @@ const mainList = computed(() => {
path: '/agent',
icon: MessageCirclePlus,
activeIcon: MessageCirclePlus,
action: true
action: true,
exactActive: true
}
]
@ -164,6 +168,9 @@ const mainList = computed(() => {
const isNavItemActive = (item) => {
const activePaths = item.activePaths || [item.path]
if (item.exactActive) {
return activePaths.some((path) => route.path === path)
}
return activePaths.some((path) => route.path === path || route.path.startsWith(`${path}/`))
}
@ -297,7 +304,7 @@ provide('settingsModal', {
<ConversationNavSection
v-if="!sidebarCollapsed"
class="sidebar-conversations"
:current-chat-id="currentThreadId"
:current-chat-id="activeConversationThreadId"
:chats-list="threads"
:has-more-chats="hasMoreThreads"
:is-loading-more="isLoadingMoreThreads"