mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-07 08:57:19 +08:00
commit
76f6a9df89
@ -1,52 +1,26 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useNav } from "../hooks/nav";
|
import { useNav } from "../hooks/nav";
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import Search from "./search/index.vue";
|
import Search from "./search/index.vue";
|
||||||
import Notice from "./notice/index.vue";
|
import Notice from "./notice/index.vue";
|
||||||
import mixNav from "./sidebar/mixNav.vue";
|
import mixNav from "./sidebar/mixNav.vue";
|
||||||
import avatars from "/@/assets/avatars.jpg";
|
import avatars from "/@/assets/avatars.jpg";
|
||||||
import { watch, getCurrentInstance } from "vue";
|
|
||||||
import Breadcrumb from "./sidebar/breadCrumb.vue";
|
import Breadcrumb from "./sidebar/breadCrumb.vue";
|
||||||
import { deviceDetection } from "@pureadmin/utils";
|
import { deviceDetection } from "@pureadmin/utils";
|
||||||
import screenfull from "../components/screenfull/index.vue";
|
import screenfull from "../components/screenfull/index.vue";
|
||||||
|
import { useTranslationLang } from "../hooks/useTranslationLang";
|
||||||
import globalization from "/@/assets/svg/globalization.svg?component";
|
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const { locale, t } = useI18n();
|
|
||||||
const instance =
|
|
||||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
|
||||||
const {
|
const {
|
||||||
logout,
|
logout,
|
||||||
onPanel,
|
onPanel,
|
||||||
changeTitle,
|
|
||||||
pureApp,
|
pureApp,
|
||||||
username,
|
username,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass,
|
getDropdownItemClass
|
||||||
changeWangeditorLanguage
|
|
||||||
} = useNav();
|
} = useNav();
|
||||||
|
|
||||||
watch(
|
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
||||||
() => locale.value,
|
|
||||||
() => {
|
|
||||||
changeTitle(route.meta);
|
|
||||||
locale.value === "en"
|
|
||||||
? changeWangeditorLanguage(locale.value)
|
|
||||||
: changeWangeditorLanguage("zh-CN");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function translationCh() {
|
|
||||||
instance.locale = { locale: "zh" };
|
|
||||||
locale.value = "zh";
|
|
||||||
}
|
|
||||||
|
|
||||||
function translationEn() {
|
|
||||||
instance.locale = { locale: "en" };
|
|
||||||
locale.value = "en";
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { SearchModal } from "./components";
|
import { SearchModal } from "./components";
|
||||||
import useBoolean from "../../hooks/useBoolean";
|
import { useBoolean } from "../../hooks/useBoolean";
|
||||||
const { bool: show, toggle } = useBoolean();
|
const { bool: show, toggle } = useBoolean();
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
toggle();
|
toggle();
|
||||||
|
@ -6,33 +6,24 @@ import {
|
|||||||
reactive,
|
reactive,
|
||||||
computed,
|
computed,
|
||||||
nextTick,
|
nextTick,
|
||||||
useCssModule,
|
useCssModule
|
||||||
getCurrentInstance
|
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { find } from "lodash-unified";
|
|
||||||
import { getConfig } from "/@/config";
|
import { getConfig } from "/@/config";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import panel from "../panel/index.vue";
|
import panel from "../panel/index.vue";
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
import { templateRef } from "@vueuse/core";
|
import { templateRef } from "@vueuse/core";
|
||||||
import { TinyColor } from "@ctrl/tinycolor";
|
|
||||||
import { themeColorsType } from "../../types";
|
|
||||||
import { routerArrays } from "/@/layout/types";
|
import { routerArrays } from "/@/layout/types";
|
||||||
import type { StorageConfigs } from "/#/index";
|
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
|
||||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||||
|
import { useDataThemeChange } from "/@/layout/hooks/useDataThemeChange";
|
||||||
import {
|
import {
|
||||||
useDark,
|
useDark,
|
||||||
debounce,
|
debounce,
|
||||||
storageLocal,
|
storageLocal,
|
||||||
storageSession
|
storageSession
|
||||||
} from "@pureadmin/utils";
|
} from "@pureadmin/utils";
|
||||||
import {
|
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
|
||||||
darken,
|
|
||||||
lighten,
|
|
||||||
toggleTheme
|
|
||||||
} from "@pureadmin/theme/dist/browser-utils";
|
|
||||||
|
|
||||||
import dayIcon from "/@/assets/svg/day.svg?component";
|
import dayIcon from "/@/assets/svg/day.svg?component";
|
||||||
import darkIcon from "/@/assets/svg/dark.svg?component";
|
import darkIcon from "/@/assets/svg/dark.svg?component";
|
||||||
@ -40,44 +31,21 @@ import darkIcon from "/@/assets/svg/dark.svg?component";
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isDark } = useDark();
|
const { isDark } = useDark();
|
||||||
const { isSelect } = useCssModule();
|
const { isSelect } = useCssModule();
|
||||||
const body = document.documentElement as HTMLElement;
|
|
||||||
const instance =
|
|
||||||
getCurrentInstance().appContext.app.config.globalProperties.$storage;
|
|
||||||
|
|
||||||
const instanceConfig =
|
|
||||||
getCurrentInstance().appContext.app.config.globalProperties.$config;
|
|
||||||
|
|
||||||
let themeColors = ref<Array<themeColorsType>>([
|
|
||||||
/* 道奇蓝(默认) */
|
|
||||||
{ color: "#1b2a47", themeColor: "default" },
|
|
||||||
/* 亮白色 */
|
|
||||||
{ color: "#ffffff", themeColor: "light" },
|
|
||||||
/* 猩红色 */
|
|
||||||
{ color: "#f5222d", themeColor: "dusk" },
|
|
||||||
/* 橙红色 */
|
|
||||||
{ color: "#fa541c", themeColor: "volcano" },
|
|
||||||
/* 金色 */
|
|
||||||
{ color: "#fadb14", themeColor: "yellow" },
|
|
||||||
/* 绿宝石 */
|
|
||||||
{ color: "#13c2c2", themeColor: "mingQing" },
|
|
||||||
/* 酸橙绿 */
|
|
||||||
{ color: "#52c41a", themeColor: "auroraGreen" },
|
|
||||||
/* 深粉色 */
|
|
||||||
{ color: "#eb2f96", themeColor: "pink" },
|
|
||||||
/* 深紫罗兰色 */
|
|
||||||
{ color: "#722ed1", themeColor: "saucePurple" }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
|
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
|
||||||
const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
|
const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
|
||||||
const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
|
const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
|
||||||
|
|
||||||
let layoutTheme =
|
const {
|
||||||
ref(storageLocal.getItem<StorageConfigs>("responsive-layout")) ||
|
body,
|
||||||
ref({
|
instance,
|
||||||
layout: instanceConfig?.Layout ?? "vertical",
|
dataTheme,
|
||||||
theme: instanceConfig?.Theme ?? "default"
|
layoutTheme,
|
||||||
});
|
themeColors,
|
||||||
|
dataThemeChange,
|
||||||
|
setEpThemeColor,
|
||||||
|
setLayoutThemeColor
|
||||||
|
} = useDataThemeChange();
|
||||||
|
|
||||||
/* body添加layout属性,作用于src/style/sidebar.scss */
|
/* body添加layout属性,作用于src/style/sidebar.scss */
|
||||||
if (unref(layoutTheme)) {
|
if (unref(layoutTheme)) {
|
||||||
@ -162,7 +130,7 @@ function onReset() {
|
|||||||
router.push("/login");
|
router.push("/login");
|
||||||
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
|
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
|
||||||
useAppStoreHook().setLayout(Layout);
|
useAppStoreHook().setLayout(Layout);
|
||||||
useEpThemeStoreHook().setEpThemeColor(EpThemeColor);
|
setEpThemeColor(EpThemeColor);
|
||||||
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
|
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
|
||||||
toggleClass(Grey, "html-grey", document.querySelector("html"));
|
toggleClass(Grey, "html-grey", document.querySelector("html"));
|
||||||
toggleClass(Weak, "html-weakness", document.querySelector("html"));
|
toggleClass(Weak, "html-weakness", document.querySelector("html"));
|
||||||
@ -245,75 +213,6 @@ function setLayoutModel(layout: string) {
|
|||||||
useAppStoreHook().setLayout(layout);
|
useAppStoreHook().setLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 设置导航主题色 */
|
|
||||||
function setLayoutThemeColor(theme: string) {
|
|
||||||
layoutTheme.value.theme = theme;
|
|
||||||
toggleTheme({
|
|
||||||
scopeName: `layout-theme-${theme}`
|
|
||||||
});
|
|
||||||
instance.layout = {
|
|
||||||
layout: useAppStoreHook().layout,
|
|
||||||
theme,
|
|
||||||
darkMode: dataTheme.value,
|
|
||||||
sidebarStatus: instance.layout.sidebarStatus,
|
|
||||||
epThemeColor: instance.layout.epThemeColor
|
|
||||||
};
|
|
||||||
|
|
||||||
if (theme === "default" || theme === "light") {
|
|
||||||
setEpThemeColor(getConfig().EpThemeColor);
|
|
||||||
} else {
|
|
||||||
const colors = find(themeColors.value, { themeColor: theme });
|
|
||||||
setEpThemeColor(colors.color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description 自动计算hover和active颜色
|
|
||||||
* @see {@link https://element-plus.org/zh-CN/component/button.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A2%9C%E8%89%B2}
|
|
||||||
*/
|
|
||||||
const shadeBgColor = (color: string): string => {
|
|
||||||
return new TinyColor(color).shade(10).toString();
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 设置ep主题色 */
|
|
||||||
const setEpThemeColor = (color: string) => {
|
|
||||||
useEpThemeStoreHook().setEpThemeColor(color);
|
|
||||||
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
|
|
||||||
document.documentElement.style.setProperty("--el-color-primary", color);
|
|
||||||
for (let i = 1; i <= 9; i++) {
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
`--el-color-primary-light-${i}`,
|
|
||||||
lighten(color, i / 10)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for (let i = 1; i <= 2; i++) {
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
`--el-color-primary-dark-${i}`,
|
|
||||||
darken(color, i / 10)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let dataTheme = ref<boolean>(instance.layout.darkMode);
|
|
||||||
|
|
||||||
/** 日间、夜间主题切换 */
|
|
||||||
function dataThemeChange() {
|
|
||||||
/* 如果当前是light夜间主题,默认切换到default主题 */
|
|
||||||
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
|
|
||||||
setLayoutThemeColor("default");
|
|
||||||
} else {
|
|
||||||
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataTheme.value) {
|
|
||||||
instance.layout.darkMode = true;
|
|
||||||
document.documentElement.classList.add("dark");
|
|
||||||
} else {
|
|
||||||
instance.layout.darkMode = false;
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 初始化项目配置 */
|
/* 初始化项目配置 */
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
settings.greyVal &&
|
settings.greyVal &&
|
||||||
|
@ -173,8 +173,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu {
|
.el-dropdown-menu {
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
li {
|
li {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
export default function useBoolean(initValue = false) {
|
export function useBoolean(initValue = false) {
|
||||||
const bool = ref(initValue);
|
const bool = ref(initValue);
|
||||||
|
|
||||||
function setBool(value: boolean) {
|
function setBool(value: boolean) {
|
||||||
|
120
src/layout/hooks/useDataThemeChange.ts
Normal file
120
src/layout/hooks/useDataThemeChange.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { getConfig } from "/@/config";
|
||||||
|
import { find } from "lodash-unified";
|
||||||
|
import { useLayout } from "./useLayout";
|
||||||
|
import { themeColorsType } from "../types";
|
||||||
|
import { TinyColor } from "@ctrl/tinycolor";
|
||||||
|
import { ref, getCurrentInstance } from "vue";
|
||||||
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
|
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||||
|
import {
|
||||||
|
darken,
|
||||||
|
lighten,
|
||||||
|
toggleTheme
|
||||||
|
} from "@pureadmin/theme/dist/browser-utils";
|
||||||
|
|
||||||
|
export function useDataThemeChange() {
|
||||||
|
const { layoutTheme } = useLayout();
|
||||||
|
const themeColors = ref<Array<themeColorsType>>([
|
||||||
|
/* 道奇蓝(默认) */
|
||||||
|
{ color: "#1b2a47", themeColor: "default" },
|
||||||
|
/* 亮白色 */
|
||||||
|
{ color: "#ffffff", themeColor: "light" },
|
||||||
|
/* 猩红色 */
|
||||||
|
{ color: "#f5222d", themeColor: "dusk" },
|
||||||
|
/* 橙红色 */
|
||||||
|
{ color: "#fa541c", themeColor: "volcano" },
|
||||||
|
/* 金色 */
|
||||||
|
{ color: "#fadb14", themeColor: "yellow" },
|
||||||
|
/* 绿宝石 */
|
||||||
|
{ color: "#13c2c2", themeColor: "mingQing" },
|
||||||
|
/* 酸橙绿 */
|
||||||
|
{ color: "#52c41a", themeColor: "auroraGreen" },
|
||||||
|
/* 深粉色 */
|
||||||
|
{ color: "#eb2f96", themeColor: "pink" },
|
||||||
|
/* 深紫罗兰色 */
|
||||||
|
{ color: "#722ed1", themeColor: "saucePurple" }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const body = document.documentElement as HTMLElement;
|
||||||
|
const instance =
|
||||||
|
getCurrentInstance().appContext.app.config.globalProperties.$storage;
|
||||||
|
|
||||||
|
/** 设置导航主题色 */
|
||||||
|
function setLayoutThemeColor(theme = "default") {
|
||||||
|
layoutTheme.value.theme = theme;
|
||||||
|
toggleTheme({
|
||||||
|
scopeName: `layout-theme-${theme}`
|
||||||
|
});
|
||||||
|
instance.layout = {
|
||||||
|
layout: useAppStoreHook().layout,
|
||||||
|
theme,
|
||||||
|
darkMode: dataTheme.value,
|
||||||
|
sidebarStatus: instance.layout.sidebarStatus,
|
||||||
|
epThemeColor: instance.layout.epThemeColor
|
||||||
|
};
|
||||||
|
|
||||||
|
if (theme === "default" || theme === "light") {
|
||||||
|
setEpThemeColor(getConfig().EpThemeColor);
|
||||||
|
} else {
|
||||||
|
const colors = find(themeColors.value, { themeColor: theme });
|
||||||
|
setEpThemeColor(colors.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 自动计算hover和active颜色
|
||||||
|
* @see {@link https://element-plus.org/zh-CN/component/button.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A2%9C%E8%89%B2}
|
||||||
|
*/
|
||||||
|
const shadeBgColor = (color: string): string => {
|
||||||
|
return new TinyColor(color).shade(10).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 设置ep主题色 */
|
||||||
|
const setEpThemeColor = (color: string) => {
|
||||||
|
useEpThemeStoreHook().setEpThemeColor(color);
|
||||||
|
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
|
||||||
|
document.documentElement.style.setProperty("--el-color-primary", color);
|
||||||
|
for (let i = 1; i <= 9; i++) {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
`--el-color-primary-light-${i}`,
|
||||||
|
lighten(color, i / 10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= 2; i++) {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
`--el-color-primary-dark-${i}`,
|
||||||
|
darken(color, i / 10)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dataTheme = ref<boolean>(instance?.layout?.darkMode);
|
||||||
|
|
||||||
|
/** 日间、夜间主题切换 */
|
||||||
|
function dataThemeChange() {
|
||||||
|
/* 如果当前是light夜间主题,默认切换到default主题 */
|
||||||
|
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
|
||||||
|
setLayoutThemeColor("default");
|
||||||
|
} else {
|
||||||
|
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataTheme.value) {
|
||||||
|
instance.layout.darkMode = true;
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
instance.layout.darkMode = false;
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
body,
|
||||||
|
instance,
|
||||||
|
dataTheme,
|
||||||
|
layoutTheme,
|
||||||
|
themeColors,
|
||||||
|
dataThemeChange,
|
||||||
|
setEpThemeColor,
|
||||||
|
setLayoutThemeColor
|
||||||
|
};
|
||||||
|
}
|
61
src/layout/hooks/useLayout.ts
Normal file
61
src/layout/hooks/useLayout.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { routerArrays } from "../types";
|
||||||
|
import { computed, getCurrentInstance } from "vue";
|
||||||
|
import { useMultiTagsStore } from "/@/store/modules/multiTags";
|
||||||
|
|
||||||
|
export function useLayout() {
|
||||||
|
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
||||||
|
|
||||||
|
const initStorage = () => {
|
||||||
|
// 路由
|
||||||
|
if (
|
||||||
|
useMultiTagsStore().multiTagsCache &&
|
||||||
|
(!instance.$storage.tags || instance.$storage.tags.length === 0)
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
|
instance.$storage.tags = routerArrays;
|
||||||
|
}
|
||||||
|
// 国际化
|
||||||
|
if (!instance.$storage.locale) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
instance.$storage.locale = { locale: instance.$config?.Locale ?? "zh" };
|
||||||
|
useI18n().locale.value = instance.$config?.Locale ?? "zh";
|
||||||
|
}
|
||||||
|
// 导航
|
||||||
|
if (!instance.$storage.layout) {
|
||||||
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
|
instance.$storage.layout = {
|
||||||
|
layout: instance.$config?.Layout ?? "vertical",
|
||||||
|
theme: instance.$config?.Theme ?? "default",
|
||||||
|
darkMode: instance.$config?.DarkMode ?? false,
|
||||||
|
sidebarStatus: instance.$config?.SidebarStatus ?? true,
|
||||||
|
epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 灰色模式、色弱模式、隐藏标签页
|
||||||
|
if (!instance.$storage.configure) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
instance.$storage.configure = {
|
||||||
|
grey: instance.$config?.Grey ?? false,
|
||||||
|
weak: instance.$config?.Weak ?? false,
|
||||||
|
hideTabs: instance.$config?.HideTabs ?? false,
|
||||||
|
showLogo: instance.$config?.ShowLogo ?? true,
|
||||||
|
showModel: instance.$config?.ShowModel ?? "smart",
|
||||||
|
multiTagsCache: instance.$config?.MultiTagsCache ?? false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
|
||||||
|
const layout = computed(() => {
|
||||||
|
return instance.$storage?.layout.layout;
|
||||||
|
});
|
||||||
|
const layoutTheme = computed(() => {
|
||||||
|
return instance.$storage.layout;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
layout,
|
||||||
|
instance,
|
||||||
|
layoutTheme,
|
||||||
|
initStorage
|
||||||
|
};
|
||||||
|
}
|
38
src/layout/hooks/useTranslationLang.ts
Normal file
38
src/layout/hooks/useTranslationLang.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { useNav } from "./nav";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { watch, getCurrentInstance } from "vue";
|
||||||
|
|
||||||
|
export function useTranslationLang() {
|
||||||
|
const { changeTitle, changeWangeditorLanguage } = useNav();
|
||||||
|
const { locale, t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const instance =
|
||||||
|
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||||
|
|
||||||
|
function translationCh() {
|
||||||
|
instance.locale = { locale: "zh" };
|
||||||
|
locale.value = "zh";
|
||||||
|
}
|
||||||
|
|
||||||
|
function translationEn() {
|
||||||
|
instance.locale = { locale: "en" };
|
||||||
|
locale.value = "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => locale.value,
|
||||||
|
() => {
|
||||||
|
changeTitle(route.meta);
|
||||||
|
locale.value === "en"
|
||||||
|
? changeWangeditorLanguage(locale.value)
|
||||||
|
: changeWangeditorLanguage("zh-CN");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
t,
|
||||||
|
locale,
|
||||||
|
translationCh,
|
||||||
|
translationEn
|
||||||
|
};
|
||||||
|
}
|
@ -1,20 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
|
||||||
h,
|
|
||||||
reactive,
|
|
||||||
computed,
|
|
||||||
onMounted,
|
|
||||||
defineComponent,
|
|
||||||
getCurrentInstance
|
|
||||||
} from "vue";
|
|
||||||
import { setType } from "./types";
|
import { setType } from "./types";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { routerArrays } from "./types";
|
|
||||||
import { emitter } from "/@/utils/mitt";
|
import { emitter } from "/@/utils/mitt";
|
||||||
|
import { useLayout } from "./hooks/useLayout";
|
||||||
import { useAppStoreHook } from "/@/store/modules/app";
|
import { useAppStoreHook } from "/@/store/modules/app";
|
||||||
import { deviceDetection, useDark } from "@pureadmin/utils";
|
import { deviceDetection, useDark } from "@pureadmin/utils";
|
||||||
import { useMultiTagsStore } from "/@/store/modules/multiTags";
|
|
||||||
import { useSettingStoreHook } from "/@/store/modules/settings";
|
import { useSettingStoreHook } from "/@/store/modules/settings";
|
||||||
|
import { h, reactive, computed, onMounted, defineComponent } from "vue";
|
||||||
|
|
||||||
import backTop from "/@/assets/svg/back_top.svg?component";
|
import backTop from "/@/assets/svg/back_top.svg?component";
|
||||||
import fullScreen from "/@/assets/svg/full_screen.svg?component";
|
import fullScreen from "/@/assets/svg/full_screen.svg?component";
|
||||||
@ -30,49 +21,8 @@ import Horizontal from "./components/sidebar/horizontal.vue";
|
|||||||
const { isDark } = useDark();
|
const { isDark } = useDark();
|
||||||
const isMobile = deviceDetection();
|
const isMobile = deviceDetection();
|
||||||
const pureSetting = useSettingStoreHook();
|
const pureSetting = useSettingStoreHook();
|
||||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
|
||||||
|
|
||||||
// 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
|
const { instance, layout } = useLayout();
|
||||||
const layout = computed(() => {
|
|
||||||
// 路由
|
|
||||||
if (
|
|
||||||
useMultiTagsStore().multiTagsCache &&
|
|
||||||
(!instance.$storage.tags || instance.$storage.tags.length === 0)
|
|
||||||
) {
|
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
|
||||||
instance.$storage.tags = routerArrays;
|
|
||||||
}
|
|
||||||
// 国际化
|
|
||||||
if (!instance.$storage.locale) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
instance.$storage.locale = { locale: instance.$config?.Locale ?? "zh" };
|
|
||||||
useI18n().locale.value = instance.$config?.Locale ?? "zh";
|
|
||||||
}
|
|
||||||
// 导航
|
|
||||||
if (!instance.$storage.layout) {
|
|
||||||
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
|
||||||
instance.$storage.layout = {
|
|
||||||
layout: instance.$config?.Layout ?? "vertical",
|
|
||||||
theme: instance.$config?.Theme ?? "default",
|
|
||||||
darkMode: instance.$config?.DarkMode ?? false,
|
|
||||||
sidebarStatus: instance.$config?.SidebarStatus ?? true,
|
|
||||||
epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// 灰色模式、色弱模式、隐藏标签页
|
|
||||||
if (!instance.$storage.configure) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
instance.$storage.configure = {
|
|
||||||
grey: instance.$config?.Grey ?? false,
|
|
||||||
weak: instance.$config?.Weak ?? false,
|
|
||||||
hideTabs: instance.$config?.HideTabs ?? false,
|
|
||||||
showLogo: instance.$config?.ShowLogo ?? true,
|
|
||||||
showModel: instance.$config?.ShowModel ?? "smart",
|
|
||||||
multiTagsCache: instance.$config?.MultiTagsCache ?? false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return instance.$storage?.layout.layout;
|
|
||||||
});
|
|
||||||
|
|
||||||
const set: setType = reactive({
|
const set: setType = reactive({
|
||||||
sidebar: computed(() => {
|
sidebar: computed(() => {
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-dropdown-menu {
|
.el-dropdown-menu {
|
||||||
padding: 2px 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-range-separator {
|
.el-range-separator {
|
||||||
|
@ -7,15 +7,23 @@ import qrCode from "./components/qrCode.vue";
|
|||||||
import regist from "./components/regist.vue";
|
import regist from "./components/regist.vue";
|
||||||
import update from "./components/update.vue";
|
import update from "./components/update.vue";
|
||||||
import { initRouter } from "/@/router/utils";
|
import { initRouter } from "/@/router/utils";
|
||||||
|
import { useNav } from "/@/layout/hooks/nav";
|
||||||
import { message } from "@pureadmin/components";
|
import { message } from "@pureadmin/components";
|
||||||
import type { FormInstance } from "element-plus";
|
import type { FormInstance } from "element-plus";
|
||||||
import { storageSession } from "@pureadmin/utils";
|
import { storageSession } from "@pureadmin/utils";
|
||||||
import { ref, reactive, watch, computed, getCurrentInstance } from "vue";
|
|
||||||
import { operates, thirdParty } from "./utils/enums";
|
import { operates, thirdParty } from "./utils/enums";
|
||||||
|
import { useLayout } from "/@/layout/hooks/useLayout";
|
||||||
import { useUserStoreHook } from "/@/store/modules/user";
|
import { useUserStoreHook } from "/@/store/modules/user";
|
||||||
import { bg, avatar, currentWeek } from "./utils/static";
|
import { bg, avatar, currentWeek } from "./utils/static";
|
||||||
import { ReImageVerify } from "/@/components/ReImageVerify";
|
import { ReImageVerify } from "/@/components/ReImageVerify";
|
||||||
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
|
||||||
|
import { useTranslationLang } from "/@/layout/hooks/useTranslationLang";
|
||||||
|
import { useDataThemeChange } from "/@/layout/hooks/useDataThemeChange";
|
||||||
|
import { ref, reactive, watch, computed, getCurrentInstance } from "vue";
|
||||||
|
|
||||||
|
import dayIcon from "/@/assets/svg/day.svg?component";
|
||||||
|
import darkIcon from "/@/assets/svg/dark.svg?component";
|
||||||
|
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "Login"
|
name: "Login"
|
||||||
@ -31,6 +39,13 @@ const currentPage = computed(() => {
|
|||||||
return useUserStoreHook().currentPage;
|
return useUserStoreHook().currentPage;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { initStorage } = useLayout();
|
||||||
|
initStorage();
|
||||||
|
|
||||||
|
const { dataTheme, dataThemeChange } = useDataThemeChange();
|
||||||
|
const { getDropdownItemStyle, getDropdownItemClass } = useNav();
|
||||||
|
const { locale, translationCh, translationEn } = useTranslationLang();
|
||||||
|
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
password: "admin123",
|
password: "admin123",
|
||||||
@ -67,138 +82,184 @@ function onHandle(value) {
|
|||||||
watch(imgCode, value => {
|
watch(imgCode, value => {
|
||||||
useUserStoreHook().SET_VERIFYCODE(value);
|
useUserStoreHook().SET_VERIFYCODE(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dataThemeChange();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<img :src="bg" class="wave" />
|
<div class="wh-full select-none">
|
||||||
<div class="login-container">
|
<img :src="bg" class="wave" />
|
||||||
<div class="img">
|
<div class="flex-c absolute right-5 top-3">
|
||||||
<component :is="currentWeek" />
|
<!-- 主题 -->
|
||||||
|
<el-switch
|
||||||
|
v-model="dataTheme"
|
||||||
|
inline-prompt
|
||||||
|
:active-icon="dayIcon"
|
||||||
|
:inactive-icon="darkIcon"
|
||||||
|
@change="dataThemeChange"
|
||||||
|
/>
|
||||||
|
<!-- 国际化 -->
|
||||||
|
<el-dropdown trigger="click">
|
||||||
|
<globalization
|
||||||
|
class="hover:color-primary !hover:bg-transparent w-20px h-20px ml-1.5 cursor-pointer outline-none duration-300"
|
||||||
|
/>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu class="translation">
|
||||||
|
<el-dropdown-item
|
||||||
|
:style="getDropdownItemStyle(locale, 'zh')"
|
||||||
|
:class="['!dark:color-white', getDropdownItemClass(locale, 'zh')]"
|
||||||
|
@click="translationCh"
|
||||||
|
>
|
||||||
|
<IconifyIconOffline
|
||||||
|
class="check-zh"
|
||||||
|
v-show="locale === 'zh'"
|
||||||
|
icon="check"
|
||||||
|
/>
|
||||||
|
简体中文
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:style="getDropdownItemStyle(locale, 'en')"
|
||||||
|
:class="['!dark:color-white', getDropdownItemClass(locale, 'en')]"
|
||||||
|
@click="translationEn"
|
||||||
|
>
|
||||||
|
<span class="check-en" v-show="locale === 'en'">
|
||||||
|
<IconifyIconOffline icon="check" />
|
||||||
|
</span>
|
||||||
|
English
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-box">
|
<div class="login-container">
|
||||||
<div class="login-form">
|
<div class="img">
|
||||||
<avatar class="avatar" />
|
<component :is="currentWeek" />
|
||||||
<Motion>
|
</div>
|
||||||
<h2>{{ title }}</h2>
|
<div class="login-box">
|
||||||
</Motion>
|
<div class="login-form">
|
||||||
|
<avatar class="avatar" />
|
||||||
<el-form
|
<Motion>
|
||||||
v-if="currentPage === 0"
|
<h2 class="outline-none">{{ title }}</h2>
|
||||||
ref="ruleFormRef"
|
|
||||||
:model="ruleForm"
|
|
||||||
:rules="loginRules"
|
|
||||||
size="large"
|
|
||||||
@keyup.enter="onLogin(ruleFormRef)"
|
|
||||||
>
|
|
||||||
<Motion :delay="100">
|
|
||||||
<el-form-item prop="username">
|
|
||||||
<el-input
|
|
||||||
clearable
|
|
||||||
v-model="ruleForm.username"
|
|
||||||
placeholder="账号"
|
|
||||||
:prefix-icon="useRenderIcon('user')"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</Motion>
|
</Motion>
|
||||||
|
|
||||||
<Motion :delay="150">
|
<el-form
|
||||||
<el-form-item prop="password">
|
v-if="currentPage === 0"
|
||||||
<el-input
|
ref="ruleFormRef"
|
||||||
clearable
|
:model="ruleForm"
|
||||||
show-password
|
:rules="loginRules"
|
||||||
v-model="ruleForm.password"
|
size="large"
|
||||||
placeholder="密码"
|
@keyup.enter="onLogin(ruleFormRef)"
|
||||||
:prefix-icon="useRenderIcon('lock')"
|
>
|
||||||
/>
|
<Motion :delay="100">
|
||||||
</el-form-item>
|
<el-form-item prop="username">
|
||||||
</Motion>
|
<el-input
|
||||||
|
clearable
|
||||||
|
v-model="ruleForm.username"
|
||||||
|
placeholder="账号"
|
||||||
|
:prefix-icon="useRenderIcon('user')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
<Motion :delay="200">
|
<Motion :delay="150">
|
||||||
<el-form-item prop="verifyCode">
|
<el-form-item prop="password">
|
||||||
<el-input
|
<el-input
|
||||||
clearable
|
clearable
|
||||||
v-model="ruleForm.verifyCode"
|
show-password
|
||||||
placeholder="验证码"
|
v-model="ruleForm.password"
|
||||||
:prefix-icon="
|
placeholder="密码"
|
||||||
useRenderIcon('ri:shield-keyhole-line', { online: true })
|
:prefix-icon="useRenderIcon('lock')"
|
||||||
"
|
/>
|
||||||
>
|
</el-form-item>
|
||||||
<template v-slot:append>
|
</Motion>
|
||||||
<ReImageVerify v-model:code="imgCode" />
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</Motion>
|
|
||||||
|
|
||||||
<Motion :delay="250">
|
<Motion :delay="200">
|
||||||
<el-form-item>
|
<el-form-item prop="verifyCode">
|
||||||
<div class="w-full h-20px flex justify-between items-center">
|
<el-input
|
||||||
<el-checkbox v-model="checked">记住密码</el-checkbox>
|
clearable
|
||||||
<el-button
|
v-model="ruleForm.verifyCode"
|
||||||
link
|
placeholder="验证码"
|
||||||
type="primary"
|
:prefix-icon="
|
||||||
@click="useUserStoreHook().SET_CURRENTPAGE(4)"
|
useRenderIcon('ri:shield-keyhole-line', { online: true })
|
||||||
|
"
|
||||||
>
|
>
|
||||||
忘记密码?
|
<template v-slot:append>
|
||||||
</el-button>
|
<ReImageVerify v-model:code="imgCode" />
|
||||||
</div>
|
</template>
|
||||||
<el-button
|
</el-input>
|
||||||
class="w-full mt-4"
|
</el-form-item>
|
||||||
size="default"
|
</Motion>
|
||||||
type="primary"
|
|
||||||
:loading="loading"
|
|
||||||
@click="onLogin(ruleFormRef)"
|
|
||||||
>
|
|
||||||
登录
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</Motion>
|
|
||||||
|
|
||||||
<Motion :delay="300">
|
<Motion :delay="250">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<div class="w-full h-20px flex justify-between items-center">
|
<div class="w-full h-20px flex justify-between items-center">
|
||||||
|
<el-checkbox v-model="checked">记住密码</el-checkbox>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="useUserStoreHook().SET_CURRENTPAGE(4)"
|
||||||
|
>
|
||||||
|
忘记密码?
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
<el-button
|
<el-button
|
||||||
v-for="(item, index) in operates"
|
|
||||||
:key="index"
|
|
||||||
class="w-full mt-4"
|
class="w-full mt-4"
|
||||||
size="default"
|
size="default"
|
||||||
@click="onHandle(index + 1)"
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="onLogin(ruleFormRef)"
|
||||||
>
|
>
|
||||||
{{ item.title }}
|
登录
|
||||||
</el-button>
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
|
||||||
|
<Motion :delay="300">
|
||||||
|
<el-form-item>
|
||||||
|
<div class="w-full h-20px flex justify-between items-center">
|
||||||
|
<el-button
|
||||||
|
v-for="(item, index) in operates"
|
||||||
|
:key="index"
|
||||||
|
class="w-full mt-4"
|
||||||
|
size="default"
|
||||||
|
@click="onHandle(index + 1)"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</Motion>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<Motion v-if="currentPage === 0" :delay="350">
|
||||||
|
<el-form-item>
|
||||||
|
<el-divider>
|
||||||
|
<p class="text-gray-500 text-xs">第三方登录</p>
|
||||||
|
</el-divider>
|
||||||
|
<div class="w-full flex justify-evenly">
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in thirdParty"
|
||||||
|
:key="index"
|
||||||
|
:title="`${item.title}登录`"
|
||||||
|
>
|
||||||
|
<IconifyIconOnline
|
||||||
|
:icon="`ri:${item.icon}-fill`"
|
||||||
|
width="20"
|
||||||
|
class="cursor-pointer text-gray-500 hover:text-blue-400"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</Motion>
|
</Motion>
|
||||||
</el-form>
|
<!-- 手机号登录 -->
|
||||||
|
<phone v-if="currentPage === 1" />
|
||||||
<Motion v-if="currentPage === 0" :delay="350">
|
<!-- 二维码登录 -->
|
||||||
<el-form-item>
|
<qrCode v-if="currentPage === 2" />
|
||||||
<el-divider>
|
<!-- 注册 -->
|
||||||
<p class="text-gray-500 text-xs">第三方登录</p>
|
<regist v-if="currentPage === 3" />
|
||||||
</el-divider>
|
<!-- 忘记密码 -->
|
||||||
<div class="w-full flex justify-evenly">
|
<update v-if="currentPage === 4" />
|
||||||
<span
|
</div>
|
||||||
v-for="(item, index) in thirdParty"
|
|
||||||
:key="index"
|
|
||||||
:title="`${item.title}登录`"
|
|
||||||
>
|
|
||||||
<IconifyIconOnline
|
|
||||||
:icon="`ri:${item.icon}-fill`"
|
|
||||||
width="20"
|
|
||||||
class="cursor-pointer text-gray-500 hover:text-blue-400"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
|
||||||
</Motion>
|
|
||||||
<!-- 手机号登录 -->
|
|
||||||
<phone v-if="currentPage === 1" />
|
|
||||||
<!-- 二维码登录 -->
|
|
||||||
<qrCode v-if="currentPage === 2" />
|
|
||||||
<!-- 注册 -->
|
|
||||||
<regist v-if="currentPage === 3" />
|
|
||||||
<!-- 忘记密码 -->
|
|
||||||
<update v-if="currentPage === 4" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -212,4 +273,20 @@ watch(imgCode, value => {
|
|||||||
:deep(.el-input-group__append, .el-input-group__prepend) {
|
:deep(.el-input-group__append, .el-input-group__prepend) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.translation {
|
||||||
|
::v-deep(.el-dropdown-menu__item) {
|
||||||
|
padding: 5px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-zh {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-en {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user