mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-07 00:47:19 +08:00
feat: add persistence of tabs
This commit is contained in:
parent
762833e545
commit
62ad9e6f40
@ -20,12 +20,7 @@
|
|||||||
</app-link>
|
</app-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-submenu
|
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
|
||||||
v-else
|
|
||||||
ref="subMenu"
|
|
||||||
:index="resolvePath(item.path)"
|
|
||||||
popper-append-to-body
|
|
||||||
>
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<i :class="item.meta.icon"></i>
|
<i :class="item.meta.icon"></i>
|
||||||
<span>{{ $t(item.meta.title) }}</span>
|
<span>{{ $t(item.meta.title) }}</span>
|
||||||
@ -53,16 +48,16 @@ export default defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object as PropType<RouteRecordRaw>,
|
type: Object as PropType<RouteRecordRaw>,
|
||||||
required: true,
|
required: true
|
||||||
},
|
},
|
||||||
isNest: {
|
isNest: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
},
|
},
|
||||||
basePath: {
|
basePath: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: ""
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const onlyOneChild = ref<RouteRecordRaw>({} as any);
|
const onlyOneChild = ref<RouteRecordRaw>({} as any);
|
||||||
@ -71,7 +66,7 @@ export default defineComponent({
|
|||||||
children: RouteRecordRaw[] = [],
|
children: RouteRecordRaw[] = [],
|
||||||
parent: RouteRecordRaw
|
parent: RouteRecordRaw
|
||||||
) {
|
) {
|
||||||
const showingChildren = children.filter((item) => {
|
const showingChildren = children.filter(item => {
|
||||||
if (item.hidden) {
|
if (item.hidden) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -96,6 +91,6 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return { hasOneShowingChild, resolvePath, onlyOneChild };
|
return { hasOneShowingChild, resolvePath, onlyOneChild };
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -22,12 +22,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from "vue";
|
import { computed, defineComponent, unref, nextTick } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import SidebarItem from "./SidebarItem.vue";
|
import SidebarItem from "./SidebarItem.vue";
|
||||||
import { algorithm } from "../../../utils/algorithm";
|
import { algorithm } from "../../../utils/algorithm";
|
||||||
import { useDynamicRoutesHook } from "../tag/tagsHook";
|
import { useDynamicRoutesHook } from "../tag/tagsHook";
|
||||||
|
import { emitter } from "/@/utils/mitt";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "sidebar",
|
name: "sidebar",
|
||||||
@ -55,15 +56,27 @@ export default defineComponent({
|
|||||||
if (parentPathIndex > 0) {
|
if (parentPathIndex > 0) {
|
||||||
parentPath = indexPath.slice(0, parentPathIndex);
|
parentPath = indexPath.slice(0, parentPathIndex);
|
||||||
}
|
}
|
||||||
dynamicRouteTags(indexPath, parentPath);
|
// 找到当前路由的信息
|
||||||
|
function findCurrentRoute(routes) {
|
||||||
|
return routes.map((item, key) => {
|
||||||
|
if (item.path === indexPath) {
|
||||||
|
dynamicRouteTags(indexPath, parentPath, item);
|
||||||
|
} else {
|
||||||
|
if (item.children) findCurrentRoute(item.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
findCurrentRoute(algorithm.increaseIndexes(router));
|
||||||
|
emitter.emit("changLayoutRoute", indexPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
routes: computed(() => algorithm.increaseIndexes(router)),
|
routes: computed(() => algorithm.increaseIndexes(router)),
|
||||||
activeMenu,
|
activeMenu,
|
||||||
isCollapse: computed(() => !store.getters.sidebar.opened),
|
isCollapse: computed(() => !store.getters.sidebar.opened),
|
||||||
menuSelect,
|
menuSelect
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -42,11 +42,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useDynamicRoutesHook } from "./tagsHook"
|
import { useDynamicRoutesHook } from "./tagsHook"
|
||||||
import { useRoute, useRouter } from "vue-router"
|
import { useRoute, useRouter } from "vue-router"
|
||||||
import { ref, watchEffect, onBeforeMount, unref } from "vue"
|
import { ref, watchEffect, onBeforeMount, unref, nextTick } from "vue"
|
||||||
import { storageLocal } from "/@/utils/storage"
|
import { storageLocal } from "/@/utils/storage"
|
||||||
import { emitter } from "/@/utils/mitt"
|
import { emitter } from "/@/utils/mitt"
|
||||||
import { toggleClass, removeClass } from "/@/utils/operate"
|
import { toggleClass, removeClass } from "/@/utils/operate"
|
||||||
import { nextTick } from 'vue'
|
import { homeRoute } from "./type"
|
||||||
let refreshDiv = "refresh-div"
|
let refreshDiv = "refresh-div"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -73,20 +73,25 @@ export default {
|
|||||||
icon: "el-icon-more",
|
icon: "el-icon-more",
|
||||||
text: "关闭其他标签页",
|
text: "关闭其他标签页",
|
||||||
divided: true,
|
divided: true,
|
||||||
disabled: false
|
disabled: unref(routesLength) > 2 ? false : true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "el-icon-minus",
|
icon: "el-icon-minus",
|
||||||
text: "关闭全部标签页",
|
text: "关闭全部标签页",
|
||||||
divided: false,
|
divided: false,
|
||||||
disabled: false
|
disabled: unref(routesLength) > 1 ? false : true
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
function deleteMenu(item) {
|
function deleteMenu(item) {
|
||||||
let tagslen = storageLocal.getItem("routesInStorage").length - 1
|
let tagslen = storageLocal.getItem("routesInStorage").length
|
||||||
if (tagslen === 1) {
|
if (tagslen === 2) {
|
||||||
tagsViews.value[1].disabled = true
|
Array.from([1, 2, 3]).forEach(v => {
|
||||||
|
tagsViews.value[v].disabled = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (tagslen === 3) {
|
||||||
|
tagsViews.value[2].disabled = true
|
||||||
}
|
}
|
||||||
deleteDynamicTag(item, route.path)
|
deleteDynamicTag(item, route.path)
|
||||||
}
|
}
|
||||||
@ -115,6 +120,7 @@ export default {
|
|||||||
|
|
||||||
function onClickDrop(key, item) {
|
function onClickDrop(key, item) {
|
||||||
if (item.disabled) return
|
if (item.disabled) return
|
||||||
|
// 当前路由信息
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 0:
|
case 0:
|
||||||
// 重新加载
|
// 重新加载
|
||||||
@ -122,25 +128,22 @@ export default {
|
|||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
// 关闭当前标签页
|
// 关闭当前标签页
|
||||||
deleteMenu({ meta: route.meta, path: route.path })
|
deleteMenu({ path: route.path, meta: route.meta })
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
// 关闭其他标签页
|
// 关闭其他标签页
|
||||||
|
dRoutes.value = [homeRoute, { path: route.path, meta: route.meta }]
|
||||||
|
storageLocal.setItem("routesInStorage", dRoutes.value)
|
||||||
|
tagsViews.value[2].disabled = true
|
||||||
break
|
break
|
||||||
case 3:
|
case 3:
|
||||||
// 关闭全部标签页
|
// 关闭全部标签页
|
||||||
dRoutes.value = [{
|
dRoutes.value = [homeRoute]
|
||||||
path: "/welcome",
|
|
||||||
meta: {
|
|
||||||
title: "home",
|
|
||||||
icon: "el-icon-s-home",
|
|
||||||
showLink: true,
|
|
||||||
savedPosition: false,
|
|
||||||
},
|
|
||||||
}]
|
|
||||||
storageLocal.setItem("routesInStorage", dRoutes.value)
|
storageLocal.setItem("routesInStorage", dRoutes.value)
|
||||||
router.push("/welcome")
|
router.push("/welcome")
|
||||||
tagsViews.value[1].disabled = true
|
Array.from([1, 2, 3]).forEach(v => {
|
||||||
|
tagsViews.value[v].disabled = true
|
||||||
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,6 +153,20 @@ export default {
|
|||||||
if (unref(showTags) === key) return
|
if (unref(showTags) === key) return
|
||||||
showTags.value = key
|
showTags.value = key
|
||||||
})
|
})
|
||||||
|
|
||||||
|
emitter.on("changLayoutRoute", (indexPath) => {
|
||||||
|
let currentLen = storageLocal.getItem("routesInStorage").length
|
||||||
|
if (currentLen === 1) {
|
||||||
|
Array.from([1, 3]).forEach(v => {
|
||||||
|
tagsViews.value[v].disabled = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (currentLen >= 2) {
|
||||||
|
Array.from([1, 2, 3]).forEach(v => {
|
||||||
|
tagsViews.value[v].disabled = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { reactive, toRefs, nextTick, computed } from "vue";
|
import { reactive, toRefs, unref, nextTick, computed } from "vue";
|
||||||
import { storageLocal } from "/@/utils/storage";
|
import { storageLocal } from "/@/utils/storage";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import { homeRoute } from "./type";
|
||||||
|
|
||||||
interface InterDynamic {
|
interface InterDynamic {
|
||||||
dRoutes: object[];
|
dRoutes: object[];
|
||||||
@ -24,21 +25,57 @@ let dynamic: InterDynamic = reactive({
|
|||||||
|
|
||||||
export function useDynamicRoutesHook() {
|
export function useDynamicRoutesHook() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const routesLength = computed(() => {
|
||||||
|
return storageLocal.getItem("routesInStorage")
|
||||||
|
? storageLocal.getItem("routesInStorage").length
|
||||||
|
: 0;
|
||||||
|
});
|
||||||
|
// 返回当前路由组成的数组
|
||||||
|
const routesStorageLists = computed(() => {
|
||||||
|
return storageLocal.getItem("routesInStorage")
|
||||||
|
? storageLocal.getItem("routesInStorage")
|
||||||
|
: [];
|
||||||
|
});
|
||||||
/**
|
/**
|
||||||
* @param value string 当前menu对应的路由path
|
* @param value string 当前menu对应的路由path
|
||||||
* @param parentPath string 当前路由中父级路由
|
* @param parentPath string 当前路由中父级路由
|
||||||
*/
|
*/
|
||||||
const dynamicRouteTags = (value: string, parentPath: string): void => {
|
const dynamicRouteTags = (
|
||||||
|
value: string,
|
||||||
|
parentPath: string,
|
||||||
|
route: any
|
||||||
|
): void => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (unref(routesStorageLists).length > 2) {
|
||||||
|
dynamic.dRoutes = unref(routesStorageLists);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const hasValue = dynamic.dRoutes.some((item: any) => {
|
const hasValue = dynamic.dRoutes.some((item: any) => {
|
||||||
return item.path === value;
|
return item.path === value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (route) {
|
||||||
|
let ramStorage = storageLocal.getItem("routesInStorage");
|
||||||
|
nextTick(() => {
|
||||||
|
let currentIndex = ramStorage.findIndex((v) => v.path === route.path);
|
||||||
|
if (currentIndex !== -1) return;
|
||||||
|
ramStorage.push({ path: route.path, meta: route.meta });
|
||||||
|
storageLocal.setItem("routesInStorage", ramStorage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function concatPath(arr: object[], value: string, parentPath: string) {
|
function concatPath(arr: object[], value: string, parentPath: string) {
|
||||||
if (!hasValue) {
|
if (!hasValue) {
|
||||||
arr.forEach((arrItem: any) => {
|
arr.forEach((arrItem: any) => {
|
||||||
let pathConcat = parentPath + "/" + arrItem.path;
|
let pathConcat = parentPath + "/" + arrItem.path;
|
||||||
if (arrItem.path === value || pathConcat === value) {
|
if (arrItem.path === value || pathConcat === value) {
|
||||||
dynamic.dRoutes.push({ path: value, meta: arrItem.meta });
|
dynamic.dRoutes.push({ path: value, meta: arrItem.meta });
|
||||||
|
|
||||||
|
unref(routesLength) === 0
|
||||||
|
? storageLocal.setItem("routesInStorage", dynamic.dRoutes)
|
||||||
|
: [];
|
||||||
} else {
|
} else {
|
||||||
if (arrItem.children && arrItem.children.length > 0) {
|
if (arrItem.children && arrItem.children.length > 0) {
|
||||||
concatPath(arrItem.children, value, parentPath);
|
concatPath(arrItem.children, value, parentPath);
|
||||||
@ -48,12 +85,6 @@ export function useDynamicRoutesHook() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
concatPath(router.options.routes, value, parentPath);
|
concatPath(router.options.routes, value, parentPath);
|
||||||
// if (storageLocal.getItem("routesInStorage") && storageLocal.getItem("routesInStorage").length > 2) {
|
|
||||||
// let lens = storageLocal.getItem("routesInStorage").length
|
|
||||||
// let itemss = storageLocal.getItem("routesInStorage")[lens - 1]
|
|
||||||
// dynamic.dRoutes.push({ path: itemss.path, meta: itemss.meta })
|
|
||||||
// }
|
|
||||||
storageLocal.setItem("routesInStorage", dynamic.dRoutes);
|
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* @param value any 当前删除tag路由
|
* @param value any 当前删除tag路由
|
||||||
@ -76,16 +107,12 @@ export function useDynamicRoutesHook() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const routesLength = computed(() => {
|
|
||||||
return storageLocal.getItem("routesInStorage")
|
|
||||||
? storageLocal.getItem("routesInStorage").length
|
|
||||||
: 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...toRefs(dynamic),
|
...toRefs(dynamic),
|
||||||
dynamicRouteTags,
|
dynamicRouteTags,
|
||||||
deleteDynamicTag,
|
deleteDynamicTag,
|
||||||
routesLength,
|
routesLength,
|
||||||
|
routesStorageLists,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
15
src/layout/components/tag/type.ts
Normal file
15
src/layout/components/tag/type.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
interface RouteModel {
|
||||||
|
readonly path: string;
|
||||||
|
readonly meta: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定首页路由
|
||||||
|
export const homeRoute: RouteModel = {
|
||||||
|
path: "/welcome",
|
||||||
|
meta: {
|
||||||
|
title: "home",
|
||||||
|
icon: "el-icon-s-home",
|
||||||
|
showLink: true,
|
||||||
|
savedPosition: false,
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user