release: update 3.4.5
This commit is contained in:
@@ -1,23 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
h,
|
||||
ref,
|
||||
computed,
|
||||
Transition,
|
||||
defineComponent,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import backTop from "/@/assets/svg/back_top.svg?component";
|
||||
import { h, computed, Transition, defineComponent } from "vue";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
|
||||
const props = defineProps({
|
||||
fixedHeader: Boolean
|
||||
});
|
||||
const keepAlive: Boolean = ref(
|
||||
getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
|
||||
);
|
||||
const instance =
|
||||
getCurrentInstance().appContext.app.config.globalProperties.$storage;
|
||||
|
||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
||||
|
||||
const keepAlive = computed(() => {
|
||||
return $config?.KeepAlive;
|
||||
});
|
||||
|
||||
const transitions = computed(() => {
|
||||
return route => {
|
||||
@@ -26,11 +21,11 @@ const transitions = computed(() => {
|
||||
});
|
||||
|
||||
const hideTabs = computed(() => {
|
||||
return instance?.configure.hideTabs;
|
||||
return $storage?.configure.hideTabs;
|
||||
});
|
||||
|
||||
const layout = computed(() => {
|
||||
return instance?.layout.layout === "vertical";
|
||||
return $storage?.layout.layout === "vertical";
|
||||
});
|
||||
|
||||
const getSectionStyle = computed(() => {
|
||||
|
@@ -1,65 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useNav } from "../hooks/nav";
|
||||
import { useRoute } from "vue-router";
|
||||
import Search from "./search/index.vue";
|
||||
import Notice from "./notice/index.vue";
|
||||
import mixNav from "./sidebar/mixNav.vue";
|
||||
import avatars from "/@/assets/avatars.jpg";
|
||||
import Hamburger from "./sidebar/hamBurger.vue";
|
||||
import { watch, getCurrentInstance } from "vue";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import Breadcrumb from "./sidebar/breadCrumb.vue";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
import topCollapse from "./sidebar/topCollapse.vue";
|
||||
import screenfull from "../components/screenfull/index.vue";
|
||||
import { useTranslationLang } from "../hooks/useTranslationLang";
|
||||
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||
|
||||
const route = useRoute();
|
||||
const { locale, t } = useI18n();
|
||||
const instance =
|
||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||
const {
|
||||
layout,
|
||||
device,
|
||||
logout,
|
||||
onPanel,
|
||||
changeTitle,
|
||||
toggleSideBar,
|
||||
pureApp,
|
||||
username,
|
||||
avatarsStyle,
|
||||
getDropdownItemStyle
|
||||
toggleSideBar,
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
changeTitle(route.meta);
|
||||
}
|
||||
);
|
||||
|
||||
function translationCh() {
|
||||
instance.locale = { locale: "zh" };
|
||||
locale.value = "zh";
|
||||
}
|
||||
|
||||
function translationEn() {
|
||||
instance.locale = { locale: "en" };
|
||||
locale.value = "en";
|
||||
}
|
||||
const { t, locale, translationCh, translationEn } = useTranslationLang();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<Hamburger
|
||||
v-if="pureApp.layout !== 'mix'"
|
||||
:is-active="pureApp.sidebar.opened"
|
||||
<div
|
||||
class="navbar bg-[#fff] shadow-sm shadow-[rgba(0, 21, 41, 0.08)] dark:shadow-[#0d0d0d]"
|
||||
>
|
||||
<topCollapse
|
||||
v-if="device === 'mobile'"
|
||||
class="hamburger-container"
|
||||
:is-active="pureApp.sidebar.opened"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
|
||||
<Breadcrumb v-if="pureApp.layout !== 'mix'" class="breadcrumb-container" />
|
||||
<Breadcrumb
|
||||
v-if="layout !== 'mix' && device !== 'mobile'"
|
||||
class="breadcrumb-container"
|
||||
/>
|
||||
|
||||
<mixNav v-if="pureApp.layout === 'mix'" />
|
||||
<mixNav v-if="layout === 'mix'" />
|
||||
|
||||
<div v-if="pureApp.layout === 'vertical'" class="vertical-header-right">
|
||||
<div v-if="layout === 'vertical'" class="vertical-header-right">
|
||||
<!-- 菜单搜索 -->
|
||||
<Search />
|
||||
<!-- 通知 -->
|
||||
@@ -68,34 +54,41 @@ function translationEn() {
|
||||
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
|
||||
<!-- 国际化 -->
|
||||
<el-dropdown id="header-translation" trigger="click">
|
||||
<globalization />
|
||||
<globalization
|
||||
class="navbar-bg-hover w-40px h-48px p-11px cursor-pointer outline-none"
|
||||
/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="translation">
|
||||
<el-dropdown-item
|
||||
:style="getDropdownItemStyle(locale, 'zh')"
|
||||
:class="['!dark:color-white', getDropdownItemClass(locale, 'zh')]"
|
||||
@click="translationCh"
|
||||
><IconifyIconOffline
|
||||
>
|
||||
<IconifyIconOffline
|
||||
class="check-zh"
|
||||
v-show="locale === 'zh'"
|
||||
icon="check"
|
||||
/>简体中文</el-dropdown-item
|
||||
>
|
||||
/>
|
||||
简体中文
|
||||
</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
|
||||
<IconifyIconOffline icon="check" />
|
||||
</span>
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<!-- 退出登陆 -->
|
||||
<!-- 退出登录 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
<span class="el-dropdown-link navbar-bg-hover">
|
||||
<img v-if="avatars" :src="avatars" :style="avatarsStyle" />
|
||||
<p v-if="username">{{ username }}</p>
|
||||
<p v-if="username" class="dark:color-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
@@ -103,13 +96,14 @@ function translationEn() {
|
||||
<IconifyIconOffline
|
||||
icon="logout-circle-r-line"
|
||||
style="margin: 5px"
|
||||
/>{{ t("buttons.hsLoginOut") }}</el-dropdown-item
|
||||
>
|
||||
/>
|
||||
{{ t("buttons.hsLoginOut") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<span
|
||||
class="el-icon-setting"
|
||||
class="el-icon-setting navbar-bg-hover"
|
||||
:title="t('buttons.hssystemSet')"
|
||||
@click="onPanel"
|
||||
>
|
||||
@@ -124,16 +118,12 @@ function translationEn() {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 48px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.vertical-header-right {
|
||||
@@ -144,31 +134,6 @@ function translationEn() {
|
||||
color: #000000d9;
|
||||
justify-content: flex-end;
|
||||
|
||||
:deep(.dropdown-badge) {
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.screen-full {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.globalization {
|
||||
height: 48px;
|
||||
width: 40px;
|
||||
padding: 11px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dropdown-link {
|
||||
height: 48px;
|
||||
padding: 10px;
|
||||
@@ -178,10 +143,6 @@ function translationEn() {
|
||||
cursor: pointer;
|
||||
color: #000000d9;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -200,15 +161,12 @@ function translationEn() {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,14 +15,13 @@ notices.value.forEach(notice => {
|
||||
});
|
||||
|
||||
function tabClick() {
|
||||
// @ts-expect-error
|
||||
dropdownDom.value.handleOpen();
|
||||
(dropdownDom as any).value.handleOpen();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dropdown ref="dropdownDom" trigger="click" placement="bottom-end">
|
||||
<span class="dropdown-badge">
|
||||
<span class="dropdown-badge navbar-bg-hover select-none">
|
||||
<el-badge :value="noticesNum" :max="99">
|
||||
<span class="header-notice-icon">
|
||||
<IconifyIconOffline icon="bell" />
|
||||
|
@@ -44,7 +44,9 @@ function hoverDescription(event, description) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="notice-container">
|
||||
<div
|
||||
class="notice-container border-b-1 border-[#f0f0f0] dark:border-[#303030]"
|
||||
>
|
||||
<el-avatar
|
||||
v-if="props.noticeItem.avatar"
|
||||
:size="30"
|
||||
@@ -52,7 +54,7 @@ function hoverDescription(event, description) {
|
||||
class="notice-container-avatar"
|
||||
/>
|
||||
<div class="notice-container-text">
|
||||
<div class="notice-text-title">
|
||||
<div class="notice-text-title color-[#000000d9] dark:color-white">
|
||||
<el-tooltip
|
||||
popper-class="notice-title-popper"
|
||||
:disabled="!titleTooltip"
|
||||
@@ -72,7 +74,8 @@ function hoverDescription(event, description) {
|
||||
:type="props.noticeItem?.status"
|
||||
size="small"
|
||||
class="notice-title-extra"
|
||||
>{{ props.noticeItem?.extra }}
|
||||
>
|
||||
{{ props.noticeItem?.extra }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
@@ -90,7 +93,7 @@ function hoverDescription(event, description) {
|
||||
{{ props.noticeItem.description }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div class="notice-text-datetime">
|
||||
<div class="notice-text-datetime color-[#00000073] dark:color-white">
|
||||
{{ props.noticeItem.datetime }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,7 +111,7 @@ function hoverDescription(event, description) {
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
// border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.notice-container-avatar {
|
||||
margin-right: 16px;
|
||||
@@ -127,7 +130,6 @@ function hoverDescription(event, description) {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 1.5715;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
cursor: pointer;
|
||||
|
||||
.notice-title-content {
|
||||
@@ -149,7 +151,6 @@ function hoverDescription(event, description) {
|
||||
.notice-text-datetime {
|
||||
font-size: 12px;
|
||||
line-height: 1.5715;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.notice-text-description {
|
||||
|
@@ -5,7 +5,7 @@ import { emitter } from "/@/utils/mitt";
|
||||
|
||||
let show = ref<Boolean>(false);
|
||||
const target = ref(null);
|
||||
onClickOutside(target, event => {
|
||||
onClickOutside(target, (event: any) => {
|
||||
if (event.clientX > target.value.offsetLeft) return;
|
||||
show.value = false;
|
||||
});
|
||||
@@ -18,15 +18,15 @@ emitter.on("openPanel", () => {
|
||||
<template>
|
||||
<div :class="{ show: show }" class="right-panel-container">
|
||||
<div class="right-panel-background" />
|
||||
<div ref="target" class="right-panel">
|
||||
<div ref="target" class="right-panel bg-white dark:bg-dark">
|
||||
<div class="right-panel-items">
|
||||
<div class="project-configuration">
|
||||
<h3>项目配置</h3>
|
||||
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
|
||||
<IconifyIconOffline icon="close-bold" />
|
||||
<IconifyIconOffline icon="close" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div style="border-bottom: 1px solid #dcdfe6" />
|
||||
<div class="border-b-1 border-[#dcdfe6] dark:border-[#303030]" />
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@ emitter.on("openPanel", () => {
|
||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transform: translate(100%);
|
||||
background: #fff;
|
||||
// background: #fff;
|
||||
z-index: 40000;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ emitter.on("openPanel", () => {
|
||||
margin-left: 10px;
|
||||
|
||||
i {
|
||||
font-size: 16px;
|
||||
font-size: 20px;
|
||||
margin-right: 20px;
|
||||
|
||||
&:hover {
|
||||
|
@@ -1,13 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { useFullscreen } from "@vueuse/core";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useFullscreen } from "@vueuse/core";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { isFullscreen, toggle } = useFullscreen();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="screen-full" @click="toggle">
|
||||
<div
|
||||
class="screen-full w-36px h-48px flex-ac cursor-pointer navbar-bg-hover"
|
||||
@click="toggle"
|
||||
>
|
||||
<FontIcon
|
||||
:title="
|
||||
isFullscreen ? t('buttons.hsexitfullscreen') : t('buttons.hsfullscreen')
|
||||
@@ -16,13 +19,3 @@ const { isFullscreen, toggle } = useFullscreen();
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.screen-full {
|
||||
width: 36px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="search-footer">
|
||||
<div class="search-footer color-[#333] dark:color-white">
|
||||
<span class="search-footer-item">
|
||||
<enterOutlined class="icon" />
|
||||
确认
|
||||
@@ -23,7 +23,6 @@ import mdiKeyboardEsc from "/@/assets/svg/mdi_keyboard_esc.svg?component";
|
||||
<style lang="scss" scoped>
|
||||
.search-footer {
|
||||
display: flex;
|
||||
color: #333;
|
||||
|
||||
.search-footer-item {
|
||||
display: flex;
|
||||
|
@@ -2,8 +2,9 @@
|
||||
import { useRouter } from "vue-router";
|
||||
import SearchResult from "./SearchResult.vue";
|
||||
import SearchFooter from "./SearchFooter.vue";
|
||||
import { deleteChildren } from "/@/utils/tree";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { deleteChildren } from "@pureadmin/utils";
|
||||
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
|
||||
import { ref, watch, computed, nextTick, shallowRef } from "vue";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
@@ -17,6 +18,7 @@ interface Emits {
|
||||
(e: "update:value", val: boolean): void;
|
||||
}
|
||||
|
||||
const { device } = useNav();
|
||||
const emit = defineEmits<Emits>();
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
const router = useRouter();
|
||||
@@ -130,7 +132,12 @@ onKeyStroke("ArrowDown", handleDown);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog top="5vh" v-model="show" :before-close="handleClose">
|
||||
<el-dialog
|
||||
top="5vh"
|
||||
:width="device === 'mobile' ? '80vw' : '50vw'"
|
||||
v-model="show"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<el-input
|
||||
ref="inputRef"
|
||||
v-model="keyword"
|
||||
|
@@ -1,24 +1,3 @@
|
||||
<template>
|
||||
<div class="result">
|
||||
<template v-for="item in options" :key="item.path">
|
||||
<div
|
||||
class="result-item"
|
||||
:style="{
|
||||
background:
|
||||
item?.path === active ? useEpThemeStoreHook().epThemeColor : '',
|
||||
color: item.path === active ? '#fff' : ''
|
||||
}"
|
||||
@click="handleTo"
|
||||
@mouseenter="handleMouse(item)"
|
||||
>
|
||||
<component :is="useRenderIcon(item.meta?.icon ?? 'bookmark')" />
|
||||
<span class="result-item-title">{{ t(item.meta?.title) }}</span>
|
||||
<enterOutlined />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@@ -49,6 +28,17 @@ interface Emits {
|
||||
const props = withDefaults(defineProps<Props>(), {});
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const itemStyle = computed(() => {
|
||||
return item => {
|
||||
return {
|
||||
background:
|
||||
item?.path === active.value ? useEpThemeStoreHook().epThemeColor : "",
|
||||
color: item.path === active.value ? "#fff" : "",
|
||||
fontSize: item.path === active.value ? "16px" : "14px"
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const active = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
@@ -67,6 +57,24 @@ function handleTo() {
|
||||
emit("enter");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="result">
|
||||
<template v-for="item in options" :key="item.path">
|
||||
<div
|
||||
class="result-item dark:bg-[#1d1d1d]"
|
||||
:style="itemStyle(item)"
|
||||
@click="handleTo"
|
||||
@mouseenter="handleMouse(item)"
|
||||
>
|
||||
<component :is="useRenderIcon(item.meta?.icon ?? 'bookmark-2-line')" />
|
||||
<span class="result-item-title">{{ t(item.meta?.title) }}</span>
|
||||
<enterOutlined />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.result {
|
||||
padding-bottom: 12px;
|
||||
@@ -78,8 +86,9 @@ function handleTo() {
|
||||
margin-top: 8px;
|
||||
padding: 14px;
|
||||
border-radius: 4px;
|
||||
background: #e5e7eb;
|
||||
cursor: pointer;
|
||||
border: 0.1px solid #ccc;
|
||||
transition: all 0.3s;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { SearchModal } from "./components";
|
||||
import useBoolean from "../../hooks/useBoolean";
|
||||
import { useBoolean } from "../../hooks/useBoolean";
|
||||
const { bool: show, toggle } = useBoolean();
|
||||
function handleSearch() {
|
||||
toggle();
|
||||
@@ -8,23 +8,11 @@ function handleSearch() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-container" @click="handleSearch">
|
||||
<div
|
||||
class="search-container w-40px h-48px flex-c cursor-pointer navbar-bg-hover"
|
||||
@click="handleSearch"
|
||||
>
|
||||
<IconifyIconOffline icon="search" />
|
||||
</div>
|
||||
<SearchModal v-model:value="show" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 48px;
|
||||
width: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,75 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
reactive,
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
reactive,
|
||||
computed,
|
||||
nextTick,
|
||||
useCssModule,
|
||||
getCurrentInstance
|
||||
useCssModule
|
||||
} from "vue";
|
||||
import { find } from "lodash-unified";
|
||||
import { getConfig } from "/@/config";
|
||||
import { useRouter } from "vue-router";
|
||||
import panel from "../panel/index.vue";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { resetRouter } from "/@/router";
|
||||
import { templateRef } from "@vueuse/core";
|
||||
import { debounce } from "/@/utils/debounce";
|
||||
import { themeColorsType } from "../../types";
|
||||
import { routerArrays } from "/@/layout/types";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import { shadeBgColor } from "../../theme/element-plus";
|
||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||
import { storageLocal, storageSession } from "/@/utils/storage";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
import { createNewStyle, writeNewStyle } from "../../theme/element-plus";
|
||||
import { useDataThemeChange } from "/@/layout/hooks/useDataThemeChange";
|
||||
import {
|
||||
useDark,
|
||||
debounce,
|
||||
useGlobal,
|
||||
storageLocal,
|
||||
storageSession
|
||||
} from "@pureadmin/utils";
|
||||
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
|
||||
|
||||
import dayIcon from "/@/assets/svg/day.svg?component";
|
||||
import darkIcon from "/@/assets/svg/dark.svg?component";
|
||||
|
||||
const router = useRouter();
|
||||
const { device } = useNav();
|
||||
const { isDark } = useDark();
|
||||
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 { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||
|
||||
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
|
||||
const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
|
||||
const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
|
||||
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
|
||||
|
||||
let layoutTheme =
|
||||
ref(storageLocal.getItem("responsive-layout")) ||
|
||||
ref({
|
||||
layout: instanceConfig?.Layout ?? "vertical",
|
||||
theme: instanceConfig?.Theme ?? "default"
|
||||
});
|
||||
const {
|
||||
body,
|
||||
dataTheme,
|
||||
layoutTheme,
|
||||
themeColors,
|
||||
dataThemeChange,
|
||||
setEpThemeColor,
|
||||
setLayoutThemeColor
|
||||
} = useDataThemeChange();
|
||||
|
||||
// body添加layout属性,作用于src/style/sidebar.scss
|
||||
/* body添加layout属性,作用于src/style/sidebar.scss */
|
||||
if (unref(layoutTheme)) {
|
||||
let layout = unref(layoutTheme).layout;
|
||||
let theme = unref(layoutTheme).theme;
|
||||
@@ -79,20 +61,18 @@ if (unref(layoutTheme)) {
|
||||
setLayoutModel(layout);
|
||||
}
|
||||
|
||||
// 默认灵动模式
|
||||
const markValue = ref(instance.configure?.showModel ?? "smart");
|
||||
/** 默认灵动模式 */
|
||||
const markValue = ref($storage.configure?.showModel ?? "smart");
|
||||
|
||||
const logoVal = ref(instance.configure?.showLogo ?? true);
|
||||
|
||||
const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
|
||||
const logoVal = ref($storage.configure?.showLogo ?? true);
|
||||
|
||||
const settings = reactive({
|
||||
greyVal: instance.configure.grey,
|
||||
weakVal: instance.configure.weak,
|
||||
tabsVal: instance.configure.hideTabs,
|
||||
showLogo: instance.configure.showLogo,
|
||||
showModel: instance.configure.showModel,
|
||||
multiTagsCache: instance.configure.multiTagsCache
|
||||
greyVal: $storage.configure.grey,
|
||||
weakVal: $storage.configure.weak,
|
||||
tabsVal: $storage.configure.hideTabs,
|
||||
showLogo: $storage.configure.showLogo,
|
||||
showModel: $storage.configure.showModel,
|
||||
multiTagsCache: $storage.configure.multiTagsCache
|
||||
});
|
||||
|
||||
const getThemeColorStyle = computed(() => {
|
||||
@@ -101,10 +81,17 @@ const getThemeColorStyle = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
/** 当网页为暗黑模式时不显示亮白色切换选项 */
|
||||
const showThemeColors = computed(() => {
|
||||
return themeColor => {
|
||||
return themeColor === "light" && isDark.value ? false : true;
|
||||
};
|
||||
});
|
||||
|
||||
function storageConfigureChange<T>(key: string, val: T): void {
|
||||
const storageConfigure = instance.configure;
|
||||
const storageConfigure = $storage.configure;
|
||||
storageConfigure[key] = val;
|
||||
instance.configure = storageConfigure;
|
||||
$storage.configure = storageConfigure;
|
||||
}
|
||||
|
||||
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
||||
@@ -114,13 +101,13 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
|
||||
targetEl.className = flag ? `${className} ${clsName} ` : className;
|
||||
}
|
||||
|
||||
// 灰色模式设置
|
||||
/** 灰色模式设置 */
|
||||
const greyChange = (value): void => {
|
||||
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
|
||||
storageConfigureChange("grey", value);
|
||||
};
|
||||
|
||||
// 色弱模式设置
|
||||
/** 色弱模式设置 */
|
||||
const weekChange = (value): void => {
|
||||
toggleClass(
|
||||
settings.weakVal,
|
||||
@@ -133,7 +120,7 @@ const weekChange = (value): void => {
|
||||
const tagsChange = () => {
|
||||
let showVal = settings.tabsVal;
|
||||
storageConfigureChange("hideTabs", showVal);
|
||||
emitter.emit("tagViewsChange", showVal);
|
||||
emitter.emit("tagViewsChange", showVal as unknown as string);
|
||||
};
|
||||
|
||||
const multiTagsCacheChange = () => {
|
||||
@@ -142,27 +129,19 @@ const multiTagsCacheChange = () => {
|
||||
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
|
||||
};
|
||||
|
||||
// 清空缓存并返回登录页
|
||||
/** 清空缓存并返回登录页 */
|
||||
function onReset() {
|
||||
router.push("/login");
|
||||
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
|
||||
useAppStoreHook().setLayout(Layout);
|
||||
useEpThemeStoreHook().setEpThemeColor(EpThemeColor);
|
||||
setEpThemeColor(EpThemeColor);
|
||||
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
|
||||
toggleClass(Grey, "html-grey", document.querySelector("html"));
|
||||
toggleClass(Weak, "html-weakness", document.querySelector("html"));
|
||||
useMultiTagsStoreHook().handleTags("equal", [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "menus.hshome",
|
||||
icon: "home-filled"
|
||||
}
|
||||
}
|
||||
]);
|
||||
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
||||
storageLocal.clear();
|
||||
storageSession.clear();
|
||||
resetRouter();
|
||||
}
|
||||
|
||||
function onChange(label) {
|
||||
@@ -170,7 +149,7 @@ function onChange(label) {
|
||||
emitter.emit("tagViewsShowModel", label);
|
||||
}
|
||||
|
||||
// 侧边栏Logo
|
||||
/** 侧边栏Logo */
|
||||
function logoChange() {
|
||||
unref(logoVal)
|
||||
? storageConfigureChange("showLogo", true)
|
||||
@@ -184,7 +163,9 @@ function setFalse(Doms): any {
|
||||
});
|
||||
}
|
||||
|
||||
watch(instance, ({ layout }) => {
|
||||
watch($storage, ({ layout }) => {
|
||||
/* 设置wangeditorV5主题色 */
|
||||
body.style.setProperty("--w-e-toolbar-active-color", layout["epThemeColor"]);
|
||||
switch (layout["layout"]) {
|
||||
case "vertical":
|
||||
toggleClass(true, isSelect, unref(verticalRef));
|
||||
@@ -204,7 +185,7 @@ watch(instance, ({ layout }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 主题色 激活选择项
|
||||
/** 主题色 激活选择项 */
|
||||
const getThemeColor = computed(() => {
|
||||
return current => {
|
||||
if (
|
||||
@@ -223,83 +204,27 @@ const getThemeColor = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
// 设置导航模式
|
||||
/** 设置导航模式 */
|
||||
function setLayoutModel(layout: string) {
|
||||
layoutTheme.value.layout = layout;
|
||||
window.document.body.setAttribute("layout", layout);
|
||||
instance.layout = {
|
||||
$storage.layout = {
|
||||
layout,
|
||||
theme: layoutTheme.value.theme,
|
||||
darkMode: instance.layout.darkMode,
|
||||
sidebarStatus: instance.layout.sidebarStatus,
|
||||
epThemeColor: instance.layout.epThemeColor
|
||||
darkMode: $storage.layout?.darkMode,
|
||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||
epThemeColor: $storage.layout?.epThemeColor
|
||||
};
|
||||
useAppStoreHook().setLayout(layout);
|
||||
}
|
||||
|
||||
// 存放夜间主题切换前的导航主题色
|
||||
let tempLayoutThemeColor;
|
||||
|
||||
// 设置导航主题色
|
||||
function setLayoutThemeColor(theme: string) {
|
||||
tempLayoutThemeColor = instance.layout.theme;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置ep主题色
|
||||
const setEpThemeColor = (color: string) => {
|
||||
// @ts-expect-error
|
||||
writeNewStyle(createNewStyle(color));
|
||||
useEpThemeStoreHook().setEpThemeColor(color);
|
||||
body.style.setProperty("--el-color-primary-active", shadeBgColor(color));
|
||||
};
|
||||
|
||||
let dataTheme = ref<boolean>(instance.layout.darkMode);
|
||||
|
||||
// 日间、夜间主题切换
|
||||
function dataThemeChange() {
|
||||
if (dataTheme.value) {
|
||||
body.setAttribute("data-theme", "dark");
|
||||
setLayoutThemeColor("light");
|
||||
} else {
|
||||
body.setAttribute("data-theme", "");
|
||||
tempLayoutThemeColor && setLayoutThemeColor(tempLayoutThemeColor);
|
||||
instance.layout = {
|
||||
layout: useAppStoreHook().layout,
|
||||
theme: instance.layout.theme,
|
||||
darkMode: dataTheme.value,
|
||||
sidebarStatus: instance.layout.sidebarStatus,
|
||||
epThemeColor: instance.layout.epThemeColor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//初始化项目配置
|
||||
/* 初始化项目配置 */
|
||||
nextTick(() => {
|
||||
settings.greyVal &&
|
||||
document.querySelector("html")?.setAttribute("class", "html-grey");
|
||||
settings.weakVal &&
|
||||
document.querySelector("html")?.setAttribute("class", "html-weakness");
|
||||
settings.tabsVal && tagsChange();
|
||||
// @ts-expect-error
|
||||
writeNewStyle(createNewStyle(epThemeColor.value));
|
||||
dataThemeChange();
|
||||
});
|
||||
</script>
|
||||
@@ -329,7 +254,12 @@ nextTick(() => {
|
||||
</li>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="item" content="顶部模式" placement="bottom">
|
||||
<el-tooltip
|
||||
v-if="device !== 'mobile'"
|
||||
class="item"
|
||||
content="顶部模式"
|
||||
placement="bottom"
|
||||
>
|
||||
<li
|
||||
:class="layoutTheme.layout === 'horizontal' ? $style.isSelect : ''"
|
||||
ref="horizontalRef"
|
||||
@@ -340,7 +270,12 @@ nextTick(() => {
|
||||
</li>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="item" content="混合模式" placement="bottom">
|
||||
<el-tooltip
|
||||
v-if="device !== 'mobile'"
|
||||
class="item"
|
||||
content="混合模式"
|
||||
placement="bottom"
|
||||
>
|
||||
<li
|
||||
:class="layoutTheme.layout === 'mix' ? $style.isSelect : ''"
|
||||
ref="mixRef"
|
||||
@@ -352,11 +287,12 @@ nextTick(() => {
|
||||
</el-tooltip>
|
||||
</ul>
|
||||
|
||||
<el-divider v-show="!dataTheme">主题色</el-divider>
|
||||
<ul class="theme-color" v-show="!dataTheme">
|
||||
<el-divider>主题色</el-divider>
|
||||
<ul class="theme-color">
|
||||
<li
|
||||
v-for="(item, index) in themeColors"
|
||||
:key="index"
|
||||
v-show="showThemeColors(item.themeColor)"
|
||||
:style="getThemeColorStyle(item.color)"
|
||||
@click="setLayoutThemeColor(item.themeColor)"
|
||||
>
|
||||
@@ -372,7 +308,7 @@ nextTick(() => {
|
||||
|
||||
<el-divider>界面显示</el-divider>
|
||||
<ul class="setting">
|
||||
<li v-show="!dataTheme">
|
||||
<li>
|
||||
<span>灰色模式</span>
|
||||
<el-switch
|
||||
v-model="settings.greyVal"
|
||||
@@ -383,7 +319,7 @@ nextTick(() => {
|
||||
@change="greyChange"
|
||||
/>
|
||||
</li>
|
||||
<li v-show="!dataTheme">
|
||||
<li>
|
||||
<span>色弱模式</span>
|
||||
<el-switch
|
||||
v-model="settings.weakVal"
|
||||
@@ -446,13 +382,13 @@ nextTick(() => {
|
||||
@click="onReset"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
icon="logout-circle-r-line"
|
||||
icon="fa-sign-out"
|
||||
width="15"
|
||||
height="15"
|
||||
style="margin-right: 4px"
|
||||
/>
|
||||
清空缓存并返回登录页</el-button
|
||||
>
|
||||
清空缓存并返回登录页
|
||||
</el-button>
|
||||
</panel>
|
||||
</template>
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { isEqual } from "lodash-unified";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { ref, watch, onMounted, toRaw } from "vue";
|
||||
import { getParentPaths, findRouteByPath } from "/@/router/utils";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
||||
@@ -9,24 +9,29 @@ import { useRoute, useRouter, RouteLocationMatched } from "vue-router";
|
||||
const route = useRoute();
|
||||
const levelList = ref([]);
|
||||
const router = useRouter();
|
||||
const routes = router.options.routes;
|
||||
const multiTags = useMultiTagsStoreHook().multiTags;
|
||||
const routes: any = router.options.routes;
|
||||
const multiTags: any = useMultiTagsStoreHook().multiTags;
|
||||
|
||||
const isDashboard = (route: RouteLocationMatched): boolean | string => {
|
||||
const name = route && (route.name as string);
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
return name.trim().toLocaleLowerCase() === "welcome".toLocaleLowerCase();
|
||||
if (!name) return false;
|
||||
return name.trim().toLocaleLowerCase() === "Welcome".toLocaleLowerCase();
|
||||
};
|
||||
|
||||
const getBreadcrumb = (): void => {
|
||||
// 当前路由信息
|
||||
let currentRoute;
|
||||
|
||||
if (Object.keys(route.query).length > 0) {
|
||||
multiTags.forEach(item => {
|
||||
if (isEqual(route.query, item?.query)) {
|
||||
currentRoute = item;
|
||||
currentRoute = toRaw(item);
|
||||
}
|
||||
});
|
||||
} else if (Object.keys(route.params).length > 0) {
|
||||
multiTags.forEach(item => {
|
||||
if (isEqual(route.params, item?.params)) {
|
||||
currentRoute = toRaw(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -38,29 +43,12 @@ const getBreadcrumb = (): void => {
|
||||
let matched = [];
|
||||
// 获取每个父级路径对应的路由信息
|
||||
parentRoutes.forEach(path => {
|
||||
if (path !== "/") {
|
||||
matched.push(findRouteByPath(path, routes));
|
||||
}
|
||||
if (path !== "/") matched.push(findRouteByPath(path, routes));
|
||||
});
|
||||
if (router.currentRoute.value.meta?.refreshRedirect) {
|
||||
matched.unshift(
|
||||
findRouteByPath(
|
||||
router.currentRoute.value.meta.refreshRedirect as string,
|
||||
routes
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// 过滤与子级相同标题的父级路由
|
||||
matched = matched.filter(item => {
|
||||
return !item.redirect || (item.redirect && item.children.length !== 1);
|
||||
});
|
||||
}
|
||||
if (currentRoute?.path !== "/welcome") {
|
||||
matched.push(currentRoute);
|
||||
}
|
||||
|
||||
const first = matched[0];
|
||||
if (!isDashboard(first)) {
|
||||
if (currentRoute?.path !== "/welcome") matched.push(currentRoute);
|
||||
|
||||
if (!isDashboard(matched[0])) {
|
||||
matched = [
|
||||
{
|
||||
path: "/welcome",
|
||||
@@ -70,59 +58,51 @@ const getBreadcrumb = (): void => {
|
||||
].concat(matched);
|
||||
}
|
||||
|
||||
matched.forEach((item, index) => {
|
||||
if (currentRoute?.query || currentRoute?.params) return;
|
||||
if (item?.children) {
|
||||
item.children.forEach(v => {
|
||||
if (v?.meta?.title === item?.meta?.title) {
|
||||
matched.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
levelList.value = matched.filter(
|
||||
item => item?.meta && item?.meta.title !== false
|
||||
);
|
||||
};
|
||||
|
||||
getBreadcrumb();
|
||||
const handleLink = (item: RouteLocationMatched): void => {
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect as any);
|
||||
} else {
|
||||
router.push(path);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBreadcrumb();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => getBreadcrumb()
|
||||
);
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
() => getBreadcrumb()
|
||||
);
|
||||
|
||||
const handleLink = (item: RouteLocationMatched): any => {
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect.toString());
|
||||
return;
|
||||
() => {
|
||||
getBreadcrumb();
|
||||
}
|
||||
router.push(path);
|
||||
};
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<el-breadcrumb class="!leading-[50px] select-none" separator="/">
|
||||
<transition-group appear name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
|
||||
<span
|
||||
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
|
||||
class="no-redirect"
|
||||
>{{ transformI18n(item.meta.title) }}</span
|
||||
>
|
||||
<a v-else @click.prevent="handleLink(item)">
|
||||
<el-breadcrumb-item v-for="item in levelList" :key="item.path">
|
||||
<a @click.prevent="handleLink(item)">
|
||||
{{ transformI18n(item.meta.title) }}
|
||||
</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,63 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||
export interface Props {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isActive: false
|
||||
});
|
||||
|
||||
const fillColor = ref<string>("");
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "toggleClick"): void;
|
||||
}>();
|
||||
|
||||
const toggleClick = () => {
|
||||
emit("toggleClick");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="classes.container"
|
||||
:title="props.isActive ? '点击折叠' : '点击展开'"
|
||||
@click="toggleClick"
|
||||
@mouseenter="fillColor = useEpThemeStoreHook().epThemeColor"
|
||||
@mouseleave="fillColor = ''"
|
||||
>
|
||||
<svg
|
||||
:fill="fillColor"
|
||||
:class="['hamburger', props.isActive ? 'is-active' : '']"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path
|
||||
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style module="classes" scoped>
|
||||
.container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
@@ -1,70 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useNav } from "../../hooks/nav";
|
||||
import { ref, watch } from "vue";
|
||||
import Search from "../search/index.vue";
|
||||
import Notice from "../notice/index.vue";
|
||||
import { templateRef } from "@vueuse/core";
|
||||
import SidebarItem from "./sidebarItem.vue";
|
||||
import avatars from "/@/assets/avatars.jpg";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import screenfull from "../screenfull/index.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import { watch, nextTick, onMounted, getCurrentInstance } from "vue";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||
|
||||
const route = useRoute();
|
||||
const { locale, t } = useI18n();
|
||||
const routers = useRouter().options.routes;
|
||||
const menuRef = templateRef<ElRef | null>("menu", null);
|
||||
const instance =
|
||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||
const title =
|
||||
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
|
||||
const menuRef = ref();
|
||||
|
||||
const { t, route, locale, translationCh, translationEn } =
|
||||
useTranslationLang(menuRef);
|
||||
const {
|
||||
title,
|
||||
routers,
|
||||
logout,
|
||||
backHome,
|
||||
onPanel,
|
||||
changeTitle,
|
||||
handleResize,
|
||||
menuSelect,
|
||||
username,
|
||||
avatarsStyle,
|
||||
getDropdownItemStyle
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
handleResize(menuRef.value);
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
changeTitle(route.meta);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
menuSelect(route.path, routers);
|
||||
}
|
||||
);
|
||||
|
||||
function translationCh() {
|
||||
instance.locale = { locale: "zh" };
|
||||
locale.value = "zh";
|
||||
handleResize(menuRef.value);
|
||||
}
|
||||
|
||||
function translationEn() {
|
||||
instance.locale = { locale: "en" };
|
||||
locale.value = "en";
|
||||
handleResize(menuRef.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -74,11 +43,11 @@ function translationEn() {
|
||||
<h4>{{ title }}</h4>
|
||||
</div>
|
||||
<el-menu
|
||||
ref="menu"
|
||||
class="horizontal-header-menu"
|
||||
mode="horizontal"
|
||||
:default-active="route.path"
|
||||
router
|
||||
ref="menuRef"
|
||||
mode="horizontal"
|
||||
class="horizontal-header-menu"
|
||||
:default-active="route.path"
|
||||
@select="indexPath => menuSelect(indexPath, routers)"
|
||||
>
|
||||
<sidebar-item
|
||||
@@ -97,33 +66,39 @@ function translationEn() {
|
||||
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
|
||||
<!-- 国际化 -->
|
||||
<el-dropdown id="header-translation" trigger="click">
|
||||
<globalization />
|
||||
<globalization
|
||||
class="navbar-bg-hover w-40px h-48px p-11px cursor-pointer outline-none"
|
||||
/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="translation">
|
||||
<el-dropdown-item
|
||||
:style="getDropdownItemStyle(locale, 'zh')"
|
||||
:class="['!dark:color-white', getDropdownItemClass(locale, 'zh')]"
|
||||
@click="translationCh"
|
||||
>
|
||||
<span class="check-zh" v-show="locale === 'zh'">
|
||||
<IconifyIconOffline icon="check" /> </span
|
||||
>简体中文
|
||||
<IconifyIconOffline icon="check" />
|
||||
</span>
|
||||
简体中文
|
||||
</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
|
||||
>
|
||||
<IconifyIconOffline icon="check" />
|
||||
</span>
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<!-- 退出登陆 -->
|
||||
<!-- 退出登录 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
<span class="el-dropdown-link navbar-bg-hover">
|
||||
<img v-if="avatars" :src="avatars" :style="avatarsStyle" />
|
||||
<p v-if="username">{{ username }}</p>
|
||||
<p v-if="username" class="dark:color-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
@@ -132,13 +107,13 @@ function translationEn() {
|
||||
icon="logout-circle-r-line"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
{{ t("buttons.hsLoginOut") }}</el-dropdown-item
|
||||
>
|
||||
{{ t("buttons.hsLoginOut") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<span
|
||||
class="el-icon-setting"
|
||||
class="el-icon-setting navbar-bg-hover"
|
||||
:title="t('buttons.hssystemSet')"
|
||||
@click="onPanel"
|
||||
>
|
||||
|
47
src/layout/components/sidebar/leftCollapse.vue
Normal file
47
src/layout/components/sidebar/leftCollapse.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { useDark } from "@pureadmin/utils";
|
||||
|
||||
interface Props {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isActive: false
|
||||
});
|
||||
const { isDark } = useDark();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "toggleClick"): void;
|
||||
}>();
|
||||
|
||||
const toggleClick = () => {
|
||||
emit("toggleClick");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<el-tooltip
|
||||
placement="right"
|
||||
:effect="isDark ? 'dark' : 'light'"
|
||||
:content="props.isActive ? '点击折叠' : '点击展开'"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
:icon="props.isActive ? 'menu-fold' : 'menu-unfold'"
|
||||
class="cursor-pointer inline-block align-middle color-primary hover:color-primary !dark:hover:color-white w-16px h-16px ml-4 mb-1"
|
||||
@click="toggleClick"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
box-shadow: 0 0 6px -2px var(--el-color-primary);
|
||||
}
|
||||
</style>
|
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance } from "vue";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
|
||||
const props = defineProps({
|
||||
collapse: Boolean
|
||||
});
|
||||
|
||||
const title =
|
||||
getCurrentInstance().appContext.config.globalProperties.$config?.Title;
|
||||
const { title } = useNav();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@@ -1,47 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Search from "../search/index.vue";
|
||||
import Notice from "../notice/index.vue";
|
||||
import { useNav } from "../../hooks/nav";
|
||||
import { templateRef } from "@vueuse/core";
|
||||
import avatars from "/@/assets/avatars.jpg";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import screenfull from "../screenfull/index.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
import { ref, toRaw, watch, onMounted } from "vue";
|
||||
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
|
||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||
import { getParentPaths, findRouteByPath } from "/@/router/utils";
|
||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
import globalization from "/@/assets/svg/globalization.svg?component";
|
||||
import { ref, watch, nextTick, onMounted, getCurrentInstance } from "vue";
|
||||
|
||||
const route = useRoute();
|
||||
const { locale, t } = useI18n();
|
||||
const routers = useRouter().options.routes;
|
||||
const menuRef = templateRef<ElRef | null>("menu", null);
|
||||
const instance =
|
||||
getCurrentInstance().appContext.config.globalProperties.$storage;
|
||||
const menuRef = ref();
|
||||
let defaultActive = ref(null);
|
||||
|
||||
const { t, route, locale, translationCh, translationEn } =
|
||||
useTranslationLang(menuRef);
|
||||
const {
|
||||
device,
|
||||
routers,
|
||||
logout,
|
||||
onPanel,
|
||||
changeTitle,
|
||||
toggleSideBar,
|
||||
handleResize,
|
||||
menuSelect,
|
||||
resolvePath,
|
||||
pureApp,
|
||||
username,
|
||||
avatarsStyle,
|
||||
getDropdownItemStyle
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass
|
||||
} = useNav();
|
||||
|
||||
let defaultActive = ref(null);
|
||||
|
||||
function getDefaultActive(routePath) {
|
||||
const wholeMenus = usePermissionStoreHook().wholeMenus;
|
||||
// 当前路由的父级路径
|
||||
/** 当前路由的父级路径 */
|
||||
const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
|
||||
defaultActive.value = findRouteByPath(
|
||||
parentRoutes,
|
||||
@@ -51,67 +43,24 @@ function getDefaultActive(routePath) {
|
||||
|
||||
onMounted(() => {
|
||||
getDefaultActive(route.path);
|
||||
nextTick(() => {
|
||||
handleResize(menuRef.value);
|
||||
});
|
||||
});
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
changeTitle(route.meta);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
getDefaultActive(route.path);
|
||||
}
|
||||
);
|
||||
|
||||
function translationCh() {
|
||||
instance.locale = { locale: "zh" };
|
||||
locale.value = "zh";
|
||||
handleResize(menuRef.value);
|
||||
}
|
||||
|
||||
function translationEn() {
|
||||
instance.locale = { locale: "en" };
|
||||
locale.value = "en";
|
||||
handleResize(menuRef.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="horizontal-header">
|
||||
<div
|
||||
:class="classes.container"
|
||||
:title="pureApp.sidebar.opened ? '点击折叠' : '点击展开'"
|
||||
@click="toggleSideBar"
|
||||
>
|
||||
<svg
|
||||
:fill="useEpThemeStoreHook().fill"
|
||||
:class="[
|
||||
'hamburger',
|
||||
pureApp.sidebar.opened ? 'is-active-hamburger' : ''
|
||||
]"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path
|
||||
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-if="device !== 'mobile'" class="horizontal-header">
|
||||
<el-menu
|
||||
ref="menu"
|
||||
class="horizontal-header-menu"
|
||||
mode="horizontal"
|
||||
:default-active="defaultActive"
|
||||
router
|
||||
ref="menuRef"
|
||||
mode="horizontal"
|
||||
class="horizontal-header-menu"
|
||||
:default-active="defaultActive"
|
||||
@select="indexPath => menuSelect(indexPath, routers)"
|
||||
>
|
||||
<el-menu-item
|
||||
@@ -120,10 +69,15 @@ function translationEn() {
|
||||
:index="resolvePath(route) || route.redirect"
|
||||
>
|
||||
<template #title>
|
||||
<div v-show="route.meta.icon" :class="['el-icon', route.meta.icon]">
|
||||
<component :is="useRenderIcon(route.meta && route.meta.icon)" />
|
||||
<div
|
||||
v-if="toRaw(route.meta.icon)"
|
||||
:class="['sub-menu-icon', route.meta.icon]"
|
||||
>
|
||||
<component
|
||||
:is="useRenderIcon(route.meta && toRaw(route.meta.icon))"
|
||||
/>
|
||||
</div>
|
||||
<span>{{ transformI18n(route.meta.title) }}</span>
|
||||
<span class="select-none">{{ transformI18n(route.meta.title) }}</span>
|
||||
<FontIcon
|
||||
v-if="route.meta.extraIcon"
|
||||
width="30px"
|
||||
@@ -144,31 +98,39 @@ function translationEn() {
|
||||
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
|
||||
<!-- 国际化 -->
|
||||
<el-dropdown id="header-translation" trigger="click">
|
||||
<globalization />
|
||||
<globalization
|
||||
class="navbar-bg-hover w-40px h-48px p-11px cursor-pointer outline-none"
|
||||
/>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="translation">
|
||||
<el-dropdown-item
|
||||
:style="getDropdownItemStyle(locale, 'zh')"
|
||||
:class="['!dark:color-white', getDropdownItemClass(locale, 'zh')]"
|
||||
@click="translationCh"
|
||||
><span class="check-zh" v-show="locale === 'zh'"
|
||||
><IconifyIconOffline icon="check" /></span
|
||||
>简体中文</el-dropdown-item
|
||||
>
|
||||
<span class="check-zh" v-show="locale === 'zh'">
|
||||
<IconifyIconOffline icon="check" />
|
||||
</span>
|
||||
简体中文
|
||||
</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
|
||||
>
|
||||
<span class="check-en" v-show="locale === 'en'">
|
||||
<IconifyIconOffline icon="check" />
|
||||
</span>
|
||||
English
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<!-- 退出登陆 -->
|
||||
<!-- 退出登录 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link">
|
||||
<span class="el-dropdown-link navbar-bg-hover">
|
||||
<img v-if="avatars" :src="avatars" :style="avatarsStyle" />
|
||||
<p v-if="username">{{ username }}</p>
|
||||
<p v-if="username" class="dark:color-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
@@ -177,13 +139,13 @@ function translationEn() {
|
||||
icon="logout-circle-r-line"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
{{ t("buttons.hsLoginOut") }}</el-dropdown-item
|
||||
>
|
||||
{{ t("buttons.hsLoginOut") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<span
|
||||
class="el-icon-setting"
|
||||
class="el-icon-setting navbar-bg-hover"
|
||||
:title="t('buttons.hssystemSet')"
|
||||
@click="onPanel"
|
||||
>
|
||||
@@ -193,26 +155,7 @@ function translationEn() {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style module="classes" scoped>
|
||||
.container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hamburger {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active-hamburger {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.translation {
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
padding: 5px 40px;
|
||||
|
@@ -1,14 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import path from "path";
|
||||
import { useNav } from "../../hooks/nav";
|
||||
import { childrenType } from "../../types";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
|
||||
import { ref, PropType, nextTick, computed, CSSProperties } from "vue";
|
||||
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
|
||||
|
||||
const { pureApp } = useNav();
|
||||
const menuMode = ["vertical", "mix"].includes(pureApp.layout);
|
||||
const { layout, isCollapse } = useNav();
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
@@ -25,7 +23,7 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const getExtraIconStyle = computed((): CSSProperties => {
|
||||
if (useAppStoreHook().getSidebarStatus) {
|
||||
if (!isCollapse.value) {
|
||||
return {
|
||||
position: "absolute",
|
||||
right: "10px"
|
||||
@@ -46,7 +44,7 @@ const getNoDropdownStyle = computed((): CSSProperties => {
|
||||
|
||||
const getDivStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
width: pureApp.sidebar.opened ? "" : "100%",
|
||||
width: !isCollapse.value ? "" : "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
@@ -54,9 +52,8 @@ const getDivStyle = computed((): CSSProperties => {
|
||||
};
|
||||
});
|
||||
|
||||
const getMenuTextStyle = computed((): CSSProperties => {
|
||||
const getMenuTextStyle = computed(() => {
|
||||
return {
|
||||
width: pureApp.sidebar.opened ? "125px" : "",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
outline: "none"
|
||||
@@ -65,14 +62,14 @@ const getMenuTextStyle = computed((): CSSProperties => {
|
||||
|
||||
const getSubTextStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
width: pureApp.sidebar.opened ? "125px" : "",
|
||||
width: !isCollapse.value ? "210px" : "",
|
||||
display: "inline-block",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
};
|
||||
});
|
||||
|
||||
const getSpanStyle = computed((): CSSProperties => {
|
||||
const getSpanStyle = computed(() => {
|
||||
return {
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis"
|
||||
@@ -148,21 +145,31 @@ function resolvePath(routePath) {
|
||||
:class="{ 'submenu-title-noDropdown': !isNest }"
|
||||
:style="getNoDropdownStyle"
|
||||
>
|
||||
<div class="sub-menu-icon" v-show="props.item.meta.icon">
|
||||
<div class="sub-menu-icon" v-if="toRaw(props.item.meta.icon)">
|
||||
<component
|
||||
:is="
|
||||
useRenderIcon(
|
||||
onlyOneChild.meta.icon ||
|
||||
(props.item.meta && props.item.meta.icon)
|
||||
toRaw(onlyOneChild.meta.icon) ||
|
||||
(props.item.meta && toRaw(props.item.meta.icon))
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
!pureApp.sidebar.opened &&
|
||||
pureApp.layout === 'mix' &&
|
||||
props.item?.pathList?.length === 2
|
||||
isCollapse &&
|
||||
layout === 'vertical' &&
|
||||
props.item?.pathList?.length === 1
|
||||
"
|
||||
:style="getDivStyle"
|
||||
>
|
||||
<span :style="getMenuTextStyle">
|
||||
{{ transformI18n(onlyOneChild.meta.title) }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
isCollapse && layout === 'mix' && props.item?.pathList?.length === 2
|
||||
"
|
||||
:style="getDivStyle"
|
||||
>
|
||||
@@ -172,9 +179,9 @@ function resolvePath(routePath) {
|
||||
</div>
|
||||
<template #title>
|
||||
<div :style="getDivStyle">
|
||||
<span v-if="!menuMode">{{
|
||||
transformI18n(onlyOneChild.meta.title)
|
||||
}}</span>
|
||||
<span v-if="layout === 'horizontal'">
|
||||
{{ transformI18n(onlyOneChild.meta.title) }}
|
||||
</span>
|
||||
<el-tooltip
|
||||
v-else
|
||||
placement="top"
|
||||
@@ -205,24 +212,21 @@ function resolvePath(routePath) {
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<el-sub-menu
|
||||
v-else
|
||||
ref="subMenu"
|
||||
:index="resolvePath(props.item.path)"
|
||||
popper-append-to-body
|
||||
>
|
||||
<el-sub-menu v-else ref="subMenu" :index="resolvePath(props.item.path)">
|
||||
<template #title>
|
||||
<div v-show="props.item.meta.icon" class="sub-menu-icon">
|
||||
<div v-if="toRaw(props.item.meta.icon)" class="sub-menu-icon">
|
||||
<component
|
||||
:is="useRenderIcon(props.item.meta && props.item.meta.icon)"
|
||||
:is="useRenderIcon(props.item.meta && toRaw(props.item.meta.icon))"
|
||||
/>
|
||||
</div>
|
||||
<span v-if="!menuMode">{{ transformI18n(props.item.meta.title) }}</span>
|
||||
<span v-if="layout === 'horizontal'">
|
||||
{{ transformI18n(props.item.meta.title) }}
|
||||
</span>
|
||||
<el-tooltip
|
||||
v-else
|
||||
placement="top"
|
||||
:offset="-10"
|
||||
:disabled="!pureApp.sidebar.opened || !props.item.showTooltip"
|
||||
:disabled="!isCollapse || !props.item.showTooltip"
|
||||
>
|
||||
<template #content>
|
||||
{{ transformI18n(props.item.meta.title) }}
|
||||
|
30
src/layout/components/sidebar/topCollapse.vue
Normal file
30
src/layout/components/sidebar/topCollapse.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isActive: false
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "toggleClick"): void;
|
||||
}>();
|
||||
|
||||
const toggleClick = () => {
|
||||
emit("toggleClick");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="px-3 mr-1 navbar-bg-hover"
|
||||
:title="props.isActive ? '点击折叠' : '点击展开'"
|
||||
@click="toggleClick"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
:icon="props.isActive ? 'menu-fold' : 'menu-unfold'"
|
||||
class="inline-block align-middle hover:color-primary !dark:hover:color-white"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
@@ -1,26 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import Logo from "./logo.vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { useNav } from "../../hooks/nav";
|
||||
import SidebarItem from "./sidebarItem.vue";
|
||||
import { storageLocal } from "/@/utils/storage";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import leftCollapse from "./leftCollapse.vue";
|
||||
import type { StorageConfigs } from "/#/index";
|
||||
import { useNav } from "/@/layout/hooks/useNav";
|
||||
import { storageLocal } from "@pureadmin/utils";
|
||||
import { ref, computed, watch, onBeforeMount } from "vue";
|
||||
import { findRouteByPath, getParentPaths } from "/@/router/utils";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
|
||||
const route = useRoute();
|
||||
const routers = useRouter().options.routes;
|
||||
const showLogo = ref(
|
||||
storageLocal.getItem("responsive-configure")?.showLogo ?? true
|
||||
storageLocal.getItem<StorageConfigs>("responsive-configure")?.showLogo ?? true
|
||||
);
|
||||
|
||||
const { pureApp, isCollapse, menuSelect } = useNav();
|
||||
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
|
||||
useNav();
|
||||
|
||||
let subMenuData = ref([]);
|
||||
|
||||
const menuData = computed(() => {
|
||||
return pureApp.layout === "mix"
|
||||
return pureApp.layout === "mix" && device.value !== "mobile"
|
||||
? subMenuData.value
|
||||
: usePermissionStoreHook().wholeMenus;
|
||||
});
|
||||
@@ -59,25 +61,33 @@ watch(
|
||||
<template>
|
||||
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
|
||||
<Logo v-if="showLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-scrollbar
|
||||
wrap-class="scrollbar-wrapper"
|
||||
:class="[device === 'mobile' ? 'mobile' : 'pc']"
|
||||
>
|
||||
<el-menu
|
||||
:default-active="route.path"
|
||||
:collapse="isCollapse"
|
||||
unique-opened
|
||||
router
|
||||
:collapse-transition="false"
|
||||
unique-opened
|
||||
mode="vertical"
|
||||
class="outer-most"
|
||||
class="outer-most select-none"
|
||||
:collapse="isCollapse"
|
||||
:default-active="route.path"
|
||||
:collapse-transition="false"
|
||||
@select="indexPath => menuSelect(indexPath, routers)"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="routes in menuData"
|
||||
:key="routes.path"
|
||||
:item="routes"
|
||||
class="outer-most"
|
||||
:base-path="routes.path"
|
||||
class="outer-most select-none"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
<leftCollapse
|
||||
v-if="device !== 'mobile'"
|
||||
:is-active="pureApp.sidebar.opened"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -43,7 +43,7 @@
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-regular);
|
||||
color: var(--el-text-color-primary);
|
||||
background: #fff;
|
||||
position: relative;
|
||||
box-shadow: 0 0 1px #888;
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
color: var(--el-text-color-primary);
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
color: #000000d9;
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
@@ -160,7 +160,7 @@
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
// background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
@@ -173,8 +173,6 @@
|
||||
}
|
||||
|
||||
.el-dropdown-menu {
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -207,7 +205,7 @@
|
||||
}
|
||||
|
||||
.scroll-item.is-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
// background-color: var(--el-color-primary-light-9);
|
||||
position: relative;
|
||||
color: #fff;
|
||||
|
||||
@@ -220,7 +218,7 @@
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--el-color-primary);
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +226,7 @@
|
||||
.arrow-right {
|
||||
width: 40px;
|
||||
height: 38px;
|
||||
color: #00000073;
|
||||
color: var(--el-text-color-primary);
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
|
@@ -1,115 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
unref,
|
||||
toRaw,
|
||||
reactive,
|
||||
nextTick,
|
||||
computed,
|
||||
ComputedRef,
|
||||
CSSProperties,
|
||||
onBeforeMount,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
|
||||
import close from "/@/assets/svg/close.svg?component";
|
||||
import refresh from "/@/assets/svg/refresh.svg?component";
|
||||
import closeAll from "/@/assets/svg/close_all.svg?component";
|
||||
import closeLeft from "/@/assets/svg/close_left.svg?component";
|
||||
import closeOther from "/@/assets/svg/close_other.svg?component";
|
||||
import closeRight from "/@/assets/svg/close_right.svg?component";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { storageLocal } from "/@/utils/storage";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { RouteConfigs } from "../../types";
|
||||
import { useTags } from "../../hooks/useTag";
|
||||
import { routerArrays } from "/@/layout/types";
|
||||
import { isEqual, isEmpty } from "lodash-unified";
|
||||
import { transformI18n, $t } from "/@/plugins/i18n";
|
||||
import { RouteConfigs, tagsViewsType } from "../../types";
|
||||
import { toggleClass, removeClass } from "@pureadmin/utils";
|
||||
import { useResizeObserver, useDebounceFn } from "@vueuse/core";
|
||||
import { useSettingStoreHook } from "/@/store/modules/settings";
|
||||
import { handleAliveRoute, delAliveRoutes } from "/@/router/utils";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||
import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
|
||||
import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core";
|
||||
import { ref, watch, unref, toRaw, nextTick, onBeforeMount } from "vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const translateX = ref<number>(0);
|
||||
const activeIndex = ref<number>(-1);
|
||||
let refreshButton = "refresh-button";
|
||||
const instance = getCurrentInstance();
|
||||
const pureSetting = useSettingStoreHook();
|
||||
const tabDom = templateRef<HTMLElement | null>("tabDom", null);
|
||||
const containerDom = templateRef<HTMLElement | null>("containerDom", null);
|
||||
const scrollbarDom = templateRef<HTMLElement | null>("scrollbarDom", null);
|
||||
const showTags =
|
||||
ref(storageLocal.getItem("responsive-configure").hideTabs) ?? "false";
|
||||
let multiTags: ComputedRef<Array<RouteConfigs>> = computed(() => {
|
||||
return useMultiTagsStoreHook()?.multiTags;
|
||||
});
|
||||
const {
|
||||
route,
|
||||
router,
|
||||
visible,
|
||||
showTags,
|
||||
instance,
|
||||
multiTags,
|
||||
tagsViews,
|
||||
buttonTop,
|
||||
buttonLeft,
|
||||
showModel,
|
||||
translateX,
|
||||
activeIndex,
|
||||
getTabStyle,
|
||||
iconIsActive,
|
||||
linkIsActive,
|
||||
currentSelect,
|
||||
scheduleIsActive,
|
||||
getContextMenuStyle,
|
||||
closeMenu,
|
||||
onMounted,
|
||||
onMouseenter,
|
||||
onMouseleave,
|
||||
transformI18n
|
||||
} = useTags();
|
||||
|
||||
const linkIsActive = computed(() => {
|
||||
return item => {
|
||||
if (Object.keys(route.query).length === 0) {
|
||||
if (route.path === item.path) {
|
||||
return "is-active";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
if (isEqual(route?.query, item?.query)) {
|
||||
return "is-active";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const scheduleIsActive = computed(() => {
|
||||
return item => {
|
||||
if (Object.keys(route.query).length === 0) {
|
||||
if (route.path === item.path) {
|
||||
return "schedule-active";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
if (isEqual(route?.query, item?.query)) {
|
||||
return "schedule-active";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const iconIsActive = computed(() => {
|
||||
return (item, index) => {
|
||||
if (index === 0) return;
|
||||
if (Object.keys(route.query).length === 0) {
|
||||
if (route.path === item.path) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (isEqual(route?.query, item?.query)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
const tabDom = ref();
|
||||
const containerDom = ref();
|
||||
const scrollbarDom = ref();
|
||||
|
||||
const dynamicTagView = () => {
|
||||
const index = multiTags.value.findIndex(item => {
|
||||
if (item?.query) {
|
||||
return isEqual(route?.query, item?.query);
|
||||
if (item.query) {
|
||||
return isEqual(route.query, item.query);
|
||||
} else if (item.params) {
|
||||
return isEqual(route.params, item.params);
|
||||
} else {
|
||||
return item.path === route.path;
|
||||
}
|
||||
@@ -117,23 +55,9 @@ const dynamicTagView = () => {
|
||||
moveToView(index);
|
||||
};
|
||||
|
||||
watch([route], () => {
|
||||
activeIndex.value = -1;
|
||||
dynamicTagView();
|
||||
});
|
||||
|
||||
useResizeObserver(
|
||||
scrollbarDom,
|
||||
useDebounceFn(() => {
|
||||
dynamicTagView();
|
||||
}, 200)
|
||||
);
|
||||
|
||||
const tabNavPadding = 10;
|
||||
const moveToView = (index: number): void => {
|
||||
if (!instance.refs["dynamic" + index]) {
|
||||
return;
|
||||
}
|
||||
const tabNavPadding = 10;
|
||||
if (!instance.refs["dynamic" + index]) return;
|
||||
const tabItemEl = instance.refs["dynamic" + index][0];
|
||||
const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft;
|
||||
const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth;
|
||||
@@ -143,7 +67,6 @@ const moveToView = (index: number): void => {
|
||||
: 0;
|
||||
// 已有标签页总长度(包含溢出部分)
|
||||
const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
|
||||
|
||||
if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
|
||||
translateX.value = 0;
|
||||
} else if (tabItemElOffsetLeft < -translateX.value) {
|
||||
@@ -192,68 +115,6 @@ const handleScroll = (offset: number): void => {
|
||||
}
|
||||
};
|
||||
|
||||
const tagsViews = reactive<Array<tagsViewsType>>([
|
||||
{
|
||||
icon: refresh,
|
||||
text: $t("buttons.hsreload"),
|
||||
divided: false,
|
||||
disabled: false,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: close,
|
||||
text: $t("buttons.hscloseCurrentTab"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeLeft,
|
||||
text: $t("buttons.hscloseLeftTabs"),
|
||||
divided: true,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeRight,
|
||||
text: $t("buttons.hscloseRightTabs"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeOther,
|
||||
text: $t("buttons.hscloseOtherTabs"),
|
||||
divided: true,
|
||||
disabled: multiTags.value.length > 2 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeAll,
|
||||
text: $t("buttons.hscloseAllTabs"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
}
|
||||
]);
|
||||
|
||||
// 显示模式,默认灵动模式显示
|
||||
const showModel = ref(
|
||||
storageLocal.getItem("responsive-configure")?.showModel || "smart"
|
||||
);
|
||||
if (!showModel.value) {
|
||||
const configure = storageLocal.getItem("responsive-configure");
|
||||
configure.showModel = "card";
|
||||
storageLocal.setItem("responsive-configure", configure);
|
||||
}
|
||||
|
||||
let visible = ref(false);
|
||||
let buttonLeft = ref(0);
|
||||
let buttonTop = ref(0);
|
||||
|
||||
// 当前右键选中的路由信息
|
||||
let currentSelect = ref({});
|
||||
|
||||
function dynamicRouteTag(value: string, parentPath: string): void {
|
||||
const hasValue = multiTags.value.some(item => {
|
||||
return item.path === value;
|
||||
@@ -278,11 +139,12 @@ function dynamicRouteTag(value: string, parentPath: string): void {
|
||||
});
|
||||
}
|
||||
}
|
||||
concatPath(router.options.routes, value, parentPath);
|
||||
concatPath(router.options.routes as any, value, parentPath);
|
||||
}
|
||||
|
||||
// 重新加载
|
||||
/** 刷新路由 */
|
||||
function onFresh() {
|
||||
const refreshButton = "refresh-button";
|
||||
toggleClass(true, refreshButton, document.querySelector(".rotate"));
|
||||
const { fullPath, query } = unref(route);
|
||||
router.replace({
|
||||
@@ -302,6 +164,10 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
if (item.path === obj.path) {
|
||||
return item.query === obj.query;
|
||||
}
|
||||
} else if (item.params) {
|
||||
if (item.path === obj.path) {
|
||||
return item.params === obj.params;
|
||||
}
|
||||
} else {
|
||||
return item.path === obj.path;
|
||||
}
|
||||
@@ -313,23 +179,12 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
other?: boolean
|
||||
): void => {
|
||||
if (other) {
|
||||
useMultiTagsStoreHook().handleTags("equal", [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "menus.hshome",
|
||||
icon: "home-filled"
|
||||
}
|
||||
},
|
||||
obj
|
||||
]);
|
||||
useMultiTagsStoreHook().handleTags("equal", [routerArrays[0], obj]);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", {
|
||||
startIndex,
|
||||
length
|
||||
});
|
||||
}) as any;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -351,24 +206,25 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
: handleAliveRoute(route.matched, "delete");
|
||||
// 如果删除当前激活tag就自动切换到最后一个tag
|
||||
if (tag === "left") return;
|
||||
nextTick(() => {
|
||||
router.push({
|
||||
path: newRoute[0].path,
|
||||
query: newRoute[0].query
|
||||
});
|
||||
});
|
||||
if (newRoute[0]?.query) {
|
||||
router.push({ name: newRoute[0].name, query: newRoute[0].query });
|
||||
} else if (newRoute[0]?.params) {
|
||||
router.push({ name: newRoute[0].name, params: newRoute[0].params });
|
||||
} else {
|
||||
router.push({ path: newRoute[0].path });
|
||||
}
|
||||
} else {
|
||||
// 删除缓存路由
|
||||
tag ? delAliveRoutes(delAliveRouteList) : delAliveRoutes([obj]);
|
||||
if (!multiTags.value.length) return;
|
||||
let isHasActiveTag = multiTags.value.some(item => {
|
||||
return item.path === route.path;
|
||||
});
|
||||
!isHasActiveTag &&
|
||||
router.push({
|
||||
path: newRoute[0].path,
|
||||
query: newRoute[0].query
|
||||
});
|
||||
if (multiTags.value.some(item => item.path === route.path)) return;
|
||||
if (newRoute[0]?.query) {
|
||||
router.push({ name: newRoute[0].name, query: newRoute[0].query });
|
||||
} else if (newRoute[0]?.params) {
|
||||
router.push({ name: newRoute[0].name, params: newRoute[0].params });
|
||||
} else {
|
||||
router.push({ path: newRoute[0].path });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +241,8 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||
path: selectRoute.path,
|
||||
meta: selectRoute.meta,
|
||||
name: selectRoute.name,
|
||||
query: selectRoute.query
|
||||
query: selectRoute?.query,
|
||||
params: selectRoute?.params
|
||||
};
|
||||
} else {
|
||||
selectTagRoute = { path: route.path, meta: route.meta };
|
||||
@@ -394,7 +251,7 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||
// 当前路由信息
|
||||
switch (key) {
|
||||
case 0:
|
||||
// 重新加载
|
||||
// 刷新路由
|
||||
onFresh();
|
||||
break;
|
||||
case 1:
|
||||
@@ -428,21 +285,16 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleCommand(command: object) {
|
||||
// @ts-expect-error
|
||||
function handleCommand(command: any) {
|
||||
const { key, item } = command;
|
||||
onClickDrop(key, item);
|
||||
}
|
||||
|
||||
// 触发右键中菜单的点击事件
|
||||
/** 触发右键中菜单的点击事件 */
|
||||
function selectTag(key, item) {
|
||||
onClickDrop(key, item, currentSelect.value);
|
||||
}
|
||||
|
||||
function closeMenu() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
function showMenus(value: boolean) {
|
||||
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
||||
tagsViews[v].show = value;
|
||||
@@ -455,7 +307,7 @@ function disabledMenus(value: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
// 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页
|
||||
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
||||
function showMenuModel(
|
||||
currentPath: string,
|
||||
query: object = {},
|
||||
@@ -515,7 +367,7 @@ function openMenu(tag, e) {
|
||||
// 右键菜单为首页,只显示刷新
|
||||
showMenus(false);
|
||||
tagsViews[0].show = true;
|
||||
} else if (route.path !== tag.path) {
|
||||
} else if (route.path !== tag.path && route.name !== tag.name) {
|
||||
// 右键菜单不匹配当前路由,隐藏刷新
|
||||
tagsViews[0].show = false;
|
||||
showMenuModel(tag.path, tag.query);
|
||||
@@ -543,63 +395,36 @@ function openMenu(tag, e) {
|
||||
} else {
|
||||
buttonLeft.value = left;
|
||||
}
|
||||
pureSetting.hiddenSideBar
|
||||
useSettingStoreHook().hiddenSideBar
|
||||
? (buttonTop.value = e.clientY)
|
||||
: (buttonTop.value = e.clientY - 40);
|
||||
setTimeout(() => {
|
||||
nextTick(() => {
|
||||
visible.value = true;
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// 触发tags标签切换
|
||||
function tagOnClick(item) {
|
||||
router.push({
|
||||
path: item?.path,
|
||||
query: item?.query
|
||||
});
|
||||
showMenuModel(item?.path, item?.query);
|
||||
}
|
||||
|
||||
// 鼠标移入
|
||||
function onMouseenter(index) {
|
||||
if (index) activeIndex.value = index;
|
||||
if (unref(showModel) === "smart") {
|
||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
||||
return;
|
||||
toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
|
||||
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
|
||||
} else {
|
||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
||||
toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
|
||||
toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移出
|
||||
function onMouseleave(index) {
|
||||
activeIndex.value = -1;
|
||||
if (unref(showModel) === "smart") {
|
||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
||||
return;
|
||||
toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
|
||||
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
|
||||
} else {
|
||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
||||
toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
|
||||
toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
val => {
|
||||
if (val) {
|
||||
document.body.addEventListener("click", closeMenu);
|
||||
/** 触发tags标签切换 */
|
||||
function tagOnClick(item) {
|
||||
const { name, path } = item;
|
||||
if (name) {
|
||||
if (item.query) {
|
||||
router.push({
|
||||
name,
|
||||
query: item.query
|
||||
});
|
||||
} else if (item.params) {
|
||||
router.push({
|
||||
name,
|
||||
params: item.params
|
||||
});
|
||||
} else {
|
||||
document.body.removeEventListener("click", closeMenu);
|
||||
router.push({ name });
|
||||
}
|
||||
} else {
|
||||
router.push({ path });
|
||||
}
|
||||
);
|
||||
// showMenuModel(item?.path, item?.query);
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (!instance) return;
|
||||
@@ -608,9 +433,9 @@ onBeforeMount(() => {
|
||||
showMenuModel(route.fullPath);
|
||||
|
||||
// 触发隐藏标签页
|
||||
emitter.on("tagViewsChange", key => {
|
||||
if (unref(showTags) === key) return;
|
||||
showTags.value = key;
|
||||
emitter.on("tagViewsChange", (key: any) => {
|
||||
if (unref(showTags as any) === key) return;
|
||||
(showTags as any).value = key;
|
||||
});
|
||||
|
||||
// 改变标签风格
|
||||
@@ -627,14 +452,18 @@ onBeforeMount(() => {
|
||||
});
|
||||
});
|
||||
|
||||
const getTabStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
transform: `translateX(${translateX.value}px)`
|
||||
};
|
||||
watch([route], () => {
|
||||
activeIndex.value = -1;
|
||||
dynamicTagView();
|
||||
});
|
||||
|
||||
const getContextMenuStyle = computed((): CSSProperties => {
|
||||
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
|
||||
onMounted(() => {
|
||||
useResizeObserver(
|
||||
scrollbarDom,
|
||||
useDebounceFn(() => {
|
||||
dynamicTagView();
|
||||
}, 200)
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -661,8 +490,11 @@ const getContextMenuStyle = computed((): CSSProperties => {
|
||||
@mouseleave.prevent="onMouseleave(index)"
|
||||
@click="tagOnClick(item)"
|
||||
>
|
||||
<router-link :to="item.path"
|
||||
>{{ transformI18n(item.meta.title) }}
|
||||
<router-link
|
||||
:to="item.path"
|
||||
class="!dark:color-text_color_primary !dark:hover:color-primary"
|
||||
>
|
||||
{{ transformI18n(item.meta.title) }}
|
||||
</router-link>
|
||||
<span
|
||||
v-if="
|
||||
@@ -703,7 +535,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
|
||||
>
|
||||
<li v-if="item.show" @click="selectTag(key, item)">
|
||||
<component :is="toRaw(item.icon)" :key="key" />
|
||||
{{ t(item.text) }}
|
||||
{{ transformI18n(item.text) }}
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
@@ -712,7 +544,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
|
||||
<ul class="right-button">
|
||||
<li>
|
||||
<span
|
||||
:title="t('buttons.hsrefreshRoute')"
|
||||
:title="transformI18n('buttons.hsrefreshRoute')"
|
||||
class="el-icon-refresh-right rotate"
|
||||
@click="onFresh"
|
||||
>
|
||||
@@ -725,7 +557,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
|
||||
placement="bottom-end"
|
||||
@command="handleCommand"
|
||||
>
|
||||
<IconifyIconOffline icon="arrow-down" />
|
||||
<IconifyIconOffline icon="arrow-down" class="dark:color-white" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@@ -740,7 +572,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
|
||||
:key="key"
|
||||
style="margin-right: 6px"
|
||||
/>
|
||||
{{ t(item.text) }}
|
||||
{{ transformI18n(item.text) }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
|
@@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<div
|
||||
class="frame"
|
||||
v-loading="loading"
|
||||
:element-loading-text="t('status.hsLoad')"
|
||||
>
|
||||
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ref, unref, onMounted, nextTick } from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "FrameView"
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const loading = ref(false);
|
||||
const loading = ref(true);
|
||||
const currentRoute = useRoute();
|
||||
const frameSrc = ref<string>("");
|
||||
const frameRef = ref<HTMLElement | null>(null);
|
||||
@@ -22,6 +16,7 @@ const frameRef = ref<HTMLElement | null>(null);
|
||||
if (unref(currentRoute.meta)?.frameSrc) {
|
||||
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
|
||||
}
|
||||
unref(currentRoute.meta)?.frameLoading === false && hideLoading();
|
||||
|
||||
function hideLoading() {
|
||||
loading.value = false;
|
||||
@@ -45,11 +40,20 @@ function init() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="frame"
|
||||
v-loading="loading"
|
||||
:element-loading-text="t('status.hsLoad')"
|
||||
>
|
||||
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.frame {
|
||||
height: calc(100vh - 88px);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
export default function useBoolean(initValue = false) {
|
||||
export function useBoolean(initValue = false) {
|
||||
const bool = ref(initValue);
|
||||
|
||||
function setBool(value: boolean) {
|
||||
|
116
src/layout/hooks/useDataThemeChange.ts
Normal file
116
src/layout/hooks/useDataThemeChange.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { ref } from "vue";
|
||||
import { getConfig } from "/@/config";
|
||||
import { find } from "lodash-unified";
|
||||
import { useLayout } from "./useLayout";
|
||||
import { themeColorsType } from "../types";
|
||||
import { TinyColor } from "@ctrl/tinycolor";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||
import {
|
||||
darken,
|
||||
lighten,
|
||||
toggleTheme
|
||||
} from "@pureadmin/theme/dist/browser-utils";
|
||||
|
||||
export function useDataThemeChange() {
|
||||
const { layoutTheme, layout } = 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 { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||
const dataTheme = ref<boolean>($storage?.layout?.darkMode);
|
||||
const body = document.documentElement as HTMLElement;
|
||||
|
||||
/** 设置导航主题色 */
|
||||
function setLayoutThemeColor(theme = "default") {
|
||||
layoutTheme.value.theme = theme;
|
||||
toggleTheme({
|
||||
scopeName: `layout-theme-${theme}`
|
||||
});
|
||||
$storage.layout = {
|
||||
layout: layout.value,
|
||||
theme,
|
||||
darkMode: dataTheme.value,
|
||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||
epThemeColor: $storage.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)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** 日间、夜间主题切换 */
|
||||
function dataThemeChange() {
|
||||
/* 如果当前是light夜间主题,默认切换到default主题 */
|
||||
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
|
||||
setLayoutThemeColor("default");
|
||||
} else {
|
||||
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
|
||||
}
|
||||
|
||||
if (dataTheme.value) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
body,
|
||||
dataTheme,
|
||||
layoutTheme,
|
||||
themeColors,
|
||||
dataThemeChange,
|
||||
setEpThemeColor,
|
||||
setLayoutThemeColor
|
||||
};
|
||||
}
|
60
src/layout/hooks/useLayout.ts
Normal file
60
src/layout/hooks/useLayout.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { routerArrays } from "../types";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import { useMultiTagsStore } from "/@/store/modules/multiTags";
|
||||
|
||||
export function useLayout() {
|
||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
||||
|
||||
const initStorage = () => {
|
||||
/** 路由 */
|
||||
if (
|
||||
useMultiTagsStore().multiTagsCache &&
|
||||
(!$storage.tags || $storage.tags.length === 0)
|
||||
) {
|
||||
$storage.tags = routerArrays;
|
||||
}
|
||||
/** 国际化 */
|
||||
if (!$storage.locale) {
|
||||
$storage.locale = { locale: $config?.Locale ?? "zh" };
|
||||
useI18n().locale.value = $config?.Locale ?? "zh";
|
||||
}
|
||||
/** 导航 */
|
||||
if (!$storage.layout) {
|
||||
$storage.layout = {
|
||||
layout: $config?.Layout ?? "vertical",
|
||||
theme: $config?.Theme ?? "default",
|
||||
darkMode: $config?.DarkMode ?? false,
|
||||
sidebarStatus: $config?.SidebarStatus ?? true,
|
||||
epThemeColor: $config?.EpThemeColor ?? "#409EFF"
|
||||
};
|
||||
}
|
||||
/** 灰色模式、色弱模式、隐藏标签页 */
|
||||
if (!$storage.configure) {
|
||||
$storage.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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/** 清空缓存后从serverConfig.json读取默认配置并赋值到storage中 */
|
||||
const layout = computed(() => {
|
||||
return $storage?.layout.layout;
|
||||
});
|
||||
|
||||
const layoutTheme = computed(() => {
|
||||
return $storage.layout;
|
||||
});
|
||||
|
||||
return {
|
||||
layout,
|
||||
layoutTheme,
|
||||
initStorage
|
||||
};
|
||||
}
|
@@ -1,22 +1,28 @@
|
||||
import { computed } from "vue";
|
||||
import { router } from "/@/router";
|
||||
import { getConfig } from "/@/config";
|
||||
import { useRouter } from "vue-router";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { routeMetaType } from "../types";
|
||||
import { remainingPaths } from "/@/router";
|
||||
import type { StorageConfigs } from "/#/index";
|
||||
import { routerArrays } from "/@/layout/types";
|
||||
import { transformI18n } from "/@/plugins/i18n";
|
||||
import { storageSession } from "/@/utils/storage";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import { remainingPaths, resetRouter } from "/@/router";
|
||||
import { storageSession, useGlobal } from "@pureadmin/utils";
|
||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
|
||||
const errorInfo = "当前路由配置不正确,请检查配置";
|
||||
|
||||
export function useNav() {
|
||||
const pureApp = useAppStoreHook();
|
||||
// 用户名
|
||||
const username: string = storageSession.getItem("info")?.username;
|
||||
const routers = useRouter().options.routes;
|
||||
/** 用户名 */
|
||||
const username: string =
|
||||
storageSession.getItem<StorageConfigs>("info")?.username;
|
||||
|
||||
// 设置国际化选中后的样式
|
||||
/** 设置国际化选中后的样式 */
|
||||
const getDropdownItemStyle = computed(() => {
|
||||
return (locale, t) => {
|
||||
return {
|
||||
@@ -26,6 +32,12 @@ export function useNav() {
|
||||
};
|
||||
});
|
||||
|
||||
const getDropdownItemClass = computed(() => {
|
||||
return (locale, t) => {
|
||||
return locale === t ? "" : "!dark:hover:color-primary";
|
||||
};
|
||||
});
|
||||
|
||||
const avatarsStyle = computed(() => {
|
||||
return username ? { marginRight: "10px" } : "";
|
||||
});
|
||||
@@ -34,17 +46,32 @@ export function useNav() {
|
||||
return !pureApp.getSidebarStatus;
|
||||
});
|
||||
|
||||
// 动态title
|
||||
const device = computed(() => {
|
||||
return pureApp.getDevice;
|
||||
});
|
||||
|
||||
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
|
||||
const layout = computed(() => {
|
||||
return $storage?.layout?.layout;
|
||||
});
|
||||
|
||||
const title = computed(() => {
|
||||
return $config.Title;
|
||||
});
|
||||
|
||||
/** 动态title */
|
||||
function changeTitle(meta: routeMetaType) {
|
||||
const Title = getConfig().Title;
|
||||
if (Title) document.title = `${transformI18n(meta.title)} | ${Title}`;
|
||||
else document.title = transformI18n(meta.title);
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
/** 退出登录 */
|
||||
function logout() {
|
||||
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
|
||||
storageSession.removeItem("info");
|
||||
router.push("/login");
|
||||
resetRouter();
|
||||
}
|
||||
|
||||
function backHome() {
|
||||
@@ -60,7 +87,7 @@ export function useNav() {
|
||||
}
|
||||
|
||||
function handleResize(menuRef) {
|
||||
menuRef.handleResize();
|
||||
menuRef?.handleResize();
|
||||
}
|
||||
|
||||
function resolvePath(route) {
|
||||
@@ -81,7 +108,7 @@ export function useNav() {
|
||||
if (parentPathIndex > 0) {
|
||||
parentPath = indexPath.slice(0, parentPathIndex);
|
||||
}
|
||||
// 找到当前路由的信息
|
||||
/** 找到当前路由的信息 */
|
||||
function findCurrentRoute(indexPath: string, routes) {
|
||||
if (!routes) return console.error(errorInfo);
|
||||
return routes.map(item => {
|
||||
@@ -89,7 +116,7 @@ export function useNav() {
|
||||
if (item.redirect) {
|
||||
findCurrentRoute(item.redirect, item.children);
|
||||
} else {
|
||||
// 切换左侧菜单 通知标签页
|
||||
/** 切换左侧菜单 通知标签页 */
|
||||
emitter.emit("changLayoutRoute", {
|
||||
indexPath,
|
||||
parentPath
|
||||
@@ -103,13 +130,18 @@ export function useNav() {
|
||||
findCurrentRoute(indexPath, routers);
|
||||
}
|
||||
|
||||
// 判断路径是否参与菜单
|
||||
/** 判断路径是否参与菜单 */
|
||||
function isRemaining(path: string): boolean {
|
||||
return remainingPaths.includes(path);
|
||||
}
|
||||
|
||||
return {
|
||||
title,
|
||||
device,
|
||||
layout,
|
||||
logout,
|
||||
routers,
|
||||
$storage,
|
||||
backHome,
|
||||
onPanel,
|
||||
changeTitle,
|
||||
@@ -121,6 +153,7 @@ export function useNav() {
|
||||
pureApp,
|
||||
username,
|
||||
avatarsStyle,
|
||||
getDropdownItemStyle
|
||||
getDropdownItemStyle,
|
||||
getDropdownItemClass
|
||||
};
|
||||
}
|
218
src/layout/hooks/useTag.ts
Normal file
218
src/layout/hooks/useTag.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import {
|
||||
ref,
|
||||
unref,
|
||||
watch,
|
||||
computed,
|
||||
reactive,
|
||||
onMounted,
|
||||
CSSProperties,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import { tagsViewsType } from "../types";
|
||||
import { isEqual } from "lodash-unified";
|
||||
import type { StorageConfigs } from "/#/index";
|
||||
import { useEventListener } from "@vueuse/core";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { transformI18n, $t } from "/@/plugins/i18n";
|
||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
|
||||
import { storageLocal, toggleClass, hasClass } from "@pureadmin/utils";
|
||||
|
||||
import close from "/@/assets/svg/close.svg?component";
|
||||
import refresh from "/@/assets/svg/refresh.svg?component";
|
||||
import closeAll from "/@/assets/svg/close_all.svg?component";
|
||||
import closeLeft from "/@/assets/svg/close_left.svg?component";
|
||||
import closeOther from "/@/assets/svg/close_other.svg?component";
|
||||
import closeRight from "/@/assets/svg/close_right.svg?component";
|
||||
|
||||
export function useTags() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
const buttonTop = ref(0);
|
||||
const buttonLeft = ref(0);
|
||||
const translateX = ref(0);
|
||||
const visible = ref(false);
|
||||
const activeIndex = ref(-1);
|
||||
// 当前右键选中的路由信息
|
||||
const currentSelect = ref({});
|
||||
|
||||
/** 显示模式,默认灵动模式 */
|
||||
const showModel = ref(
|
||||
storageLocal.getItem<StorageConfigs>("responsive-configure")?.showModel ||
|
||||
"smart"
|
||||
);
|
||||
/** 是否隐藏标签页,默认显示 */
|
||||
const showTags =
|
||||
ref(
|
||||
storageLocal.getItem<StorageConfigs>("responsive-configure").hideTabs
|
||||
) ?? ref("false");
|
||||
const multiTags: any = computed(() => {
|
||||
return useMultiTagsStoreHook().multiTags;
|
||||
});
|
||||
|
||||
const tagsViews = reactive<Array<tagsViewsType>>([
|
||||
{
|
||||
icon: refresh,
|
||||
text: $t("buttons.hsreload"),
|
||||
divided: false,
|
||||
disabled: false,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: close,
|
||||
text: $t("buttons.hscloseCurrentTab"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeLeft,
|
||||
text: $t("buttons.hscloseLeftTabs"),
|
||||
divided: true,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeRight,
|
||||
text: $t("buttons.hscloseRightTabs"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeOther,
|
||||
text: $t("buttons.hscloseOtherTabs"),
|
||||
divided: true,
|
||||
disabled: multiTags.value.length > 2 ? false : true,
|
||||
show: true
|
||||
},
|
||||
{
|
||||
icon: closeAll,
|
||||
text: $t("buttons.hscloseAllTabs"),
|
||||
divided: false,
|
||||
disabled: multiTags.value.length > 1 ? false : true,
|
||||
show: true
|
||||
}
|
||||
]);
|
||||
|
||||
function conditionHandle(item, previous, next) {
|
||||
if (
|
||||
Object.keys(route.query).length === 0 &&
|
||||
Object.keys(route.params).length === 0
|
||||
) {
|
||||
return route.path === item.path ? previous : next;
|
||||
} else if (Object.keys(route.query).length > 0) {
|
||||
return isEqual(route.query, item.query) ? previous : next;
|
||||
} else {
|
||||
return isEqual(route.params, item.params) ? previous : next;
|
||||
}
|
||||
}
|
||||
|
||||
const iconIsActive = computed(() => {
|
||||
return (item, index) => {
|
||||
if (index === 0) return;
|
||||
return conditionHandle(item, true, false);
|
||||
};
|
||||
});
|
||||
|
||||
const linkIsActive = computed(() => {
|
||||
return item => {
|
||||
return conditionHandle(item, "is-active", "");
|
||||
};
|
||||
});
|
||||
|
||||
const scheduleIsActive = computed(() => {
|
||||
return item => {
|
||||
return conditionHandle(item, "schedule-active", "");
|
||||
};
|
||||
});
|
||||
|
||||
const getTabStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
transform: `translateX(${translateX.value}px)`
|
||||
};
|
||||
});
|
||||
|
||||
const getContextMenuStyle = computed((): CSSProperties => {
|
||||
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
|
||||
});
|
||||
|
||||
const closeMenu = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
/** 鼠标移入添加激活样式 */
|
||||
function onMouseenter(index) {
|
||||
if (index) activeIndex.value = index;
|
||||
if (unref(showModel) === "smart") {
|
||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
||||
return;
|
||||
toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
|
||||
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
|
||||
} else {
|
||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
||||
toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
|
||||
toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
|
||||
}
|
||||
}
|
||||
|
||||
/** 鼠标移出恢复默认样式 */
|
||||
function onMouseleave(index) {
|
||||
activeIndex.value = -1;
|
||||
if (unref(showModel) === "smart") {
|
||||
if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
|
||||
return;
|
||||
toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
|
||||
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
|
||||
} else {
|
||||
if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
|
||||
toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
|
||||
toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!showModel.value) {
|
||||
const configure = storageLocal.getItem<StorageConfigs>(
|
||||
"responsive-configure"
|
||||
);
|
||||
configure.showModel = "card";
|
||||
storageLocal.setItem("responsive-configure", configure);
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
() => {
|
||||
useEventListener(document, "click", closeMenu);
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
route,
|
||||
router,
|
||||
visible,
|
||||
showTags,
|
||||
instance,
|
||||
multiTags,
|
||||
showModel,
|
||||
tagsViews,
|
||||
buttonTop,
|
||||
buttonLeft,
|
||||
translateX,
|
||||
activeIndex,
|
||||
getTabStyle,
|
||||
iconIsActive,
|
||||
linkIsActive,
|
||||
currentSelect,
|
||||
scheduleIsActive,
|
||||
getContextMenuStyle,
|
||||
$t,
|
||||
closeMenu,
|
||||
onMounted,
|
||||
onMouseenter,
|
||||
onMouseleave,
|
||||
transformI18n
|
||||
};
|
||||
}
|
37
src/layout/hooks/useTranslationLang.ts
Normal file
37
src/layout/hooks/useTranslationLang.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useNav } from "./useNav";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import { watch, type Ref } from "vue";
|
||||
|
||||
export function useTranslationLang(ref?: Ref) {
|
||||
const { $storage, changeTitle, handleResize } = useNav();
|
||||
const { locale, t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
function translationCh() {
|
||||
$storage.locale = { locale: "zh" };
|
||||
locale.value = "zh";
|
||||
ref && handleResize(ref.value);
|
||||
}
|
||||
|
||||
function translationEn() {
|
||||
$storage.locale = { locale: "en" };
|
||||
locale.value = "en";
|
||||
ref && handleResize(ref.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
changeTitle(route.meta);
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
t,
|
||||
route,
|
||||
locale,
|
||||
translationCh,
|
||||
translationEn
|
||||
};
|
||||
}
|
@@ -1,20 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
h,
|
||||
reactive,
|
||||
computed,
|
||||
onMounted,
|
||||
defineComponent,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import { setType } from "./types";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { routerArrays } from "./types";
|
||||
import { emitter } from "/@/utils/mitt";
|
||||
import { useLayout } from "./hooks/useLayout";
|
||||
import { useAppStoreHook } from "/@/store/modules/app";
|
||||
import { deviceDetection } from "/@/utils/deviceDetection";
|
||||
import { useMultiTagsStore } from "/@/store/modules/multiTags";
|
||||
import { useSettingStoreHook } from "/@/store/modules/settings";
|
||||
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
|
||||
import { h, reactive, computed, onMounted, defineComponent } from "vue";
|
||||
|
||||
import backTop from "/@/assets/svg/back_top.svg?component";
|
||||
import fullScreen from "/@/assets/svg/full_screen.svg?component";
|
||||
@@ -27,51 +18,11 @@ import setting from "./components/setting/index.vue";
|
||||
import Vertical from "./components/sidebar/vertical.vue";
|
||||
import Horizontal from "./components/sidebar/horizontal.vue";
|
||||
|
||||
const { isDark } = useDark();
|
||||
const { layout } = useLayout();
|
||||
const isMobile = deviceDetection();
|
||||
const pureSetting = useSettingStoreHook();
|
||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
|
||||
|
||||
// 清空缓存后从serverConfig.json读取默认配置并赋值到storage中
|
||||
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 { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||
|
||||
const set: setType = reactive({
|
||||
sidebar: computed(() => {
|
||||
@@ -96,18 +47,18 @@ const set: setType = reactive({
|
||||
}),
|
||||
|
||||
hideTabs: computed(() => {
|
||||
return instance.$storage?.configure.hideTabs;
|
||||
return $storage?.configure.hideTabs;
|
||||
})
|
||||
});
|
||||
|
||||
function setTheme(layoutModel: string) {
|
||||
window.document.body.setAttribute("layout", layoutModel);
|
||||
instance.$storage.layout = {
|
||||
$storage.layout = {
|
||||
layout: `${layoutModel}`,
|
||||
theme: instance.$storage.layout?.theme,
|
||||
darkMode: instance.$storage.layout?.darkMode,
|
||||
sidebarStatus: instance.$storage.layout?.sidebarStatus,
|
||||
epThemeColor: instance.$storage.layout?.epThemeColor
|
||||
theme: $storage.layout?.theme,
|
||||
darkMode: $storage.layout?.darkMode,
|
||||
sidebarStatus: $storage.layout?.sidebarStatus,
|
||||
epThemeColor: $storage.layout?.epThemeColor
|
||||
};
|
||||
}
|
||||
|
||||
@@ -123,7 +74,7 @@ let isAutoCloseSidebar = true;
|
||||
emitter.on("resize", ({ detail }) => {
|
||||
if (isMobile) return;
|
||||
let { width } = detail;
|
||||
width <= 670 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
|
||||
width <= 760 ? setTheme("vertical") : setTheme(useAppStoreHook().layout);
|
||||
/** width app-wrapper类容器宽度
|
||||
* 0 < width <= 760 隐藏侧边栏
|
||||
* 760 < width <= 990 折叠侧边栏
|
||||
@@ -138,7 +89,7 @@ emitter.on("resize", ({ detail }) => {
|
||||
isAutoCloseSidebar = false;
|
||||
}
|
||||
} else if (width > 990) {
|
||||
if (!set.sidebar.isClickHamburger) {
|
||||
if (!set.sidebar.isClickCollapse) {
|
||||
toggle("desktop", true);
|
||||
isAutoCloseSidebar = true;
|
||||
}
|
||||
@@ -165,7 +116,9 @@ const layoutHeader = defineComponent({
|
||||
class: { "fixed-header": set.fixedHeader },
|
||||
style: [
|
||||
set.hideTabs && layout.value.includes("horizontal")
|
||||
? "box-shadow: 0 1px 4px rgb(0 21 41 / 8%);"
|
||||
? isDark.value
|
||||
? "box-shadow: 0 1px 4px #0d0d0d"
|
||||
: "box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08)"
|
||||
: ""
|
||||
]
|
||||
},
|
||||
@@ -185,10 +138,14 @@ const layoutHeader = defineComponent({
|
||||
default: () => [
|
||||
h(
|
||||
"span",
|
||||
{ onClick: onFullScreen },
|
||||
{
|
||||
onClick: onFullScreen
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
!pureSetting.hiddenSideBar ? h(fullScreen) : h(exitScreen)
|
||||
!pureSetting.hiddenSideBar
|
||||
? h(fullScreen, { class: "dark:color-white" })
|
||||
: h(exitScreen, { class: "dark:color-white" })
|
||||
]
|
||||
}
|
||||
)
|
||||
@@ -234,7 +191,8 @@ const layoutHeader = defineComponent({
|
||||
<el-backtop
|
||||
title="回到顶部"
|
||||
target=".main-container .el-scrollbar__wrap"
|
||||
><backTop />
|
||||
>
|
||||
<backTop />
|
||||
</el-backtop>
|
||||
<layout-header />
|
||||
<!-- 主体内容 -->
|
||||
|
@@ -2,6 +2,10 @@
|
||||
import { unref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "Redirect"
|
||||
});
|
||||
|
||||
const { currentRoute, replace } = useRouter();
|
||||
|
||||
const { params, query } = unref(currentRoute);
|
||||
|
@@ -1,84 +0,0 @@
|
||||
/* 动态改变element-plus主题色 */
|
||||
import rgbHex from "rgb-hex";
|
||||
import epCss from "./element.scss";
|
||||
import { TinyColor } from "@ctrl/tinycolor";
|
||||
import { convert } from "css-color-function";
|
||||
|
||||
// 色值表
|
||||
const formula = {
|
||||
"shade-1": "color(primary shade(10%))",
|
||||
"light-1": "color(primary tint(10%))",
|
||||
"light-2": "color(primary tint(20%))",
|
||||
"light-3": "color(primary tint(30%))",
|
||||
"light-4": "color(primary tint(40%))",
|
||||
"light-5": "color(primary tint(50%))",
|
||||
"light-6": "color(primary tint(60%))",
|
||||
"light-7": "color(primary tint(70%))",
|
||||
"light-8": "color(primary tint(80%))",
|
||||
"light-9": "color(primary tint(90%))"
|
||||
};
|
||||
|
||||
// 把生成的样式表写入到style中
|
||||
export const writeNewStyle = (newStyle: string): void => {
|
||||
const style = window.document.createElement("style");
|
||||
style.innerText = newStyle;
|
||||
window.document.head.appendChild(style);
|
||||
};
|
||||
|
||||
// 根据主题色,生成最新的样式表
|
||||
export const createNewStyle = (
|
||||
primaryStyle: Record<string, any>
|
||||
): Record<string, any> => {
|
||||
// 根据主色生成色值表
|
||||
const colors = createColors(primaryStyle);
|
||||
// 在当前ep的默认样式表中标记需要替换的色值
|
||||
let cssText = getStyleTemplate(epCss);
|
||||
// 遍历生成的色值表,在 默认样式表 进行全局替换
|
||||
Object.keys(colors).forEach(key => {
|
||||
cssText = cssText.replace(
|
||||
new RegExp("(:|\\s+)" + key, "g"),
|
||||
"$1" + colors[key]
|
||||
);
|
||||
});
|
||||
return cssText;
|
||||
};
|
||||
|
||||
export const createColors = (
|
||||
primary: Record<string, any>
|
||||
): Record<string, any> => {
|
||||
if (!primary) return;
|
||||
const colors = {
|
||||
primary
|
||||
};
|
||||
Object.keys(formula).forEach(key => {
|
||||
const value = formula[key].replace(/primary/, primary);
|
||||
colors[key] = "#" + rgbHex(convert(value));
|
||||
});
|
||||
return colors;
|
||||
};
|
||||
|
||||
const getStyleTemplate = (data: Record<string, any>): Record<string, any> => {
|
||||
const colorMap = {
|
||||
"#3a8ee6": "shade-1",
|
||||
"#409eff": "primary",
|
||||
"#53a8ff": "light-1",
|
||||
"#66b1ff": "light-2",
|
||||
"#79bbff": "light-3",
|
||||
"#8cc5ff": "light-4",
|
||||
"#a0cfff": "light-5",
|
||||
"#b3d8ff": "light-6",
|
||||
"#c6e2ff": "light-7",
|
||||
"#d9ecff": "light-8",
|
||||
"#ecf5ff": "light-9"
|
||||
};
|
||||
Object.keys(colorMap).forEach(key => {
|
||||
const value = colorMap[key];
|
||||
data = data.replace(new RegExp(key, "ig"), value);
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
// 自动计算hover和active颜色 https://element-plus.gitee.io/zh-CN/component/button.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E9%A2%9C%E8%89%B2-%E6%B5%8B%E8%AF%95%E7%89%88
|
||||
export const shadeBgColor = (color: string): string => {
|
||||
return new TinyColor(color).shade(10).toString();
|
||||
};
|
@@ -1,2 +0,0 @@
|
||||
/* 通过scss模块本地导入element-plus的全局样式文件,解决vite2.7.13版本后使用 import epCss from "element-plus/dist/index.css",打包后加载不到样式的问题 */
|
||||
@import "element-plus/dist/index.css";
|
@@ -1,6 +1,20 @@
|
||||
/**
|
||||
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
|
||||
*/
|
||||
|
||||
import { EpThemeColor } from "../../../public/serverConfig.json";
|
||||
|
||||
type MultipleScopeVarsItem = {
|
||||
scopeName: string;
|
||||
varsContent: string;
|
||||
};
|
||||
|
||||
/** 将vxe默认主题色和ep默认主题色保持一致 */
|
||||
const vxeColor = EpThemeColor;
|
||||
/** 预设主题色 */
|
||||
const themeColors = {
|
||||
default: {
|
||||
color: "#409EFF",
|
||||
vxeColor,
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#001529",
|
||||
menuHover: "#4091f7",
|
||||
@@ -13,7 +27,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#4091f7"
|
||||
},
|
||||
light: {
|
||||
color: "#409EFF",
|
||||
vxeColor,
|
||||
subMenuActiveText: "#409eff",
|
||||
menuBg: "#fff",
|
||||
menuHover: "#e0ebf6",
|
||||
@@ -26,7 +40,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#4091f7"
|
||||
},
|
||||
dusk: {
|
||||
color: "#f5222d",
|
||||
vxeColor: "#f5222d",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#2a0608",
|
||||
menuHover: "#e13c39",
|
||||
@@ -39,7 +53,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#e13c39"
|
||||
},
|
||||
volcano: {
|
||||
color: "#fa541c",
|
||||
vxeColor: "#fa541c",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#2b0e05",
|
||||
menuHover: "#e85f33",
|
||||
@@ -52,7 +66,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#e85f33"
|
||||
},
|
||||
yellow: {
|
||||
color: "#fadb14",
|
||||
vxeColor: "#fadb14",
|
||||
subMenuActiveText: "#d25f00",
|
||||
menuBg: "#2b2503",
|
||||
menuHover: "#f6da4d",
|
||||
@@ -65,7 +79,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#f6da4d"
|
||||
},
|
||||
mingQing: {
|
||||
color: "#13c2c2",
|
||||
vxeColor: "#13c2c2",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#032121",
|
||||
menuHover: "#59bfc1",
|
||||
@@ -78,7 +92,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#59bfc1"
|
||||
},
|
||||
auroraGreen: {
|
||||
color: "#52c41a",
|
||||
vxeColor: "#52c41a",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#0b1e15",
|
||||
menuHover: "#60ac80",
|
||||
@@ -91,7 +105,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#60ac80"
|
||||
},
|
||||
pink: {
|
||||
color: "#eb2f96",
|
||||
vxeColor: "#eb2f96",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#28081a",
|
||||
menuHover: "#d84493",
|
||||
@@ -104,7 +118,7 @@ const themeColors = {
|
||||
menuActiveBefore: "#d84493"
|
||||
},
|
||||
saucePurple: {
|
||||
color: "#722ed1",
|
||||
vxeColor: "#722ed1",
|
||||
subMenuActiveText: "#fff",
|
||||
menuBg: "#130824",
|
||||
menuHover: "#693ac9",
|
||||
@@ -118,19 +132,28 @@ const themeColors = {
|
||||
}
|
||||
};
|
||||
|
||||
type MultipleScopeVarsItem = {
|
||||
scopeName: string;
|
||||
path: string;
|
||||
varsContent: string;
|
||||
};
|
||||
|
||||
export function genScssMultipleScopeVars(): MultipleScopeVarsItem[] {
|
||||
/**
|
||||
* @description 将预设主题色处理成主题插件所需格式
|
||||
*/
|
||||
export const genScssMultipleScopeVars = (): MultipleScopeVarsItem[] => {
|
||||
const result = [] as MultipleScopeVarsItem[];
|
||||
Object.keys(themeColors).forEach(key => {
|
||||
result.push({
|
||||
scopeName: `layout-theme-${key}`,
|
||||
varsContent: `$primary-color: ${themeColors[key].color} !default;$vxe-primary-color: $primary-color;$subMenuActiveText: ${themeColors[key].subMenuActiveText} !default;$menuBg: ${themeColors[key].menuBg} !default;$menuHover: ${themeColors[key].menuHover} !default;$subMenuBg: ${themeColors[key].subMenuBg} !default;$subMenuActiveBg: ${themeColors[key].subMenuActiveBg} !default;$navTextColor: ${themeColors[key].navTextColor} !default;$menuText: ${themeColors[key].menuText} !default;$sidebarLogo: ${themeColors[key].sidebarLogo} !default;$menuTitleHover: ${themeColors[key].menuTitleHover} !default;$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;`
|
||||
varsContent: `
|
||||
$vxe-primary-color: ${themeColors[key].vxeColor} !default;
|
||||
$subMenuActiveText: ${themeColors[key].subMenuActiveText} !default;
|
||||
$menuBg: ${themeColors[key].menuBg} !default;
|
||||
$menuHover: ${themeColors[key].menuHover} !default;
|
||||
$subMenuBg: ${themeColors[key].subMenuBg} !default;
|
||||
$subMenuActiveBg: ${themeColors[key].subMenuActiveBg} !default;
|
||||
$navTextColor: ${themeColors[key].navTextColor} !default;
|
||||
$menuText: ${themeColors[key].menuText} !default;
|
||||
$sidebarLogo: ${themeColors[key].sidebarLogo} !default;
|
||||
$menuTitleHover: ${themeColors[key].menuTitleHover} !default;
|
||||
$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;
|
||||
`
|
||||
} as MultipleScopeVarsItem);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
@@ -12,7 +12,6 @@ export const routerArrays: Array<RouteConfigs> = [
|
||||
|
||||
export type routeMetaType = {
|
||||
title?: string;
|
||||
i18n?: boolean;
|
||||
icon?: string;
|
||||
showLink?: boolean;
|
||||
savedPosition?: boolean;
|
||||
@@ -23,6 +22,7 @@ export type RouteConfigs = {
|
||||
path?: string;
|
||||
parentPath?: string;
|
||||
query?: object;
|
||||
params?: object;
|
||||
meta?: routeMetaType;
|
||||
children?: RouteConfigs[];
|
||||
name?: string;
|
||||
@@ -44,7 +44,7 @@ export interface setType {
|
||||
sidebar: {
|
||||
opened: boolean;
|
||||
withoutAnimation: boolean;
|
||||
isClickHamburger: boolean;
|
||||
isClickCollapse: boolean;
|
||||
};
|
||||
device: string;
|
||||
fixedHeader: boolean;
|
||||
@@ -65,7 +65,6 @@ export type childrenType = {
|
||||
meta?: {
|
||||
icon?: string;
|
||||
title?: string;
|
||||
i18n?: boolean;
|
||||
showParent?: boolean;
|
||||
extraIcon?: {
|
||||
svg?: boolean;
|
||||
|
Reference in New Issue
Block a user