'admin-21.01.24:完成各布局导航处理、动画细节等'

This commit is contained in:
lyt-Top 2021-01-24 21:53:46 +08:00
parent a09bcf8e60
commit 94dd0f7439
20 changed files with 621 additions and 179 deletions

View File

@ -1,5 +1,5 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"
import { store } from "/@/store/index.ts";
import { store } from "/@/store/index.ts"
// 定义动态路由
export const dynamicRoutes = [
@ -280,7 +280,8 @@ export const dynamicRoutes = [
{
path: '/fun',
name: 'funIndex',
component: () => import('/@/views/fun/index.vue'),
component: () => import('/@/views/layout/routerView/parent.vue'),
redirect: '/fun/tagsView',
meta: {
title: '功能',
isLink: '',
@ -288,7 +289,22 @@ export const dynamicRoutes = [
isKeepAlive: true,
isAffix: false,
icon: 'iconfont icon-crew_feature'
}
},
children: [
{
path: '/fun/tagsView',
name: 'funTagsView',
component: () => import('/@/views/fun/tagsView/index.vue'),
meta: {
title: 'tagsView 操作',
isLink: '',
isHide: false,
isKeepAlive: true,
isAffix: false,
icon: 'el-icon-thumb'
}
}
]
},
{
path: '/pages',

View File

@ -51,7 +51,6 @@ body,
width: 100%;
}
.layout-el-aside-br-color {
border-top: 1px solid rgb(238, 238, 238);
border-right: 1px solid rgb(238, 238, 238);
}
.layout-aside-width-default {
@ -78,6 +77,9 @@ body,
display: flex;
overflow: hidden;
}
.layout-hide {
display: none;
}
}
/* element plus 全局样式

View File

@ -692,6 +692,10 @@
/* NavMenu 导航菜单
------------------------------- */
// 去掉默认 focus 背景的高亮
.el-menu-item:focus {
background: transparent !important;
}
// 默认样式修改
.el-menu {
border-right: none !important;
@ -706,6 +710,7 @@
// horizontal 水平方向时
.el-menu--horizontal > .el-menu-item.is-active,
.el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
border-bottom: 3px solid !important;
border-bottom-color: set-color(primary);
color: set-color(primary);
}
@ -786,6 +791,10 @@
width: 24px;
text-align: center;
}
// element plus 本身字体图标
.el-submenu [class^='el-icon-'] {
font-size: 14px !important;
}
/* Tabs 标签页
------------------------------- */

View File

@ -1,21 +0,0 @@
<template>
<div>
funIndex
<el-input v-model="val"></el-input>
</div>
</template>
<script lang="ts">
import { toRefs, reactive } from "vue";
export default {
name: "funIndex",
setup() {
const state = reactive({
val: "",
});
return {
...toRefs(state),
};
},
};
</script>

View File

@ -0,0 +1,110 @@
<template>
<el-card shadow="hover" header="tagsView 功能演示">
<el-form :inline="true" :model="formInline" size="small" label-width="60px" style="margin-bottom:1px;">
<el-form-item label="功能:">
<el-select v-model="formInline.selectId" placeholder="请选择">
<el-option v-for="item in selectOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="路径:">
<el-input v-model="formInline.path" placeholder="路径如:/fun/tagsView" style="max-width:203px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onImplementClick">点击执行</el-button>
</el-form-item>
</el-form>
<el-divider></el-divider>
<el-button type="primary" size="small" icon="el-icon-refresh-right" @click="refreshCurrentTagsView">刷新当前页
</el-button>
<el-button type="info" size="small" icon="el-icon-close" @click="closeCurrentTagsView">关闭当前页</el-button>
<el-button type="warning" size="small" icon="el-icon-circle-close" @click="closeOtherTagsView">关闭其它</el-button>
<el-button type="danger" size="small" icon="el-icon-folder-delete" @click="closeAllTagsView">全部关闭</el-button>
</el-card>
</template>
<script lang="ts">
import { getCurrentInstance, reactive, toRefs } from "vue";
import { useRoute } from "vue-router";
export default {
name: "funTagsView",
setup() {
const { proxy } = getCurrentInstance();
const route = useRoute();
const state = reactive({
formInline: {
path: "",
selectId: 0,
},
selectOptions: [
{
value: 0,
label: "刷新当前",
},
{
value: 1,
label: "关闭当前",
},
{
value: 2,
label: "关闭其它",
},
{
value: 3,
label: "关闭全部",
},
],
});
// 0 1 2 3
// 1 tagsView
const refreshCurrentTagsView = () => {
proxy.mittBus.emit("onCurrentContextmenuClick", {
id: 0,
path: route.path,
});
};
// 2 tagsView
const closeCurrentTagsView = () => {
proxy.mittBus.emit("onCurrentContextmenuClick", {
id: 1,
path: route.path,
});
};
// 3 tagsView
const closeOtherTagsView = () => {
proxy.mittBus.emit("onCurrentContextmenuClick", {
id: 2,
path: route.path,
});
};
// 4 tagsView
const closeAllTagsView = () => {
proxy.mittBus.emit("onCurrentContextmenuClick", {
id: 3,
path: route.path,
});
};
//
const onImplementClick = () => {
proxy.mittBus.emit("onCurrentContextmenuClick", {
id: state.formInline.selectId,
path: state.formInline.path,
});
};
return {
refreshCurrentTagsView,
closeCurrentTagsView,
closeOtherTagsView,
closeAllTagsView,
onImplementClick,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.el-form-item {
margin-bottom: 0 !important;
}
</style>

View File

@ -2,7 +2,7 @@
<el-aside :class="setCollapseWidth">
<Logo v-if="setShowLogo" />
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef">
<Vertical :menuList="menuList" />
<Vertical :menuList="menuList" :class="setCollapseWidth" />
</el-scrollbar>
</el-aside>
</template>
@ -16,6 +16,7 @@ import {
getCurrentInstance,
ref,
onBeforeMount,
onUnmounted,
} from "vue";
import { useStore } from "/@/store/index.ts";
import { dynamicRoutes } from "/@/router/index.ts";
@ -32,6 +33,7 @@ export default {
});
// //
const setFilterRoutes = () => {
if (store.state.themeConfig.layout === "columns") return false;
store.dispatch("setRoutes", dynamicRoutes[0].children);
state.menuList = filterRoutesFun(store.state.routes);
};
@ -86,9 +88,26 @@ export default {
proxy.$refs.layoutAsideScrollbarRef.update();
}
});
//
//
onBeforeMount(() => {
setFilterRoutes();
proxy.mittBus.on("setSendColumnsChildren", (res) => {
state.menuList = res.children;
});
proxy.mittBus.on("setSendClassicChildren", (res) => {
let { layout, isClassicSplitMenu } = store.state.themeConfig;
if (layout === "classic" && isClassicSplitMenu)
state.menuList = res.children;
});
proxy.mittBus.on("getBreadcrumbIndexSetFilterRoutes", () => {
setFilterRoutes();
});
});
//
onUnmounted(() => {
proxy.mittBus.off("setSendColumnsChildren");
proxy.mittBus.off("setSendClassicChildren");
proxy.mittBus.off("getBreadcrumbIndexSetFilterRoutes");
});
return {
setCollapseWidth,

View File

@ -2,20 +2,21 @@
<div class="layout-columns-aside">
<el-scrollbar>
<ul>
<li v-for="(v,k) in columnsAsideList" :key="k" @click="onColumnsAsideDown(v,k)"
:ref="el => { if (el) columnsAsideOffsetTopRefs[k] = el }" :class="{'layout-columns-active':liIndex === k} ">
<li v-for="(v,k) in columnsAsideList" :key="k" @click="onColumnsAsideMenuClick(v,k)"
:ref="el => { if (el) columnsAsideOffsetTopRefs[k] = el }" :class="{'layout-columns-active':liIndex === k}"
:title="v.meta.title">
<div class="layout-columns-aside-li-box">
<template v-if="!v.meta.isLink">
<i :class="v.meta.icon"></i>
<div class="layout-columns-aside-li-box-title">
{{v.meta.title}}
{{v.meta.title && v.meta.title.length >= 2 ? v.meta.title.substr(0,2) : v.meta.title}}
</div>
</template>
<template v-else>
<a :href="v.meta.isLink" target="_blank">
<i :class="v.meta.icon"></i>
<div class="layout-columns-aside-li-box-title">
{{v.meta.title}}
{{v.meta.title && v.meta.title.length >= 2 ? v.meta.title.substr(0,2) : v.meta.title}}
</div>
</a>
</template>
@ -28,46 +29,29 @@
</template>
<script lang="ts">
import { reactive, toRefs, ref, computed } from "vue";
import {
reactive,
toRefs,
ref,
computed,
onMounted,
nextTick,
getCurrentInstance,
} from "vue";
import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router";
import { useStore } from "/@/store/index.ts";
import { dynamicRoutes } from "/@/router/index.ts";
export default {
name: "layoutColumnsAside",
setup() {
const columnsAsideOffsetTopRefs = ref([]);
const columnsAsideActiveRef = ref();
const { proxy } = getCurrentInstance();
const store = useStore();
const route = useRoute();
const router = useRouter();
const state = reactive({
columnsAsideList: [
{
path: "/home",
meta: {
title: "首页",
icon: "el-icon-medal-1",
},
},
{
path: "/home",
meta: {
title: "小米",
icon: "el-icon-trophy",
},
},
{
path: "/home",
meta: {
title: "谷歌",
icon: "el-icon-basketball",
isLink: "https://www.ele.me",
},
},
{
path: "/home",
meta: {
title: "苹果",
icon: "el-icon-coffee-cup",
},
},
],
columnsAsideList: [],
liIndex: 0,
difference: 0,
});
@ -82,18 +66,72 @@ export default {
else if (columnsAsideStyle === "columnsCard")
return "layout-columns-card-active";
});
//
const onColumnsAsideDown = (v: Object, k: number) => {
//
const setColumnsAsideMove = (v: Object, k: number) => {
state.liIndex = k;
columnsAsideActiveRef.value.style.top = `${
columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference
}px`;
};
//
const onColumnsAsideMenuClick = (v: Object, k: number) => {
setColumnsAsideMove(v, k);
let { path, redirect } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
//
const onColumnsAsideDown = (v: Object, k: number) => {
nextTick(() => {
setColumnsAsideMove(v, k);
});
};
// //
const setFilterRoutes = () => {
store.dispatch("setRoutes", dynamicRoutes[0].children);
state.columnsAsideList = filterRoutesFun(store.state.routes);
const resData = setSendChildren(route.path);
onColumnsAsideDown(resData.item[0], resData.item[0].k);
proxy.mittBus.emit("setSendColumnsChildren", resData);
};
//
const setSendChildren = (path: string) => {
const currentPathSplit = path.split("/");
let currentData: object = {};
state.columnsAsideList.map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v["k"] = k;
currentData["item"] = [{ ...v }];
currentData["children"] = [{ ...v }];
if (v.children) currentData["children"] = v.children;
}
});
return currentData;
};
//
const filterRoutesFun = (arr: Array<object>) => {
return arr
.filter((item) => !item.meta.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
//
onMounted(() => {
setFilterRoutes();
});
//
onBeforeRouteUpdate((to) => {
proxy.mittBus.emit("setSendColumnsChildren", setSendChildren(to.path));
});
return {
columnsAsideOffsetTopRefs,
columnsAsideActiveRef,
onColumnsAsideDown,
setColumnsAsideStyle,
onColumnsAsideMenuClick,
...toRefs(state),
};
},
@ -118,6 +156,9 @@ export default {
z-index: 1;
.layout-columns-aside-li-box {
margin: auto;
.layout-columns-aside-li-box-title {
padding-top: 1px;
}
}
a {
text-decoration: none;

View File

@ -37,7 +37,7 @@ export default {
<style scoped lang="scss">
.layout-logo {
width: 220px;
height: 49px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;

View File

@ -20,13 +20,7 @@
</template>
<script lang="ts">
import {
toRefs,
reactive,
onBeforeMount,
computed,
getCurrentInstance,
} from "vue";
import { toRefs, reactive, computed, getCurrentInstance, onMounted } from "vue";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
import { useStore } from "/@/store/index.ts";
export default {
@ -37,28 +31,55 @@ export default {
const route = useRoute();
const router = useRouter();
const state = reactive({
breadcrumbList: [{ meta: { title: "", icon: "" } }], // v-for
breadcrumbList: [],
routeSplit: [],
routeSplitFirst: "",
routeSplitIndex: 1,
});
const getBreadcrumbList = (matched: any) => {
state.breadcrumbList = matched;
};
//
const getThemeConfig = computed(() => {
return store.state.themeConfig;
});
//
const onBreadcrumbClick = (v: object) => {
const { redirect, path } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
// /
const onThemeConfigChange = () => {
proxy.mittBus.emit("onMenuClick");
store.state.themeConfig.isCollapse = !store.state.themeConfig.isCollapse;
};
const getThemeConfig = computed(() => {
return store.state.themeConfig;
});
onBeforeMount(() => {
state.breadcrumbList = route.matched;
//
const getBreadcrumbList = (arr: Array<object>) => {
arr.map((item) => {
state.routeSplit.map((v, k, arrs) => {
if (state.routeSplitFirst === item.path) {
state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
state.breadcrumbList.push(item);
state.routeSplitIndex++;
if (item.children) getBreadcrumbList(item.children);
}
});
});
};
//
const initRouteSplit = (path: string) => {
state.breadcrumbList = [store.state.routes[0]];
state.routeSplit = path.split("/");
state.routeSplit.shift();
state.routeSplitFirst = `/${state.routeSplit[0]}`;
state.routeSplitIndex = 1;
getBreadcrumbList(store.state.routes);
};
//
onMounted(() => {
initRouteSplit(route.path);
});
//
onBeforeRouteUpdate((to) => {
getBreadcrumbList(to.matched);
initRouteSplit(to.path);
});
return {
onThemeConfigChange,

View File

@ -8,8 +8,17 @@
</template>
<script lang="ts">
import { computed, reactive, toRefs } from "vue";
import {
computed,
reactive,
toRefs,
onMounted,
onUnmounted,
getCurrentInstance,
} from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { useStore } from "/@/store/index.ts";
import { dynamicRoutes } from "/@/router/index.ts";
import Breadcrumb from "/@/views/layout/navBars/breadcrumb/breadcrumb.vue";
import User from "/@/views/layout/navBars/breadcrumb/user.vue";
import Logo from "/@/views/layout/logo/index.vue";
@ -18,59 +27,17 @@ export default {
name: "layoutBreadcrumbIndex",
components: { Breadcrumb, User, Logo, Horizontal },
setup() {
const { proxy } = getCurrentInstance();
const store = useStore();
const route = useRoute();
const state = reactive({
menuList: [
{
path: "/home",
meta: {
title: "首页",
icon: "el-icon-s-home",
},
children: [
{
path: "/home",
meta: {
title: "微软",
icon: "el-icon-s-flag",
},
},
{
path: "/docs",
meta: {
title: "文档",
icon: "el-icon-s-flag",
},
},
{
path: "/docs1",
meta: {
title: "文档1",
icon: "el-icon-s-flag",
},
},
],
},
{
path: "/docs2",
meta: {
title: "文档2",
icon: "el-icon-s-management",
isLink: "https://www.ele.me",
},
},
{
path: "/docs3",
meta: {
title: "文档3",
icon: "el-icon-s-management",
},
},
],
menuList: [],
});
//
const getThemeConfig = computed(() => {
return store.state.themeConfig;
});
// logo /
const setIsShowLogo = computed(() => {
let { isShowLogo, layout } = store.state.themeConfig;
return (
@ -78,12 +45,76 @@ export default {
(isShowLogo && layout === "transverse")
);
});
//
const isLayoutTransverse = computed(() => {
let { layout, isClassicSplitMenu } = store.state.themeConfig;
return (
layout === "transverse" || (isClassicSplitMenu && layout === "classic")
);
});
// //
const setFilterRoutes = () => {
let { layout, isClassicSplitMenu } = store.state.themeConfig;
store.dispatch("setRoutes", dynamicRoutes[0].children);
if (layout === "classic" && isClassicSplitMenu) {
state.menuList = delClassicChildren(
filterRoutesFun(store.state.routes)
);
const resData = setSendClassicChildren(route.path);
proxy.mittBus.emit("setSendClassicChildren", resData);
} else {
state.menuList = filterRoutesFun(store.state.routes);
}
};
// children
const delClassicChildren = (arr: Array<object>) => {
arr.map((v) => {
if (v.children) delete v.children;
});
return arr;
};
//
const filterRoutesFun = (arr: Array<object>) => {
return arr
.filter((item) => !item.meta.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
//
const setSendClassicChildren = (path: string) => {
const currentPathSplit = path.split("/");
let currentData: object = {};
filterRoutesFun(store.state.routes).map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v["k"] = k;
currentData["item"] = [{ ...v }];
currentData["children"] = [{ ...v }];
if (v.children) currentData["children"] = v.children;
}
});
return currentData;
};
//
onMounted(() => {
setFilterRoutes();
proxy.mittBus.on("getBreadcrumbIndexSetFilterRoutes", () => {
setFilterRoutes();
});
});
//
onUnmounted(() => {
proxy.mittBus.off("getBreadcrumbIndexSetFilterRoutes");
});
//
onBeforeRouteUpdate((to) => {
proxy.mittBus.emit(
"setSendClassicChildren",
setSendClassicChildren(to.path)
);
});
return {
getThemeConfig,
setIsShowLogo,
@ -102,5 +133,6 @@ export default {
padding-right: 15px;
background: var(--bg-topBar);
overflow: hidden;
border-bottom: 1px solid rgb(238, 238, 238);
}
</style>

View File

@ -40,6 +40,8 @@
</el-color-picker>
</div>
</div>
<!-- 菜单 / 顶栏 -->
<el-divider content-position="left">菜单 / 顶栏</el-divider>
<div class="layout-breadcrumb-seting-bar-flex">
<div class="layout-breadcrumb-seting-bar-flex-label">顶栏背景</div>
@ -158,10 +160,12 @@
<el-switch v-model="getThemeConfig.isShowLogo" @change="onIsShowLogoChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex mt15"
:style="{opacity:getThemeConfig.layout === 'transverse' ? 0.5 : 1}">
<div class="layout-breadcrumb-seting-bar-flex-label">面包屑 Breadcrumb</div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isBreadcrumb" @change="onIsBreadcrumbChange"></el-switch>
<el-switch v-model="getThemeConfig.isBreadcrumb" :disabled="getThemeConfig.layout === 'transverse'"
@change="onIsBreadcrumbChange"></el-switch>
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
@ -183,7 +187,7 @@
</div>
</div>
<div class="layout-breadcrumb-seting-bar-flex mt15">
<div class="layout-breadcrumb-seting-bar-flex-label">开启缓存 TagsView</div>
<div class="layout-breadcrumb-seting-bar-flex-label">开启 TagsView 缓存 </div>
<div class="layout-breadcrumb-seting-bar-flex-value">
<el-switch v-model="getThemeConfig.isCacheTagsView" @change="setLocalThemeConfig"></el-switch>
</div>
@ -432,6 +436,7 @@ export default defineComponent({
nextTick(() => {
setTimeout(() => {
let els = document.querySelector(".el-menu-item.is-active");
if (!els) return false;
let attr = "el-menu-item is-active";
if (getThemeConfig.value.isMenuBarColorHighlight)
els.setAttribute("class", `${attr} add-is-active`);
@ -458,6 +463,7 @@ export default defineComponent({
const onClassicSplitMenuChange = () => {
getThemeConfig.value.isBreadcrumb = false;
setLocalThemeConfig();
proxy.mittBus.emit("getBreadcrumbIndexSetFilterRoutes");
};
// 4 --> Logo
const onIsShowLogoChange = () => {

View File

@ -1,5 +1,5 @@
<template>
<div class="layout-navbars-breadcrumb-user">
<div class="layout-navbars-breadcrumb-user" :style="setFlexAutoStyle">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="el-icon-search" title="菜单搜索"></i>
</div>
@ -29,16 +29,27 @@
</template>
<script lang="ts">
import { ref, getCurrentInstance } from "vue";
import { ref, getCurrentInstance, computed } from "vue";
import { useStore } from "/@/store/index.ts";
export default {
name: "layoutBreadcrumbUser",
setup() {
const { proxy } = getCurrentInstance();
const store = useStore();
const onLayoutSetingClick = () => {
proxy.mittBus.emit("openSetingsDrawer");
};
const setFlexAutoStyle = computed(() => {
if (
!store.state.themeConfig.isBreadcrumb &&
store.state.themeConfig.layout !== "transverse" &&
!store.state.themeConfig.isClassicSplitMenu
)
return { flex: 1 };
});
return {
onLayoutSetingClick,
setFlexAutoStyle,
};
},
};
@ -48,7 +59,6 @@ export default {
.layout-navbars-breadcrumb-user {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
&-link {
height: 100%;

View File

@ -5,7 +5,8 @@
v-show="isShow">
<ul class="el-dropdown-menu">
<template v-for="(v,k) in dropdownList" :key="k">
<li class="el-dropdown-menu__item" aria-disabled="false" tabindex="-1" v-if="!v.affix">
<li class="el-dropdown-menu__item" aria-disabled="false" tabindex="-1" v-if="!v.affix"
@click="onCurrentContextmenuClick(v.id)">
<i :class="v.icon"></i>
<span>{{v.txt}}</span>
</li>
@ -32,7 +33,7 @@ export default defineComponent({
type: Object,
},
},
setup(props) {
setup(props, { emit }) {
const state = reactive({
isShow: false,
dropdownList: [
@ -41,14 +42,20 @@ export default defineComponent({
{ id: 2, txt: "关闭其它", affix: false, icon: "el-icon-circle-close" },
{ id: 3, txt: "全部关闭", affix: false, icon: "el-icon-folder-delete" },
],
path: {},
});
// x,y
const dropdown = computed(() => {
return props.dropdown;
});
//
const onCurrentContextmenuClick = (id: number) => {
emit("currentContextmenuClick", { id, path: state.path });
};
//
const openContextmenu = (meta: object) => {
meta.isAffix
const openContextmenu = (item: object) => {
state.path = item.path;
item.meta.isAffix
? (state.dropdownList[1].affix = true)
: (state.dropdownList[1].affix = false);
closeContextmenu();
@ -72,6 +79,7 @@ export default defineComponent({
dropdown,
openContextmenu,
closeContextmenu,
onCurrentContextmenuClick,
...toRefs(state),
};
},

View File

@ -59,10 +59,14 @@ export default {
}
}
};
const updateScrollbar = () => {
proxy.$refs.elScrollbarRef.update();
};
return {
setScrollLeft,
onHandleScroll,
moveToTarget,
updateScrollbar,
...toRefs(state),
};
},

View File

@ -3,7 +3,7 @@
v-show="getThemeConfig.isTagsview">
<Scroll ref="scrollRef">
<ul class="layout-navbars-tagsview-ul" :class="setTagsStyle">
<li v-for="(v,k) in tagsViewList" :key="k" class="layout-navbars-tagsview-ul-li"
<li v-for="(v,k) in tagsViewList" :key="k" class="layout-navbars-tagsview-ul-li" :data-name="v.name"
:class="{'is-active':isActive(v.path)}" @contextmenu.prevent="onContextmenu(v,$event)"
@click="onTagsClick(v,k)" :ref="el => { if (el) tagsRefs[k] = el }">
<i class="iconfont icon-webicon318 layout-navbars-tagsview-ul-li-iconfont font14" v-if="isActive(v.path)"></i>
@ -11,15 +11,17 @@
v-if="!isActive(v.path) && getThemeConfig.isTagsviewIcon"></i>
<span>{{v.meta.title}}</span>
<template v-if="isActive(v.path)">
<i class="el-icon-refresh-right ml5"></i>
<i class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-active" v-if="!v.meta.isAffix"></i>
<i class="el-icon-refresh-right ml5" @click="refreshCurrentTagsView(v.path)"></i>
<i class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-active" v-if="!v.meta.isAffix"
@click="closeCurrentTagsView(v.path)"></i>
</template>
<i class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-three" v-if="!v.meta.isAffix"></i>
<i class="el-icon-close layout-navbars-tagsview-ul-li-icon layout-icon-three" v-if="!v.meta.isAffix"
@click="closeCurrentTagsView(v.path)"></i>
</li>
</ul>
</Scroll>
</div>
<Contextmenu :dropdown="dropdown" ref="contextmenuRef" />
<Contextmenu :dropdown="dropdown" ref="contextmenuRef" @currentContextmenuClick="onCurrentContextmenuClick" />
</template>
<script lang="ts">
@ -31,6 +33,9 @@ import {
ref,
nextTick,
onBeforeUpdate,
onBeforeMount,
onUnmounted,
getCurrentInstance,
} from "vue";
import { useRoute, useRouter, onBeforeRouteUpdate } from "vue-router";
import { useStore } from "/@/store/index.ts";
@ -42,6 +47,7 @@ export default {
name: "layoutTagsView",
components: { Contextmenu, Scroll },
setup() {
const { proxy } = getCurrentInstance();
const tagsRefs = ref([]);
const scrollRef = ref();
const contextmenuRef = ref();
@ -71,8 +77,8 @@ export default {
return store.state.themeConfig;
});
// tagsViewList
const addBrowserSetSession = () => {
setSession("tagsViewList", state.tagsViewList);
const addBrowserSetSession = (tagsViewList: Array<object>) => {
setSession("tagsViewList", tagsViewList);
};
// vuex isAffix
const initTagsView = () => {
@ -93,12 +99,71 @@ export default {
getTagsRefsIndex(route.path);
moveToCurrentTag();
};
// tagsViewisHide tagsView
// 1 tagsViewisHide tagsView
const addTagsView = (path: string) => {
if (state.tagsViewList.some((v) => v.path === path)) return false;
const item = store.state.tagsViewRoutes.find((v) => v.path === path);
if (!item.meta.isHide) state.tagsViewList.push({ ...item });
addBrowserSetSession();
addBrowserSetSession(state.tagsViewList);
};
// 2 tagsView
const refreshCurrentTagsView = (path: string) => {
proxy.mittBus.emit("onTagsViewRefreshRouterView", path);
};
// 3 tagsViewisAffix
const closeCurrentTagsView = (path: string) => {
state.tagsViewList.map((v, k, arr) => {
if (!v.meta.isAffix) {
if (v.path === path) {
state.tagsViewList.splice(k, 1);
setTimeout(() => {
router.push(arr[arr.length - 1].path);
}, 0);
}
}
});
addBrowserSetSession(state.tagsViewList);
};
// 4 tagsViewisAffix
const closeOtherTagsView = (path: string) => {
state.tagsViewList = [];
store.state.tagsViewRoutes.map((v) => {
if (v.meta.isAffix && !v.meta.isHide) state.tagsViewList.push({ ...v });
});
addTagsView(path);
};
// 5 tagsViewisAffix
const closeAllTagsView = (path: string) => {
state.tagsViewList = [];
store.state.tagsViewRoutes.map((v) => {
if (v.meta.isAffix && !v.meta.isHide) {
state.tagsViewList.push({ ...v });
if (state.tagsViewList.some((v) => v.path === path))
router.push(path);
else router.push(v.path);
}
});
addBrowserSetSession(state.tagsViewList);
};
//
const onCurrentContextmenuClick = (data: object) => {
let { id, path } = data;
switch (id) {
case 0:
refreshCurrentTagsView(path);
router.push(path);
break;
case 1:
closeCurrentTagsView(path);
break;
case 2:
router.push(path);
closeOtherTagsView(path);
break;
case 3:
closeAllTagsView(path);
break;
}
};
//
const isActive = (path: string) => {
@ -109,7 +174,7 @@ export default {
const { clientX, clientY } = e;
state.dropdown.x = clientX;
state.dropdown.y = clientY;
contextmenuRef.value.openContextmenu(v.meta);
contextmenuRef.value.openContextmenu(v);
};
// tagsView
const onTagsClick = (v: object, k: number) => {
@ -120,21 +185,49 @@ export default {
const moveToCurrentTag = () => {
nextTick(() => {
scrollRef.value.moveToTarget(tagsRefs.value[state.tagsRefsIndex]);
scrollRef.value.updateScrollbar();
});
};
// tagsView tagsView
const getTagsRefsIndex = (path: string) => {
if (state.tagsViewList.length > 0) {
state.tagsRefsIndex = state.tagsViewList.findIndex(
const tagsRefsFindIndex = state.tagsViewList.findIndex(
(item) => item.path === path
);
if (tagsRefsFindIndex <= 0) return false;
state.tagsRefsIndex = tagsRefsFindIndex;
}
};
// tagsView
const initSortable = () => {
const el = document.querySelector(".layout-navbars-tagsview-ul");
const sortable = Sortable.create(el, { animation: 300 });
if (!el) return false;
const sortable = Sortable.create(el, {
animation: 300,
dataIdAttr: "data-name",
onEnd: () => {
const sortEndList = [];
sortable.toArray().map((val) => {
state.tagsViewList.map((v) => {
if (v.name === val) sortEndList.push({ ...v });
});
});
addBrowserSetSession(sortEndList);
},
});
};
//
onBeforeMount(() => {
// 0 1 2 3
proxy.mittBus.on("onCurrentContextmenuClick", (data: object) => {
onCurrentContextmenuClick(data);
});
});
//
onUnmounted(() => {
//
proxy.mittBus.off("onCurrentContextmenuClick");
});
//
onBeforeUpdate(() => {
tagsRefs.value = [];
@ -160,6 +253,9 @@ export default {
scrollRef,
getThemeConfig,
setTagsStyle,
refreshCurrentTagsView,
closeCurrentTagsView,
onCurrentContextmenuClick,
...toRefs(state),
};
},
@ -170,7 +266,6 @@ export default {
.layout-navbars-tagsview {
flex: 1;
background-color: #ffffff;
border-top: 1px solid rgb(238, 238, 238);
&-ul {
list-style: none;
margin: 0;

View File

@ -1,20 +1,24 @@
<template>
<el-menu router :default-active="defaultActive" background-color="transparent" mode="horizontal">
<template v-for="val in menuList">
<el-submenu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
<template #title>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<span>{{ val.meta.title }}</span>
<div class="el-menu-horizontal-warp">
<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
<el-menu router :default-active="defaultActive" background-color="transparent" mode="horizontal">
<template v-for="val in menuList">
<el-submenu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
<template #title>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<span>{{ val.meta.title }}</span>
</template>
<SubItem :chil="val.children" />
</el-submenu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<template #title v-if="!val.meta.isLink">{{ val.meta.title }}</template>
<template #title v-else><a :href="val.meta.isLink" target="_blank">{{ val.meta.title }}</a></template>
</el-menu-item>
</template>
<SubItem :chil="val.children" />
</el-submenu>
<el-menu-item :index="val.path" :key="val.path" v-else>
<i :class="val.meta.icon ? val.meta.icon : ''"></i>
<template #title v-if="!val.meta.isLink">{{ val.meta.title }}</template>
<template #title v-else><a :href="val.meta.isLink" target="_blank">{{ val.meta.title }}</a></template>
</el-menu-item>
</template>
</el-menu>
</el-menu>
</el-scrollbar>
</div>
</template>
<script lang="ts">
@ -24,8 +28,11 @@ import {
computed,
defineComponent,
getCurrentInstance,
onMounted,
nextTick,
} from "vue";
import { useRoute, onBeforeRouteUpdate } from "vue-router";
import { useStore } from "/@/store/index.ts";
import SubItem from "/@/views/layout/navMenu/subItem.vue";
export default defineComponent({
name: "navMenuHorizontal",
@ -41,20 +48,67 @@ export default defineComponent({
setup(props) {
const { proxy } = getCurrentInstance();
const route = useRoute();
const store = useStore();
const state = reactive({
defaultActive: route.path,
defaultActive: null,
});
//
const menuList = computed(() => {
return props.menuList;
});
//
const onElMenuHorizontalScroll = (e: object) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft =
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft +
eventDelta / 4;
};
//
const initElMenuOffsetLeft = () => {
nextTick(() => {
let els = document.querySelector(
".el-menu.el-menu--horizontal li.is-active"
);
if (!els) return false;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft =
els.offsetLeft;
});
};
//
const setCurrentRouterHighlight = (path: string) => {
const currentPathSplit = path.split("/");
if (store.state.themeConfig.layout === "classic") {
state.defaultActive = `/${currentPathSplit[1]}`;
} else {
state.defaultActive = path;
}
};
//
onMounted(() => {
initElMenuOffsetLeft();
setCurrentRouterHighlight(route.path);
});
//
onBeforeRouteUpdate((to) => {
state.defaultActive = to.path;
setCurrentRouterHighlight(to.path);
proxy.mittBus.emit("onMenuClick");
});
return {
menuList,
onElMenuHorizontalScroll,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.el-menu-horizontal-warp {
flex: 1;
overflow: hidden;
margin-right: 30px;
.el-menu.el-menu--horizontal {
display: flex;
}
}
</style>

View File

@ -19,7 +19,6 @@
<script lang="ts">
import { computed, defineComponent } from "vue";
import { useRoute } from "vue-router";
export default defineComponent({
name: "navMenuSubItem",
props: {
@ -31,7 +30,7 @@ export default defineComponent({
},
},
setup(props) {
const route = useRoute();
//
const chil = computed(() => {
return props.chil;
});

View File

@ -49,12 +49,15 @@ export default defineComponent({
const state = reactive({
defaultActive: route.path,
});
//
const menuList = computed(() => {
return props.menuList;
});
//
const getThemeConfig = computed(() => {
return store.state.themeConfig;
});
//
onBeforeRouteUpdate((to) => {
state.defaultActive = to.path;
proxy.mittBus.emit("onMenuClick");

View File

@ -1,9 +1,9 @@
<template>
<div>
<router-view v-slot="{ Component, route }">
<div class="h100">
<router-view v-slot="{ Component }">
<transition :name="setTransitionName" mode="out-in">
<keep-alive :include="getCaches">
<component :is="Component" :key="route.path" />
<component :is="Component" :key="refreshRouterViewKey" />
</keep-alive>
</transition>
</router-view>
@ -18,18 +18,28 @@ import {
reactive,
getCurrentInstance,
watch,
onBeforeMount,
onUnmounted,
nextTick,
} from "vue";
import { useRouter, RouteRecordRaw } from "vue-router";
import {
useRoute,
useRouter,
RouteRecordRaw,
onBeforeRouteUpdate,
} from "vue-router";
import { useStore } from "/@/store/index.ts";
export default defineComponent({
name: "layoutParentView",
setup() {
const router = useRouter();
const { proxy } = getCurrentInstance();
const route = useRoute();
const router = useRouter();
const store = useStore();
const state = reactive({
transitionName: "slide-right",
headerHeight: "84px",
refreshRouterViewKey: route.path,
});
//
const setTransitionName = computed(() => {
@ -57,6 +67,24 @@ export default defineComponent({
proxy.$refs.layoutScrollbarRef.update();
}
});
//
onBeforeRouteUpdate((to) => {
state.refreshRouterViewKey = to.path;
});
//
onBeforeMount(() => {
proxy.mittBus.on("onTagsViewRefreshRouterView", (path: string) => {
if (route.path !== path) return false;
state.refreshRouterViewKey = Math.random();
nextTick(() => {
state.refreshRouterViewKey = path;
});
});
});
//
onUnmounted(() => {
proxy.mittBus.off("onTagsViewRefreshRouterView");
});
return {
getThemeConfig,
getCaches,

View File

@ -6,13 +6,19 @@
</template>
<script lang="ts">
import { toRefs, reactive } from "vue";
import { toRefs, reactive, onActivated, onMounted } from "vue";
export default {
name: "menu13",
setup() {
const state = reactive({
val: "",
});
onMounted(() => {
console.log(2222);
});
onActivated(() => {
console.log(1111);
});
return {
...toRefs(state),
};