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]
