This is an automated email from the ASF dual-hosted git repository. ovilia pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/echarts-theme-builder.git
commit 19fa4bd8fb2094d83c8ae57ca302713eecb5d06c Author: Ovilia <[email protected]> AuthorDate: Mon Sep 1 16:35:04 2025 +0800 feat: update theme working --- src/App.vue | 10 +++++--- src/components/ChartPreviewPanel.vue | 49 +++++++++++++++++++++++++++++------- src/components/ThemePanel.vue | 45 ++++++++++++++++++++++----------- src/i18n.ts | 12 ++++----- src/stores/theme.ts | 41 +++++++++++++++--------------- src/types/theme.ts | 28 ++++++++++----------- src/utils/themeGenerator.ts | 5 ---- 7 files changed, 118 insertions(+), 72 deletions(-) diff --git a/src/App.vue b/src/App.vue index 7e0b616..814e11b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,21 +1,23 @@ <script setup lang="ts"> +import { ref } from 'vue' // Simple fixed sidebar layout without responsive design import ChartPreviewPanel from './components/ChartPreviewPanel.vue' import ThemePanel from './components/ThemePanel.vue' + +// Get reference to chart preview panel +const chartPreviewRef = ref<InstanceType<typeof ChartPreviewPanel> | null>(null) </script> <template> <div id="theme-builder"> <div class="container-fluid" id="content"> <van-row class="row-container" :gutter="0"> - <!-- Left panel: Theme configuration - Fixed width --> <van-col span="6" class="theme-config"> - <ThemePanel /> + <ThemePanel :chart-preview-ref="chartPreviewRef" /> </van-col> - <!-- Right panel: Chart preview - Remaining width --> <van-col span="18" class="chart-container"> - <ChartPreviewPanel /> + <ChartPreviewPanel ref="chartPreviewRef" /> </van-col> </van-row> </div> diff --git a/src/components/ChartPreviewPanel.vue b/src/components/ChartPreviewPanel.vue index 2c2da56..d2ab378 100644 --- a/src/components/ChartPreviewPanel.vue +++ b/src/components/ChartPreviewPanel.vue @@ -23,8 +23,10 @@ import { ref, onMounted, onUnmounted, nextTick } from 'vue' import * as echarts from 'echarts' import { getChartConfigs } from '../utils/chartConfigs' +import { useThemeStore } from '../stores/theme' import type { ECharts } from 'echarts' +const themeStore = useThemeStore() const chartInstances = ref<ECharts[]>([]) const chartRefs = ref<(HTMLElement | null)[]>([]) @@ -38,23 +40,59 @@ function setChartRef(el: any, index: number) { } } +// Register and apply current theme +function registerCurrentTheme() { + const currentTheme = themeStore.getEChartsTheme(false) + echarts.registerTheme('customized', currentTheme) +} + // Initialize all charts function initializeCharts() { // Dispose existing charts chartInstances.value.forEach(chart => chart.dispose()) chartInstances.value = [] + // Register current theme + registerCurrentTheme() + // Create new chart instances displayedCharts.value.forEach((config, index) => { const container = chartRefs.value[index] if (container) { - const chart = echarts.init(container) + // Initialize chart with theme + const chart = echarts.init(container, 'customized') chart.setOption(config.option) chartInstances.value.push(chart) } }) } +// Update charts when theme changes +function updateCharts() { + if (chartInstances.value.length === 0) { + return + } + + // Get current theme and register with unique ID to force refresh + const currentTheme = themeStore.getEChartsTheme(false) + const themeId = `customized-${Date.now()}` + echarts.registerTheme(themeId, currentTheme) + + // Recreate charts with new theme + displayedCharts.value.forEach((config, index) => { + const container = chartRefs.value[index] + if (container && chartInstances.value[index]) { + chartInstances.value[index].dispose() + const chart = echarts.init(container, themeId) + chart.setOption(config.option) + chartInstances.value[index] = chart + } + }) +}// Expose updateCharts method for external calling +defineExpose({ + updateCharts +}) + // Resize charts when window resizes function handleResize() { chartInstances.value.forEach(chart => chart.resize()) @@ -99,7 +137,7 @@ onUnmounted(() => { flex: 1; display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); - gap: 20px; + gap: 12px; overflow-y: auto; padding-right: 8px; } @@ -108,13 +146,6 @@ onUnmounted(() => { background: #fff; border: 1px solid #e9ecef; border-radius: 8px; - padding: 16px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - transition: box-shadow 0.2s ease; -} - -.chart-item:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .chart-container { diff --git a/src/components/ThemePanel.vue b/src/components/ThemePanel.vue index 135f77d..8db099e 100644 --- a/src/components/ThemePanel.vue +++ b/src/components/ThemePanel.vue @@ -412,6 +412,13 @@ import { useThemeStore } from '../stores/theme' import { PRE_DEFINED_THEMES } from '../stores/theme' import ColorPicker from './ColorPicker.vue' import ColorList from './ColorList.vue' +import type ChartPreviewPanel from './ChartPreviewPanel.vue' + +// Props +interface Props { + chartPreviewRef?: InstanceType<typeof ChartPreviewPanel> | null +} +const props = defineProps<Props>() // Component state const activeNames = ref(['functions']) // Functions panel expanded by default @@ -427,7 +434,6 @@ const preDefinedThemes = PRE_DEFINED_THEMES // Methods const downloadTheme = () => { // TODO: Implement theme download - console.log('Download theme') } const importConfig = () => { @@ -436,12 +442,10 @@ const importConfig = () => { const exportConfig = () => { // TODO: Implement config export - console.log('Export config') } const refreshCharts = () => { // TODO: Implement chart refresh - console.log('Refresh charts') } const resetTheme = () => { @@ -450,11 +454,19 @@ const resetTheme = () => { const showHelp = () => { // TODO: Implement help dialog - console.log('Show help') } -const selectPreDefinedTheme = (index: number) => { - themeStore.loadPreDefinedTheme(index) +const selectPreDefinedTheme = async (index: number) => { + try { + await themeStore.loadPreDefinedTheme(index) + + // Manually trigger chart update + if (props.chartPreviewRef?.updateCharts) { + props.chartPreviewRef.updateCharts() + } + } catch (error) { + console.error('Error selecting predefined theme:', error) + } } const onAxisSettingChange = () => { @@ -525,15 +537,18 @@ const handleFileImport = (event: Event) => { } .theme-item { - padding: 8px; - border: 1px solid #ebedf0; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + width: auto; + height: 22px; + margin-bottom: 5px; + overflow: hidden; + border: 1px solid #eee; + padding: 5px; border-radius: 4px; cursor: pointer; transition: all 0.2s; - min-height: 60px; - display: flex; - flex-wrap: wrap; - align-content: flex-start; gap: 2px; } @@ -543,10 +558,12 @@ const handleFileImport = (event: Event) => { } .color-dot { - width: 12px; - height: 12px; + width: 20px; + height: 20px; + margin: 0 2px 4px 2px; border-radius: 2px; border: 1px solid rgba(0, 0, 0, 0.1); + flex-shrink: 0; } .axis-group { diff --git a/src/i18n.ts b/src/i18n.ts index ed8b5d4..bfcad36 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -2,13 +2,13 @@ import { createI18n } from 'vue-i18n' import en from './locales/en.json' import zh from './locales/zh.json' -// 从 localStorage 获取保存的语言设置,默认为中文 +// Get saved language setting from localStorage, default to Chinese const getStoredLanguage = (): string => { const stored = localStorage.getItem('echarts-theme-builder-locale') if (stored && ['en', 'zh'].includes(stored)) { return stored } - // 根据浏览器语言自动选择 + // Auto-select based on browser language const browserLang = navigator.language.toLowerCase() if (browserLang.startsWith('zh')) { return 'zh' @@ -17,7 +17,7 @@ const getStoredLanguage = (): string => { } const i18n = createI18n({ - legacy: false, // 使用 Composition API 模式 + legacy: false, // Use Composition API mode locale: getStoredLanguage(), fallbackLocale: 'en', messages: { @@ -26,7 +26,7 @@ const i18n = createI18n({ } }) -// 保存语言设置到 localStorage +// Save language setting to localStorage export const setLocale = (locale: string) => { if (['en', 'zh'].includes(locale)) { i18n.global.locale.value = locale as 'en' | 'zh' @@ -34,12 +34,12 @@ export const setLocale = (locale: string) => { } } -// 获取当前语言 +// Get current language export const getCurrentLocale = () => { return i18n.global.locale.value } -// 获取可用语言列表 +// Get available languages list export const getAvailableLocales = () => { return [ { code: 'en', name: 'English' }, diff --git a/src/stores/theme.ts b/src/stores/theme.ts index f1b8069..4be2c75 100644 --- a/src/stores/theme.ts +++ b/src/stores/theme.ts @@ -2,7 +2,7 @@ import { ref, reactive } from 'vue' import type { ThemeData, PreDefinedTheme } from '../types/theme' import { generateEChartsTheme, generateThemeJsFile, generateThemeConfigForDownload } from '../utils/themeGenerator' -// 预定义主题 +// Predefined themes configuration export const PRE_DEFINED_THEMES: PreDefinedTheme[] = [ { name: 'vintage', @@ -108,13 +108,13 @@ export const PRE_DEFINED_THEMES: PreDefinedTheme[] = [ } ] -// 默认主题配置 +// Default theme axes configuration const createDefaultAxes = () => { const types = ['all', 'category', 'value', 'log', 'time'] - const names = ['通用', '类目', '数值', '对数', '时间'] + const names = ['General', 'Category', 'Value', 'Log', 'Time'] return types.map((type, i) => ({ type, - name: names[i] + '坐标轴', + name: names[i] + ' Axis', axisLineShow: type !== 'value' && type !== 'log', axisLineColor: '#6E7079', axisTickShow: type !== 'value' && type !== 'log', @@ -194,8 +194,10 @@ export const createDefaultTheme = (): ThemeData => { } } -// 全局状态管理 -export const useThemeStore = () => { +// Global state management - Singleton pattern +let themeStoreInstance: ReturnType<typeof createThemeStore> | null = null + +const createThemeStore = () => { const theme = reactive<ThemeData>(createDefaultTheme()) const themeName = ref('customized') const isPauseChartUpdating = ref(false) @@ -210,15 +212,12 @@ export const useThemeStore = () => { } const loadPreDefinedTheme = async (index: number) => { - console.log('🏪 loadPreDefinedTheme called with index:', index) const preTheme = PRE_DEFINED_THEMES[index] if (!preTheme) { - console.error('❌ No theme found at index:', index) + console.error('No theme found at index:', index) return } - console.log('🏪 Loading theme:', preTheme.name) - try { // Load the complete theme configuration from JSON file const response = await fetch(`/themes/${preTheme.name}.json`) @@ -227,7 +226,6 @@ export const useThemeStore = () => { } const themeData = await response.json() - console.log('🏪 Loaded theme data:', themeData) if (themeData.theme) { // Convert string numbers to actual numbers @@ -248,8 +246,6 @@ export const useThemeStore = () => { loadedTheme.symbolBorderWidth = parseFloat(loadedTheme.symbolBorderWidth) } - console.log('🏪 Before applying theme:', { ...theme }) - // Apply the complete theme configuration property by property to ensure reactivity // This ensures Vue's reactive system properly detects changes Object.keys(loadedTheme).forEach(key => { @@ -268,16 +264,12 @@ export const useThemeStore = () => { // Update axis settings based on axisSeperateSetting updateAxisSetting() - console.log('🏪 After applying theme:', theme) - console.log('🏪 Theme name set to:', themeName.value) - // Force trigger reactive update by modifying a dummy property ;(theme as any).__forceUpdate = Date.now() } } catch (error) { - console.error('❌ Error loading predefined theme:', error) + console.error('Error loading predefined theme:', error) // Fallback to basic theme loading - console.log('🏪 Using fallback theme loading') theme.backgroundColor = preTheme.background theme.color = [...preTheme.theme] themeName.value = preTheme.name @@ -310,13 +302,15 @@ export const useThemeStore = () => { const exportTheme = () => { const exportData = { ...theme } - // 删除重复的 axis 选项,因为它已经包含在 theme.axes 中 + // Remove duplicate axis options since they are already included in theme.axes const { axis, ...cleanedData } = exportData return cleanedData } const getEChartsTheme = (isToExport: boolean = false) => { - return generateEChartsTheme(theme, isToExport) + // Convert reactive object to plain object to ensure proper data passing + const plainTheme = JSON.parse(JSON.stringify(theme)) + return generateEChartsTheme(plainTheme, isToExport) } const getThemeJsFile = () => { @@ -342,3 +336,10 @@ export const useThemeStore = () => { getThemeConfigForDownload } } + +export const useThemeStore = () => { + if (!themeStoreInstance) { + themeStoreInstance = createThemeStore() + } + return themeStoreInstance +} diff --git a/src/types/theme.ts b/src/types/theme.ts index fe65437..708e09f 100644 --- a/src/types/theme.ts +++ b/src/types/theme.ts @@ -1,4 +1,4 @@ -// ECharts 主题类型定义 +// ECharts theme type definitions export interface ThemeAxis { type: string name: string @@ -15,7 +15,7 @@ export interface ThemeAxis { } export interface ThemeData { - // 基础配置 + // Basic configuration seriesCnt: number backgroundColor: string titleColor: string @@ -24,38 +24,38 @@ export interface ThemeData { textColor: string markTextColor: string - // 主色板 + // Main color palette color: string[] - // 边框 + // Border borderColor: string borderWidth: number - // 视觉映射 + // Visual mapping visualMapColor: string[] - // 图例 + // Legend legendTextColor: string - // K线图 + // Candlestick chart kColor: string kColor0: string kBorderColor: string kBorderColor0: string kBorderWidth: number - // 线条 + // Line chart lineWidth: number symbolSize: number symbol: string symbolBorderWidth: number lineSmooth: boolean - // 关系图 + // Graph/Network chart graphLineWidth: number graphLineColor: string - // 地图 + // Map mapLabelColor: string mapLabelColorE: string mapBorderColor: string @@ -65,20 +65,20 @@ export interface ThemeData { mapAreaColor: string mapAreaColorE: string - // 坐标轴 + // Coordinate axis axes: ThemeAxis[] axisSeperateSetting: boolean axis: ThemeAxis[] | null - // 工具箱 + // Toolbox toolboxColor: string toolboxEmphasisColor: string - // 提示框 + // Tooltip tooltipAxisColor: string tooltipAxisWidth: number - // 时间轴 + // Timeline timelineLineColor: string timelineLineWidth: number timelineItemColor: string diff --git a/src/utils/themeGenerator.ts b/src/utils/themeGenerator.ts index 28e3973..b9dc843 100644 --- a/src/utils/themeGenerator.ts +++ b/src/utils/themeGenerator.ts @@ -7,11 +7,6 @@ import type { ThemeData } from '../types/theme' * @returns ECharts theme configuration object */ export function generateEChartsTheme(themeData: ThemeData, isToExport: boolean = false) { - console.log('🎨 generateEChartsTheme called with:', themeData) - console.log('🎨 Theme backgroundColor:', themeData.backgroundColor) - console.log('🎨 Theme colors:', themeData.color) - console.log('🎨 Theme titleColor:', themeData.titleColor) - // Halloween pumpkin symbol path const pumpkin = 'path://M237.062,81.761L237.062,81.761c-12.144-14.24-25.701-20.1-40.68-19.072 c-10.843,0.747-20.938,5.154-30.257,13.127c-9.51-5.843-19.8-9.227-30.859-10.366c0.521-3.197,1.46-6.306,2.85-9.363 c3.458-7.038,8.907-12.741,16.331-17.296c-5.609-3.384-11.227-6.799-16.854-10.279c-16.257,8.104-25.06,20.601-26.463,38.417 c-7.599,1.705-14.685,4.486-21.247,8.437c-9.164-7.677-18.996-11.917-29.496-12.632c-14.819-0.998-28.467,4.787-40.938,18.827 C6.445,96.182,0,114.867,0,136.242c-0.007 [...] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
