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 78c3f3c12ed17247e2a4d1d76e5eafe0086f1b50
Author: Ovilia <[email protected]>
AuthorDate: Wed Aug 27 15:47:57 2025 +0800

    refactor: basic layouot
---
 .gitignore                     |   1 +
 index.html                     |   4 +-
 package-lock.json              | 309 ++++++++++++++++++-
 package.json                   |   6 +-
 src/App.vue                    |  13 +-
 src/components/ColorList.vue   | 159 ++++++++++
 src/components/ColorPicker.vue |  83 ++++++
 src/components/ThemePanel.vue  | 661 +++++++++++++++++++++++++++++++++++++++++
 src/main.ts                    |  22 +-
 src/stores/theme.ts            |  37 ++-
 src/utils/chartOptions.ts      | 328 --------------------
 11 files changed, 1279 insertions(+), 344 deletions(-)

diff --git a/.gitignore b/.gitignore
index e9fa7f0..6c37f57 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ dist
 app/bower_components
 test/bower_components
 app/vendors/echarts.js
+.DS_Store
diff --git a/index.html b/index.html
index dde16aa..8471fe0 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,9 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vite + Vue + TS</title>
+    <link rel="shortcut icon" 
href="https://echarts.apache.org/zh/images/favicon.png?_v_=20240226";>
+    <title>Apache ECharts Theme Builder</title>
   </head>
   <body>
     <div id="app"></div>
diff --git a/package-lock.json b/package-lock.json
index f36d78a..e0f423a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,7 +8,11 @@
       "name": "echarts-theme-builder",
       "version": "0.0.0",
       "dependencies": {
-        "vue": "^3.5.18"
+        "echarts": "^6.0.0",
+        "vant": "^4.9.21",
+        "vue": "^3.5.18",
+        "vue-i18n": "^9.14.5",
+        "vue3-colorpicker": "^2.3.0"
       },
       "devDependencies": {
         "@vitejs/plugin-vue": "^6.0.1",
@@ -18,6 +22,12 @@
         "vue-tsc": "^3.0.5"
       }
     },
+    "node_modules/@aesoper/normal-utils": {
+      "version": "0.1.5",
+      "resolved": 
"https://registry.npmjs.org/@aesoper/normal-utils/-/normal-utils-0.1.5.tgz";,
+      "integrity": 
"sha512-LFF/6y6h5mfwhnJaWqqxuC8zzDaHCG62kMRkd8xhDtq62TQj9dM17A9DhE87W7DhiARJsHLgcina/9P4eNCN1w==",
+      "license": "MIT"
+    },
     "node_modules/@babel/helper-string-parser": {
       "version": "7.27.1",
       "resolved": 
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz";,
@@ -506,12 +516,66 @@
         "node": ">=18"
       }
     },
+    "node_modules/@intlify/core-base": {
+      "version": "9.14.5",
+      "resolved": 
"https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz";,
+      "integrity": 
"sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/message-compiler": "9.14.5",
+        "@intlify/shared": "9.14.5"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon";
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.14.5",
+      "resolved": 
"https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz";,
+      "integrity": 
"sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "9.14.5",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon";
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.14.5",
+      "resolved": 
"https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz";,
+      "integrity": 
"sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon";
+      }
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.5.5",
       "resolved": 
"https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz";,
       "integrity": 
"sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
       "license": "MIT"
     },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": 
"https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz";,
+      "integrity": 
"sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs";
+      }
+    },
     "node_modules/@rolldown/pluginutils": {
       "version": "1.0.0-beta.29",
       "resolved": 
"https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz";,
@@ -806,6 +870,27 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.20",
+      "resolved": 
"https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz";,
+      "integrity": 
"sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
+      "license": "MIT"
+    },
+    "node_modules/@vant/popperjs": {
+      "version": "1.3.0",
+      "resolved": 
"https://registry.npmjs.org/@vant/popperjs/-/popperjs-1.3.0.tgz";,
+      "integrity": 
"sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==",
+      "license": "MIT"
+    },
+    "node_modules/@vant/use": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@vant/use/-/use-1.6.0.tgz";,
+      "integrity": 
"sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/@vitejs/plugin-vue": {
       "version": "6.0.1",
       "resolved": 
"https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz";,
@@ -913,6 +998,12 @@
         "he": "^1.2.0"
       }
     },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": 
"https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz";,
+      "integrity": 
"sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
     "node_modules/@vue/language-core": {
       "version": "3.0.6",
       "resolved": 
"https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.6.tgz";,
@@ -1007,6 +1098,94 @@
         }
       }
     },
+    "node_modules/@vueuse/core": {
+      "version": "10.11.1",
+      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz";,
+      "integrity": 
"sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.20",
+        "@vueuse/metadata": "10.11.1",
+        "@vueuse/shared": "10.11.1",
+        "vue-demi": ">=0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu";
+      }
+    },
+    "node_modules/@vueuse/core/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz";,
+      "integrity": 
"sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu";
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "10.11.1",
+      "resolved": 
"https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz";,
+      "integrity": 
"sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu";
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "10.11.1",
+      "resolved": 
"https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz";,
+      "integrity": 
"sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
+      "license": "MIT",
+      "dependencies": {
+        "vue-demi": ">=0.14.8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu";
+      }
+    },
+    "node_modules/@vueuse/shared/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz";,
+      "integrity": 
"sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu";
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/alien-signals": {
       "version": "2.0.7",
       "resolved": 
"https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.7.tgz";,
@@ -1027,6 +1206,16 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/echarts": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz";,
+      "integrity": 
"sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "6.0.0"
+      }
+    },
     "node_modules/entities": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz";,
@@ -1120,6 +1309,14 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/gradient-parser": {
+      "version": "1.1.1",
+      "resolved": 
"https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.1.1.tgz";,
+      "integrity": 
"sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/he": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz";,
@@ -1130,6 +1327,21 @@
         "he": "bin/he"
       }
     },
+    "node_modules/is-plain-object": {
+      "version": "5.0.0",
+      "resolved": 
"https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz";,
+      "integrity": 
"sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": 
"https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz";,
+      "integrity": 
"sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
     "node_modules/magic-string": {
       "version": "0.30.18",
       "resolved": 
"https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz";,
@@ -1267,6 +1479,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/tinycolor2": {
+      "version": "1.6.0",
+      "resolved": 
"https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz";,
+      "integrity": 
"sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
+      "license": "MIT"
+    },
     "node_modules/tinyglobby": {
       "version": "0.2.14",
       "resolved": 
"https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz";,
@@ -1284,6 +1502,12 @@
         "url": "https://github.com/sponsors/SuperchupuDev";
       }
     },
+    "node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz";,
+      "integrity": 
"sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
+      "license": "0BSD"
+    },
     "node_modules/typescript": {
       "version": "5.8.3",
       "resolved": 
"https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz";,
@@ -1298,6 +1522,20 @@
         "node": ">=14.17"
       }
     },
+    "node_modules/vant": {
+      "version": "4.9.21",
+      "resolved": "https://registry.npmjs.org/vant/-/vant-4.9.21.tgz";,
+      "integrity": 
"sha512-hXUoZMrLLjykimFRLDlGNd+K2iYSRh9YwLMKnsVdVZ+9inUKxpqnjhOqlZwocbnYkvJlS+febf9u9aJpDol4Pw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vant/popperjs": "^1.3.0",
+        "@vant/use": "^1.6.0",
+        "@vue/shared": "^3.5.17"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vite": {
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz";,
@@ -1401,6 +1639,26 @@
         }
       }
     },
+    "node_modules/vue-i18n": {
+      "version": "9.14.5",
+      "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz";,
+      "integrity": 
"sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "9.14.5",
+        "@intlify/shared": "9.14.5",
+        "@vue/devtools-api": "^6.5.0"
+      },
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/kazupon";
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vue-tsc": {
       "version": "3.0.6",
       "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.6.tgz";,
@@ -1417,6 +1675,55 @@
       "peerDependencies": {
         "typescript": ">=5.0.0"
       }
+    },
+    "node_modules/vue-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-4.2.1.tgz";,
+      "integrity": 
"sha512-DNQZmJuOvovLUIp0BENRkdnZHbI0V4e2mNvjAZOAXKD56YGvRchtUYOXA/XqTxdv7Ng5SJLZqRKRpAhm5NLaPQ==",
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "5.0.0"
+      },
+      "engines": {
+        "node": ">=12.16.0"
+      },
+      "peerDependencies": {
+        "vue": "^2.0.0 || ^3.0.0"
+      }
+    },
+    "node_modules/vue3-colorpicker": {
+      "version": "2.3.0",
+      "resolved": 
"https://registry.npmjs.org/vue3-colorpicker/-/vue3-colorpicker-2.3.0.tgz";,
+      "integrity": 
"sha512-e3lLmBcy7mkRrNQVeUny1DjOd6E11L8H5ok5Bx4MdXmrG+RzyacRF7KkhrEWmRYPhKAsaoUrWsFkmpPAaYnE5A==",
+      "license": "MIT",
+      "dependencies": {
+        "@aesoper/normal-utils": "^0.1.5",
+        "@popperjs/core": "^2.11.8",
+        "@vueuse/core": "^10.1.2",
+        "gradient-parser": "^1.0.2",
+        "lodash-es": "^4.17.21",
+        "tinycolor2": "^1.4.2",
+        "vue-types": "^4.1.0"
+      },
+      "peerDependencies": {
+        "@aesoper/normal-utils": "^0.1.5",
+        "@popperjs/core": "^2.11.8",
+        "@vueuse/core": "^10.1.2",
+        "gradient-parser": "^1.0.2",
+        "lodash-es": "^4.17.21",
+        "tinycolor2": "^1.4.2",
+        "vue": "^3.2.6",
+        "vue-types": "^4.1.0"
+      }
+    },
+    "node_modules/zrender": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz";,
+      "integrity": 
"sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index 4527489..238eda8 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,11 @@
     "preview": "vite preview"
   },
   "dependencies": {
-    "vue": "^3.5.18"
+    "echarts": "^6.0.0",
+    "vant": "^4.9.21",
+    "vue": "^3.5.18",
+    "vue-i18n": "^9.14.5",
+    "vue3-colorpicker": "^2.3.0"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^6.0.1",
diff --git a/src/App.vue b/src/App.vue
index d5cc463..7e0b616 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,6 +1,7 @@
 <script setup lang="ts">
 // Simple fixed sidebar layout without responsive design
 import ChartPreviewPanel from './components/ChartPreviewPanel.vue'
+import ThemePanel from './components/ThemePanel.vue'
 </script>
 
 <template>
@@ -9,10 +10,7 @@ import ChartPreviewPanel from 
'./components/ChartPreviewPanel.vue'
       <van-row class="row-container" :gutter="0">
         <!-- Left panel: Theme configuration - Fixed width -->
         <van-col span="6" class="theme-config">
-          <!-- Theme configuration panel will be implemented here -->
-          <div class="placeholder">
-            Theme Configuration Panel
-          </div>
+          <ThemePanel />
         </van-col>
 
         <!-- Right panel: Chart preview - Remaining width -->
@@ -45,9 +43,10 @@ import ChartPreviewPanel from 
'./components/ChartPreviewPanel.vue'
 .theme-config {
   height: 100vh;
   overflow-y: auto;
-  background-color: #f8f9fa;
-  border-right: 1px solid #dee2e6;
-  padding: 20px;
+  background-color: #ffffff;
+  border-right: 1px solid #ccc;
+  border-bottom: 1px solid #ccc;
+  padding: 0;
   box-sizing: border-box;
   flex: 0 0 25%; /* Fixed 25% width */
 }
diff --git a/src/components/ColorList.vue b/src/components/ColorList.vue
new file mode 100644
index 0000000..696ef8c
--- /dev/null
+++ b/src/components/ColorList.vue
@@ -0,0 +1,159 @@
+<template>
+  <van-field :label="label">
+    <template #input>
+      <van-checkbox v-if="canDisable" :model-value="enabled" 
@update:model-value="$emit('update:enabled', $event)" style="margin-right: 
8px;" />
+      <div class="color-list-wrapper" v-show="!canDisable || enabled">
+        <div class="color-items">
+          <div
+            v-for="(color, index) in modelValue"
+            :key="index"
+            class="color-item"
+          >
+            <ColorPicker
+              :pureColor="color"
+              @pureColorChange="(newColor: string) => updateColor(index, 
newColor)"
+              format="hex"
+              :pickerType="'fk'"
+              :shape="'square'"
+              :size="24"
+            />
+            <van-field
+              :model-value="color"
+              @update:model-value="(value: string) => updateColorText(index, 
value)"
+              placeholder="#000000"
+              class="color-text"
+            />
+            <van-button
+              v-if="modelValue.length > 1"
+              type="danger"
+              size="mini"
+              @click="removeColor(index)"
+              icon="cross"
+            />
+          </div>
+        </div>
+        <div class="color-actions">
+          <van-button
+            type="primary"
+            size="small"
+            @click="addColor"
+            icon="plus"
+          >
+            增加
+          </van-button>
+          <van-button
+            v-if="modelValue.length > 1"
+            size="small"
+            @click="removeLastColor"
+          >
+            减少
+          </van-button>
+        </div>
+      </div>
+    </template>
+  </van-field>
+</template>
+
+<script setup lang="ts">
+import { defineProps, defineEmits } from 'vue'
+import { ColorPicker } from 'vue3-colorpicker'
+import 'vue3-colorpicker/style.css'
+
+interface Props {
+  modelValue: string[]
+  label: string
+  canDisable?: boolean
+  enabled?: boolean
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: string[]): void
+  (e: 'update:enabled', value: boolean): void
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  canDisable: false,
+  enabled: true
+})
+
+const emit = defineEmits<Emits>()
+
+const updateColor = (index: number, color: string) => {
+  const newColors = [...props.modelValue]
+  newColors[index] = color
+  emit('update:modelValue', newColors)
+}
+
+const updateColorText = (index: number, value: string) => {
+  // Validate color format
+  const colorRegex = 
/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$|^rgba?\([^)]+\)$|^[a-zA-Z]+$/
+  if (colorRegex.test(value) || value === '') {
+    const newColors = [...props.modelValue]
+    newColors[index] = value
+    emit('update:modelValue', newColors)
+  }
+}
+
+const addColor = () => {
+  const newColors = [...props.modelValue, '#333333']
+  emit('update:modelValue', newColors)
+}
+
+const removeColor = (index: number) => {
+  if (props.modelValue.length > 1) {
+    const newColors = props.modelValue.filter((_, i) => i !== index)
+    emit('update:modelValue', newColors)
+  }
+}
+
+const removeLastColor = () => {
+  if (props.modelValue.length > 1) {
+    const newColors = [...props.modelValue]
+    newColors.pop()
+    emit('update:modelValue', newColors)
+  }
+}
+</script>
+
+<style scoped>
+.color-list-wrapper {
+  width: 100%;
+}
+
+.color-items {
+  margin-bottom: 12px;
+}
+
+.color-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+}
+
+.color-text {
+  flex: 1;
+  min-width: 0;
+}
+
+.color-actions {
+  display: flex;
+  gap: 8px;
+}
+
+:deep(.van-field__control) {
+  display: flex;
+  align-items: flex-start;
+}
+
+:deep(.van-button--mini) {
+  padding: 4px;
+  min-width: 32px;
+}
+
+:deep(.vc-color-wrap) {
+  border-radius: 4px;
+  border: 1px solid #dcdee0;
+  flex-shrink: 0;
+}
+</style>
diff --git a/src/components/ColorPicker.vue b/src/components/ColorPicker.vue
new file mode 100644
index 0000000..d79e58d
--- /dev/null
+++ b/src/components/ColorPicker.vue
@@ -0,0 +1,83 @@
+<template>
+  <van-field :label="label">
+    <template #input>
+      <van-checkbox v-if="canDisable" :model-value="enabled" 
@update:model-value="$emit('update:enabled', $event)" style="margin-right: 
8px;" />
+      <div class="color-picker-wrapper" v-show="!canDisable || enabled">
+        <ColorPicker
+          :pureColor="modelValue"
+          @pureColorChange="handleColorChange"
+          format="hex"
+          :pickerType="'fk'"
+          :shape="'square'"
+          :size="32"
+        />
+        <van-field
+          :model-value="modelValue"
+          @update:model-value="handleTextChange"
+          placeholder="#000000"
+          class="color-text"
+        />
+      </div>
+    </template>
+  </van-field>
+</template>
+
+<script setup lang="ts">
+import { defineProps, defineEmits } from 'vue'
+import { ColorPicker } from 'vue3-colorpicker'
+import 'vue3-colorpicker/style.css'
+
+interface Props {
+  modelValue: string
+  label: string
+  canDisable?: boolean
+  enabled?: boolean
+}
+
+interface Emits {
+  (e: 'update:modelValue', value: string): void
+  (e: 'update:enabled', value: boolean): void
+}
+
+withDefaults(defineProps<Props>(), {
+  canDisable: false,
+  enabled: true
+})
+
+const emit = defineEmits<Emits>()
+
+const handleColorChange = (color: string) => {
+  emit('update:modelValue', color)
+}
+
+const handleTextChange = (value: string) => {
+  // Validate color format
+  const colorRegex = 
/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$|^rgba?\([^)]+\)$|^[a-zA-Z]+$/
+  if (colorRegex.test(value) || value === '') {
+    emit('update:modelValue', value)
+  }
+}
+</script>
+
+<style scoped>
+.color-picker-wrapper {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  width: 100%;
+}
+
+.color-text {
+  flex: 1;
+}
+
+:deep(.van-field__control) {
+  display: flex;
+  align-items: center;
+}
+
+:deep(.vc-color-wrap) {
+  border-radius: 4px;
+  border: 1px solid #dcdee0;
+}
+</style>
diff --git a/src/components/ThemePanel.vue b/src/components/ThemePanel.vue
new file mode 100644
index 0000000..135f77d
--- /dev/null
+++ b/src/components/ThemePanel.vue
@@ -0,0 +1,661 @@
+<template>
+  <div class="theme-panel">
+    <!-- Functions Section -->
+    <van-collapse v-model="activeNames">
+      <van-collapse-item title="功能" name="functions">
+        <div class="panel-content">
+          <!-- Action Buttons -->
+          <div class="action-buttons">
+            <van-button type="primary" size="small" @click="downloadTheme">
+              <van-icon name="down" />
+              下载主题
+            </van-button>
+            <van-button size="small" @click="importConfig">
+              <van-icon name="upgrade" />
+              导入配置
+            </van-button>
+            <van-button size="small" @click="exportConfig">
+              <van-icon name="share" />
+              导出配置
+            </van-button>
+          </div>
+
+          <div class="action-buttons">
+            <van-button size="small" @click="refreshCharts">
+              <van-icon name="replay" />
+              刷新
+            </van-button>
+            <van-button size="small" @click="resetTheme">
+              <van-icon name="delete" />
+              复原
+            </van-button>
+            <van-button size="small" @click="showHelp">
+              <van-icon name="question" />
+              帮助
+            </van-button>
+          </div>
+
+          <!-- Theme Name and Series Count -->
+          <van-field
+            v-model="themeName"
+            label="主题名称"
+            placeholder="请输入主题名称"
+          />
+
+          <van-field
+            v-model.number="theme.seriesCnt"
+            type="number"
+            label="系列数量"
+            placeholder="请输入系列数量"
+          />
+
+          <!-- Predefined Themes -->
+          <div class="predefined-themes">
+            <h4>默认方案</h4>
+            <div class="theme-grid">
+              <div
+                v-for="(themeItem, index) in preDefinedThemes"
+                :key="index"
+                class="theme-item"
+                :style="{ backgroundColor: themeItem.background }"
+                :title="themeItem.name"
+                @click="selectPreDefinedTheme(index)"
+              >
+                <div
+                  v-for="color in themeItem.theme"
+                  :key="color"
+                  class="color-dot"
+                  :style="{ backgroundColor: color }"
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+      </van-collapse-item>
+
+      <!-- Basic Configuration -->
+      <van-collapse-item title="基本配置" name="basic">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.backgroundColor"
+            label="背景"
+          />
+          <ColorPicker
+            v-model="theme.titleColor"
+            label="标题"
+          />
+          <ColorPicker
+            v-model="theme.subtitleColor"
+            label="副标题"
+          />
+          <ColorList
+            v-model="theme.color"
+            label="主题"
+          />
+          <ColorPicker
+            v-model="theme.markTextColor"
+            label="标签文字"
+          />
+          <van-field
+            v-model.number="theme.borderWidth"
+            type="number"
+            label="描边粗细"
+          />
+          <ColorPicker
+            v-model="theme.borderColor"
+            label="描边"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Visual Map -->
+      <van-collapse-item title="视觉映射" name="visualMap">
+        <div class="panel-content">
+          <ColorList
+            v-model="theme.visualMapColor"
+            label="视觉映射"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Coordinate Axis -->
+      <van-collapse-item title="坐标轴" name="axis">
+        <div class="panel-content">
+          <van-field label="坐标轴设置">
+            <template #input>
+              <van-checkbox v-model="theme.axisSeperateSetting" 
@change="onAxisSettingChange">
+                为不同类型坐标轴分别设置
+              </van-checkbox>
+            </template>
+          </van-field>
+
+          <div
+            v-for="(axis, index) in theme.axis"
+            :key="index"
+            class="axis-group"
+          >
+            <h4 v-if="axis.type !== 'all'">{{ axis.name }}</h4>
+
+            <ColorPicker
+              v-model="axis.axisLineColor"
+              :label="'轴线'"
+              :can-disable="true"
+              v-model:enabled="axis.axisLineShow"
+            />
+            <ColorPicker
+              v-model="axis.axisTickColor"
+              :label="'刻度'"
+              :can-disable="true"
+              v-model:enabled="axis.axisTickShow"
+            />
+            <ColorList
+              v-model="axis.splitLineColor"
+              :label="'网格'"
+              :can-disable="true"
+              v-model:enabled="axis.splitLineShow"
+            />
+            <ColorList
+              v-model="axis.splitAreaColor"
+              :label="'填充'"
+              :can-disable="true"
+              v-model:enabled="axis.splitAreaShow"
+            />
+            <ColorPicker
+              v-model="axis.axisLabelColor"
+              :label="'文字'"
+              :can-disable="true"
+              v-model:enabled="axis.axisLabelShow"
+            />
+          </div>
+        </div>
+      </van-collapse-item>
+
+      <!-- Legend -->
+      <van-collapse-item title="图例" name="legend">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.legendTextColor"
+            label="文字"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Toolbox -->
+      <van-collapse-item title="工具箱" name="toolbox">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.toolboxColor"
+            label="图标"
+          />
+          <ColorPicker
+            v-model="theme.toolboxEmphasisColor"
+            label="悬停"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Tooltip -->
+      <van-collapse-item title="提示框" name="tooltip">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.tooltipAxisColor"
+            label="指示线"
+          />
+          <van-field
+            v-model.number="theme.tooltipAxisWidth"
+            type="number"
+            label="宽度"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Timeline -->
+      <van-collapse-item title="时间轴" name="timeline">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.timelineItemColor"
+            label="标记"
+          />
+          <ColorPicker
+            v-model="theme.timelineItemColorE"
+            label="标记悬停"
+          />
+          <ColorPicker
+            v-model="theme.timelineCheckColor"
+            label="标记选中"
+          />
+          <ColorPicker
+            v-model="theme.timelineCheckBorderColor"
+            label="标记选中描边"
+          />
+          <van-field
+            v-model.number="theme.timelineItemBorderWidth"
+            type="number"
+            label="标记描边"
+          />
+          <ColorPicker
+            v-model="theme.timelineLineColor"
+            label="主轴"
+          />
+          <van-field
+            v-model.number="theme.timelineLineWidth"
+            type="number"
+            label="主轴宽度"
+          />
+          <ColorPicker
+            v-model="theme.timelineControlColor"
+            label="控件填充"
+          />
+          <ColorPicker
+            v-model="theme.timelineControlBorderColor"
+            label="控件描边"
+          />
+          <van-field
+            v-model.number="theme.timelineControlBorderWidth"
+            type="number"
+            label="控件描边宽度"
+          />
+          <ColorPicker
+            v-model="theme.timelineLabelColor"
+            label="文字"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Line Chart -->
+      <van-collapse-item title="折线图" name="line">
+        <div class="panel-content">
+          <van-field label="平滑曲线">
+            <template #input>
+              <van-checkbox v-model="theme.lineSmooth">
+                平滑曲线
+              </van-checkbox>
+            </template>
+          </van-field>
+
+          <van-field
+            v-model.number="theme.lineWidth"
+            type="number"
+            label="线条宽度"
+          />
+          <van-field
+            v-model.number="theme.symbolBorderWidth"
+            type="number"
+            label="图形描边"
+          />
+          <van-field
+            v-model.number="theme.symbolSize"
+            type="number"
+            label="图形大小"
+          />
+
+          <van-field label="图形形状">
+            <template #input>
+              <van-radio-group v-model="theme.symbol" direction="horizontal">
+                <van-radio name="circle">圆形</van-radio>
+                <van-radio name="emptyCircle">空心圆形</van-radio>
+                <van-radio name="rect">方形</van-radio>
+                <van-radio name="emptyRect">空心方形</van-radio>
+                <van-radio name="roundRect">圆角矩形</van-radio>
+                <van-radio name="emptyRoundRect">空心圆角矩形</van-radio>
+                <van-radio name="triangle">三角形</van-radio>
+                <van-radio name="emptyTriangle">空心三角形</van-radio>
+                <van-radio name="diamond">菱形</van-radio>
+                <van-radio name="emptyDiamond">空心菱形</van-radio>
+                <van-radio name="pin">水滴</van-radio>
+                <van-radio name="emptyPin">空心水滴</van-radio>
+                <van-radio name="arrow">箭头</van-radio>
+                <van-radio name="emptyArrow">空心箭头</van-radio>
+              </van-radio-group>
+            </template>
+          </van-field>
+        </div>
+      </van-collapse-item>
+
+      <!-- Graph -->
+      <van-collapse-item title="关系图" name="graph">
+        <div class="panel-content">
+          <van-field
+            v-model.number="theme.graphLineWidth"
+            type="number"
+            label="线条宽度"
+          />
+          <ColorPicker
+            v-model="theme.graphLineColor"
+            label="线条颜色"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- Map -->
+      <van-collapse-item title="地图" name="map">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.mapAreaColor"
+            label="区域颜色"
+          />
+          <ColorPicker
+            v-model="theme.mapBorderColor"
+            label="边界颜色"
+          />
+          <van-field
+            v-model.number="theme.mapBorderWidth"
+            type="number"
+            label="边界宽度"
+          />
+          <ColorPicker
+            v-model="theme.mapLabelColor"
+            label="标签颜色"
+          />
+          <ColorPicker
+            v-model="theme.mapAreaColorE"
+            label="悬停区域颜色"
+          />
+          <ColorPicker
+            v-model="theme.mapBorderColorE"
+            label="悬停边界颜色"
+          />
+          <van-field
+            v-model.number="theme.mapBorderWidthE"
+            type="number"
+            label="悬停边界宽度"
+          />
+          <ColorPicker
+            v-model="theme.mapLabelColorE"
+            label="悬停标签颜色"
+          />
+        </div>
+      </van-collapse-item>
+
+      <!-- K Line Chart -->
+      <van-collapse-item title="K线图" name="kline">
+        <div class="panel-content">
+          <ColorPicker
+            v-model="theme.kColor"
+            label="阳线颜色"
+          />
+          <ColorPicker
+            v-model="theme.kColor0"
+            label="阴线颜色"
+          />
+          <ColorPicker
+            v-model="theme.kBorderColor"
+            label="阳线边框"
+          />
+          <ColorPicker
+            v-model="theme.kBorderColor0"
+            label="阴线边框"
+          />
+          <van-field
+            v-model.number="theme.kBorderWidth"
+            type="number"
+            label="边框宽度"
+          />
+        </div>
+      </van-collapse-item>
+    </van-collapse>
+
+    <!-- Hidden file input for import -->
+    <input
+      ref="fileInput"
+      type="file"
+      accept=".json"
+      style="display: none"
+      @change="handleFileImport"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { useThemeStore } from '../stores/theme'
+import { PRE_DEFINED_THEMES } from '../stores/theme'
+import ColorPicker from './ColorPicker.vue'
+import ColorList from './ColorList.vue'
+
+// Component state
+const activeNames = ref(['functions'])  // Functions panel expanded by default
+const fileInput = ref<HTMLInputElement>()
+
+// Theme store
+const themeStore = useThemeStore()
+const { theme, themeName } = themeStore
+
+// Predefined themes
+const preDefinedThemes = PRE_DEFINED_THEMES
+
+// Methods
+const downloadTheme = () => {
+  // TODO: Implement theme download
+  console.log('Download theme')
+}
+
+const importConfig = () => {
+  fileInput.value?.click()
+}
+
+const exportConfig = () => {
+  // TODO: Implement config export
+  console.log('Export config')
+}
+
+const refreshCharts = () => {
+  // TODO: Implement chart refresh
+  console.log('Refresh charts')
+}
+
+const resetTheme = () => {
+  themeStore.resetTheme()
+}
+
+const showHelp = () => {
+  // TODO: Implement help dialog
+  console.log('Show help')
+}
+
+const selectPreDefinedTheme = (index: number) => {
+  themeStore.loadPreDefinedTheme(index)
+}
+
+const onAxisSettingChange = () => {
+  themeStore.updateAxisSetting()
+}
+
+const handleFileImport = (event: Event) => {
+  const target = event.target as HTMLInputElement
+  const file = target.files?.[0]
+
+  if (!file) return
+
+  const reader = new FileReader()
+  reader.onload = (e) => {
+    try {
+      const result = e.target?.result as string
+      themeStore.importTheme(result)
+    } catch (error) {
+      console.error('Failed to import theme:', error)
+      // TODO: Show error message
+    }
+  }
+  reader.readAsText(file)
+
+  // Clear input
+  target.value = ''
+}
+</script>
+
+<style scoped>
+.theme-panel {
+  height: 100%;
+  overflow-y: auto;
+}
+
+.panel-content {
+  padding: 16px;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 16px;
+  flex-wrap: wrap;
+}
+
+.action-buttons .van-button {
+  flex: 1;
+  min-width: 80px;
+  font-size: 12px;
+}
+
+.predefined-themes {
+  margin-top: 16px;
+}
+
+.predefined-themes h4 {
+  margin: 0 0 12px 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #323233;
+}
+
+.theme-grid {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 8px;
+}
+
+.theme-item {
+  padding: 8px;
+  border: 1px solid #ebedf0;
+  border-radius: 4px;
+  cursor: pointer;
+  transition: all 0.2s;
+  min-height: 60px;
+  display: flex;
+  flex-wrap: wrap;
+  align-content: flex-start;
+  gap: 2px;
+}
+
+.theme-item:hover {
+  border-color: #1989fa;
+  box-shadow: 0 2px 8px rgba(25, 137, 250, 0.15);
+}
+
+.color-dot {
+  width: 12px;
+  height: 12px;
+  border-radius: 2px;
+  border: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.axis-group {
+  margin-top: 16px;
+  padding-top: 16px;
+  border-top: 1px solid #ebedf0;
+}
+
+.axis-group h4 {
+  margin: 0 0 12px 0;
+  font-size: 14px;
+  font-weight: 500;
+  color: #323233;
+}
+
+/* Custom Vant styles */
+:deep(.van-collapse) {
+  border: none;
+  background: #fff;
+}
+
+:deep(.van-collapse-item) {
+  border-top: 1px solid #ccc;
+  background: #fff;
+}
+
+:deep(.van-collapse-item:first-child) {
+  border-top: none;
+}
+
+:deep(.van-collapse-item__title) {
+  padding: 10px 16px;
+  background-color: #fff;
+  color: #1989fa;
+  font-weight: 500;
+  border: none;
+  transition: background-color 0.3s;
+}
+
+:deep(.van-collapse-item__title:hover) {
+  background-color: #f7f8fa;
+}
+
+:deep(.van-collapse-item__content) {
+  padding: 0;
+  border-top: none;
+  background: #fff;
+}
+
+:deep(.van-collapse-item__wrapper) {
+  border: none;
+  overflow: hidden;
+  transition: height 0.3s ease;
+}
+
+:deep(.van-field) {
+  padding: 8px 16px;
+  border-bottom: 1px solid #f7f8fa;
+}
+
+:deep(.van-field:last-child) {
+  border-bottom: none;
+}
+
+:deep(.van-field__label) {
+  width: 80px;
+  font-size: 13px;
+  color: #323233;
+  text-align: right;
+  padding-right: 8px;
+}
+
+:deep(.van-field__control) {
+  font-size: 13px;
+}
+
+:deep(.van-radio-group) {
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 8px;
+  padding: 8px 0;
+}
+
+:deep(.van-radio) {
+  margin: 0;
+  font-size: 12px;
+}
+
+:deep(.van-checkbox) {
+  font-size: 12px;
+}
+
+:deep(.van-button--small) {
+  height: 28px;
+  font-size: 12px;
+}
+
+/* Color picker adjustments */
+:deep(.vc-color-wrap) {
+  width: 32px !important;
+  height: 32px !important;
+  border-radius: 4px;
+  border: 1px solid #dcdee0;
+}
+
+:deep(.vc-color-picker) {
+  background: white;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+</style>
diff --git a/src/main.ts b/src/main.ts
index a10b54a..9f9e837 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,29 @@
 import { createApp } from 'vue'
-import { Col, Row } from 'vant'
+import {
+  Col,
+  Row,
+  Collapse,
+  CollapseItem,
+  Field,
+  Button,
+  Icon,
+  Checkbox,
+  RadioGroup,
+  Radio
+} from 'vant'
+import 'vant/lib/index.css'
 import './style.css'
 import App from './App.vue'
 
 const app = createApp(App)
 app.use(Col)
 app.use(Row)
+app.use(Collapse)
+app.use(CollapseItem)
+app.use(Field)
+app.use(Button)
+app.use(Icon)
+app.use(Checkbox)
+app.use(RadioGroup)
+app.use(Radio)
 app.mount('#app')
diff --git a/src/stores/theme.ts b/src/stores/theme.ts
index f5a1412..92ef40d 100644
--- a/src/stores/theme.ts
+++ b/src/stores/theme.ts
@@ -208,10 +208,37 @@ export const useThemeStore = () => {
     themeName.value = 'customized'
   }
 
-  const loadPreDefinedTheme = (preTheme: PreDefinedTheme) => {
-    theme.backgroundColor = preTheme.background
-    theme.color = [...preTheme.theme]
-    themeName.value = preTheme.name
+  const loadPreDefinedTheme = (index: number) => {
+    const preTheme = PRE_DEFINED_THEMES[index]
+    if (preTheme) {
+      theme.backgroundColor = preTheme.background
+      theme.color = [...preTheme.theme]
+      themeName.value = preTheme.name
+    }
+  }
+
+  const updateAxisSetting = () => {
+    if (theme.axisSeperateSetting) {
+      theme.axis = theme.axes
+    } else {
+      theme.axis = [theme.axes[0]]
+    }
+  }
+
+  const importTheme = (jsonString: string) => {
+    try {
+      const data = JSON.parse(jsonString)
+      if (data.theme) {
+        Object.assign(theme, data.theme)
+        if (data.themeName) {
+          themeName.value = data.themeName
+        }
+        updateAxisSetting()
+      }
+    } catch (error) {
+      console.error('Failed to import theme:', error)
+      throw error
+    }
   }
 
   const exportTheme = () => {
@@ -228,6 +255,8 @@ export const useThemeStore = () => {
     chartDisplay,
     resetTheme,
     loadPreDefinedTheme,
+    updateAxisSetting,
+    importTheme,
     exportTheme
   }
 }
diff --git a/src/utils/chartOptions.ts b/src/utils/chartOptions.ts
deleted file mode 100644
index 427d9bd..0000000
--- a/src/utils/chartOptions.ts
+++ /dev/null
@@ -1,328 +0,0 @@
-import type { ThemeData } from '../types/theme'
-
-export interface ChartOption {
-  title?: any
-  legend?: any
-  tooltip?: any
-  xAxis?: any
-  yAxis?: any
-  series?: any[]
-  toolbox?: any
-  grid?: any
-  [key: string]: any
-}
-
-export const generateChartOptions = (theme: ThemeData) => {
-  const groupCnt = theme.seriesCnt
-  const axisCat = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
-  const dataLength = axisCat.length
-
-  const getLegend = () => {
-    const data = []
-    for (let i = 0; i < groupCnt; i++) {
-      data.push('第' + (i + 1) + '组')
-    }
-    return data
-  }
-
-  const getSeriesRandomValue = (typeName: string) => {
-    const data = []
-    const dlen = typeName === 'scatter' ? 32 : dataLength
-
-    for (let i = 0; i < groupCnt; i++) {
-      const group = []
-      for (let j = 0; j < dlen; j++) {
-        let v: any
-        if (typeName === 'scatter') {
-          v = [
-            Math.floor((Math.random() * 600 + 400) * (groupCnt - i) / 
groupCnt),
-            Math.floor((Math.random() * 600 + 400) * (groupCnt - i) / groupCnt)
-          ]
-        } else {
-          v = Math.floor((Math.random() * 600 + 400) * (groupCnt - i) / 
groupCnt)
-        }
-        group.push(v)
-      }
-
-      data.push({
-        type: typeName,
-        data: typeName === 'radar' ? [group] : group,
-        name: '第' + (i + 1) + '组',
-        markPoint: ['line', 'bar', 'scatter'].includes(typeName) ? {
-          data: [{
-            name: '最高',
-            type: 'max'
-          }]
-        } : undefined
-      })
-    }
-    return data
-  }
-
-  const getSeriesRandomStack = (typeName: string) => {
-    const data = getSeriesRandomValue(typeName)
-    data.forEach((item: any) => {
-      item.areaStyle = { normal: {} }
-      item.stack = 'total'
-    })
-    return data
-  }
-
-  const getSeriesRandomGroup = (typeName: string) => {
-    const data = []
-    for (let i = 0; i < groupCnt; i++) {
-      data.push({
-        name: getLegend()[i],
-        value: Math.floor((Math.random() * 800 + 200) * (groupCnt - i) / 
groupCnt)
-      })
-    }
-    return {
-      type: typeName,
-      data: data
-    }
-  }
-
-  const getIndicator = () => {
-    return axisCat.map(name => ({
-      name,
-      max: 1000
-    }))
-  }
-
-  // 基础配置
-  const baseOptions = {
-    title: {
-      text: '示例图表',
-      textStyle: {
-        color: theme.titleColor
-      }
-    },
-    legend: {
-      data: getLegend(),
-      right: 20,
-      textStyle: {
-        color: theme.legendTextColor
-      }
-    },
-    tooltip: {
-      trigger: 'axis',
-      axisPointer: {
-        lineStyle: {
-          color: theme.tooltipAxisColor,
-          width: theme.tooltipAxisWidth
-        }
-      }
-    },
-    toolbox: {
-      feature: {
-        restore: { show: true },
-        saveAsImage: { show: true }
-      },
-      iconStyle: {
-        normal: {
-          borderColor: theme.toolboxColor
-        },
-        emphasis: {
-          borderColor: theme.toolboxEmphasisColor
-        }
-      }
-    },
-    grid: {
-      left: '3%',
-      right: '4%',
-      bottom: '3%',
-      containLabel: true
-    }
-  }
-
-  // 图表配置集合
-  const chartOptions: { [key: string]: ChartOption } = {
-    // 柱状图
-    bar: {
-      ...baseOptions,
-      xAxis: {
-        type: 'category',
-        data: axisCat,
-        axisLine: {
-          show: theme.axes[1].axisLineShow,
-          lineStyle: { color: theme.axes[1].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[1].axisTickShow,
-          lineStyle: { color: theme.axes[1].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[1].axisLabelShow,
-          color: theme.axes[1].axisLabelColor
-        }
-      },
-      yAxis: {
-        type: 'value',
-        axisLine: {
-          show: theme.axes[2].axisLineShow,
-          lineStyle: { color: theme.axes[2].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[2].axisTickShow,
-          lineStyle: { color: theme.axes[2].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[2].axisLabelShow,
-          color: theme.axes[2].axisLabelColor
-        },
-        splitLine: {
-          show: theme.axes[2].splitLineShow,
-          lineStyle: { color: theme.axes[2].splitLineColor }
-        }
-      },
-      series: getSeriesRandomValue('bar')
-    },
-
-    // 折线图
-    line: {
-      ...baseOptions,
-      xAxis: {
-        type: 'category',
-        data: axisCat,
-        axisLine: {
-          show: theme.axes[1].axisLineShow,
-          lineStyle: { color: theme.axes[1].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[1].axisTickShow,
-          lineStyle: { color: theme.axes[1].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[1].axisLabelShow,
-          color: theme.axes[1].axisLabelColor
-        }
-      },
-      yAxis: {
-        type: 'value',
-        axisLine: {
-          show: theme.axes[2].axisLineShow,
-          lineStyle: { color: theme.axes[2].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[2].axisTickShow,
-          lineStyle: { color: theme.axes[2].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[2].axisLabelShow,
-          color: theme.axes[2].axisLabelColor
-        },
-        splitLine: {
-          show: theme.axes[2].splitLineShow,
-          lineStyle: { color: theme.axes[2].splitLineColor }
-        }
-      },
-      series: getSeriesRandomValue('line')
-    },
-
-    // 饼图
-    pie: {
-      ...baseOptions,
-      tooltip: {
-        trigger: 'item',
-        formatter: '{a} <br/>{b}: {c} ({d}%)'
-      },
-      series: [getSeriesRandomGroup('pie')]
-    },
-
-    // 散点图
-    scatter: {
-      ...baseOptions,
-      xAxis: {
-        type: 'value',
-        axisLine: {
-          show: theme.axes[2].axisLineShow,
-          lineStyle: { color: theme.axes[2].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[2].axisTickShow,
-          lineStyle: { color: theme.axes[2].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[2].axisLabelShow,
-          color: theme.axes[2].axisLabelColor
-        },
-        splitLine: {
-          show: theme.axes[2].splitLineShow,
-          lineStyle: { color: theme.axes[2].splitLineColor }
-        }
-      },
-      yAxis: {
-        type: 'value',
-        axisLine: {
-          show: theme.axes[2].axisLineShow,
-          lineStyle: { color: theme.axes[2].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[2].axisTickShow,
-          lineStyle: { color: theme.axes[2].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[2].axisLabelShow,
-          color: theme.axes[2].axisLabelColor
-        },
-        splitLine: {
-          show: theme.axes[2].splitLineShow,
-          lineStyle: { color: theme.axes[2].splitLineColor }
-        }
-      },
-      series: getSeriesRandomValue('scatter')
-    },
-
-    // 雷达图
-    radar: {
-      ...baseOptions,
-      radar: {
-        indicator: getIndicator()
-      },
-      series: getSeriesRandomValue('radar')
-    },
-
-    // 面积图
-    area: {
-      ...baseOptions,
-      xAxis: {
-        type: 'category',
-        data: axisCat,
-        axisLine: {
-          show: theme.axes[1].axisLineShow,
-          lineStyle: { color: theme.axes[1].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[1].axisTickShow,
-          lineStyle: { color: theme.axes[1].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[1].axisLabelShow,
-          color: theme.axes[1].axisLabelColor
-        }
-      },
-      yAxis: {
-        type: 'value',
-        axisLine: {
-          show: theme.axes[2].axisLineShow,
-          lineStyle: { color: theme.axes[2].axisLineColor }
-        },
-        axisTick: {
-          show: theme.axes[2].axisTickShow,
-          lineStyle: { color: theme.axes[2].axisTickColor }
-        },
-        axisLabel: {
-          show: theme.axes[2].axisLabelShow,
-          color: theme.axes[2].axisLabelColor
-        },
-        splitLine: {
-          show: theme.axes[2].splitLineShow,
-          lineStyle: { color: theme.axes[2].splitLineColor }
-        }
-      },
-      series: getSeriesRandomStack('line')
-    }
-  }
-
-  return chartOptions
-}


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

Reply via email to