release: update 4.1.0
This commit is contained in:
@@ -130,16 +130,16 @@ const transitionMain = defineComponent({
|
||||
|
||||
<style scoped>
|
||||
.app-main {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.app-main-nofixed-header {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
|
@@ -82,28 +82,28 @@ const {
|
||||
overflow: hidden;
|
||||
|
||||
.hamburger-container {
|
||||
line-height: 48px;
|
||||
height: 100%;
|
||||
float: left;
|
||||
height: 100%;
|
||||
line-height: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vertical-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 280px;
|
||||
height: 48px;
|
||||
align-items: center;
|
||||
color: #000000d9;
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-dropdown-link {
|
||||
height: 48px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
cursor: pointer;
|
||||
height: 48px;
|
||||
padding: 10px;
|
||||
color: #000000d9;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
@@ -127,9 +127,9 @@ const {
|
||||
max-width: 120px;
|
||||
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
min-width: 100%;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -46,8 +46,8 @@ notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 48px;
|
||||
width: 60px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
|
||||
.header-notice-icon {
|
||||
@@ -59,7 +59,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
width: 330px;
|
||||
|
||||
.noticeList-container {
|
||||
padding: 15px 24px 0 24px;
|
||||
padding: 15px 24px 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__header) {
|
||||
@@ -71,7 +71,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-wrap) {
|
||||
padding: 0 36px 0 36px;
|
||||
padding: 0 36px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -118,6 +118,7 @@ function hoverDescription(event, description) {
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
|
||||
// border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.notice-container-avatar {
|
||||
@@ -127,15 +128,15 @@ function hoverDescription(event, description) {
|
||||
|
||||
.notice-container-text {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
|
||||
.notice-text-title {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5715;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -143,8 +144,8 @@ function hoverDescription(event, description) {
|
||||
flex: 1;
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.notice-title-extra {
|
||||
@@ -162,8 +163,8 @@ function hoverDescription(event, description) {
|
||||
|
||||
.notice-text-description {
|
||||
display: -webkit-box;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
@@ -60,9 +60,9 @@ emitter.on("openPanel", () => {
|
||||
|
||||
<style>
|
||||
.showright-panel {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -71,23 +71,23 @@ emitter.on("openPanel", () => {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
background: rgb(0 0 0 / 20%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
width: 100%;
|
||||
max-width: 315px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.05);
|
||||
z-index: 40000;
|
||||
width: 100%;
|
||||
max-width: 315px;
|
||||
height: 100vh;
|
||||
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
|
||||
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transform: translate(100%);
|
||||
z-index: 40000;
|
||||
}
|
||||
|
||||
.show {
|
||||
@@ -95,9 +95,9 @@ emitter.on("openPanel", () => {
|
||||
|
||||
.right-panel-background {
|
||||
z-index: 20000;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@@ -106,20 +106,20 @@ emitter.on("openPanel", () => {
|
||||
}
|
||||
|
||||
.handle-button {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: -48px;
|
||||
z-index: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
z-index: 0;
|
||||
line-height: 48px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
top: 45%;
|
||||
background: rgb(24, 144, 255);
|
||||
background: rgb(24 144 255);
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
@@ -128,24 +128,24 @@ emitter.on("openPanel", () => {
|
||||
}
|
||||
|
||||
.right-panel-items {
|
||||
margin-top: 60px;
|
||||
height: calc(100vh - 60px);
|
||||
margin-top: 60px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.project-configuration {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
position: fixed;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
top: 15px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-divider--horizontal) {
|
||||
width: 90%;
|
||||
margin: 20px auto 0 auto;
|
||||
margin: 20px auto 0;
|
||||
}
|
||||
</style>
|
||||
|
@@ -81,11 +81,11 @@ function handleTo() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 56px;
|
||||
margin-top: 8px;
|
||||
padding: 14px;
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
cursor: pointer;
|
||||
border: 0.1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&-title {
|
||||
|
@@ -418,35 +418,35 @@ onBeforeMount(() => {
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.pure-datatheme {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
padding-top: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pure-theme {
|
||||
margin-top: 25px;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin-top: 25px;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
width: 18%;
|
||||
height: 45px;
|
||||
background: #f0f2f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background: #f0f2f5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%);
|
||||
|
||||
@@ -459,13 +459,13 @@ onBeforeMount(() => {
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 70%;
|
||||
height: 30%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 70%;
|
||||
height: 30%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 1px #888;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,13 +491,13 @@ onBeforeMount(() => {
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 30%;
|
||||
height: 70%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 30%;
|
||||
height: 70%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 1px #888;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,11 +505,11 @@ onBeforeMount(() => {
|
||||
}
|
||||
|
||||
.theme-color {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
@@ -519,8 +519,8 @@ onBeforeMount(() => {
|
||||
margin-right: 8px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
|
||||
&:nth-child(2) {
|
||||
border: 1px solid #ddd;
|
||||
|
@@ -11,12 +11,6 @@ const router = useRouter();
|
||||
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();
|
||||
};
|
||||
|
||||
const getBreadcrumb = (): void => {
|
||||
// 当前路由信息
|
||||
let currentRoute;
|
||||
@@ -34,28 +28,24 @@ const getBreadcrumb = (): void => {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
currentRoute = findRouteByPath(router.currentRoute.value.path, multiTags);
|
||||
currentRoute = findRouteByPath(router.currentRoute.value.path, routes);
|
||||
}
|
||||
|
||||
// 当前路由的父级路径组成的数组
|
||||
const parentRoutes = getParentPaths(router.currentRoute.value.path, routes);
|
||||
const parentRoutes = getParentPaths(
|
||||
router.currentRoute.value.name as string,
|
||||
routes,
|
||||
"name"
|
||||
);
|
||||
// 存放组成面包屑的数组
|
||||
let matched = [];
|
||||
const matched = [];
|
||||
|
||||
// 获取每个父级路径对应的路由信息
|
||||
parentRoutes.forEach(path => {
|
||||
if (path !== "/") matched.push(findRouteByPath(path, routes));
|
||||
});
|
||||
|
||||
if (currentRoute?.path !== "/welcome") matched.push(currentRoute);
|
||||
|
||||
if (!isDashboard(matched[0])) {
|
||||
matched = [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: { title: "首页" }
|
||||
} as unknown as RouteLocationMatched
|
||||
].concat(matched);
|
||||
}
|
||||
matched.push(currentRoute);
|
||||
|
||||
matched.forEach((item, index) => {
|
||||
if (currentRoute?.query || currentRoute?.params) return;
|
||||
@@ -90,6 +80,9 @@ watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
getBreadcrumb();
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
@@ -15,7 +15,7 @@ const {
|
||||
title,
|
||||
routers,
|
||||
logout,
|
||||
backHome,
|
||||
backTopMenu,
|
||||
onPanel,
|
||||
menuSelect,
|
||||
username,
|
||||
@@ -39,7 +39,7 @@ watch(
|
||||
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
|
||||
class="horizontal-header"
|
||||
>
|
||||
<div class="horizontal-header-left" @click="backHome">
|
||||
<div class="horizontal-header-left" @click="backTopMenu">
|
||||
<img src="/logo.svg" alt="logo" />
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
@@ -104,9 +104,9 @@ watch(
|
||||
max-width: 120px;
|
||||
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
min-width: 100%;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { getTopMenu } from "@/router/utils";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -6,6 +7,7 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const { title } = useNav();
|
||||
const topPath = getTopMenu().path;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -16,7 +18,7 @@ const { title } = useNav();
|
||||
key="props.collapse"
|
||||
:title="title"
|
||||
class="sidebar-logo-link"
|
||||
to="/"
|
||||
:to="topPath"
|
||||
>
|
||||
<img src="/logo.svg" alt="logo" />
|
||||
<span class="sidebar-title">{{ title }}</span>
|
||||
@@ -26,7 +28,7 @@ const { title } = useNav();
|
||||
key="expand"
|
||||
:title="title"
|
||||
class="sidebar-logo-link"
|
||||
to="/"
|
||||
:to="topPath"
|
||||
>
|
||||
<img src="/logo.svg" alt="logo" />
|
||||
<span class="sidebar-title">{{ title }}</span>
|
||||
@@ -37,33 +39,33 @@ const { title } = useNav();
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sidebar-logo-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.sidebar-logo-link {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
img {
|
||||
height: 32px;
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 2px 0 0 12px;
|
||||
color: $subMenuActiveText;
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
margin: 2px 0 0 12px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 32px;
|
||||
color: $subMenuActiveText;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -135,9 +135,9 @@ watch(
|
||||
max-width: 120px;
|
||||
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
min-width: 100%;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import path from "path";
|
||||
import { getConfig } from "@/config";
|
||||
import { menuType } from "../../types";
|
||||
import extraIcon from "./extraIcon.vue";
|
||||
import { childrenType } from "../../types";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { ref, toRaw, PropType, nextTick, computed, CSSProperties } from "vue";
|
||||
@@ -16,7 +16,7 @@ const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object as PropType<childrenType>
|
||||
type: Object as PropType<menuType>
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
@@ -111,7 +111,7 @@ const expandCloseIcon = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const onlyOneChild: childrenType = ref(null);
|
||||
const onlyOneChild: menuType = ref(null);
|
||||
// 存放菜单是否存在showTooltip属性标识
|
||||
const hoverMenuMap = new WeakMap();
|
||||
// 存储菜单文本dom元素
|
||||
@@ -148,10 +148,7 @@ function overflowSlice(text, item?: any) {
|
||||
return newText;
|
||||
}
|
||||
|
||||
function hasOneShowingChild(
|
||||
children: childrenType[] = [],
|
||||
parent: childrenType
|
||||
) {
|
||||
function hasOneShowingChild(children: menuType[] = [], parent: menuType) {
|
||||
const showingChildren = children.filter((item: any) => {
|
||||
onlyOneChild.value = item;
|
||||
return true;
|
||||
|
@@ -6,14 +6,16 @@ import SidebarItem from "./sidebarItem.vue";
|
||||
import leftCollapse from "./leftCollapse.vue";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import { storageLocal } from "@pureadmin/utils";
|
||||
import { responsiveStorageNameSpace } from "@/config";
|
||||
import { ref, computed, watch, onBeforeMount } from "vue";
|
||||
import { findRouteByPath, getParentPaths } from "@/router/utils";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
|
||||
const route = useRoute();
|
||||
const showLogo = ref(
|
||||
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showLogo ??
|
||||
true
|
||||
storageLocal().getItem<StorageConfigs>(
|
||||
`${responsiveStorageNameSpace()}configure`
|
||||
)?.showLogo ?? true
|
||||
);
|
||||
|
||||
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
|
||||
@@ -27,7 +29,12 @@ const menuData = computed(() => {
|
||||
: usePermissionStoreHook().wholeMenus;
|
||||
});
|
||||
|
||||
const loading = computed(() =>
|
||||
pureApp.layout === "mix" ? false : menuData.value.length === 0 ? true : false
|
||||
);
|
||||
|
||||
function getSubMenuData(path: string) {
|
||||
subMenuData.value = [];
|
||||
// path的上级路由组成的数组
|
||||
const parentPathArr = getParentPaths(
|
||||
path,
|
||||
@@ -53,6 +60,7 @@ onBeforeMount(() => {
|
||||
watch(
|
||||
() => [route.path, usePermissionStoreHook().wholeMenus],
|
||||
() => {
|
||||
if (route.path.includes("/redirect")) return;
|
||||
getSubMenuData(route.path);
|
||||
menuSelect(route.path, routers);
|
||||
}
|
||||
@@ -61,7 +69,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-loading="menuData.length === 0"
|
||||
v-loading="loading"
|
||||
:class="['sidebar-container', showLogo ? 'has-logo' : '']"
|
||||
>
|
||||
<Logo v-if="showLogo" :collapse="isCollapse" />
|
||||
|
@@ -1,4 +1,4 @@
|
||||
@keyframes scheduleInWidth {
|
||||
@keyframes schedule-in-width {
|
||||
from {
|
||||
width: 0;
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scheduleOutWidth {
|
||||
@keyframes schedule-out-width {
|
||||
from {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -39,41 +39,41 @@
|
||||
}
|
||||
|
||||
.tags-view {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
background: #fff;
|
||||
position: relative;
|
||||
box-shadow: 0 0 1px #888;
|
||||
|
||||
.scroll-item {
|
||||
border-radius: 3px 3px 0 0;
|
||||
padding: 0 6px;
|
||||
box-shadow: 0 0 1px #888;
|
||||
position: relative;
|
||||
margin-right: 4px;
|
||||
height: 28px;
|
||||
display: inline-block;
|
||||
height: 28px;
|
||||
padding: 0 6px;
|
||||
margin-right: 4px;
|
||||
line-height: 28px;
|
||||
transition: all 0.4s;
|
||||
cursor: pointer;
|
||||
border-radius: 3px 3px 0 0;
|
||||
box-shadow: 0 0 1px #888;
|
||||
transition: all 0.4s;
|
||||
|
||||
.el-icon-close {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
font-size: 10px;
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: font-size 0.2s;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
&:hover {
|
||||
border-radius: 50%;
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
background: #b4bccc;
|
||||
font-size: 13px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,24 +91,24 @@
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--el-text-color-primary);
|
||||
padding: 0 4px;
|
||||
color: var(--el-text-color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 5px 0;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 5px 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.tab {
|
||||
position: relative;
|
||||
float: left;
|
||||
list-style: none;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
list-style: none;
|
||||
transition: transform 0.5s ease-in-out;
|
||||
|
||||
.scroll-item {
|
||||
@@ -123,29 +123,28 @@
|
||||
|
||||
/* 右键菜单 */
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
color: var(--el-text-color-primary);
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
color: var(--el-text-color-primary);
|
||||
white-space: nowrap;
|
||||
list-style-type: none;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
outline: 0;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 7px 12px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 7px 12px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
// background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
@@ -159,11 +158,11 @@
|
||||
|
||||
.el-dropdown-menu {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
@@ -184,6 +183,7 @@
|
||||
:deep(.el-dropdown-menu__item--divided) {
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item--divided::before {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -193,7 +193,6 @@
|
||||
}
|
||||
|
||||
.scroll-item.is-active {
|
||||
// background-color: var(--el-color-primary-light-9);
|
||||
position: relative;
|
||||
color: #fff;
|
||||
|
||||
@@ -213,16 +212,16 @@
|
||||
.arrow-left,
|
||||
.arrow-right,
|
||||
.arrow-down {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 38px;
|
||||
color: var(--el-text-color-primary);
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
}
|
||||
@@ -236,8 +235,8 @@
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
box-shadow: -5px 0 5px -6px #ccc;
|
||||
border-right: 0.5px solid #ccc;
|
||||
box-shadow: -5px 0 5px -6px #ccc;
|
||||
|
||||
&:hover {
|
||||
cursor: e-resize;
|
||||
@@ -255,8 +254,8 @@
|
||||
|
||||
/* 卡片模式下鼠标移出隐藏蓝色边框 */
|
||||
.card-out {
|
||||
border: none;
|
||||
color: #666;
|
||||
border: none;
|
||||
|
||||
a {
|
||||
color: #666;
|
||||
@@ -265,32 +264,32 @@
|
||||
|
||||
/* 灵动模式 */
|
||||
.schedule-active {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
|
||||
/* 灵动模式下鼠标移入显示蓝色进度条 */
|
||||
.schedule-in {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: var(--el-color-primary);
|
||||
animation: scheduleInWidth 200ms ease-in;
|
||||
animation: schedule-in-width 200ms ease-in;
|
||||
}
|
||||
|
||||
/* 灵动模式下鼠标移出隐藏蓝色进度条 */
|
||||
.schedule-out {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: var(--el-color-primary);
|
||||
animation: scheduleOutWidth 200ms ease-in;
|
||||
animation: schedule-out-width 200ms ease-in;
|
||||
}
|
||||
|
@@ -4,10 +4,10 @@ import { RouteConfigs } from "../../types";
|
||||
import { useTags } from "../../hooks/useTag";
|
||||
import { routerArrays } from "@/layout/types";
|
||||
import { isEqual, isAllEmpty } from "@pureadmin/utils";
|
||||
import { handleAliveRoute, getTopMenu } from "@/router/utils";
|
||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||
import { ref, watch, unref, nextTick, onBeforeMount } from "vue";
|
||||
import { handleAliveRoute, delAliveRoutes } from "@/router/utils";
|
||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||
import { ref, watch, unref, toRaw, nextTick, onBeforeMount } from "vue";
|
||||
import { useResizeObserver, useDebounceFn, useFullscreen } from "@vueuse/core";
|
||||
|
||||
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
||||
@@ -48,6 +48,8 @@ const tabDom = ref();
|
||||
const containerDom = ref();
|
||||
const scrollbarDom = ref();
|
||||
const isShowArrow = ref(false);
|
||||
const topPath = getTopMenu().path;
|
||||
const { VITE_HIDE_HOME } = import.meta.env;
|
||||
const { isFullscreen, toggle } = useFullscreen();
|
||||
|
||||
const dynamicTagView = () => {
|
||||
@@ -163,13 +165,12 @@ function onFresh() {
|
||||
const { fullPath, query } = unref(route);
|
||||
router.replace({
|
||||
path: "/redirect" + fullPath,
|
||||
query: query
|
||||
query
|
||||
});
|
||||
handleAliveRoute(route as toRouteType, "refresh");
|
||||
}
|
||||
|
||||
function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
// 存放被删除的缓存路由
|
||||
let delAliveRouteList = [];
|
||||
const valueIndex: number = multiTags.value.findIndex((item: any) => {
|
||||
if (item.query) {
|
||||
if (item.path === obj.path) {
|
||||
@@ -190,9 +191,12 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
other?: boolean
|
||||
): void => {
|
||||
if (other) {
|
||||
useMultiTagsStoreHook().handleTags("equal", [routerArrays[0], obj]);
|
||||
useMultiTagsStoreHook().handleTags("equal", [
|
||||
VITE_HIDE_HOME === "false" ? routerArrays[0] : toRaw(getTopMenu()),
|
||||
obj
|
||||
]);
|
||||
} else {
|
||||
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", {
|
||||
useMultiTagsStoreHook().handleTags("splice", "", {
|
||||
startIndex,
|
||||
length
|
||||
}) as any;
|
||||
@@ -212,10 +216,6 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
}
|
||||
const newRoute = useMultiTagsStoreHook().handleTags("slice");
|
||||
if (current === route.path) {
|
||||
// 删除缓存路由
|
||||
tag
|
||||
? delAliveRoutes(delAliveRouteList)
|
||||
: handleAliveRoute(route.matched, "delete");
|
||||
// 如果删除当前激活tag就自动切换到最后一个tag
|
||||
if (tag === "left") return;
|
||||
if (newRoute[0]?.query) {
|
||||
@@ -226,8 +226,6 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
router.push({ path: newRoute[0].path });
|
||||
}
|
||||
} else {
|
||||
// 删除缓存路由
|
||||
tag ? delAliveRoutes(delAliveRouteList) : delAliveRoutes([obj]);
|
||||
if (!multiTags.value.length) return;
|
||||
if (multiTags.value.some(item => item.path === route.path)) return;
|
||||
if (newRoute[0]?.query) {
|
||||
@@ -242,6 +240,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
||||
|
||||
function deleteMenu(item, tag?: string) {
|
||||
deleteDynamicTag(item, item.path, tag);
|
||||
handleAliveRoute(route as toRouteType);
|
||||
}
|
||||
|
||||
function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||
@@ -288,7 +287,8 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
||||
startIndex: 1,
|
||||
length: multiTags.value.length
|
||||
});
|
||||
router.push("/welcome");
|
||||
router.push(topPath);
|
||||
handleAliveRoute(route as toRouteType);
|
||||
break;
|
||||
case 6:
|
||||
// 整体页面全屏
|
||||
@@ -344,7 +344,7 @@ function disabledMenus(value: boolean) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
||||
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
||||
function showMenuModel(
|
||||
currentPath: string,
|
||||
query: object = {},
|
||||
@@ -366,11 +366,11 @@ function showMenuModel(
|
||||
}
|
||||
|
||||
/**
|
||||
* currentIndex为1时,左侧的菜单是首页,则不显示关闭左侧标签页
|
||||
* currentIndex为1时,左侧的菜单顶级菜单,则不显示关闭左侧标签页
|
||||
* 如果currentIndex等于routeLength-1,右侧没有菜单,则不显示关闭右侧标签页
|
||||
*/
|
||||
if (currentIndex === 1 && routeLength !== 2) {
|
||||
// 左侧的菜单是首页,右侧存在别的菜单
|
||||
// 左侧的菜单是顶级菜单,右侧存在别的菜单
|
||||
tagsViews[2].show = false;
|
||||
Array.of(1, 3, 4, 5).forEach(v => {
|
||||
tagsViews[v].disabled = false;
|
||||
@@ -378,7 +378,7 @@ function showMenuModel(
|
||||
tagsViews[2].disabled = true;
|
||||
} else if (currentIndex === 1 && routeLength === 2) {
|
||||
disabledMenus(false);
|
||||
// 左侧的菜单是首页,右侧不存在别的菜单
|
||||
// 左侧的菜单是顶级菜单,右侧不存在别的菜单
|
||||
Array.of(2, 3, 4).forEach(v => {
|
||||
tagsViews[v].show = false;
|
||||
tagsViews[v].disabled = true;
|
||||
@@ -390,8 +390,8 @@ function showMenuModel(
|
||||
tagsViews[v].disabled = false;
|
||||
});
|
||||
tagsViews[3].disabled = true;
|
||||
} else if (currentIndex === 0 || currentPath === "/redirect/welcome") {
|
||||
// 当前路由为首页
|
||||
} else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
|
||||
// 当前路由为顶级菜单
|
||||
disabledMenus(true);
|
||||
} else {
|
||||
disabledMenus(false);
|
||||
@@ -400,8 +400,8 @@ function showMenuModel(
|
||||
|
||||
function openMenu(tag, e) {
|
||||
closeMenu();
|
||||
if (tag.path === "/welcome") {
|
||||
// 右键菜单为首页,只显示刷新
|
||||
if (tag.path === topPath) {
|
||||
// 右键菜单为顶级菜单,只显示刷新
|
||||
showMenus(false);
|
||||
tagsViews[0].show = true;
|
||||
} else if (route.path !== tag.path && route.name !== tag.name) {
|
||||
@@ -523,7 +523,7 @@ onMounted(() => {
|
||||
:class="[
|
||||
'scroll-item is-closable',
|
||||
linkIsActive(item),
|
||||
$route.path === item.path && showModel === 'card'
|
||||
route.path === item.path && showModel === 'card'
|
||||
? 'card-active'
|
||||
: ''
|
||||
]"
|
||||
@@ -607,5 +607,5 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./index.scss";
|
||||
@import url("./index.scss");
|
||||
</style>
|
||||
|
@@ -50,15 +50,15 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.frame {
|
||||
height: calc(100vh - 88px);
|
||||
z-index: 998;
|
||||
height: calc(100vh - 88px);
|
||||
|
||||
.frame-iframe {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ import { storeToRefs } from "pinia";
|
||||
import { getConfig } from "@/config";
|
||||
import { emitter } from "@/utils/mitt";
|
||||
import { routeMetaType } from "../types";
|
||||
import { getTopMenu } from "@/router/utils";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import { computed, CSSProperties } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
@@ -68,8 +69,8 @@ export function useNav() {
|
||||
useUserStoreHook().logOut();
|
||||
}
|
||||
|
||||
function backHome() {
|
||||
router.push("/welcome");
|
||||
function backTopMenu() {
|
||||
router.push(getTopMenu().path);
|
||||
}
|
||||
|
||||
function onPanel() {
|
||||
@@ -138,7 +139,7 @@ export function useNav() {
|
||||
logout,
|
||||
routers,
|
||||
$storage,
|
||||
backHome,
|
||||
backTopMenu,
|
||||
onPanel,
|
||||
getDivStyle,
|
||||
changeTitle,
|
||||
|
@@ -11,6 +11,7 @@ import {
|
||||
import { tagsViewsType } from "../types";
|
||||
import { useEventListener } from "@vueuse/core";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { responsiveStorageNameSpace } from "@/config";
|
||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||
import {
|
||||
@@ -45,13 +46,16 @@ export function useTags() {
|
||||
|
||||
/** 显示模式,默认灵动模式 */
|
||||
const showModel = ref(
|
||||
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showModel ||
|
||||
"smart"
|
||||
storageLocal().getItem<StorageConfigs>(
|
||||
`${responsiveStorageNameSpace()}configure`
|
||||
)?.showModel || "smart"
|
||||
);
|
||||
/** 是否隐藏标签页,默认显示 */
|
||||
const showTags =
|
||||
ref(
|
||||
storageLocal().getItem<StorageConfigs>("responsive-configure").hideTabs
|
||||
storageLocal().getItem<StorageConfigs>(
|
||||
`${responsiveStorageNameSpace()}configure`
|
||||
).hideTabs
|
||||
) ?? ref("false");
|
||||
const multiTags: any = computed(() => {
|
||||
return useMultiTagsStoreHook().multiTags;
|
||||
@@ -200,10 +204,13 @@ export function useTags() {
|
||||
onMounted(() => {
|
||||
if (!showModel.value) {
|
||||
const configure = storageLocal().getItem<StorageConfigs>(
|
||||
"responsive-configure"
|
||||
`${responsiveStorageNameSpace()}configure`
|
||||
);
|
||||
configure.showModel = "card";
|
||||
storageLocal().setItem("responsive-configure", configure);
|
||||
storageLocal().setItem(
|
||||
`${responsiveStorageNameSpace()}configure`,
|
||||
configure
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -179,20 +179,16 @@ const layoutHeader = defineComponent({
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin clearfix {
|
||||
.app-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
@@ -201,13 +197,13 @@ const layoutHeader = defineComponent({
|
||||
}
|
||||
|
||||
.app-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.re-screen {
|
||||
|
@@ -1,15 +1,19 @@
|
||||
import type { IconifyIcon } from "@iconify/vue";
|
||||
const { VITE_HIDE_HOME } = import.meta.env;
|
||||
|
||||
export const routerArrays: Array<RouteConfigs> = [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "首页",
|
||||
icon: "homeFilled"
|
||||
}
|
||||
}
|
||||
];
|
||||
export const routerArrays: Array<RouteConfigs> =
|
||||
VITE_HIDE_HOME === "false"
|
||||
? [
|
||||
{
|
||||
path: "/welcome",
|
||||
parentPath: "/",
|
||||
meta: {
|
||||
title: "首页",
|
||||
icon: "homeFilled"
|
||||
}
|
||||
}
|
||||
]
|
||||
: [];
|
||||
|
||||
export type routeMetaType = {
|
||||
title?: string;
|
||||
@@ -58,20 +62,23 @@ export interface setType {
|
||||
hideTabs: boolean;
|
||||
}
|
||||
|
||||
export type childrenType = {
|
||||
export type menuType = {
|
||||
id?: number;
|
||||
path?: string;
|
||||
noShowingChildren?: boolean;
|
||||
children?: childrenType[];
|
||||
children?: menuType[];
|
||||
value: unknown;
|
||||
meta?: {
|
||||
icon?: string;
|
||||
title?: string;
|
||||
rank?: number;
|
||||
showParent?: boolean;
|
||||
extraIcon?: string;
|
||||
};
|
||||
showTooltip?: boolean;
|
||||
parentId?: number;
|
||||
pathList?: number[];
|
||||
redirect?: string;
|
||||
};
|
||||
|
||||
export type themeColorsType = {
|
||||
|
Reference in New Issue
Block a user