feat(ui): 优化展现细节
This commit is contained in:
parent
ac98684932
commit
d59756c167
@ -8,7 +8,7 @@
|
|||||||
}"
|
}"
|
||||||
:style="{ '--file-panel-width': filePanelWidthStyle }"
|
:style="{ '--file-panel-width': filePanelWidthStyle }"
|
||||||
>
|
>
|
||||||
<div class="chat-header">
|
<div class="chat-header" :class="{ 'has-active-thread': !!currentChatId }">
|
||||||
<div class="header__left">
|
<div class="header__left">
|
||||||
<slot name="header-left"></slot>
|
<slot name="header-left"></slot>
|
||||||
<div
|
<div
|
||||||
@ -2606,6 +2606,10 @@ watch(currentChatId, (threadId, oldThreadId) => {
|
|||||||
flex-shrink: 0; /* Prevent header from shrinking */
|
flex-shrink: 0; /* Prevent header from shrinking */
|
||||||
transition: padding-right 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
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__left,
|
||||||
.header__right {
|
.header__right {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -3194,7 +3198,7 @@ watch(currentChatId, (threadId, oldThreadId) => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
min-height: 44px;
|
min-height: var(--header-height);
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: var(--gray-25);
|
background: var(--gray-25);
|
||||||
border-bottom: 1px solid var(--gray-100);
|
border-bottom: 1px solid var(--gray-100);
|
||||||
|
|||||||
@ -709,7 +709,7 @@ watch(
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
min-height: 44px;
|
min-height: var(--header-height);
|
||||||
background: var(--gray-25);
|
background: var(--gray-25);
|
||||||
border-bottom: 1px solid var(--gray-100);
|
border-bottom: 1px solid var(--gray-100);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@ -31,28 +31,7 @@
|
|||||||
|
|
||||||
<a-divider />
|
<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-row :gutter="24">
|
||||||
<!-- 文件类型分布 -->
|
<!-- 文件类型分布 -->
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
@ -112,7 +91,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, watch, nextTick, computed } from 'vue'
|
import { ref, onMounted, watch, nextTick, computed } from 'vue'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { getColorByIndex, getColorPalette } from '@/utils/chartColors'
|
import { getColorPalette } from '@/utils/chartColors'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
|
|
||||||
// CSS 变量解析工具函数
|
// CSS 变量解析工具函数
|
||||||
@ -136,9 +115,7 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Chart refs
|
// Chart refs
|
||||||
const dbTypeChartRef = ref(null)
|
|
||||||
const fileTypeChartRef = ref(null)
|
const fileTypeChartRef = ref(null)
|
||||||
let dbTypeChart = null
|
|
||||||
let fileTypeChart = null
|
let fileTypeChart = null
|
||||||
|
|
||||||
// File type chart data for carousel
|
// File type chart data for carousel
|
||||||
@ -168,77 +145,6 @@ const formattedStorageSize = computed(() => {
|
|||||||
// return nodes > 0 ? size / (nodes * 1024) : 0 // 转换为KB
|
// 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 = () => {
|
const initFileTypeChart = () => {
|
||||||
if (!fileTypeChartRef.value) return
|
if (!fileTypeChartRef.value) return
|
||||||
@ -399,7 +305,6 @@ const stopCarousel = () => {
|
|||||||
// 更新图表
|
// 更新图表
|
||||||
const updateCharts = () => {
|
const updateCharts = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
initDbTypeChart()
|
|
||||||
initFileTypeChart()
|
initFileTypeChart()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -415,7 +320,6 @@ watch(
|
|||||||
|
|
||||||
// 窗口大小变化时重新调整图表
|
// 窗口大小变化时重新调整图表
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (dbTypeChart) dbTypeChart.resize()
|
|
||||||
if (fileTypeChart) fileTypeChart.resize()
|
if (fileTypeChart) fileTypeChart.resize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +332,7 @@ onMounted(() => {
|
|||||||
watch(
|
watch(
|
||||||
() => themeStore.isDark,
|
() => themeStore.isDark,
|
||||||
() => {
|
() => {
|
||||||
if (props.knowledgeStats && (dbTypeChart || fileTypeChart)) {
|
if (props.knowledgeStats && fileTypeChart) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
updateCharts()
|
updateCharts()
|
||||||
})
|
})
|
||||||
@ -440,10 +344,6 @@ watch(
|
|||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
stopCarousel() // 停止轮播
|
stopCarousel() // 停止轮播
|
||||||
if (dbTypeChart) {
|
|
||||||
dbTypeChart.dispose()
|
|
||||||
dbTypeChart = null
|
|
||||||
}
|
|
||||||
if (fileTypeChart) {
|
if (fileTypeChart) {
|
||||||
fileTypeChart.dispose()
|
fileTypeChart.dispose()
|
||||||
fileTypeChart = null
|
fileTypeChart = null
|
||||||
@ -459,46 +359,11 @@ defineExpose({
|
|||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
// KnowledgeStatsComponent 特有的样式
|
// KnowledgeStatsComponent 特有的样式
|
||||||
.chart-container {
|
.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 {
|
.chart {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart--thin {
|
|
||||||
height: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 环形图容器样式
|
// 环形图容器样式
|
||||||
.donut-chart-container {
|
.donut-chart-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -112,6 +112,9 @@ const route = useRoute()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const activeTaskCount = computed(() => activeCountRef.value || 0)
|
const activeTaskCount = computed(() => activeCountRef.value || 0)
|
||||||
|
const activeConversationThreadId = computed(() => {
|
||||||
|
return route.path.startsWith('/agent') ? currentThreadId.value : null
|
||||||
|
})
|
||||||
const organizationName = computed(() => {
|
const organizationName = computed(() => {
|
||||||
return infoStore.organization.name || infoStore.branding.name || 'Yuxi'
|
return infoStore.organization.name || infoStore.branding.name || 'Yuxi'
|
||||||
})
|
})
|
||||||
@ -124,7 +127,8 @@ const mainList = computed(() => {
|
|||||||
path: '/agent',
|
path: '/agent',
|
||||||
icon: MessageCirclePlus,
|
icon: MessageCirclePlus,
|
||||||
activeIcon: MessageCirclePlus,
|
activeIcon: MessageCirclePlus,
|
||||||
action: true
|
action: true,
|
||||||
|
exactActive: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -164,6 +168,9 @@ const mainList = computed(() => {
|
|||||||
|
|
||||||
const isNavItemActive = (item) => {
|
const isNavItemActive = (item) => {
|
||||||
const activePaths = item.activePaths || [item.path]
|
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}/`))
|
return activePaths.some((path) => route.path === path || route.path.startsWith(`${path}/`))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +304,7 @@ provide('settingsModal', {
|
|||||||
<ConversationNavSection
|
<ConversationNavSection
|
||||||
v-if="!sidebarCollapsed"
|
v-if="!sidebarCollapsed"
|
||||||
class="sidebar-conversations"
|
class="sidebar-conversations"
|
||||||
:current-chat-id="currentThreadId"
|
:current-chat-id="activeConversationThreadId"
|
||||||
:chats-list="threads"
|
:chats-list="threads"
|
||||||
:has-more-chats="hasMoreThreads"
|
:has-more-chats="hasMoreThreads"
|
||||||
:is-loading-more="isLoadingMoreThreads"
|
:is-loading-more="isLoadingMoreThreads"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user