style(dashboard): 更新图表调色盘和样式优化
- 修改 base.css 中的图表调色盘颜色,提升视觉效果 - 在多个组件中引入统一的颜色工具函数,确保颜色一致性 - 优化仪表板组件的样式,提升用户体验和可读性 - 移除冗余的颜色定义,简化代码结构
This commit is contained in:
parent
e13f98f16f
commit
e592c75662
@ -82,7 +82,7 @@
|
||||
|
||||
/* Chart Palette - 图表调色板 */
|
||||
--chart-palette-1: var(--chart-primary);
|
||||
--chart-palette-2: #028ea0; /* 浅一点的青蓝 */
|
||||
--chart-palette-2: #11cbe3; /* 浅一点的青蓝 */
|
||||
--chart-palette-3: #00b8a9; /* 明亮青绿(邻近色) */
|
||||
--chart-palette-4: #f2c94c; /* 柔和金黄(对比色) */
|
||||
--chart-palette-5: #eb5757; /* 温暖红橙(强调) */
|
||||
|
||||
@ -341,11 +341,12 @@
|
||||
|
||||
/* 使用更具体的选择器来覆盖 Ant Design 的默认样式 */
|
||||
.dashboard-card .ant-card-head {
|
||||
border-bottom: 1px solid var(--gray-200) !important;
|
||||
/* border-bottom: 1px solid var(--gray-200) !important; */
|
||||
min-height: 56px !important;
|
||||
padding: 0 20px !important;
|
||||
flex-shrink: 0;
|
||||
background-color: var(--gray-0) !important;
|
||||
/* border: 1px solo; */
|
||||
}
|
||||
|
||||
.dashboard-card .ant-card-head-title {
|
||||
@ -356,11 +357,16 @@
|
||||
|
||||
.dashboard-card .ant-card-body {
|
||||
padding: 20px !important;
|
||||
padding-top: 0px !important;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
background-color: var(--gray-0) !important;
|
||||
}
|
||||
|
||||
.dashboard-card .ant-card-extra {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 统计概览样式 */
|
||||
.stats-overview {
|
||||
margin-bottom: 16px;
|
||||
|
||||
@ -98,6 +98,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick, computed } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getColorByIndex, getChartColor } from '@/utils/chartColors'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@ -164,9 +165,9 @@ const topPerformers = computed(() => {
|
||||
|
||||
// 颜色辅助函数
|
||||
const getScoreColor = (score) => {
|
||||
if (score >= 80) return '#3996ae'
|
||||
if (score >= 60) return '#82c3d6'
|
||||
return '#a3d8e8'
|
||||
if (score >= 80) return getChartColor('primary')
|
||||
if (score >= 60) return getChartColor('warning')
|
||||
return getChartColor('accent')
|
||||
}
|
||||
|
||||
|
||||
@ -252,12 +253,12 @@ const initConversationToolChart = () => {
|
||||
return item ? item.conversation_count : 0
|
||||
}),
|
||||
itemStyle: {
|
||||
color: '#3996ae',
|
||||
color: getChartColor('primary'),
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#028ea0',
|
||||
color: getChartColor('primary'),
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(2, 142, 160, 0.3)'
|
||||
}
|
||||
@ -271,12 +272,12 @@ const initConversationToolChart = () => {
|
||||
return item ? item.tool_usage_count : 0
|
||||
}),
|
||||
itemStyle: {
|
||||
color: '#00b8a9',
|
||||
color: getChartColor('accent'),
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#028ea0',
|
||||
color: getChartColor('primary'),
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(2, 142, 160, 0.3)'
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick, defineExpose, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { dashboardApi } from '@/apis/dashboard_api'
|
||||
import { getColorByIndex, truncateLegend } from '@/utils/chartColors'
|
||||
|
||||
const props = defineProps({
|
||||
loading: { type: Boolean, default: false },
|
||||
@ -81,62 +82,6 @@ let retryTimer = null
|
||||
const retryCount = ref(0)
|
||||
const maxRetry = 20
|
||||
|
||||
// Build color palette from CSS variables in base.css
|
||||
let colorPalette = []
|
||||
const buildColorPalette = () => {
|
||||
try {
|
||||
const root = document.documentElement
|
||||
const styles = getComputedStyle(root)
|
||||
const pick = (name, fallback) => {
|
||||
const v = styles.getPropertyValue(name)
|
||||
return v && v.trim() ? v.trim() : fallback
|
||||
}
|
||||
const baseVars = [
|
||||
['--chart-primary', '#3996ae'],
|
||||
['--chart-success', '#52c41a'],
|
||||
['--chart-warning', '#faad14'],
|
||||
['--chart-error', '#f5222d'],
|
||||
['--chart-secondary', '#722ed1'],
|
||||
['--chart-accent', '#13c2c2'],
|
||||
]
|
||||
const paletteVars = [
|
||||
['--chart-palette-1', '#3996ae'],
|
||||
['--chart-palette-2', '#028ea0'],
|
||||
['--chart-palette-3', '#00b8a9'],
|
||||
['--chart-palette-4', '#f2c94c'],
|
||||
['--chart-palette-5', '#eb5757'],
|
||||
['--chart-palette-6', '#2f80ed'],
|
||||
['--chart-palette-7', '#9b51e0'],
|
||||
['--chart-palette-8', '#56ccf2'],
|
||||
['--chart-palette-9', '#6fcf97'],
|
||||
['--chart-palette-10', '#333333'],
|
||||
]
|
||||
const baseColors = baseVars.map(([n, f]) => pick(n, f))
|
||||
const paletteColors = paletteVars.map(([n, f]) => pick(n, f))
|
||||
// PRIORITY: palette first, then base
|
||||
const merged = [...paletteColors, ...baseColors]
|
||||
.filter(Boolean)
|
||||
.filter((c, idx, arr) => arr.indexOf(c) === idx)
|
||||
colorPalette = merged
|
||||
} catch (e) {
|
||||
// Fallback
|
||||
colorPalette = [
|
||||
'#3996ae', '#52c41a', '#faad14', '#f5222d', '#722ed1', '#13c2c2',
|
||||
'#fa8c16', '#1890ff', '#95de64', '#69c0ff'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const getColorByIndex = (index) => {
|
||||
if (!colorPalette || colorPalette.length === 0) buildColorPalette()
|
||||
return colorPalette[index % colorPalette.length]
|
||||
}
|
||||
|
||||
const truncateLegend = (name) => {
|
||||
if (!name) return ''
|
||||
return name.length > 20 ? name.slice(0, 20) + '…' : name
|
||||
}
|
||||
|
||||
const loadCallStats = async () => {
|
||||
callStatsLoading.value = true
|
||||
try {
|
||||
@ -178,7 +123,6 @@ const renderCallStatsChart = () => {
|
||||
callStatsChart.dispose()
|
||||
}
|
||||
|
||||
if (!colorPalette || colorPalette.length === 0) buildColorPalette()
|
||||
|
||||
callStatsChart = echarts.init(container)
|
||||
|
||||
@ -295,7 +239,6 @@ const cleanup = () => {
|
||||
defineExpose({ cleanup })
|
||||
|
||||
onMounted(() => {
|
||||
buildColorPalette()
|
||||
loadCallStats()
|
||||
})
|
||||
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick, computed } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getColorByIndex, getChartColor, getColorPalette } from '@/utils/chartColors'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@ -134,38 +135,8 @@ const formattedStorageSize = computed(() => {
|
||||
// return nodes > 0 ? size / (nodes * 1024) : 0 // 转换为KB
|
||||
// })
|
||||
|
||||
// 颜色数组 - 基于主题色的协调调色板
|
||||
const colorPalette = [
|
||||
'#3996ae', // 主色调
|
||||
'#5faec2', // 主色调浅色
|
||||
'#82c3d6', // 主色调更浅
|
||||
'#a3d8e8', // 主色调最浅
|
||||
'#24839a', // 主色调深色
|
||||
'#046a82', // 主色调更深
|
||||
'#035065', // 主色调最深
|
||||
'#c4eaf5', // 极浅色
|
||||
'#e1f6fb', // 背景色
|
||||
'#f2fbfd' // 最浅背景色
|
||||
]
|
||||
|
||||
const getColorByIndex = (index) => {
|
||||
return colorPalette[index % colorPalette.length]
|
||||
}
|
||||
|
||||
// 图例/分段颜色:与 base.css 中的 --chart-palette-* 保持一致(使用固定 HEX)
|
||||
const legendPalette = [
|
||||
'#3996ae', // --chart-palette-1 -> var(--chart-primary)
|
||||
'#028ea0', // --chart-palette-2
|
||||
'#00b8a9', // --chart-palette-3
|
||||
'#f2c94c', // --chart-palette-4
|
||||
'#eb5757', // --chart-palette-5
|
||||
'#2f80ed', // --chart-palette-6
|
||||
'#9b51e0', // --chart-palette-7
|
||||
'#56ccf2', // --chart-palette-8
|
||||
'#6fcf97', // --chart-palette-9
|
||||
'#333333' // --chart-palette-10
|
||||
]
|
||||
const getLegendColorByIndex = (index) => legendPalette[index % legendPalette.length]
|
||||
// 使用统一的调色盘
|
||||
const getLegendColorByIndex = (index) => getColorByIndex(index)
|
||||
|
||||
|
||||
// 初始化数据库类型分布图 - 横向分段条
|
||||
@ -293,7 +264,7 @@ const initFileTypeChart = () => {
|
||||
show: true
|
||||
},
|
||||
data: data,
|
||||
color: legendPalette
|
||||
color: getColorPalette()
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@ -86,6 +86,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick, computed } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getColorByIndex, getChartColor, getColorPalette } from '@/utils/chartColors'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@ -197,12 +198,12 @@ const initToolsChart = () => {
|
||||
type: 'bar',
|
||||
data: data.map(item => item.count),
|
||||
itemStyle: {
|
||||
color: '#3996ae',
|
||||
color: getChartColor('primary'),
|
||||
borderRadius: [0, 4, 4, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#028ea0',
|
||||
color: getChartColor('primary'),
|
||||
shadowBlur: 10,
|
||||
shadowColor: 'rgba(2, 142, 160, 0.3)'
|
||||
}
|
||||
@ -257,7 +258,7 @@ const initErrorChart = () => {
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
},
|
||||
color: ['#3996ae', '#028ea0', '#00b8a9', '#f2c94c', '#eb5757', '#2f80ed', '#9b51e0', '#56ccf2', '#6fcf97', '#333333']
|
||||
color: getColorPalette()
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { getChartColor } from '@/utils/chartColors'
|
||||
import { dashboardApi } from '@/apis/dashboard_api'
|
||||
|
||||
// Props
|
||||
@ -105,7 +106,7 @@ const initActivityChart = () => {
|
||||
data: props.userStats.daily_active_users.map(item => item.active_users),
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#3996ae',
|
||||
color: getChartColor('primary'),
|
||||
width: 3
|
||||
},
|
||||
areaStyle: {
|
||||
@ -123,13 +124,13 @@ const initActivityChart = () => {
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#3996ae',
|
||||
color: getChartColor('primary'),
|
||||
borderWidth: 2,
|
||||
borderColor: '#fff'
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#24839a',
|
||||
color: getChartColor('primary'),
|
||||
borderWidth: 3,
|
||||
borderColor: '#fff',
|
||||
shadowBlur: 10,
|
||||
|
||||
146
web/src/utils/chartColors.js
Normal file
146
web/src/utils/chartColors.js
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Chart Color Palette Utility
|
||||
* 统一的图表调色盘工具函数
|
||||
* 从 CSS 变量中动态获取颜色,确保与主题保持一致
|
||||
*/
|
||||
|
||||
let colorPalette = []
|
||||
let isInitialized = false
|
||||
|
||||
/**
|
||||
* Build color palette from CSS variables in base.css
|
||||
* 从 base.css 中的 CSS 变量构建调色盘
|
||||
*/
|
||||
const buildColorPalette = () => {
|
||||
try {
|
||||
const root = document.documentElement
|
||||
const styles = getComputedStyle(root)
|
||||
|
||||
const pick = (name, fallback) => {
|
||||
const v = styles.getPropertyValue(name)
|
||||
return v && v.trim() ? v.trim() : fallback
|
||||
}
|
||||
|
||||
// Base chart colors
|
||||
const baseVars = [
|
||||
['--chart-primary', '#3996ae'],
|
||||
['--chart-success', '#52c41a'],
|
||||
['--chart-warning', '#faad14'],
|
||||
['--chart-error', '#f5222d'],
|
||||
['--chart-secondary', '#722ed1'],
|
||||
['--chart-accent', '#13c2c2'],
|
||||
]
|
||||
|
||||
// Extended palette colors
|
||||
const paletteVars = [
|
||||
['--chart-palette-1', '#3996ae'],
|
||||
['--chart-palette-2', '#028ea0'],
|
||||
['--chart-palette-3', '#00b8a9'],
|
||||
['--chart-palette-4', '#f2c94c'],
|
||||
['--chart-palette-5', '#eb5757'],
|
||||
['--chart-palette-6', '#2f80ed'],
|
||||
['--chart-palette-7', '#9b51e0'],
|
||||
['--chart-palette-8', '#56ccf2'],
|
||||
['--chart-palette-9', '#6fcf97'],
|
||||
['--chart-palette-10', '#333333'],
|
||||
]
|
||||
|
||||
const baseColors = baseVars.map(([n, f]) => pick(n, f))
|
||||
const paletteColors = paletteVars.map(([n, f]) => pick(n, f))
|
||||
|
||||
// Priority: palette first, then base colors
|
||||
const merged = [...paletteColors, ...baseColors]
|
||||
.filter(Boolean)
|
||||
.filter((c, idx, arr) => arr.indexOf(c) === idx) // Remove duplicates
|
||||
|
||||
colorPalette = merged
|
||||
isInitialized = true
|
||||
} catch (e) {
|
||||
console.warn('Failed to build color palette from CSS variables, using fallback:', e)
|
||||
// Fallback palette
|
||||
colorPalette = [
|
||||
'#3996ae', '#52c41a', '#faad14', '#f5222d', '#722ed1', '#13c2c2',
|
||||
'#fa8c16', '#1890ff', '#95de64', '#69c0ff'
|
||||
]
|
||||
isInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color by index from the palette
|
||||
* 根据索引从调色盘中获取颜色
|
||||
* @param {number} index - Color index
|
||||
* @returns {string} Color value
|
||||
*/
|
||||
export const getColorByIndex = (index) => {
|
||||
if (!isInitialized || colorPalette.length === 0) {
|
||||
buildColorPalette()
|
||||
}
|
||||
return colorPalette[index % colorPalette.length]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entire color palette
|
||||
* 获取完整的调色盘
|
||||
* @returns {Array<string>} Color palette array
|
||||
*/
|
||||
export const getColorPalette = () => {
|
||||
if (!isInitialized || colorPalette.length === 0) {
|
||||
buildColorPalette()
|
||||
}
|
||||
return [...colorPalette] // Return a copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific chart colors by name
|
||||
* 根据名称获取特定的图表颜色
|
||||
* @param {string} colorName - Color name (primary, success, warning, error, secondary, accent)
|
||||
* @returns {string} Color value
|
||||
*/
|
||||
export const getChartColor = (colorName) => {
|
||||
const colorMap = {
|
||||
primary: '#3996ae',
|
||||
success: '#52c41a',
|
||||
warning: '#faad14',
|
||||
error: '#f5222d',
|
||||
secondary: '#722ed1',
|
||||
accent: '#13c2c2'
|
||||
}
|
||||
|
||||
try {
|
||||
const root = document.documentElement
|
||||
const styles = getComputedStyle(root)
|
||||
const cssVarName = `--chart-${colorName}`
|
||||
const value = styles.getPropertyValue(cssVarName)
|
||||
return value && value.trim() ? value.trim() : colorMap[colorName] || colorMap.primary
|
||||
} catch (e) {
|
||||
return colorMap[colorName] || colorMap.primary
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate legend text for better display
|
||||
* 截断图例文本以便更好地显示
|
||||
* @param {string} name - Legend name
|
||||
* @param {number} maxLength - Maximum length (default: 20)
|
||||
* @returns {string} Truncated name
|
||||
*/
|
||||
export const truncateLegend = (name, maxLength = 20) => {
|
||||
if (!name) return ''
|
||||
return name.length > maxLength ? name.slice(0, maxLength) + '…' : name
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the color palette (call this when DOM is ready)
|
||||
* 初始化调色盘(在 DOM 准备好时调用)
|
||||
*/
|
||||
export const initColorPalette = () => {
|
||||
buildColorPalette()
|
||||
}
|
||||
|
||||
// Auto-initialize when module is loaded
|
||||
if (typeof window !== 'undefined' && document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initColorPalette)
|
||||
} else if (typeof window !== 'undefined') {
|
||||
initColorPalette()
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user