This commit is contained in:
KuroSago 2024-09-20 00:03:30 +08:00
parent de90300e51
commit 82705051eb
19 changed files with 492 additions and 527 deletions

View File

@ -4,7 +4,7 @@ import autoImport from 'unplugin-auto-import/vite';
import viteRestart from 'vite-plugin-restart'; import viteRestart from 'vite-plugin-restart';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import Unocss from 'unocss/vite'; import Unocss from 'unocss/vite';
import { appProvider, buildThemeConfig, createComponents } from '../vite/plugins/index'; import { appProvider, createComponents } from '../vite/plugins/index';
import { isH5 } from './platform'; import { isH5 } from './platform';
interface VitePluginConfig { interface VitePluginConfig {
@ -17,7 +17,6 @@ export function createVitePlugins({ isProd }: VitePluginConfig): PluginOption[]
// @ts-expect-error TODO uni() 会报错uni is not a function,暂时使用此方式解决 // @ts-expect-error TODO uni() 会报错uni is not a function,暂时使用此方式解决
uni?.default(), uni?.default(),
appProvider(), appProvider(),
buildThemeConfig(),
createComponents(), createComponents(),
Unocss(), Unocss(),
autoImport({ autoImport({

View File

@ -33,7 +33,6 @@
"build:quickapp-webview-union": "uni build -p quickapp-webview-union", "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"git:hooks": "npx simple-git-hooks", "git:hooks": "npx simple-git-hooks",
"postinstall": "weapp-tw patch",
"version:first": "commit-and-tag-version --first-release", "version:first": "commit-and-tag-version --first-release",
"version:alpha": "commit-and-tag-version --prerelease alpha", "version:alpha": "commit-and-tag-version --prerelease alpha",
"version:beta": "commit-and-tag-version --prerelease beta", "version:beta": "commit-and-tag-version --prerelease beta",

View File

@ -3,7 +3,7 @@ import {
argbFromHex, argbFromHex,
hexFromArgb, hexFromArgb,
} from '@material/material-color-utilities'; } from '@material/material-color-utilities';
import type { MD3ColorSchemeTokens, MD3NeutralTones, MD3Palettes, MD3Tones } from './theme.types'; import type { AppTheme, MD3ColorSchemeTokens, MD3NeutralTones, MD3Palettes, MD3Tones } from './theme.types';
/** /**
* Generates a theme based on the provided color values. * Generates a theme based on the provided color values.
@ -152,11 +152,7 @@ export function generateTheme({
success: string success: string
neutral: string neutral: string
neutralVariant: string neutralVariant: string
}): { }): AppTheme {
palette: MD3Palettes
lightScheme: MD3ColorSchemeTokens
darkScheme: MD3ColorSchemeTokens
} {
const primaryTonal = TonalPalette.fromInt(argbFromHex(primary)); const primaryTonal = TonalPalette.fromInt(argbFromHex(primary));
const secondaryTonal = TonalPalette.fromInt(argbFromHex(secondary)); const secondaryTonal = TonalPalette.fromInt(argbFromHex(secondary));
const tertiaryTonal = TonalPalette.fromInt(argbFromHex(tertiary)); const tertiaryTonal = TonalPalette.fromInt(argbFromHex(tertiary));

View File

@ -1,5 +1,6 @@
import { generateTheme } from './generateTheme'; import { generateTheme } from './generateTheme';
import type { import type {
AppTheme,
MD3ColorSchemeTokens, MD3ColorSchemeTokens,
MD3NeutralTones, MD3NeutralTones,
MD3Palettes, MD3Palettes,
@ -7,4 +8,4 @@ import type {
} from './theme.types'; } from './theme.types';
export { generateTheme }; export { generateTheme };
export type { MD3Tones, MD3NeutralTones, MD3Palettes, MD3ColorSchemeTokens }; export type { MD3Tones, MD3NeutralTones, MD3Palettes, MD3ColorSchemeTokens, AppTheme };

View File

@ -1,6 +1,12 @@
/** /**
* Represents the MD3Tones interface. * Represents the MD3Tones interface.
*/ */
export interface AppTheme {
palette: MD3Palettes
lightScheme: MD3ColorSchemeTokens
darkScheme: MD3ColorSchemeTokens
}
export interface MD3Tones { export interface MD3Tones {
0: string 0: string
10: string 10: string

View File

@ -1,23 +1,23 @@
// S、M、L、XL、XXL、XXXL // S、M、L、XL、XXL、XXXL
// S: Small 小 M: Middle 中L: Large 大XL: extra large 加大多个X则代表多重加大码X越多码数越大。 // S: Small 小 M: Middle 中L: Large 大XL: extra large 加大多个X则代表多重加大码X越多码数越大。
export enum Size { // export enum Size {
S = 's', // S = 's',
M = 'm', // M = 'm',
L = 'l', // L = 'l',
XL = 'xl', // XL = 'xl',
XXl = 'xxl', // XXl = 'xxl',
XXXL = 'xxxl', // XXXL = 'xxxl',
} // }
export enum UNOCSS_THEME_TYPE { // export enum UNOCSS_THEME_TYPE {
SCREENS = 'screens', // SCREENS = 'screens',
COLORS = 'colors', // COLORS = 'colors',
SPACING = 'spacing', // SPACING = 'spacing',
BORDERRADIUS = 'borderRadius', // BORDERRADIUS = 'borderRadius',
FONTFAMILY = 'fontFamily', // FONTFAMILY = 'fontFamily',
} // }
export enum THEME_MODEL { export enum THEME_MODEL {
LIGHT = 'light', LIGHT = 'lightScheme',
DARK = 'dark', DARK = 'darkScheme',
} }

View File

@ -5,11 +5,15 @@ export const themeMixin = {
computed: { computed: {
__themeModel() { __themeModel() {
const themeStore = storeToRefs(useSystemInfoStore()); const themeStore = storeToRefs(useSystemInfoStore());
return themeStore.themeModel.value; return themeStore.getThemeModel.value;
},
__themeKey() {
const themeStore = storeToRefs(useSystemInfoStore());
return themeStore.getThemeKey.value;
}, },
__theme() { __theme() {
const themeStore = storeToRefs(useSystemInfoStore()); const themeStore = storeToRefs(useSystemInfoStore());
const theme = themeStore.theme.value; const theme = themeStore.getTheme.value;
return Object.keys(theme) return Object.keys(theme)
.map(key => `${key}:${theme[key]};`) .map(key => `${key}:${theme[key]};`)
.join(''); .join('');

View File

@ -1,46 +0,0 @@
export interface ThemeCfg {
'--colors-primary': string
'--colors-secondary': string
'--colors-accent': string
'--colors-success': string
'--colors-warning': string
'--colors-error': string
'--colors-disable': string
'--colors-danger': string
'--colors-mark': string
'--colors-title': string
'--colors-subtitle': string
'--colors-paragraph': string
'--colors-fontColorblack': string
'--colors-fontColorPrimary': string
'--colors-fontColorInverse': string
'--colors-fontColorGrey': string
'--colors-fontColorPlaceholder': string
'--colors-fontColorDisable': string
'--colors-fontColorBottomText': string
'--colors-container': string
'--colors-page': string
'--colors-containerInverse': string
'--colors-containerHover': string
'--colors-containerMask': string
'--colors-iconButton': string
'--colors-borderColor': string
'--fontSize-sm': string
'--fontSize-base': string
'--fontSize-lg': string
'--fontSize-title': string
'--fontSize-subtitle': string
'--fontSize-paragraph': string
'--borderRadius-sm': string
'--borderRadius-base': string
'--borderRadius-lg': string
'--borderRadius-circle': string
'--spacing-rowSm': string
'--spacing-rowBase': string
'--spacing-rowLg': string
'--spacing-colSm': string
'--spacing-colBase': string
'--spacing-colLg': string
'--opacity-disabled': string
[propName: string]: string
}

View File

@ -1,7 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import ColorPaletteComponent from './ColorPalette.vue';
const props = defineProps({ const props = defineProps({
palette: { palette: {
@ -19,11 +18,11 @@ defineComponent({
<div <div
v-for="(value, keyName) in props.palette" :key="keyName" class="flex flex-col" v-for="(value, keyName) in props.palette" :key="keyName" class="flex flex-col"
> >
<h2 v-if="typeof value === 'object'"> <h2 v-if="typeof value === 'object'" class="text-onBackground m-3">
{{ keyName }} {{ keyName }}
</h2> </h2>
<div v-if="typeof value === 'object'" class="flex haha flex-1"> <div v-if="typeof value === 'object'" class="flex haha flex-1">
<ColorPaletteComponent class="flex" :palette="value" /> <ColorPalette class="flex" :palette="value" />
</div> </div>
<div <div
v-else v-else

View File

@ -1,31 +1,87 @@
<script lang="ts" setup> <script lang="ts" setup>
import { generateTheme } from '@ontos/material-design-3-theme-builder/src/index';
import ColorPalette from './ColorPalette.vue'; import ColorPalette from './ColorPalette.vue';
import Scheme from './Scheme.vue'; import Scheme from './Scheme.vue';
import { ThemeAssemble } from './themes'; import { ThemeAssemble } from '@/stores/helper/themes';
import { useSystemInfoStore } from '@/stores/modules/system';
import type { THEME_MODEL } from '@/enums/UI';
type ThemeKeys = keyof typeof ThemeAssemble; const { changeThemeKey, changeThemeModel } = useSystemInfoStore();
const theme: Ref<ThemeKeys> = ref('Watermelon');
const themeColor = computed(() => { const themeColor = computed(() => {
return generateTheme(ThemeAssemble[theme.value]); return useSystemInfoStore().theme;
}); });
const themeKey = computed(() => {
return useSystemInfoStore().getThemeKey;
});
const themeModel = computed(() => {
return useSystemInfoStore().getThemeModel;
});
type ThemeModelKey = keyof typeof THEME_MODEL;
const themeModelList: {
key: ThemeModelKey
value: string
}[] = [
{
key: 'LIGHT',
value: 'LIGHT',
},
{
key: 'DARK',
value: 'DARK',
},
];
</script> </script>
<template> <template>
<view class="bg-primary flex-1"> <view class="bg-background flex-1">
<div class="py-[32rpx] b flex flex-col b-[#123]"> <div class="py-[32rpx] b flex flex-col b-[#123]">
<h2>click it to change theme</h2> <h2 class="text-onBackground">
click it to change themeModel
</h2>
<div class="flex flex-row my-3">
<div
v-for="(item, index) in themeModelList"
:key="index"
class="flex-1 p-4 flex justify-center items-center"
:class="{
'bg-common-black text-common-white': item.key === 'DARK',
'bg-common-white text-common-black': item.key === 'LIGHT',
}"
@click="changeThemeModel({
model: item.key,
})"
>
{{ item.value }}
</div>
</div>
<div class="m-3 text-onBackground">
now : {{ themeModel }}
</div>
</div>
<div class="py-[32rpx] b flex flex-col b-[#123]">
<h2 class="text-onBackground">
click it to change themeKey
</h2>
<div class="grid grid-cols-4 gap-4"> <div class="grid grid-cols-4 gap-4">
<div v-for="(v, k) in ThemeAssemble" :key="k" class="flex flex-col justify-center items-center" @click="theme = k"> <div v-for="(v, k) in ThemeAssemble" :key="k" class="flex flex-col justify-center items-center" @click="changeThemeKey({ key: k })">
<div class="w-[100rpx] h-[100rpx] bg-[#123] rounded-[8rpx]" :style="{ backgroundColor: v.primary }" /> <div class="w-[100rpx] h-[100rpx] bg-[#123] rounded-[8rpx]" :style="{ backgroundColor: v.primary }" />
<h4>{{ k }}</h4> <h4 class="text-onBackground">
{{ k }}
</h4>
</div> </div>
</div> </div>
<h4>now : {{ theme }}</h4> <h4 class="m-3 text-onBackground">
now : {{ themeKey }}
</h4>
</div> </div>
<!-- 递归渲染色卡 --> <!-- 递归渲染色卡 -->

View File

@ -9,7 +9,7 @@
## Last Modified: 2024-06-24 11:30:88 ## Last Modified: 2024-06-24 11:30:88
Modified By: kurosago Modified By: KuroSago
## Copyright (c) 2024 self. ## Copyright (c) 2024 self.
@ -32,13 +32,13 @@ onMounted(async () => {
name: 'Home', name: 'Home',
}); });
} else { } else {
router.replace({ // router.replace({
name: 'Login', // name: 'Login',
params: { // params: {
redirect: 'Home', // redirect: 'Home',
tabBar: 'true', // tabBar: 'true',
}, // },
}); // });
} }
}); });

View File

@ -9,13 +9,15 @@
* *
* ## Last Modified: 2024-09-05 15:54:91 * ## Last Modified: 2024-09-05 15:54:91
* *
* Modified By: kurosago * Modified By: KuroSago
* *
* ## Copyright (c) 2024 self. * ## Copyright (c) 2024 self.
* *
* Use To: * Use To:
*/ */
import type { MD3ColorSchemeTokens, MD3Palettes } from '@ontos/material-design-3-theme-builder/src/index';
const Avocado = { const Avocado = {
primary: '#6b8e23', // 牛油果果肉的绿色 primary: '#6b8e23', // 牛油果果肉的绿色
secondary: '#556b2f', // 牛油果外皮的深绿色 secondary: '#556b2f', // 牛油果外皮的深绿色
@ -124,3 +126,25 @@ export const ThemeAssemble = {
Ocean, Ocean,
Forest, Forest,
}; };
export function convertSchemeToCSSVariables(scheme: MD3ColorSchemeTokens): Record<string, string> {
const cssVariables: Record<string, string> = {};
for (const [key, value] of Object.entries(scheme)) {
cssVariables[`--${key}`] = value;
}
return cssVariables;
}
export function convertPaletteToCSSVariables(palette: MD3Palettes): Record<string, string> {
const cssVariables: Record<string, string> = {};
for (const [key, value] of Object.entries(palette) as [string, Record<string, string>][]) {
for (const [subKey, subValue] of Object.entries(value)) {
cssVariables[`--${key}-${subKey}`] = subValue;
}
}
return cssVariables;
}

View File

@ -1,10 +1,13 @@
import type { AppTheme } from '@ontos/material-design-3-theme-builder/src/index';
import { generateTheme } from '@ontos/material-design-3-theme-builder/src/index';
import { ThemeAssemble, convertPaletteToCSSVariables, convertSchemeToCSSVariables } from '../helper/themes';
import { THEME_MODEL } from '@/enums/UI'; import { THEME_MODEL } from '@/enums/UI';
import type { ThemeCfg } from '@/model/theme';
import { theme as _theme } from '@/theme/theme';
type ThemeModelKey = keyof typeof THEME_MODEL; type ThemeModelKey = keyof typeof THEME_MODEL;
type AppThemeKey = keyof typeof ThemeAssemble;
interface SystemInfo { interface SystemInfo {
theme: ThemeCfg theme: AppTheme
themeKey: AppThemeKey
themeModel: ThemeModelKey themeModel: ThemeModelKey
systemInfo: UniApp.GetSystemInfoResult systemInfo: UniApp.GetSystemInfoResult
} }
@ -13,17 +16,30 @@ export const useSystemInfoStore = defineStore({
id: 'system', id: 'system',
state: (): SystemInfo => ({ state: (): SystemInfo => ({
// 主题 // 主题
theme: <ThemeCfg>{ ..._theme.light }, theme: generateTheme(ThemeAssemble.Avocado),
// 所选主题样式
themeKey: 'Avocado',
// 当前主题模式 // 当前主题模式
themeModel: 'LIGHT', themeModel: 'LIGHT',
// 系统信息 // 系统信息
systemInfo: { systemInfo: {
statusBarHeight: 0, statusBarHeight: 0,
} as UniApp.GetSystemInfoResult, } as UniApp.GetSystemInfoResult,
}), }),
getters: {}, getters: {
getTheme: (state) => {
// 主题
const schemeData = convertSchemeToCSSVariables(state.theme[THEME_MODEL[state.themeModel]]);
// 调色板
const palette = convertPaletteToCSSVariables(state.theme.palette);
return {
...schemeData,
...palette,
};
},
getThemeKey: state => state.themeKey,
getThemeModel: state => state.themeModel,
},
actions: { actions: {
// 获取系统信息 // 获取系统信息
async getSystemInfo() { async getSystemInfo() {
@ -31,10 +47,15 @@ export const useSystemInfoStore = defineStore({
this.systemInfo = res; this.systemInfo = res;
}, },
// 切换主题 // 切换模式 明亮/暗黑
changeThemeModel({ model }: { model: ThemeModelKey } = { model: 'DARK' }) { changeThemeModel({ model }: { model: ThemeModelKey } = { model: 'DARK' }) {
this.themeModel = model; this.themeModel = model;
this.theme = { ..._theme[THEME_MODEL[model]] }; },
// 切换主题 色彩
changeThemeKey({ key }: { key: AppThemeKey }) {
this.themeKey = key;
this.theme = generateTheme(ThemeAssemble[key]);
}, },
}, },
persist: { persist: {

View File

@ -1,219 +0,0 @@
/**
*
* @note : ( key )
* --
* colors
* primary
* key
* key , bug
*/
/**
* secondary
* accent
* tertiary 线
* neutral
*/
import type { THEME_MODEL } from '@/enums/UI';
import type { ThemeCfg } from '@/model/theme';
export const theme: {
[key in THEME_MODEL]: ThemeCfg
} = {
light: {
/* light */
/* 颜色 */
'--colors-primary': '#FFE274', // 主色
'--colors-secondary': '#5A7767', // 次要
'--colors-accent': '#FB9300', // 强调
'--colors-success': '#4cd964',
'--colors-warning': '#f0ad4e',
'--colors-error': '#dd524d',
'--colors-disable': '#ddd',
'--colors-danger': '#FF6843',
'--colors-mark': 'rgba(0,0,0,0.4)',
/* 文章场景相关 */
'--colors-title': '#2c405a', // 文章标题颜色
'--colors-subtitle': '#555555', // 二级标题颜色
'--colors-paragraph': '#3f536e', // 文章段落颜色
/* 文字基本颜色 */
'--colors-fontColorblack': '#333', // 基本色 - 不切换
'--colors-fontColorPrimary': '#333', // 基本色
'--colors-fontColorInverse': '#fff', // 反色
'--colors-fontColorGrey': '#999', // 辅助灰色,如加载更多的提示信息
'--colors-fontColorPlaceholder': '#808080',
'--colors-fontColorDisable': '#c0c0c0',
'--colors-fontColorBottomText': '#6F6E80', // 底部tabbar文字颜色
/* 背景颜色 */
'--colors-container': '#ffffff',
'--colors-page': '#F6F4F0',
'--colors-containerInverse': '#000000', // 背景反色
'--colors-containerHover': 'rgba(255, 226, 116, .5)', // 点击状态颜色
'--colors-secondaryHover': 'rgba(90,119,103, .5)', // 点击状态颜色
'--colors-containerMask': 'rgba(0, 0, 0, 0.4)', // 遮罩颜色
// '--colors-iconButton': '#F5F5EC',
'--colors-iconButton': '#E4E4E4', // ICON 按钮背景色
/* 边框颜色 */
'--colors-borderColor': '#c8c7cc',
/* 文字尺寸 */
'--fontSize-sm': '24rpx',
'--fontSize-base': '28rpx',
'--fontSize-lg': '32rpx',
/* 文章场景相关 */
'--fontSize-title': '40rpx',
'--fontSize-subtitle': '36rpx',
'--fontSize-paragraph': '32rpx',
/* Border Radius */
'--borderRadius-sm': '4rpx',
'--borderRadius-base': '6rpx',
'--borderRadius-lg': '12rpx',
'--borderRadius-circle': '50%',
/* 水平间距 */
'--spacing-rowSm': '12px',
'--spacing-rowBase': '24rpx',
'--spacing-rowLg': '32rpx',
/* 垂直间距 */
'--spacing-colSm': '8rpx',
'--spacing-colBase': '16rpx',
'--spacing-colLg': '24rpx',
/* 透明度 */
'--opacity-disabled': '0.3', // 组件禁用态的透明度
},
dark: {
/* dark */
/* 颜色 */
'--colors-primary': '#123',
'--colors-secondary': '#5A7767', // 次要
'--colors-accent': '#FB9300', // 强调
'--colors-success': '#4cd964',
'--colors-warning': '#f0ad4e',
'--colors-error': '#dd524d',
'--colors-disable': '#ddd',
'--colors-danger': '#FF6843',
'--colors-mark': 'rgba(26, 26, 26,0.4)',
/* 文章场景相关 */
'--colors-title': '#2c405a', // 文章标题颜色
'--colors-subtitle': '#555555', // 二级标题颜色
'--colors-paragraph': '#3f536e', // 文章段落颜色
/* 文字基本颜色 */
'--colors-fontColorblack': '#333', // 基本色 - 不切换
'--colors-fontColorPrimary': '#999', // 基本色
'--colors-fontColorInverse': '#333', // 反色
'--colors-fontColorGrey': '#666', // 辅助灰色,如加载更多的提示信息
'--colors-fontColorPlaceholder': '#808080',
'--colors-fontColorDisable': '#c0c0c0',
'--colors-fontColorBottomText': '#6F6E80', // 底部tabbar文字颜色
/* 背景颜色 */
'--colors-container': '#000000',
'--colors-page': '#191919',
'--colors-containerInverse': '#ffffff', // 背景反色
'--colors-containerHover': 'rgba(255, 226, 116, .5)', // 点击状态颜色
'--colors-secondaryHover': 'rgba(90,119,103, .5)', // 点击状态颜色
'--colors-containerMask': 'rgba(0, 0, 0, 0.4)', // 遮罩颜色
'--colors-iconButton': '#808080', // ICON 按钮背景色
/* 边框颜色 */
'--colors-borderColor': '#c8c7cc',
/* 文字尺寸 */
'--fontSize-sm': '24rpx',
'--fontSize-base': '28rpx',
'--fontSize-lg': '32rpx',
/* 文章场景相关 */
'--fontSize-title': '40rpx',
'--fontSize-subtitle': '36rpx',
'--fontSize-paragraph': '32rpx',
/* Border Radius */
'--borderRadius-sm': '4rpx',
'--borderRadius-base': '6rpx',
'--borderRadius-lg': '12rpx',
'--borderRadius-circle': '50%',
/* 水平间距 */
'--spacing-rowSm': '12px',
'--spacing-rowBase': '24rpx',
'--spacing-rowLg': '32rpx',
/* 垂直间距 */
'--spacing-colSm': '8rpx',
'--spacing-colBase': '16rpx',
'--spacing-colLg': '24rpx',
/* 透明度 */
'--opacity-disabled': '0.3', // 组件禁用态的透明度
},
};
enum ExtendStyle {
SPACING = 'spacing',
// BORDERRADIUS = 'borderRadius' ,
// FONTSIZE = 'fontSize',
OPACITY = 'opacity',
}
// 构建 theme
export function buildTheme() {
const spacing = {
0: '0px',
};
let i = 0;
while (i <= 750) {
spacing[`${i}px`] = `${i}px`;
spacing[`${i}rpx`] = `${i}rpx`;
i++;
}
const _theme: any = {
extend: {
// spacing,
},
};
const flattenedObj: Map<string, any> = new Map([]);
for (const [_, themeItem] of Object.entries(theme)) {
for (const [key, _] of Object.entries(themeItem)) {
flattenedObj.set(key, _);
}
}
const extendList = [ExtendStyle.OPACITY];
flattenedObj.forEach((_v: string, k: string) => {
const [_, key, value] = k.match(/^--([^-]+)-(.+)$/) || [0, 'key', 'value'];
if (extendList.includes(key as ExtendStyle)) {
if (!_theme.extend[key]) {
_theme.extend[key] = {};
}
_theme.extend[key][value] = `var(${k})`;
} else {
if (!_theme[key]) {
_theme[key] = {};
}
_theme[key][value] = `var(${k})`;
}
});
// console.log('_theme ===>>>', _theme);
return _theme;
}

242
src/theme/vars.ts Normal file
View File

@ -0,0 +1,242 @@
/**
* ## File: /src/theme/vars.ts
*
* Project: uniapp_vue3_vite_ts
*
* Created Date: 2024-09-19 20:54:62
*
* Author: KuroSago
*
* ## Last Modified: 2024-09-19 20:54:62
*
* Modified By: KuroSago
*
* ## Copyright (c) 2024 self.
*
* Use To:
*/
import type { MD3ColorSchemeTokens, MD3Palettes } from '@ontos/material-design-3-theme-builder/src/index';
const palette: MD3Palettes = {
primary: {
0: 'var(--primary-0)',
10: 'var(--primary-10)',
20: 'var(--primary-20)',
30: 'var(--primary-30)',
40: 'var(--primary-40)',
50: 'var(--primary-50)',
60: 'var(--primary-60)',
70: 'var(--primary-70)',
80: 'var(--primary-80)',
90: 'var(--primary-90)',
95: 'var(--primary-95)',
99: 'var(--primary-99)',
100: 'var(--primary-100)',
},
secondary: {
0: 'var(--secondary-0)',
10: 'var(--secondary-10)',
20: 'var(--secondary-20)',
30: 'var(--secondary-30)',
40: 'var(--secondary-40)',
50: 'var(--secondary-50)',
60: 'var(--secondary-60)',
70: 'var(--secondary-70)',
80: 'var(--secondary-80)',
90: 'var(--secondary-90)',
95: 'var(--secondary-95)',
99: 'var(--secondary-99)',
100: 'var(--secondary-100)',
},
tertiary: {
0: 'var(--tertiary-0)',
10: 'var(--tertiary-10)',
20: 'var(--tertiary-20)',
30: 'var(--tertiary-30)',
40: 'var(--tertiary-40)',
50: 'var(--tertiary-50)',
60: 'var(--tertiary-60)',
70: 'var(--tertiary-70)',
80: 'var(--tertiary-80)',
90: 'var(--tertiary-90)',
95: 'var(--tertiary-95)',
99: 'var(--tertiary-99)',
100: 'var(--tertiary-100)',
},
error: {
0: 'var(--error-0)',
10: 'var(--error-10)',
20: 'var(--error-20)',
30: 'var(--error-30)',
40: 'var(--error-40)',
50: 'var(--error-50)',
60: 'var(--error-60)',
70: 'var(--error-70)',
80: 'var(--error-80)',
90: 'var(--error-90)',
95: 'var(--error-95)',
99: 'var(--error-99)',
100: 'var(--error-100)',
},
info: {
0: 'var(--info-0)',
10: 'var(--info-10)',
20: 'var(--info-20)',
30: 'var(--info-30)',
40: 'var(--info-40)',
50: 'var(--info-50)',
60: 'var(--info-60)',
70: 'var(--info-70)',
80: 'var(--info-80)',
90: 'var(--info-90)',
95: 'var(--info-95)',
99: 'var(--info-99)',
100: 'var(--info-100)',
},
warning: {
0: 'var(--warning-0)',
10: 'var(--warning-10)',
20: 'var(--warning-20)',
30: 'var(--warning-30)',
40: 'var(--warning-40)',
50: 'var(--warning-50)',
60: 'var(--warning-60)',
70: 'var(--warning-70)',
80: 'var(--warning-80)',
90: 'var(--warning-90)',
95: 'var(--warning-95)',
99: 'var(--warning-99)',
100: 'var(--warning-100)',
},
success: {
0: 'var(--success-0)',
10: 'var(--success-10)',
20: 'var(--success-20)',
30: 'var(--success-30)',
40: 'var(--success-40)',
50: 'var(--success-50)',
60: 'var(--success-60)',
70: 'var(--success-70)',
80: 'var(--success-80)',
90: 'var(--success-90)',
95: 'var(--success-95)',
99: 'var(--success-99)',
100: 'var(--success-100)',
},
neutral: {
0: 'var(--neutral-0)',
4: 'var(--neutral-4)',
6: 'var(--neutral-6)',
10: 'var(--neutral-10)',
12: 'var(--neutral-12)',
17: 'var(--neutral-17)',
20: 'var(--neutral-20)',
22: 'var(--neutral-22)',
24: 'var(--neutral-24)',
30: 'var(--neutral-30)',
40: 'var(--neutral-40)',
50: 'var(--neutral-50)',
60: 'var(--neutral-60)',
70: 'var(--neutral-70)',
80: 'var(--neutral-80)',
90: 'var(--neutral-90)',
92: 'var(--neutral-92)',
94: 'var(--neutral-94)',
95: 'var(--neutral-95)',
96: 'var(--neutral-96)',
98: 'var(--neutral-98)',
99: 'var(--neutral-99)',
100: 'var(--neutral-100)',
},
neutralVariant: {
0: 'var(--neutralVariant-0)',
10: 'var(--neutralVariant-10)',
20: 'var(--neutralVariant-20)',
30: 'var(--neutralVariant-30)',
40: 'var(--neutralVariant-40)',
50: 'var(--neutralVariant-50)',
60: 'var(--neutralVariant-60)',
70: 'var(--neutralVariant-70)',
80: 'var(--neutralVariant-80)',
90: 'var(--neutralVariant-90)',
95: 'var(--neutralVariant-95)',
99: 'var(--neutralVariant-99)',
100: 'var(--neutralVariant-100)',
},
common: {
white: 'var(--common-white)',
black: 'var(--common-black)',
},
};
const scheme: MD3ColorSchemeTokens = {
primary: 'var(--primary)',
onPrimary: 'var(--onPrimary)',
primaryContainer: 'var(--primaryContainer)',
onPrimaryContainer: 'var(--onPrimaryContainer)',
secondary: 'var(--secondary)',
onSecondary: 'var(--onSecondary)',
secondaryContainer: 'var(--secondaryContainer)',
onSecondaryContainer: 'var(--onSecondaryContainer)',
tertiary: 'var(--tertiary)',
onTertiary: 'var(--onTertiary)',
tertiaryContainer: 'var(--tertiaryContainer)',
onTertiaryContainer: 'var(--onTertiaryContainer)',
error: 'var(--error)',
onError: 'var(--onError)',
errorContainer: 'var(--errorContainer)',
onErrorContainer: 'var(--onErrorContainer)',
info: 'var(--info)',
onInfo: 'var(--onInfo)',
infoContainer: 'var(--infoContainer)',
onInfoContainer: 'var(--onInfoContainer)',
warning: 'var(--warning)',
onWarning: 'var(--onWarning)',
warningContainer: 'var(--warningContainer)',
onWarningContainer: 'var(--onWarningContainer)',
success: 'var(--success)',
onSuccess: 'var(--onSuccess)',
successContainer: 'var(--successContainer)',
onSuccessContainer: 'var(--onSuccessContainer)',
surface: 'var(--surface)',
onSurface: 'var(--onSurface)',
surfaceVariant: 'var(--surfaceVariant)',
onSurfaceVariant: 'var(--onSurfaceVariant)',
surfaceContainerLowest: 'var(--surfaceContainerLowest)',
surfaceContainerLow: 'var(--surfaceContainerLow)',
surfaceContainer: 'var(--surfaceContainer)',
surfaceContainerHigh: 'var(--surfaceContainerHigh)',
surfaceContainerHighest: 'var(--surfaceContainerHighest)',
surfaceTint: 'var(--surfaceTint)',
outline: 'var(--outline)',
outlineVariant: 'var(--outlineVariant)',
shadow: 'var(--shadow)',
background: 'var(--background)',
onBackground: 'var(--onBackground)',
inversePrimary: 'var(--inversePrimary)',
inverseSurface: 'var(--inverseSurface)',
inverseOnSurface: 'var(--inverseOnSurface)',
};
// 拉平色板 palette 为 tokens
export function getPaletteTokens() {
const paletteTokens: Record<string, string> = {};
for (const [key, value] of Object.entries(palette)) {
for (const [subKey, subValue] of Object.entries(value as Record<string, string>)) {
paletteTokens[`${key}-${subKey}`] = subValue;
}
}
return paletteTokens;
}
export function themeVars() {
const paletteTokens = getPaletteTokens();
const colors = {
...paletteTokens,
...scheme,
};
// console.log('colors', colors);
return {
colors,
};
};

View File

@ -11,6 +11,6 @@ declare module 'vue' {
BasicButton: typeof import('./../components/BasicButton/index.vue')['default'] BasicButton: typeof import('./../components/BasicButton/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
'svg-icon': typeof import('./../components/SvgIcon/index.vue')['default'] SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
} }
} }

View File

@ -1,17 +1,16 @@
/**
/**
* unocss defineConfig * unocss defineConfig
* @link unocss: https://github.com/unocss/unocss * @link unocss: https://github.com/unocss/unocss
* @link unocss-preset-weapp: https://github.com/MellowCo/unocss-preset-weapp * @link unocss-preset-weapp: https://github.com/MellowCo/unocss-preset-weapp
* */ */
import { defineConfig, presetIcons } from 'unocss'; import { defineConfig, presetIcons } from 'unocss';
import presetWeapp from 'unocss-preset-weapp'; import presetWeapp from 'unocss-preset-weapp';
import { transformerAttributify, transformerClass } from 'unocss-preset-weapp/transformer'; import { transformerAttributify, transformerClass } from 'unocss-preset-weapp/transformer';
import transformerDirectives from '@unocss/transformer-directives'; import transformerDirectives from '@unocss/transformer-directives';
import { themeVars } from './src/theme/vars';
const transformRules = { const transformRules = {
'.': '-d111-', '.': '-d111-',
'/': '-s111-', '/': '-s111-',
':': '-c111-', ':': '-c111-',
@ -22,18 +21,18 @@
')': '-b111r-', ')': '-b111r-',
'[': '-f111l-', '[': '-f111l-',
']': '-f111r-', ']': '-f111r-',
$: '-r111-', '$': '-r111-',
',': '-r222-', ',': '-r222-',
}; };
const prefix = ''; const prefix = '';
export default defineConfig({ export default defineConfig({
presets: [ presets: [
// https://github.com/MellowCo/unocss-preset-weapp // https://github.com/MellowCo/unocss-preset-weapp
presetWeapp({ presetWeapp({
nonValuedAttribute: true, nonValuedAttribute: true,
prefix: prefix, prefix,
whRpx: true, whRpx: true,
transform: true, transform: true,
platform: 'uniapp', platform: 'uniapp',
@ -56,14 +55,19 @@
'border-base': 'border border-gray-500_10', 'border-base': 'border border-gray-500_10',
'z-tar-both': 'z-988', 'z-tar-both': 'z-988',
'head-fixed': 'fixed top-0 left-0 w-full z-tar-both', 'head-fixed': 'fixed top-0 left-0 w-full z-tar-both',
center: 'flex justify-center items-center', 'center': 'flex justify-center items-center',
}, },
], ],
theme: {"extend":{"opacity":{"disabled":"var(--opacity-disabled)"}},"colors":{"primary":"var(--colors-primary)","secondary":"var(--colors-secondary)","accent":"var(--colors-accent)","success":"var(--colors-success)","warning":"var(--colors-warning)","error":"var(--colors-error)","disable":"var(--colors-disable)","danger":"var(--colors-danger)","mark":"var(--colors-mark)","title":"var(--colors-title)","subtitle":"var(--colors-subtitle)","paragraph":"var(--colors-paragraph)","fontColorblack":"var(--colors-fontColorblack)","fontColorPrimary":"var(--colors-fontColorPrimary)","fontColorInverse":"var(--colors-fontColorInverse)","fontColorGrey":"var(--colors-fontColorGrey)","fontColorPlaceholder":"var(--colors-fontColorPlaceholder)","fontColorDisable":"var(--colors-fontColorDisable)","fontColorBottomText":"var(--colors-fontColorBottomText)","container":"var(--colors-container)","page":"var(--colors-page)","containerInverse":"var(--colors-containerInverse)","containerHover":"var(--colors-containerHover)","secondaryHover":"var(--colors-secondaryHover)","containerMask":"var(--colors-containerMask)","iconButton":"var(--colors-iconButton)","borderColor":"var(--colors-borderColor)"},"fontSize":{"sm":"var(--fontSize-sm)","base":"var(--fontSize-base)","lg":"var(--fontSize-lg)","title":"var(--fontSize-title)","subtitle":"var(--fontSize-subtitle)","paragraph":"var(--fontSize-paragraph)"},"borderRadius":{"sm":"var(--borderRadius-sm)","base":"var(--borderRadius-base)","lg":"var(--borderRadius-lg)","circle":"var(--borderRadius-circle)"},"spacing":{"rowSm":"var(--spacing-rowSm)","rowBase":"var(--spacing-rowBase)","rowLg":"var(--spacing-rowLg)","colSm":"var(--spacing-colSm)","colBase":"var(--spacing-colBase)","colLg":"var(--spacing-colLg)"}}, theme: {
transformers: [ extend: { opacity: { disabled: 'var(--opacity-disabled)' } },
...themeVars(),
},
transformers:
[
transformerDirectives({ transformerDirectives({
applyVariable: ['--at-apply', '--uno-apply', '--uno'], applyVariable: ['--at-apply', '--uno-apply', '--uno'],
enforce: 'pre' enforce: 'pre',
}), }),
transformerAttributify({ transformerAttributify({
classPrefix: prefix, classPrefix: prefix,
@ -74,5 +78,4 @@
transformRules, transformRules,
}), }),
], ],
}); });

View File

@ -1,119 +0,0 @@
import * as fs from 'node:fs';
import { buildTheme } from '../../src/theme/theme';
export default function buildThemeConfig() {
const fileName = './unocss.config.ts';
const _data = buildTheme();
// 写入时会先清空文件
fs.writeFile(fileName, `${createFileByTemplate(_data)}`, (err: any) => {
if (err) {
console.error(err);
} else {
// console.log('构建 css 提示文件完成');
console.table(flatten(_data));
}
});
// 写完文件后使用编辑器格式化一下
}
function flatten(obj: any, parentKey = ''): any {
let result: any = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof value === 'object' && value !== null) {
const flattened = flatten(value, newKey);
result = { ...result, ...flattened };
} else {
result[newKey] = value;
}
}
return result;
}
// 通过模版生成文件
function createFileByTemplate(theme: any) {
const file_string = `
/**
* unocss defineConfig
* @link unocss: https://github.com/unocss/unocss
* @link unocss-preset-weapp: https://github.com/MellowCo/unocss-preset-weapp
* */
import { defineConfig, presetIcons } from 'unocss';
import presetWeapp from 'unocss-preset-weapp';
import { transformerAttributify, transformerClass } from 'unocss-preset-weapp/transformer';
import transformerDirectives from '@unocss/transformer-directives';
const transformRules = {
'.': '-d111-',
'/': '-s111-',
':': '-c111-',
'%': '-p111-',
'!': '-e111-',
'#': '-w111-',
'(': '-b111l-',
')': '-b111r-',
'[': '-f111l-',
']': '-f111r-',
$: '-r111-',
',': '-r222-',
};
const prefix = '';
export default defineConfig({
presets: [
// https://github.com/MellowCo/unocss-preset-weapp
presetWeapp({
nonValuedAttribute: true,
prefix: prefix,
whRpx: true,
transform: true,
platform: 'uniapp',
transformRules,
}),
// 由 Iconify 提供支持的纯 CSS 图标解决方案
presetIcons({
scale: 1,
warn: true,
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
],
// transformers: [transformerDirectives()],
// attributify: false,
shortcuts: [
{
'border-base': 'border border-gray-500_10',
'z-tar-both': 'z-988',
'head-fixed': 'fixed top-0 left-0 w-full z-tar-both',
center: 'flex justify-center items-center',
},
],
theme: ${JSON.stringify(theme)},
transformers: [
transformerDirectives({
applyVariable: ['--at-apply', '--uno-apply', '--uno'],
enforce: 'pre'
}),
transformerAttributify({
classPrefix: prefix,
transformRules,
nonValuedAttribute: true,
}),
transformerClass({
transformRules,
}),
],
});
`;
return file_string;
}

View File

@ -1,5 +1,4 @@
import appProvider from './appProvider'; import appProvider from './appProvider';
import buildThemeConfig from './build.unocss.config';
import createComponents from './components'; import createComponents from './components';
export { appProvider, buildThemeConfig, createComponents }; export { appProvider, createComponents };