refactor: ♻️ 优化主题和主题色集中监听,避免多处初始化

This commit is contained in:
hxr 2024-03-03 23:04:07 +08:00
parent 8456f1f26d
commit 17c29ee971
4 changed files with 93 additions and 87 deletions

View File

@ -55,7 +55,6 @@ import { useSettingsStore, usePermissionStore, useAppStore } from "@/store";
import { Sunny, Moon } from "@element-plus/icons-vue"; import { Sunny, Moon } from "@element-plus/icons-vue";
import { LayoutEnum } from "@/enums/LayoutEnum"; import { LayoutEnum } from "@/enums/LayoutEnum";
import { ThemeEnum } from "@/enums/ThemeEnum"; import { ThemeEnum } from "@/enums/ThemeEnum";
import { genMixColor } from "@/utils/color";
const route = useRoute(); const route = useRoute();
const appStore = useAppStore(); const appStore = useAppStore();
@ -76,30 +75,15 @@ const settingsVisible = computed({
*/ */
function changeThemeColor(color: string) { function changeThemeColor(color: string) {
settingsStore.changeThemeColor(color); settingsStore.changeThemeColor(color);
const { DEFAULT, dark, light } = genMixColor(color);
setStyleProperty(`--el-color-primary`, DEFAULT);
setStyleProperty(`--el-color-primary-dark-2`, dark[2]);
setStyleProperty(`--el-color-primary-light-3`, light[3]);
setStyleProperty(`--el-color-primary-light-5`, light[5]);
setStyleProperty(`--el-color-primary-light-7`, light[7]);
setStyleProperty(`--el-color-primary-light-8`, light[8]);
setStyleProperty(`--el-color-primary-light-9`, light[9]);
}
function setStyleProperty(propName: string, value: string) {
document.documentElement.style.setProperty(propName, value);
} }
/** /**
* 切换主题 * 切换主题
*/ */
const isDark = ref<boolean>(settingsStore.theme === ThemeEnum.DARK); const isDark = ref<boolean>(settingsStore.theme === ThemeEnum.DARK);
const changeTheme = (isDark: any) => { const changeTheme = (val: any) => {
useToggle(isDark); isDark.value = val;
const theme = isDark ? ThemeEnum.DARK : ThemeEnum.LIGHT; settingsStore.changeTheme(isDark.value ? ThemeEnum.DARK : ThemeEnum.LIGHT);
settingsStore.changeTheme(theme);
document.documentElement.classList.toggle("dark", theme === ThemeEnum.DARK);
}; };
/** /**
@ -148,11 +132,6 @@ function findOutermostParent(tree: any[], findName: string) {
return null; return null;
} }
onMounted(() => {
changeTheme(settingsStore.theme == ThemeEnum.DARK); //
changeThemeColor(settingsStore.themeColor); //
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -160,4 +139,3 @@ onMounted(() => {
@apply py-1 flex-x-between; @apply py-1 flex-x-between;
} }
</style> </style>
@/utils/color

View File

@ -1,4 +1,7 @@
import defaultSettings from "@/settings"; import defaultSettings from "@/settings";
import { genMixColor } from "@/utils/color";
import { setStyleProperty } from "@/utils";
import { ThemeEnum } from "@/enums/ThemeEnum";
type SettingsValue = boolean | string; type SettingsValue = boolean | string;
@ -32,6 +35,33 @@ export const useSettingsStore = defineStore("setting", () => {
defaultSettings.watermarkEnabled defaultSettings.watermarkEnabled
); );
watch(
[theme, themeColor],
([newTheme, newThemeColor], [oldTheme, oldThemeColor]) => {
if (newTheme !== oldTheme) {
if (newTheme === ThemeEnum.DARK) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}
if (newThemeColor !== oldThemeColor) {
const { DEFAULT, dark, light } = genMixColor(newThemeColor);
setStyleProperty(`--el-color-primary`, DEFAULT);
setStyleProperty(`--el-color-primary-dark-2`, dark[2]);
setStyleProperty(`--el-color-primary-light-3`, light[3]);
setStyleProperty(`--el-color-primary-light-5`, light[5]);
setStyleProperty(`--el-color-primary-light-7`, light[7]);
setStyleProperty(`--el-color-primary-light-8`, light[8]);
setStyleProperty(`--el-color-primary-light-9`, light[9]);
}
},
{
immediate: true, // 立即执行,确保在侦听器创建时执行一次
}
);
const settingsMap: Record<string, Ref<SettingsValue>> = { const settingsMap: Record<string, Ref<SettingsValue>> = {
fixedHeader, fixedHeader,
tagsView, tagsView,
@ -62,9 +92,12 @@ export const useSettingsStore = defineStore("setting", () => {
/** /**
* *
*
* @param color
*
*/ */
function changeThemeColor(val: string) { function changeThemeColor(color: string) {
themeColor.value = val; themeColor.value = color;
} }
/** /**

View File

@ -30,6 +30,8 @@ export function removeClass(ele: HTMLElement, cls: string) {
} }
/** /**
*
*
* @param {string} path * @param {string} path
* @returns {Boolean} * @returns {Boolean}
*/ */
@ -37,3 +39,13 @@ export function isExternal(path: string) {
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path); const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
return isExternal; return isExternal;
} }
/**
* Style属性
*
* @param propName
* @param value
*/
export function setStyleProperty(propName: string, value: string) {
document.documentElement.style.setProperty(propName, value);
}

View File

@ -7,9 +7,7 @@
inline-prompt inline-prompt
:active-icon="Moon" :active-icon="Moon"
:inactive-icon="Sunny" :inactive-icon="Sunny"
active-color="var(--el-fill-color-dark)" @change="toggleTheme"
inactive-color="var(--el-color-primary)"
@change="handleThemeChange"
/> />
<lang-select class="ml-2 cursor-pointer" /> <lang-select class="ml-2 cursor-pointer" />
</div> </div>
@ -117,61 +115,36 @@
<script setup lang="ts"> <script setup lang="ts">
import { useSettingsStore, useUserStore, useAppStore } from "@/store"; import { useSettingsStore, useUserStore, useAppStore } from "@/store";
import { Sunny, Moon } from "@element-plus/icons-vue";
import router from "@/router";
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
import { getCaptchaApi } from "@/api/auth"; import { getCaptchaApi } from "@/api/auth";
import { LoginData } from "@/api/auth/types"; import { LoginData } from "@/api/auth/types";
import { Sunny, Moon } from "@element-plus/icons-vue";
const route = useRoute(); import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
const userStore = useUserStore(); import router from "@/router";
const settingsStore = useSettingsStore();
import defaultSettings from "@/settings"; import defaultSettings from "@/settings";
import { ThemeEnum } from "@/enums/ThemeEnum"; import { ThemeEnum } from "@/enums/ThemeEnum";
/**
* 明亮/暗黑主题切换
*/
const isDark = ref<boolean>(settingsStore.theme === ThemeEnum.DARK);
const handleThemeChange = (isDark: any) => {
const theme = isDark ? ThemeEnum.DARK : ThemeEnum.LIGHT;
settingsStore.changeTheme(theme);
document.documentElement.classList.toggle("dark", theme === ThemeEnum.DARK);
};
/** // Stores
* 根据屏幕宽度切换设备模式 const userStore = useUserStore();
*/ const settingsStore = useSettingsStore();
const appStore = useAppStore(); const appStore = useAppStore();
const { width, height } = useWindowSize();
// Internationalization
const { t } = useI18n();
// Reactive states
const isDark = ref(settingsStore.theme === ThemeEnum.DARK);
const icpVisible = ref(true); const icpVisible = ref(true);
watchEffect(() => {
// >=1200px >=992px >=768px
if (width.value < 992) {
appStore.toggleDevice("mobile");
} else {
appStore.toggleDevice("desktop");
}
if (height.value < 600) {
icpVisible.value = false;
} else {
icpVisible.value = true;
}
});
const loading = ref(false); // loading const loading = ref(false); // loading
const isCapslock = ref(false); // const isCapslock = ref(false); //
const captchaBase64 = ref(); // Base64 const captchaBase64 = ref(); // Base64
const loginFormRef = ref(ElForm); // ref const loginFormRef = ref(ElForm); // ref
const { height } = useWindowSize();
const loginData = ref<LoginData>({ const loginData = ref<LoginData>({
username: "admin", username: "admin",
password: "123456", password: "123456",
}); });
const { t } = useI18n();
const loginRules = computed(() => { const loginRules = computed(() => {
const prefix = appStore.language === "en" ? "Please enter " : "请输入"; const prefix = appStore.language === "en" ? "Please enter " : "请输入";
return { return {
@ -206,13 +179,6 @@ const loginRules = computed(() => {
}; };
}); });
/**
* 检查输入大小写状态
*/
function checkCapslock(e: any) {
isCapslock.value = e.getModifierState("CapsLock");
}
/** /**
* 获取验证码 * 获取验证码
*/ */
@ -226,6 +192,7 @@ function getCaptcha() {
/** /**
* 登录 * 登录
*/ */
const route = useRoute();
function handleLogin() { function handleLogin() {
loginFormRef.value.validate((valid: boolean) => { loginFormRef.value.validate((valid: boolean) => {
if (valid) { if (valid) {
@ -234,9 +201,7 @@ function handleLogin() {
.login(loginData.value) .login(loginData.value)
.then(() => { .then(() => {
const query: LocationQuery = route.query; const query: LocationQuery = route.query;
const redirect = (query.redirect as LocationQueryValue) ?? "/"; const redirect = (query.redirect as LocationQueryValue) ?? "/";
const otherQueryParams = Object.keys(query).reduce( const otherQueryParams = Object.keys(query).reduce(
(acc: any, cur: string) => { (acc: any, cur: string) => {
if (cur !== "redirect") { if (cur !== "redirect") {
@ -250,7 +215,6 @@ function handleLogin() {
router.push({ path: redirect, query: otherQueryParams }); router.push({ path: redirect, query: otherQueryParams });
}) })
.catch(() => { .catch(() => {
//
getCaptcha(); getCaptcha();
}) })
.finally(() => { .finally(() => {
@ -260,17 +224,36 @@ function handleLogin() {
}); });
} }
/**
* 主题切换
*/
const toggleTheme = () => {
const newTheme =
settingsStore.theme === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
settingsStore.changeTheme(newTheme);
};
/**
* 根据屏幕宽度切换设备模式
*/
watchEffect(() => {
if (height.value < 600) {
icpVisible.value = false;
} else {
icpVisible.value = true;
}
});
/**
* 检查输入大小写
*/
function checkCapslock(e: any) {
isCapslock.value = e.getModifierState("CapsLock");
}
onMounted(() => { onMounted(() => {
getCaptcha(); getCaptcha();
//
const theme = useSettingsStore().theme;
useSettingsStore().changeSetting({ key: "theme", value: theme });
if (theme == "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
}); });
</script> </script>