赞
踩
提示:动态切换主题使用的是css3的var函数现实
示例:切换--main-bg-color的值,使用<div style="--main-bg-color:red"> </div> 那么当前标签下的所有节点使用到当前变量的都会发生变化
- :root {
- --main-bg-color: coral;
- }
-
- #div1 {
- background-color: var(--main-bg-color);
- }
-
- #div2 {
- background-color: var(--main-bg-color);
- }
通过浏览器检查,发现element ui库的变量
提示: 众所周知css是从上往下执行,如果我们可以在hand标签里最后一个添加一个style标签,是不是就可以重写掉element ui的变量
代码如下(示例):
代码如下:
- const style = document.createElement("style");
- style.innerText = ":root{--el-color-primary:red}";
- document.head.appendChild(style);
可以发现elementui的主色调已经被我们替换为红色
- $types: primary, success, warning, danger, error, info;
- @each $type in $types {
- @for $i from 1 through 9 {
- @include set-color-mix-level($type, $i, 'light', $color-white);
- }
- }
-
- // --el-color-primary-dark-2
- @each $type in $types {
- @include set-color-mix-level($type, 2, 'dark', $color-black);
- }
不难发现 --el-color-primary-dark-2变量 是当前primary颜色跟黑色混和百分之20生成的颜色
所以设置一个primary的颜色会生成会生成1-9的和白色混的渐变色,生产一个百分之20的和黑色混的颜色
推断类型: 通过一个主色调变量,生成n个渐变色
键值类型:一个变量对应一个值
- interface ThemeSetting {
- /**
- *element-ui Namespace
- */
- namespace: string;
- /**
- * 数据分隔符
- */
- division: string;
- /**
- * 前缀
- */
- startDivision: string;
- /**
- * 颜色外推设置
- */
- colorInferSetting: ColorInferSetting;
- }
-
- /**
- * 颜色混和设置
- */
- interface ColorInferSetting {
- /**
- * 与白色混
- */
- light: Array<number>;
- /**
- * 与黑色混
- */
- dark: Array<number>;
- /**
- * 类型
- */
- type: string;
- }
-
- /**
- * 平滑数据
- */
- interface KeyValueData {
- [propName: string]: string;
- }
- type UpdateInferData = KeyValueData;
-
- type UpdateKeyValueData = KeyValueData;
- /**
- *平滑数据
- */
- interface InferData {
- /**
- * 设置
- */
- setting?: ColorInferSetting | any;
- /**
- * 健
- */
- key: string;
- /**
- * 值
- */
- value: string;
- }
-
- export type {
- KeyValueData,
- InferData,
- ThemeSetting,
- UpdateInferData,
- UpdateKeyValueData,
- };

- import type { ThemeSetting } from "./type";
- const setting: ThemeSetting = {
- namespace: "el",
- division: "-",
- startDivision: "--",
- colorInferSetting: {
- light: [3, 5, 7, 8, 9],
- dark: [2],
- type: "color",
- },
- };
- export default setting;
- import type { InferData } from "./type";
- const inferDatas: Array<InferData> = [];
- export default inferDatas;
- import type { KeyValueData } from "./type";
- const keyValueData: KeyValueData = {
- "menu-item-height": "56px",
- };
- export default keyValueData;
- import type {
- ThemeSetting,
- InferData,
- KeyValueData,
- UpdateInferData,
- UpdateKeyValueData,
- } from "./type";
- import tinycolor from "@ctrl/tinycolor";
-
- declare global {
- interface ChildNode {
- innerText: string;
- }
- }
- class Theme {
- /**
- * 主题设置
- */
- themeSetting: ThemeSetting;
- /**
- * 键值数据
- */
- keyValue: KeyValueData;
- /**
- * 外推数据
- */
- inferDatas: Array<InferData>;
- /**
- *是否是第一次初始化
- */
- isFirstWriteStyle: boolean;
- /**
- * 混色白
- */
- colorWhite: string;
- /**
- * 混色黑
- */
- colorBlack: string;
-
- constructor(
- themeSetting: ThemeSetting,
- keyValue: KeyValueData,
- inferDatas: Array<InferData>
- ) {
- this.themeSetting = themeSetting;
- this.keyValue = keyValue;
- this.inferDatas = inferDatas;
- this.isFirstWriteStyle = true;
- this.colorWhite = "#ffffff";
- this.colorBlack = "#000000";
- }
-
- /**
- * 拼接
- * @param setting 主题设置
- * @param names 需要拼接的所有值
- * @returns 拼接后的数据
- */
- getVarName = (setting: ThemeSetting, ...names: Array<string>) => {
- return (
- setting.startDivision +
- setting.namespace +
- setting.division +
- names.join(setting.division)
- );
- };
-
- /**
- * 转换外推数据
- * @param setting 主题设置对象
- * @param inferData 外推数据
- * @returns
- */
- mapInferMainStyle = (setting: ThemeSetting, inferData: InferData) => {
- const key: string = this.getVarName(
- setting,
- inferData.setting
- ? inferData.setting.type
- : setting.colorInferSetting.type,
- inferData.key
- );
- return {
- [key]: inferData.value,
- ...this.mapInferDataStyle(setting, inferData),
- };
- };
- /**
- * 转换外推数据
- * @param setting 设置
- * @param inferDatas 外推数据
- */
- mapInferData = (setting: ThemeSetting, inferDatas: Array<InferData>) => {
- return inferDatas
- .map((itemData) => {
- return this.mapInferMainStyle(setting, itemData);
- })
- .reduce((pre, next) => {
- return { ...pre, ...next };
- }, {});
- };
- /**
- * 转换外推数据
- * @param setting 主题设置对象
- * @param inferData 外推数据
- * @returns
- */
- mapInferDataStyle = (setting: ThemeSetting, inferData: InferData) => {
- const inferSetting = inferData.setting
- ? inferData.setting
- : setting.colorInferSetting;
- if (inferSetting.type === "color") {
- return Object.keys(inferSetting)
- .map((key: string) => {
- if (key === "light" || key === "dark") {
- return inferSetting[key]
- .map((l) => {
- const varName = this.getVarName(
- setting,
- inferSetting.type,
- inferData.key,
- key,
- l.toString()
- );
- return {
- [varName]: tinycolor(inferData.value)
- .mix(
- key === "light" ? this.colorWhite : this.colorBlack,
- l * 10
- )
- .toHexString(),
- };
- })
- .reduce((pre, next) => {
- return { ...pre, ...next };
- }, {});
- }
- return {};
- })
- .reduce((pre, next) => {
- return { ...pre, ...next };
- }, {});
- }
- return {};
- };
-
- /**
- *
- * @param themeSetting 主题设置
- * @param keyValueData 键值数据
- * @returns 映射后的键值数据
- */
- mapKeyValue = (themeSetting: ThemeSetting, keyValueData: KeyValueData) => {
- return Object.keys(keyValueData)
- .map((key: string) => {
- return {
- [this.updateKeyBySetting(key, themeSetting)]: keyValueData[key],
- };
- })
- .reduce((pre, next) => {
- return { ...pre, ...next };
- }, {});
- };
- /**
- * 根据配置文件修改Key
- * @param key key
- * @param themeSetting 主题设置
- * @returns
- */
- updateKeyBySetting = (key: string, themeSetting: ThemeSetting) => {
- return key.startsWith(themeSetting.startDivision)
- ? key
- : key.startsWith(themeSetting.namespace)
- ? themeSetting.startDivision + key
- : key.startsWith(themeSetting.division)
- ? themeSetting.startDivision + themeSetting.namespace
- : themeSetting.startDivision +
- themeSetting.namespace +
- themeSetting.division +
- key;
- };
- /**
- *
- * @param setting 主题设置
- * @param keyValue 主题键值对数据
- * @param inferDatas 外推数据
- * @returns 合并后的键值对数据
- */
- tokeyValueStyle = () => {
- return {
- ...this.mapInferData(this.themeSetting, this.inferDatas),
- ...this.mapKeyValue(this.themeSetting, this.keyValue),
- };
- };
-
- /**
- * 将keyValue对象转换为S
- * @param keyValue
- * @returns
- */
- toString = (keyValue: KeyValueData) => {
- const inner = Object.keys(keyValue)
- .map((key: string) => {
- return key + ":" + keyValue[key] + ";";
- })
- .join("");
- return `@charset "UTF-8";:root{${inner}}`;
- };
-
- /**
- *
- * @param elNewStyle 新的变量样式
- */
- writeNewStyle = (elNewStyle: string) => {
- if (this.isFirstWriteStyle) {
- const style = document.createElement("style");
- style.innerText = elNewStyle;
- document.head.appendChild(style);
- this.isFirstWriteStyle = false;
- } else {
- if (document.head.lastChild) {
- document.head.lastChild.innerText = elNewStyle;
- }
- }
- };
-
- /**
- * 修改数据并且写入dom
- * @param updateInferData 平滑数据修改
- * @param updateKeyvalueData keyValue数据修改
- */
- updateWrite = (
- updateInferData?: UpdateInferData,
- updateKeyvalueData?: UpdateKeyValueData
- ) => {
- this.update(updateInferData, updateKeyvalueData);
- const newStyle = this.tokeyValueStyle();
- const newStyleString = this.toString(newStyle);
- this.writeNewStyle(newStyleString);
- };
-
- /**
- * 修改数据
- * @param inferData
- * @param keyvalueData
- */
- update = (
- updateInferData?: UpdateInferData,
- updateKeyvalueData?: UpdateKeyValueData
- ) => {
- if (updateInferData) {
- this.updateInferData(updateInferData);
- }
- if (updateKeyvalueData) {
- this.updateOrCreateKeyValueData(updateKeyvalueData);
- }
- };
-
- /**
- * 修改外推数据 外推数据只能修改,不能新增
- * @param inferData
- */
- updateInferData = (updateInferData: UpdateInferData) => {
- Object.keys(updateInferData).forEach((key) => {
- const findInfer = this.inferDatas.find((itemInfer) => {
- return itemInfer.key === key;
- });
- if (findInfer) {
- findInfer.value = updateInferData[key];
- } else {
- this.inferDatas.push({ key, value: updateInferData[key] });
- }
- });
- };
-
- /**
- * 修改KeyValue数据
- * @param keyvalueData keyValue数据
- */
- updateOrCreateKeyValueData = (updateKeyvalueData: UpdateKeyValueData) => {
- Object.keys(updateKeyvalueData).forEach((key) => {
- const newKey = this.updateKeyBySetting(key, this.themeSetting);
- this.keyValue[newKey] = updateKeyvalueData[newKey];
- });
- };
- }
-
- export default Theme;

- // 引入主题对象
- import Theme from "./theme";
- // 引入默认推断数据
- import inferDatas from "./theme/inferDatas";
- // 引入默认keyValue数据
- import keyValueData from "./theme/keyValueData";
- // 引入设置对象
- import setting from "./theme/setting";
- const app = createApp(App);
- // 创建主题对象
- const theme = new Theme(setting, keyValueData, inferDatas);
- // 将主题对象放到全局变量中
- app.config.globalProperties.theme = theme;
- import { reactive, getCurrentInstance } from "vue";
- import type { ComponentInternalInstance } from "vue";
- const { appContext } = getCurrentInstance() as ComponentInternalInstance;
- const theme = appContext.config.globalProperties.theme;
- theme.updateWrite({ primary: form.themeColor });
API
- 修改推断数据
- theme.updateWrite({
- primary: "#409eff",
- success: "#67c23a",
- warning: "#e6a23c",
- danger: "#f56c6c",
- error: "#f56c6c",
- info: "#909399",
- });
- 修改键值类型数据
- theme.updateWrite(undefined, {
- "--el-menu-active-color": "red",
- "--el-menu-bg-color": "pink",
- "--el-menu-item-height": "22px",
- });
- 一起修改
- theme.updateWrite(
- {
- primary: "#409eff",
- success: "#67c23a",
- warning: "#e6a23c",
- danger: "#f56c6c",
- error: "#f56c6c",
- info: "#909399",
- },
- {
- "--el-menu-active-color": "red",
- "--el-menu-bg-color": "pink",
- "--el-menu-item-height": "22px",
- }
- );
主题数据可以存储在数据库中,通过接口查询后调用函数修改即可完成动态主题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。