refactor: ♻️ 优化主题和主题色集中监听,避免多处初始化
This commit is contained in:
parent
8456f1f26d
commit
17c29ee971
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue