Commit a7d0edf5 by zhuxichen

feat: 临时的主题方案

parent 828f3fb6
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
"pug-plain-loader": "^1.1.0", "pug-plain-loader": "^1.1.0",
"pug-plugin": "^5.3.0", "pug-plugin": "^5.3.0",
"start-server-and-test": "^2.0.8", "start-server-and-test": "^2.0.8",
"stylus": "^0.64.0",
"tailwindcss": "^3.4.15", "tailwindcss": "^3.4.15",
"typescript": "~5.6.3", "typescript": "~5.6.3",
"unplugin-auto-import": "^0.18.6", "unplugin-auto-import": "^0.18.6",
......
<script setup lang="ts"> <script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue' import WelcomeItem from './WelcomeItem.vue';
import DocumentationIcon from './icons/IconDocumentation.vue' import DocumentationIcon from './icons/IconDocumentation.vue';
import ToolingIcon from './icons/IconTooling.vue' import ToolingIcon from './icons/IconTooling.vue';
import EcosystemIcon from './icons/IconEcosystem.vue' import EcosystemIcon from './icons/IconEcosystem.vue';
import CommunityIcon from './icons/IconCommunity.vue' import CommunityIcon from './icons/IconCommunity.vue';
import SupportIcon from './icons/IconSupport.vue' import SupportIcon from './icons/IconSupport.vue';
import { updateThemeVariable } from '@/plugins/switchTheme';
</script> </script>
<template> <template>
<div class="vvv_color">测试主题stylus使用 $parimary-color</div>
<div class="vvv_color2">测试主题less使用 @warning-color</div>
<button @click="updateThemeVariable('primary-color', '#ff0000')">点击改变默认主题色</button>
<WelcomeItem> <WelcomeItem>
<template #icon> <template #icon>
<DocumentationIcon /> <DocumentationIcon />
...@@ -88,3 +92,14 @@ import SupportIcon from './icons/IconSupport.vue' ...@@ -88,3 +92,14 @@ import SupportIcon from './icons/IconSupport.vue'
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>. <a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem> </WelcomeItem>
</template> </template>
<style lang="stylus" scoped>
.vvv_color
color $primary-color
</style>
<style lang="less" scoped>
.vvv_color2 {
color: @warning-color;
}
</style>
import './assets/main.css' import './assets/main.css';
import './assets/tailwind.css' import './assets/tailwind.css';
import { createApp } from 'vue' import { createApp } from 'vue';
import App from './App.vue' import App from './App.vue';
import router from './router' import router from './router';
import { initializeTheme } from './plugins/switchTheme';
const app = createApp(App) const app = createApp(App);
// 读取默认主题模式
const defaultThemeMode = import.meta.env.VITE_THEME_MODE || 'default';
app.use(router) // 初始化默认主题
initializeTheme(defaultThemeMode).then(() => {
console.log(`Default theme initialized: ${defaultThemeMode}`);
});
app.mount('#app') app.use(router);
app.mount('#app');
export const switchTheme = (themeFile: string) => { import path from 'path';
fetch(themeFile) import fs from 'fs';
.then((response) => response.text()) let currentTheme: Record<string, string> = {};
.then((themeContent) => {
(window as any).less.modifyVars({ // 初始化主题
hack: `true; ${themeContent}`, export async function initializeTheme(themeMode: string = 'default') {
}); try {
}) const theme = (await import(`@/styles/theme.${themeMode}.mjs`)).default;
.catch((error) => console.error('Failed to switch theme:', error)); currentTheme = { ...theme };
}; refreshViteInjectedVariables(); // 刷新 Vite 变量
console.log(`Initialized theme: ${themeMode}`);
// use: switchTheme('src/styles/theme.default.less'); } catch (error) {
console.error(`Failed to initialize theme: ${themeMode}`, error);
}
}
// 动态切换主题
export async function switchTheme(themeMode: string) {
try {
const newTheme = (await import(`@/styles/theme.${themeMode}.mjs`)).default;
currentTheme = { ...newTheme };
refreshViteInjectedVariables(); // 刷新 Vite 变量
console.log(`Switched to theme: ${themeMode}`);
} catch (error) {
console.error(`Failed to switch theme to ${themeMode}:`, error);
}
}
// 更新单个主题变量
export function updateThemeVariable(key: string, value: string) {
currentTheme[key] = value;
refreshViteInjectedVariables(); // 刷新 Vite 变量
console.log(`Updated ${key} to ${value}`);
}
// 刷新 Vite 的动态变量注入
function refreshViteInjectedVariables() {
const lessContent = generateLessVariables(currentTheme);
const stylusContent = generateStylusVariables(currentTheme);
fs.writeFileSync(
path.resolve(__dirname, '../styles/_generated-theme.less'), // 确保路径正确
lessContent,
);
fs.writeFileSync(
path.resolve(__dirname, '../styles/_generated-theme.styl'), // 确保路径正确
stylusContent,
);
if (import.meta.hot) {
import.meta.hot.invalidate(); // 触发 Vite 热更新
}
}
// 工具函数:生成 Less 变量
function generateLessVariables(theme: Record<string, string>) {
return Object.entries(theme)
.map(([key, value]) => `@${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)
.join('\n');
}
// 工具函数:生成 Stylus 变量
function generateStylusVariables(theme: Record<string, string>) {
return Object.entries(theme)
.map(([key, value]) => `$${key.replace(/([A-Z])/g, '-$1').toLowerCase()} = ${value}`)
.join('\n');
}
// 自定义 Ant Design 主题变量
@primary-color: #42b983; @primary-color: #42b983;
@link-color: #1d39c4; @link-color: #1d39c4;
@success-color: #52c41a; @success-color: #52c41a;
...@@ -6,6 +5,4 @@ ...@@ -6,6 +5,4 @@
@error-color: #f5222d; @error-color: #f5222d;
@font-size-base: 14px; @font-size-base: 14px;
@border-radius-base: 2px; @border-radius-base: 2px;
// 其他全局变量
@background-color: #f5f5f5; @background-color: #f5f5f5;
\ No newline at end of file
$primary-color = #42b983
$link-color = #1d39c4
$success-color = #52c41a
$warning-color = #faad14
$error-color = #f5222d
$font-size-base = 14px
$border-radius-base = 2px
$background-color = #f5f5f5
\ No newline at end of file
const defaultTheme = {
'primary-color': '#42b983',
'link-color': '#1d39c4',
'success-color': '#52c41a',
'warning-color': '#faad14',
'error-color': '#f5222d',
'font-size-base': '14px',
'border-radius-base': '2px',
'background-color': '#f5f5f5',
};
export default defaultTheme;
<script setup lang="ts"> <script setup lang="ts"></script>
import TheWelcome from '../components/TheWelcome.vue'
</script>
<template> <template>
<main> <main>
......
...@@ -4,24 +4,56 @@ import Components from 'unplugin-vue-components/vite'; ...@@ -4,24 +4,56 @@ import Components from 'unplugin-vue-components/vite';
import AutoImport from 'unplugin-auto-import/vite'; import AutoImport from 'unplugin-auto-import/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
import path from 'path'; import path from 'path';
import fs from 'fs';
export default defineConfig(({ mode }) => { // 当前主题的存储
// 如果没有指定 mode,则默认加载 .env 文件 let currentTheme: Record<string, string> = {};
// 动态生成 Less 和 Stylus 的变量
function generateLessVariables(theme: Record<string, string>) {
return Object.entries(theme)
.map(([key, value]) => `@${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)
.join('\n');
}
function generateStylusVariables(theme: Record<string, string>) {
return Object.entries(theme)
.map(([key, value]) => `$${key.replace(/([A-Z])/g, '-$1').toLowerCase()} = ${value}`)
.join('\n');
}
// 生成全局样式文件
function generateGlobalStyles(theme: Record<string, string>) {
const lessContent = generateLessVariables(theme);
const stylusContent = generateStylusVariables(theme);
fs.writeFileSync(path.resolve(__dirname, 'src/styles/_generated-theme.less'), lessContent);
fs.writeFileSync(path.resolve(__dirname, 'src/styles/_generated-theme.styl'), stylusContent);
}
// 动态加载主题文件
async function loadTheme(themeMode: string) {
const themePath = path.resolve(__dirname, `src/styles/theme.${themeMode}.mjs`);
return (await import(themePath)).default; // 使用动态导入加载主题文件
}
export default defineConfig(async ({ mode }) => {
const actualMode = mode || ''; const actualMode = mode || '';
const env = loadEnv(actualMode, process.cwd(), 'VITE_'); const env = loadEnv(actualMode, process.cwd(), 'VITE_');
const themeMode = env.VITE_THEME_MODE || 'default';
// 读取主题模式 // 初始化主题
const themeMode = env.VITE_THEME_MODE || 'default'; // 默认使用 default 主题 currentTheme = await loadTheme(themeMode);
// 生成全局样式文件
generateGlobalStyles(currentTheme);
return { return {
plugins: [ plugins: [
vue(), vue(),
Components({ Components({
resolvers: [AntDesignVueResolver()], // 按需加载 Ant Design Vue 组件 resolvers: [AntDesignVueResolver()],
dts: 'src/components.d.ts', dts: 'src/components.d.ts',
}), }),
// 自动按需导入 Vue API
AutoImport({ AutoImport({
imports: ['vue', 'vue-router'], imports: ['vue', 'vue-router'],
resolvers: [AntDesignVueResolver()], resolvers: [AntDesignVueResolver()],
...@@ -32,20 +64,11 @@ export default defineConfig(({ mode }) => { ...@@ -32,20 +64,11 @@ export default defineConfig(({ mode }) => {
preprocessorOptions: { preprocessorOptions: {
less: { less: {
javascriptEnabled: true, javascriptEnabled: true,
modifyVars: { additionalData: `@import "${path.resolve(__dirname, 'src/styles/_generated-theme.less')}";`,
// 动态加载 Ant Design 主题变量
hack: `true; @import "${path.resolve(
__dirname,
`src/styles/theme.${themeMode}.less`,
)}";`,
},
},
}, },
stylus: {
additionalData: `@import "${path.resolve(__dirname, 'src/styles/_generated-theme.styl')}";`,
}, },
define: {
// 将环境变量注入到代码中,这样可以使用process.env去获取一些变量
'process.env': {
...env,
}, },
}, },
resolve: { resolve: {
......
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
# yarn lockfile v1 # yarn lockfile v1
"@adobe/css-tools@~4.3.3":
version "4.3.3"
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff"
integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==
"@alloc/quick-lru@^5.2.0": "@alloc/quick-lru@^5.2.0":
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
...@@ -2819,7 +2824,7 @@ glob-parent@^6.0.2: ...@@ -2819,7 +2824,7 @@ glob-parent@^6.0.2:
dependencies: dependencies:
is-glob "^4.0.3" is-glob "^4.0.3"
glob@^10.3.10, glob@^10.3.3: glob@^10.3.10, glob@^10.3.3, glob@^10.4.5:
version "10.4.5" version "10.4.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
...@@ -4423,7 +4428,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2: ...@@ -4423,7 +4428,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@^1.2.4: sax@^1.2.4, sax@~1.4.1:
version "1.4.1" version "1.4.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f"
integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==
...@@ -4566,6 +4571,11 @@ source-map@^0.6.0, source-map@~0.6.0: ...@@ -4566,6 +4571,11 @@ source-map@^0.6.0, source-map@~0.6.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.7.3:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
speakingurl@^14.0.1: speakingurl@^14.0.1:
version "14.0.1" version "14.0.1"
resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53"
...@@ -4683,6 +4693,17 @@ stylis@^4.1.3: ...@@ -4683,6 +4693,17 @@ stylis@^4.1.3:
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4"
integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==
stylus@^0.64.0:
version "0.64.0"
resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.64.0.tgz#af99253f1254c851528c44eddc3ccf1f831942f1"
integrity sha512-ZIdT8eUv8tegmqy1tTIdJv9We2DumkNZFdCF5mz/Kpq3OcTaxSuCAYZge6HKK2CmNC02G1eJig2RV7XTw5hQrA==
dependencies:
"@adobe/css-tools" "~4.3.3"
debug "^4.3.2"
glob "^10.4.5"
sax "~1.4.1"
source-map "^0.7.3"
sucrase@^3.35.0: sucrase@^3.35.0:
version "3.35.0" version "3.35.0"
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment