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 92e9056adae89832eab034726175fb7695e83684
Author: Ovilia <[email protected]>
AuthorDate: Fri Aug 29 15:14:57 2025 +0800

    feat: update themes
---
 src/stores/theme.ts         |  88 +++++++++++-
 src/utils/themeGenerator.ts | 318 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 403 insertions(+), 3 deletions(-)

diff --git a/src/stores/theme.ts b/src/stores/theme.ts
index 92ef40d..f1b8069 100644
--- a/src/stores/theme.ts
+++ b/src/stores/theme.ts
@@ -1,5 +1,6 @@
 import { ref, reactive } from 'vue'
 import type { ThemeData, PreDefinedTheme } from '../types/theme'
+import { generateEChartsTheme, generateThemeJsFile, 
generateThemeConfigForDownload } from '../utils/themeGenerator'
 
 // 预定义主题
 export const PRE_DEFINED_THEMES: PreDefinedTheme[] = [
@@ -208,9 +209,75 @@ export const useThemeStore = () => {
     themeName.value = 'customized'
   }
 
-  const loadPreDefinedTheme = (index: number) => {
+  const loadPreDefinedTheme = async (index: number) => {
+    console.log('🏪 loadPreDefinedTheme called with index:', index)
     const preTheme = PRE_DEFINED_THEMES[index]
-    if (preTheme) {
+    if (!preTheme) {
+      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`)
+      if (!response.ok) {
+        throw new Error(`Failed to load theme: ${preTheme.name}`)
+      }
+
+      const themeData = await response.json()
+      console.log('🏪 Loaded theme data:', themeData)
+
+      if (themeData.theme) {
+        // Convert string numbers to actual numbers
+        const loadedTheme = { ...themeData.theme }
+        if (typeof loadedTheme.seriesCnt === 'string') {
+          loadedTheme.seriesCnt = parseInt(loadedTheme.seriesCnt, 10)
+        }
+        if (typeof loadedTheme.borderWidth === 'string') {
+          loadedTheme.borderWidth = parseFloat(loadedTheme.borderWidth)
+        }
+        if (typeof loadedTheme.lineWidth === 'string') {
+          loadedTheme.lineWidth = parseFloat(loadedTheme.lineWidth)
+        }
+        if (typeof loadedTheme.symbolSize === 'string') {
+          loadedTheme.symbolSize = parseFloat(loadedTheme.symbolSize)
+        }
+        if (typeof loadedTheme.symbolBorderWidth === 'string') {
+          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 => {
+          if (key in theme) {
+            (theme as any)[key] = loadedTheme[key]
+          }
+        })
+
+        themeName.value = themeData.themeName || preTheme.name
+
+        // Ensure color is an array
+        if (typeof theme.color === 'string') {
+          theme.color = [theme.color]
+        }
+
+        // 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)
+      // Fallback to basic theme loading
+      console.log('🏪 Using fallback theme loading')
       theme.backgroundColor = preTheme.background
       theme.color = [...preTheme.theme]
       themeName.value = preTheme.name
@@ -248,6 +315,18 @@ export const useThemeStore = () => {
     return cleanedData
   }
 
+  const getEChartsTheme = (isToExport: boolean = false) => {
+    return generateEChartsTheme(theme, isToExport)
+  }
+
+  const getThemeJsFile = () => {
+    return generateThemeJsFile(theme, themeName.value)
+  }
+
+  const getThemeConfigForDownload = () => {
+    return generateThemeConfigForDownload(theme, themeName.value, 1)
+  }
+
   return {
     theme,
     themeName,
@@ -257,6 +336,9 @@ export const useThemeStore = () => {
     loadPreDefinedTheme,
     updateAxisSetting,
     importTheme,
-    exportTheme
+    exportTheme,
+    getEChartsTheme,
+    getThemeJsFile,
+    getThemeConfigForDownload
   }
 }
diff --git a/src/utils/themeGenerator.ts b/src/utils/themeGenerator.ts
new file mode 100644
index 0000000..28e3973
--- /dev/null
+++ b/src/utils/themeGenerator.ts
@@ -0,0 +1,318 @@
+import type { ThemeData } from '../types/theme'
+
+/**
+ * Generate ECharts theme configuration based on theme data
+ * @param themeData - Theme configuration data
+ * @param isToExport - Whether this is for export (includes backgroundColor)
+ * @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 [...]
+
+  // Generate series style configuration
+  const seriesStyle = {
+    itemStyle: {
+      borderWidth: themeData.symbolBorderWidth
+    },
+    lineStyle: {
+      width: themeData.lineWidth
+    },
+    symbolSize: themeData.symbolSize,
+    symbol: themeData.symbol,
+    smooth: themeData.lineSmooth
+  }
+
+  // Generate item style configuration
+  const itemStyle = {
+    borderWidth: themeData.borderWidth,
+    borderColor: themeData.borderColor
+  }
+
+  const border = {
+    itemStyle: itemStyle
+  } as any
+
+  // Special case for Halloween theme
+  if (themeData.symbol === 'halloween') {
+    seriesStyle.symbol = pumpkin
+    border.symbol = pumpkin
+  }
+
+  // Generate map configuration
+  const mapConfig = {
+    itemStyle: {
+      areaColor: themeData.mapAreaColor,
+      borderColor: themeData.mapBorderColor,
+      borderWidth: themeData.mapBorderWidth
+    },
+    label: {
+      color: themeData.mapLabelColor
+    },
+    emphasis: {
+      itemStyle: {
+        areaColor: themeData.mapAreaColorE,
+        borderColor: themeData.mapBorderColorE,
+        borderWidth: themeData.mapBorderWidthE
+      },
+      label: {
+        color: themeData.mapLabelColorE
+      }
+    }
+  }
+
+  // Generate axis configuration helper function
+  const getAxisConfig = (axisType: number) => {
+    let axisIndex = 0
+    if (themeData.axisSeperateSetting && axisType > 0 && axisType < 
themeData.axes.length) {
+      axisIndex = axisType
+    }
+
+    const axisData = themeData.axes[axisIndex]
+    return {
+      axisLine: {
+        show: axisData.axisLineShow,
+        lineStyle: {
+          color: axisData.axisLineColor
+        }
+      },
+      axisTick: {
+        show: axisData.axisTickShow,
+        lineStyle: {
+          color: axisData.axisTickColor
+        }
+      },
+      axisLabel: {
+        show: axisData.axisLabelShow,
+        color: axisData.axisLabelColor
+      },
+      splitLine: {
+        show: axisData.splitLineShow,
+        lineStyle: {
+          color: axisData.splitLineColor
+        }
+      },
+      splitArea: {
+        show: axisData.splitAreaShow,
+        areaStyle: {
+          color: axisData.splitAreaColor
+        }
+      }
+    }
+  }
+
+  // Generate graph style configuration
+  const graphStyle = {
+    ...seriesStyle,
+    color: themeData.color,
+    lineStyle: {
+      width: themeData.graphLineWidth,
+      color: themeData.graphLineColor
+    },
+    label: {
+      color: themeData.markTextColor
+    },
+    itemStyle: {
+      ...itemStyle,
+      borderWidth: themeData.borderWidth,
+      borderColor: themeData.borderColor
+    }
+  }
+
+  // Generate candlestick configuration
+  const candlestickConfig = {
+    itemStyle: {
+      color: themeData.kColor,
+      color0: themeData.kColor0,
+      borderColor: themeData.kBorderColor,
+      borderColor0: themeData.kBorderColor0,
+      borderWidth: themeData.kBorderWidth
+    }
+  }
+
+  // Generate bar configuration
+  const barConfig = {
+    itemStyle: {
+      barBorderWidth: themeData.borderWidth,
+      barBorderColor: themeData.borderColor
+    }
+  }
+
+  // Generate mark point configuration
+  const markPointConfig = {
+    label: {
+      color: themeData.markTextColor
+    },
+    emphasis: {
+      label: {
+        color: themeData.markTextColor
+      }
+    }
+  }
+
+  // Main theme configuration object
+  const themeConfig = {
+    color: themeData.color,
+    backgroundColor: isToExport ? themeData.backgroundColor : 'transparent',
+    textStyle: themeData.textColorShow ? {
+      color: themeData.textColor
+    } : {},
+    title: {
+      textStyle: {
+        color: themeData.titleColor
+      },
+      subtextStyle: {
+        color: themeData.subtitleColor
+      }
+    },
+    line: seriesStyle,
+    radar: seriesStyle,
+    bar: barConfig,
+    pie: border,
+    scatter: border,
+    boxplot: border,
+    parallel: border,
+    sankey: border,
+    funnel: border,
+    gauge: border,
+    candlestick: candlestickConfig,
+    graph: graphStyle,
+    map: mapConfig,
+    geo: mapConfig,
+    categoryAxis: getAxisConfig(1),
+    valueAxis: getAxisConfig(2),
+    logAxis: getAxisConfig(3),
+    timeAxis: getAxisConfig(4),
+    toolbox: {
+      iconStyle: {
+        borderColor: themeData.toolboxColor
+      },
+      emphasis: {
+        iconStyle: {
+          borderColor: themeData.toolboxEmphasisColor
+        }
+      }
+    },
+    legend: {
+      textStyle: {
+        color: themeData.legendTextColor
+      }
+    },
+    tooltip: {
+      axisPointer: {
+        lineStyle: {
+          color: themeData.tooltipAxisColor,
+          width: themeData.tooltipAxisWidth
+        },
+        crossStyle: {
+          color: themeData.tooltipAxisColor,
+          width: themeData.tooltipAxisWidth
+        }
+      }
+    },
+    timeline: {
+      lineStyle: {
+        color: themeData.timelineLineColor,
+        width: themeData.timelineLineWidth
+      },
+      itemStyle: {
+        color: themeData.timelineItemColor,
+        borderWidth: themeData.timelineItemBorderWidth
+      },
+      controlStyle: {
+        color: themeData.timelineControlColor,
+        borderColor: themeData.timelineControlBorderColor,
+        borderWidth: themeData.timelineControlBorderWidth
+      },
+      checkpointStyle: {
+        color: themeData.timelineCheckColor,
+        borderColor: themeData.timelineCheckBorderColor
+      },
+      label: {
+        color: themeData.timelineLabelColor
+      },
+      emphasis: {
+        itemStyle: {
+          color: themeData.timelineItemColorE
+        },
+        controlStyle: {
+          color: themeData.timelineControlColor,
+          borderColor: themeData.timelineControlBorderColor,
+          borderWidth: themeData.timelineControlBorderWidth
+        },
+        label: {
+          color: themeData.timelineLabelColor
+        }
+      }
+    },
+    visualMap: {
+      color: themeData.visualMapColor
+    },
+    markPoint: markPointConfig
+  }
+
+  return themeConfig
+}
+
+/**
+ * Generate JavaScript file content for theme export
+ * @param themeData - Theme configuration data
+ * @param themeName - Name of the theme
+ * @returns JavaScript file content as string
+ */
+export function generateThemeJsFile(themeData: ThemeData, themeName: string): 
string {
+  const themeConfig = generateEChartsTheme(themeData, true)
+
+  // Format theme with 4 spaces indentation
+  let themeJson = JSON.stringify(themeConfig, null, '    ')
+  // Indent each line with 4 spaces
+  themeJson = themeJson.split('\n').join('\n    ')
+
+  return `(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports', 'echarts'], factory);
+    } else if (typeof exports === 'object' && typeof exports.nodeName !== 
'string') {
+        // CommonJS
+        factory(exports, require('echarts'));
+    } else {
+        // Browser globals
+        factory({}, root.echarts);
+    }
+}(this, function (exports, echarts) {
+    var log = function (msg) {
+        if (typeof console !== 'undefined') {
+            console && console.error && console.error(msg);
+        }
+    };
+    if (!echarts) {
+        log('ECharts is not Loaded');
+        return;
+    }
+    echarts.registerTheme('${themeName}', ${themeJson});
+}));`
+}
+
+/**
+ * Generate theme configuration for download
+ * @param themeData - Theme configuration data
+ * @param themeName - Name of the theme
+ * @param version - Version number
+ * @returns Configuration object for download
+ */
+export function generateThemeConfigForDownload(themeData: ThemeData, 
themeName: string, version: number = 1) {
+  const cleanedTheme = { ...themeData }
+  // Remove duplicate axis option as it's included in axes
+  delete (cleanedTheme as any).axis
+
+  return {
+    version,
+    themeName,
+    theme: cleanedTheme
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to