release: update 2.8.0

This commit is contained in:
xiaoxian521
2022-01-05 14:17:06 +08:00
parent 749633e32c
commit 3418f44be4
41 changed files with 543 additions and 418 deletions

View File

@@ -27,12 +27,23 @@ const transitions = computed(() => {
});
const hideTabs = computed(() => {
return instance?.sets.hideTabs;
return instance?.configure.hideTabs;
});
const layout = computed(() => {
return instance?.layout.layout === "vertical";
});
const getSectionStyle = computed(() => {
return [
hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 85px;" : "",
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
props.fixedHeader ? "" : "padding-top: 0;"
];
});
const transitionMain = defineComponent({
render() {
return h(
@@ -71,12 +82,7 @@ const transitionMain = defineComponent({
<template>
<section
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
:style="[
hideTabs && layout ? 'padding-top: 48px;' : '',
!hideTabs && layout ? 'padding-top: 85px;' : '',
hideTabs && !layout ? 'padding-top: 48px' : '',
!hideTabs && !layout ? 'padding-top: 85px;' : ''
]"
:style="getSectionStyle"
>
<router-view>
<template #default="{ Component, route }">

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { emitter } from "/@/utils/mitt";
import Notice from "./notice/index.vue";
@@ -22,6 +23,15 @@ const route = useRoute();
let usename = storageSession.getItem("info")?.username;
const { locale } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@@ -73,29 +83,23 @@ function translationEn() {
<div class="vertical-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@@ -114,14 +118,14 @@ function translationEn() {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@@ -230,7 +234,7 @@ function translationEn() {
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@@ -242,12 +246,10 @@ function translationEn() {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@@ -259,7 +261,6 @@ function translationEn() {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

View File

@@ -8,8 +8,8 @@ const { isFullscreen, toggle } = useFullscreen();
<i
:title="
isFullscreen
? $t('message.hsexitfullscreen')
: $t('message.hsfullscreen')
? $t('buttons.hsexitfullscreen')
: $t('buttons.hsfullscreen')
"
:class="
isFullscreen

View File

@@ -11,9 +11,9 @@ import {
} from "vue";
import rgbHex from "rgb-hex";
import { find } from "lodash-es";
import panel from "../panel/index.vue";
import { getConfig } from "/@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt";
import { templateRef } from "@vueuse/core";
import dayIcon from "/@/assets/svg/day.svg";
@@ -79,19 +79,33 @@ if (unref(layoutTheme)) {
}
// 默认灵动模式
const markValue = ref(storageLocal.getItem("showModel") || "smart");
const markValue = ref(instance.configure?.showModel ?? "smart");
const logoVal = ref(storageLocal.getItem("logoVal") || "1");
const logoVal = ref(instance.configure?.showLogo ?? true);
const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
const settings = reactive({
greyVal: instance.sets.grey,
weakVal: instance.sets.weak,
tabsVal: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
greyVal: instance.configure.grey,
weakVal: instance.configure.weak,
tabsVal: instance.configure.hideTabs,
showLogo: instance.configure.showLogo,
showModel: instance.configure.showModel,
multiTagsCache: instance.configure.multiTagsCache
});
const getThemeColorStyle = computed(() => {
return rgb => {
return { background: `rgb(${rgb})` };
};
});
function changeStorageConfigure(key, val) {
const storageConfigure = instance.configure;
storageConfigure[key] = val;
instance.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
@@ -102,12 +116,7 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
// 灰色模式设置
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
instance.sets = {
grey: value,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("grey", value);
};
// 色弱模式设置
@@ -117,33 +126,18 @@ const weekChange = (value): void => {
"html-weakness",
document.querySelector("html")
);
instance.sets = {
grey: instance.sets.grey,
weak: value,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("weak", value);
};
const tagsChange = () => {
let showVal = settings.tabsVal;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: showVal,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("hideTabs", showVal);
emitter.emit("tagViewsChange", showVal);
};
const multiTagsCacheChange = () => {
let multiTagsCache = settings.multiTagsCache;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: multiTagsCache
};
changeStorageConfigure("multiTagsCache", multiTagsCache);
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
@@ -160,30 +154,30 @@ function onReset() {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
]);
useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
useEpThemeStoreHook().setEpThemeColor("#409EFF");
useEpThemeStoreHook().setEpThemeColor(getConfig().EpThemeColor);
storageLocal.clear();
storageSession.clear();
router.push("/login");
}
function onChange(label) {
storageLocal.setItem("showModel", label);
changeStorageConfigure("showModel", label);
emitter.emit("tagViewsShowModel", label);
}
// 侧边栏Logo
function logoChange() {
unref(logoVal) === "1"
? storageLocal.setItem("logoVal", "1")
: storageLocal.setItem("logoVal", "-1");
unref(logoVal)
? changeStorageConfigure("showLogo", true)
: changeStorageConfigure("showLogo", false);
emitter.emit("logoChange", unref(logoVal));
}
@@ -232,13 +226,19 @@ function setLayoutModel(layout: string) {
instance.layout = {
layout,
theme: layoutTheme.value.theme,
darkMode: instance.layout.darkMode
darkMode: instance.layout.darkMode,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
useAppStoreHook().setLayout(layout);
}
// 存放夜间主题切换前的导航主题色
let tempLayoutThemeColor;
// 设置导航主题色
function setLayoutThemeColor(theme: string) {
tempLayoutThemeColor = instance.layout.theme;
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
@@ -246,11 +246,13 @@ function setLayoutThemeColor(theme: string) {
instance.layout = {
layout: useAppStoreHook().layout,
theme,
darkMode: dataTheme.value
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
if (theme === "default" || theme === "light") {
setEpThemeColor("#409EFF");
setEpThemeColor(getConfig().EpThemeColor);
} else {
const colors = find(themeColors.value, { themeColor: theme });
const color = "#" + rgbHex(colors.rgb);
@@ -274,10 +276,13 @@ function dataThemeChange() {
setLayoutThemeColor("light");
} else {
body.setAttribute("data-theme", "");
tempLayoutThemeColor && setLayoutThemeColor(tempLayoutThemeColor);
instance.layout = {
layout: useAppStoreHook().layout,
theme: instance.layout.theme,
darkMode: dataTheme.value
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
}
}
@@ -338,7 +343,7 @@ nextTick(() => {
<li
v-for="(item, index) in themeColors"
:key="index"
:style="{ background: `rgb(${item.rgb})` }"
:style="getThemeColorStyle(item.rgb)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
@@ -394,8 +399,8 @@ nextTick(() => {
<el-switch
v-model="logoVal"
inline-prompt
active-value="1"
inactive-value="-1"
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text=""
inactive-text=""

View File

@@ -65,7 +65,7 @@ const getBreadcrumb = (): void => {
{
path: "/welcome",
parentPath: "/",
meta: { title: "message.hshome", i18n: true }
meta: { title: "menus.hshome", i18n: true }
} as unknown as RouteLocationMatched
].concat(matched);
}

View File

@@ -35,6 +35,15 @@ const routers = useRouter().options.routes;
let usename = storageSession.getItem("info")?.username;
const { locale, t } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@@ -140,29 +149,23 @@ onMounted(() => {
</el-menu>
<div class="horizontal-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@@ -181,14 +184,14 @@ onMounted(() => {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@@ -200,7 +203,7 @@ onMounted(() => {
<style lang="scss" scoped>
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@@ -212,12 +215,10 @@ onMounted(() => {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@@ -229,7 +230,6 @@ onMounted(() => {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

View File

@@ -1,11 +1,19 @@
<script setup lang="ts">
import {
ref,
PropType,
nextTick,
computed,
CSSProperties,
getCurrentInstance
} from "vue";
import path from "path";
import { PropType, ref, nextTick, getCurrentInstance } from "vue";
import { childrenType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { transformI18n } from "/@/plugins/i18n";
import { findIconReg } from "/@/components/ReIcon";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { useAppStoreHook } from "/@/store/modules/app";
const instance = getCurrentInstance().appContext.app.config.globalProperties;
const menuMode = instance.$storage.layout?.layout === "vertical";
const pureApp = useAppStoreHook();
@@ -24,6 +32,48 @@ const props = defineProps({
}
});
const getNoDropdownStyle = computed((): CSSProperties => {
return {
display: "flex",
alignItems: "center"
};
});
const getDivStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "" : "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getMenuTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
overflow: "hidden",
textOverflow: "ellipsis",
outline: "none"
};
});
const getSubTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const getSpanStyle = computed((): CSSProperties => {
return {
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const onlyOneChild: childrenType = ref(null);
// 存放菜单是否存在showTooltip属性标识
const hoverMenuMap = new WeakMap();
@@ -88,7 +138,7 @@ function resolvePath(routePath) {
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
style="display: flex; align-items: center"
:style="getNoDropdownStyle"
>
<el-icon v-show="props.item.meta.icon">
<component
@@ -101,15 +151,7 @@ function resolvePath(routePath) {
></component>
</el-icon>
<template #title>
<div
:style="{
width: pureApp.sidebar.opened ? '' : '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
overflow: 'hidden'
}"
>
<div :style="getDivStyle">
<span v-if="!menuMode">{{
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
}}</span>
@@ -126,12 +168,7 @@ function resolvePath(routePath) {
</template>
<span
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
overflow: 'hidden',
textOverflow: 'ellipsis',
outline: 'none'
}"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{
@@ -175,15 +212,10 @@ function resolvePath(routePath) {
</template>
<div
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
display: 'inline-block',
overflow: 'hidden',
textOverflow: 'ellipsis'
}"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
<span style="overflow: hidden; text-overflow: ellipsis">
<span :style="getSpanStyle">
{{ transformI18n(props.item.meta.title, props.item.meta.i18n) }}
</span>
</div>

View File

@@ -12,7 +12,9 @@ import { usePermissionStoreHook } from "/@/store/modules/permission";
const route = useRoute();
const pureApp = useAppStoreHook();
const router = useRouter().options.routes;
const showLogo = ref(storageLocal.getItem("logoVal") || "1");
const showLogo = ref(
storageLocal.getItem("responsive-configure")?.showLogo ?? true
);
const isCollapse = computed(() => {
return !pureApp.getSidebarStatus;
});
@@ -58,7 +60,7 @@ onBeforeMount(() => {
<template>
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
<Logo v-if="showLogo === '1'" :collapse="isCollapse" />
<Logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"

View File

@@ -207,7 +207,6 @@
li {
width: 100%;
margin: 0;
padding: 0 12px;
cursor: pointer;
display: flex;
align-items: center;

View File

@@ -2,12 +2,13 @@
import {
ref,
watch,
onBeforeMount,
unref,
nextTick,
computed,
getCurrentInstance,
ComputedRef
ComputedRef,
CSSProperties,
onBeforeMount,
getCurrentInstance
} from "vue";
import close from "/@/assets/svg/close.svg";
@@ -17,6 +18,7 @@ import closeLeft from "/@/assets/svg/close_left.svg";
import closeOther from "/@/assets/svg/close_other.svg";
import closeRight from "/@/assets/svg/close_right.svg";
import { $t as t } from "/@/plugins/i18n";
import { emitter } from "/@/utils/mitt";
import { isEqual, isEmpty } from "lodash-es";
import { transformI18n } from "/@/plugins/i18n";
@@ -187,42 +189,42 @@ const handleScroll = (offset: number): void => {
const tagsViews = ref<Array<tagsViewsType>>([
{
icon: refresh,
text: "message.hsreload",
text: t("buttons.hsreload"),
divided: false,
disabled: false,
show: true
},
{
icon: close,
text: "message.hscloseCurrentTab",
text: t("buttons.hscloseCurrentTab"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeLeft,
text: "message.hscloseLeftTabs",
text: t("buttons.hscloseLeftTabs"),
divided: true,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeRight,
text: "message.hscloseRightTabs",
text: t("buttons.hscloseRightTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeOther,
text: "message.hscloseOtherTabs",
text: t("buttons.hscloseOtherTabs"),
divided: true,
disabled: multiTags.value.length > 2 ? false : true,
show: true
},
{
icon: closeAll,
text: "message.hscloseAllTabs",
text: t("buttons.hscloseAllTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
@@ -230,9 +232,13 @@ const tagsViews = ref<Array<tagsViewsType>>([
]);
// 显示模式,默认灵动模式显示
const showModel = ref(storageLocal.getItem("showModel") || "smart");
const showModel = ref(
storageLocal.getItem("responsive-configure")?.showModel || "smart"
);
if (!showModel.value) {
storageLocal.setItem("showModel", "card");
const configure = storageLocal.getItem("responsive-configure");
configure.showModel = "card";
storageLocal.setItem("responsive-configure", configure);
}
let visible = ref(false);
@@ -306,7 +312,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
showLink: true
@@ -609,17 +615,23 @@ onBeforeMount(() => {
});
});
});
const getTabStyle = computed((): CSSProperties => {
return {
transform: `translateX(${translateX.value}px)`
};
});
const getContextMenuStyle = computed((): CSSProperties => {
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
});
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<i class="ri-arrow-left-s-line" @click="handleScroll(200)"></i>
<div ref="scrollbarDom" class="scroll-container">
<div
class="tab"
ref="tabDom"
:style="{ transform: `translateX(${translateX}px)` }"
>
<div class="tab" ref="tabDom" :style="getTabStyle">
<div
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
@@ -663,7 +675,7 @@ onBeforeMount(() => {
<ul
v-show="visible"
:key="Math.random()"
:style="{ left: buttonLeft + 'px', top: buttonTop + 'px' }"
:style="getContextMenuStyle"
class="contextmenu"
>
<div
@@ -682,7 +694,7 @@ onBeforeMount(() => {
<ul class="right-button">
<li>
<el-icon
:title="$t('message.hsrefreshRoute')"
:title="$t('buttons.hsrefreshRoute')"
class="el-icon-refresh-right rotate"
@click="onFresh"
>

View File

@@ -16,8 +16,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
import fullScreen from "/@/assets/svg/full_screen.svg";
import exitScreen from "/@/assets/svg/exit_screen.svg";
import { deviceDetection } from "/@/utils/deviceDetection";
import { useSettingStoreHook } from "/@/store/modules/settings";
import { useMultiTagsStore } from "/@/store/modules/multiTags";
import { useSettingStoreHook } from "/@/store/modules/settings";
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
@@ -52,16 +52,20 @@ const layout = computed(() => {
instance.$storage.layout = {
layout: instance.$config?.Layout ?? "vertical",
theme: instance.$config?.Theme ?? "default",
darkMode: instance.$config?.DarkMode ?? false
darkMode: instance.$config?.DarkMode ?? false,
sidebarStatus: instance.$config?.SidebarStatus ?? true,
epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
};
}
// 灰色模式、色弱模式、隐藏标签页
if (!instance.$storage.sets) {
if (!instance.$storage.configure) {
// eslint-disable-next-line
instance.$storage.sets = {
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
};
}
@@ -91,7 +95,7 @@ const set: setType = reactive({
}),
hideTabs: computed(() => {
return instance.$storage?.sets.hideTabs;
return instance.$storage?.configure.hideTabs;
})
});
@@ -100,7 +104,9 @@ function setTheme(layoutModel: string) {
instance.$storage.layout = {
layout: `${layoutModel}`,
theme: instance.$storage.layout?.theme,
darkMode: instance.$storage.layout?.darkMode
darkMode: instance.$storage.layout?.darkMode,
sidebarStatus: instance.$storage.layout?.sidebarStatus,
epThemeColor: instance.$storage.layout?.epThemeColor
};
}

View File

@@ -3,9 +3,9 @@ export const routerArrays: Array<RouteConfigs> = [
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
icon: "HomeFilled",
showLink: true
}
}

View File

@@ -43,6 +43,7 @@ import {
ElEmpty,
ElCollapse,
ElCollapseItem,
ElTreeV2,
// 指令
ElLoading,
ElInfiniteScroll
@@ -94,7 +95,8 @@ const components = [
ElAvatar,
ElEmpty,
ElCollapse,
ElCollapseItem
ElCollapseItem,
ElTreeV2
];
// https://element-plus.org/zh-CN/component/icon.html
@@ -113,7 +115,14 @@ import {
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
} from "@element-plus/icons-vue";
// Icon
@@ -132,7 +141,14 @@ export const iconComponents = [
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
];
export function useElementPlus(app: App) {

View File

@@ -1,104 +1,20 @@
import { siphonI18n } from "./index";
// element-plus国际化
import enLocale from "element-plus/lib/locale/lang/en";
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
// 导航菜单配置
export const menusConfig = {
zh: {
message: {
hshome: "首页",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
externalLink: "外链"
}
},
en: {
message: {
hshome: "Home",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
externalLink: "External Link"
}
}
};
// 按钮配置
export const buttonConfig = {
zh: {
message: {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
}
},
en: {
message: {
hsLoginOut: "loginOut",
hsfullscreen: "fullScreen",
hsexitfullscreen: "exitFullscreen",
hsrefreshRoute: "refreshRoute",
hslogin: "login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
}
}
};
// 配置
// export const xxxx = {
// zh: {
// message: {},
// },
// en: {
// message: {},
// },
// };
const localesList = [menusConfig, buttonConfig];
// 项目内自定义国际化
const zhModules = import.meta.globEager("./zh-CN/**/*.ts");
const enModules = import.meta.globEager("./en/**/*.ts");
export const localesConfigs = {
zh: {
message: Object.assign({}, ...localesList.map(v => v.zh.message)),
...siphonI18n(zhModules, "zh-CN"),
...zhLocale
},
en: {
message: Object.assign({}, ...localesList.map(v => v.en.message)),
...siphonI18n(enModules, "en"),
...enLocale
}
};

View File

@@ -0,0 +1,21 @@
export default {
hsLoginOut: "LoginOut",
hsfullscreen: "FullScreen",
hsexitfullscreen: "ExitFullscreen",
hsrefreshRoute: "RefreshRoute",
hslogin: "Login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
};

View File

@@ -0,0 +1,38 @@
export default {
hshome: "Home",
hslogin: "Login",
hssysManagement: "System Manage",
hsBaseinfo: "Base Info",
hsDict: "Dict Manage",
hseditor: "Editor",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "Components",
hsvideo: "Video Components",
hsmap: "Map Components",
hsdraggable: "Draggable Components",
hssplitPane: "Split Pane",
hsbutton: "Button Components",
hscropping: "Picture Cropping",
hscountTo: "Digital Animation",
hsselector: "Selector Components",
hsflowChart: "Flow Chart",
hsseamless: "Seamless Scroll",
hscontextmenu: "Context Menu",
hsmenus: "MultiLevel Menu",
hsmenu1: "Menu1",
"hsmenu1-1": "Menu1-1",
"hsmenu1-2": "Menu1-2",
"hsmenu1-2-1": "Menu1-2-1",
"hsmenu1-2-2": "Menu1-2-2",
"hsmenu1-3": "Menu1-3",
hsmenu2: "Menu2",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
hstabs: "Tabs Operate",
hsMenuTree: "Menu Tree",
hsguide: "Guide",
externalLink: "External Link"
};

View File

@@ -1,19 +1,10 @@
// 多组件库的国际化和本地项目国际化兼容
import { App } from "vue";
import { set } from "lodash-es";
import { createI18n } from "vue-i18n";
import { localesConfigs } from "./config";
import { storageLocal } from "/@/utils/storage";
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}
/**
* 国际化转换工具函数
* @param message message
@@ -37,3 +28,47 @@ export function transformI18n(message: string | object = "", isI18n = false) {
return message;
}
}
/**
* 从模块中抽取国际化
* @param langs 存放国际化模块
* @param prefix 语言 默认 zh-CN
* @returns obj 格式:{模块名.**}
*/
export function siphonI18n(
langs: Record<string, Record<string, any>>,
prefix = "zh-CN"
) {
const langsObj: Recordable = {};
Object.keys(langs).forEach((key: string) => {
let fileName = key.replace(`./${prefix}/`, "").replace(/^\.\//, "");
fileName = fileName.substring(0, fileName.lastIndexOf("."));
const keyList = fileName.split("/");
const moduleName = keyList.shift();
const objKey = keyList.join(".");
const langFileModule = langs[key].default;
if (moduleName) {
if (objKey) {
set(langsObj, moduleName, langsObj[moduleName] || {});
set(langsObj[moduleName], objKey, langFileModule);
} else {
set(langsObj, moduleName, langFileModule || {});
}
}
});
return langsObj;
}
// 此函数只是配合i18n Ally插件来进行国际化智能提示并无实际意义只对提示起作用如果不需要国际化可删除
export const $t = (key: string) => key;
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}

View File

@@ -0,0 +1,21 @@
export default {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
};

View File

@@ -0,0 +1,38 @@
export default {
hshome: "首页",
hslogin: "登陆",
hssysManagement: "系统管理",
hsBaseinfo: "基础信息",
hsDict: "字典管理",
hseditor: "编辑器",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "组件",
hsvideo: "视频组件",
hsmap: "地图组件",
hsdraggable: "拖拽组件",
hssplitPane: "切割面板",
hsbutton: "按钮组件",
hscropping: "图片裁剪",
hscountTo: "数字动画",
hsselector: "选择器组件",
hsflowChart: "流程图",
hsseamless: "无缝滚动",
hscontextmenu: "右键菜单",
hsmenus: "多级菜单",
hsmenu1: "菜单1",
"hsmenu1-1": "菜单1-1",
"hsmenu1-2": "菜单1-2",
"hsmenu1-2-1": "菜单1-2-1",
"hsmenu1-2-2": "菜单1-2-2",
"hsmenu1-3": "菜单1-3",
hsmenu2: "菜单2",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
hstabs: "标签页操作",
hsMenuTree: "菜单树结构",
hsguide: "引导页",
externalLink: "外链"
};

View File

@@ -117,8 +117,9 @@ router.beforeEach((to: toRouteType, _from, next) => {
route?.meta?.rank !== 0 &&
routePartent.length === 0
) {
if (!route?.meta?.refreshRedirect) return;
const { name, meta } = findRouteByPath(
route?.meta?.refreshRedirect,
route.meta.refreshRedirect,
routes
);
handTag(

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const errorRouter = {
@@ -7,7 +8,7 @@ const errorRouter = {
redirect: "/error/401",
meta: {
icon: "Position",
title: "message.hserror",
title: $t("menus.hserror"),
showLink: true,
i18n: true,
rank: 7
@@ -18,7 +19,7 @@ const errorRouter = {
name: "401",
component: () => import("/@/views/error/401.vue"),
meta: {
title: "message.hsfourZeroOne",
title: $t("menus.hsfourZeroOne"),
i18n: true,
showLink: true
}
@@ -28,7 +29,7 @@ const errorRouter = {
name: "404",
component: () => import("/@/views/error/404.vue"),
meta: {
title: "message.hsfourZeroFour",
title: $t("menus.hsfourZeroFour"),
i18n: true,
showLink: true
}

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const externalLink = {
@@ -6,7 +7,7 @@ const externalLink = {
component: Layout,
meta: {
icon: "Link",
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 190
@@ -15,7 +16,7 @@ const externalLink = {
{
path: "https://github.com/xiaoxian521/vue-pure-admin",
meta: {
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 191

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const homeRouter = {
@@ -7,6 +8,7 @@ const homeRouter = {
redirect: "/welcome",
meta: {
icon: "HomeFilled",
title: $t("menus.hshome"),
showLink: true,
i18n: true,
rank: 0
@@ -17,7 +19,7 @@ const homeRouter = {
name: "welcome",
component: () => import("/@/views/welcome.vue"),
meta: {
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: true
}

View File

@@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const remainingRouter = [
@@ -6,7 +7,7 @@ const remainingRouter = [
name: "login",
component: () => import("/@/views/login.vue"),
meta: {
title: "message.hslogin",
title: $t("menus.hslogin"),
showLink: false,
i18n: true,
rank: 101
@@ -18,7 +19,7 @@ const remainingRouter = [
component: Layout,
meta: {
icon: "HomeFilled",
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: false,
rank: 104

View File

@@ -13,7 +13,7 @@ import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "/@/layout/types";
import { usePermissionStoreHook } from "/@/store/modules/permission";
// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由
import { getAsyncRoutes } from "/@/api/routes";
@@ -212,11 +212,13 @@ const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
// 过滤后端传来的动态路由 重新生成规范路由
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => {
if (v.redirect) {
v.component = Layout;
} else {
v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
v.component = modulesRoutes[modulesRoutesKeys[index]];
}
if (v.children) {
addAsyncRoutes(v.children);

View File

@@ -1,17 +1,17 @@
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
import { store } from "/@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
export const useAppStore = defineStore({
id: "pure-app",
state: (): appType => ({
sidebar: {
opened: storageLocal.getItem("sidebarStatus")
? !!+storageLocal.getItem("sidebarStatus")
: true,
opened:
storageLocal.getItem("responsive-layout")?.sidebarStatus ??
getConfig().SidebarStatus,
withoutAnimation: false,
isClickHamburger: false
},
@@ -30,20 +30,22 @@ export const useAppStore = defineStore({
},
actions: {
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
const layout = storageLocal.getItem("responsive-layout");
if (opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = true;
storageLocal.setItem("sidebarStatus", true);
layout.sidebarStatus = true;
} else if (!opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = false;
storageLocal.setItem("sidebarStatus", false);
layout.sidebarStatus = false;
} else if (!opened && !resize) {
this.sidebar.withoutAnimation = false;
this.sidebar.opened = !this.sidebar.opened;
this.sidebar.isClickHamburger = !this.sidebar.opened;
storageLocal.setItem("sidebarStatus", this.sidebar.opened);
layout.sidebarStatus = this.sidebar.opened;
}
storageLocal.setItem("responsive-layout", layout);
},
TOGGLE_DEVICE(device: string) {
this.device = device;

View File

@@ -1,11 +1,14 @@
import { store } from "/@/store";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
export const useEpThemeStore = defineStore({
id: "pure-epTheme",
state: () => ({
epThemeColor: storageLocal.getItem("epThemeColor") || "#409EFF"
epThemeColor:
storageLocal.getItem("responsive-layout")?.epThemeColor ??
getConfig().EpThemeColor
}),
getters: {
getEpThemeColor() {
@@ -14,8 +17,10 @@ export const useEpThemeStore = defineStore({
},
actions: {
setEpThemeColor(newColor) {
const layout = storageLocal.getItem("responsive-layout");
this.epThemeColor = newColor;
storageLocal.setItem("epThemeColor", newColor);
layout.epThemeColor = newColor;
storageLocal.setItem("responsive-layout", layout);
}
}
});

View File

@@ -8,21 +8,21 @@ export const useMultiTagsStore = defineStore({
id: "pure-multiTags",
state: () => ({
// 存储标签页信息(路由信息)
multiTags: storageLocal.getItem("responsive-sets").multiTagsCache
multiTags: storageLocal.getItem("responsive-configure").multiTagsCache
? storageLocal.getItem("responsive-tags")
: [
{
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
],
multiTagsCache: storageLocal.getItem("responsive-sets").multiTagsCache
multiTagsCache: storageLocal.getItem("responsive-configure").multiTagsCache
}),
getters: {
getMultiTagsCache() {

View File

@@ -58,8 +58,8 @@ export const useUserStore = defineStore({
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}

View File

@@ -18,15 +18,19 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
default: Storage.getData(undefined, "layout") ?? {
layout: config.Layout ?? "vertical",
theme: config.Theme ?? "default",
darkMode: config.DarkMode ?? false
darkMode: config.DarkMode ?? false,
sidebarStatus: config.SidebarStatus ?? true,
epThemeColor: config.EpThemeColor ?? "409EFF"
}
},
sets: {
configure: {
type: Object,
default: Storage.getData(undefined, "sets") ?? {
default: Storage.getData(undefined, "configure") ?? {
grey: config.Grey ?? false,
weak: config.Weak ?? false,
hideTabs: config.HideTabs ?? false,
showLogo: config.ShowLogo ?? true,
showModel: config.ShowModel ?? "smart",
multiTagsCache: config.MultiTagsCache ?? false
}
}
@@ -41,7 +45,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "HomeFilled",
showLink: true