mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-06 16:37:18 +08:00
feat: add tags
This commit is contained in:
parent
651ac333ee
commit
1eed20ebce
@ -60,11 +60,11 @@ export default defineComponent({
|
||||
() => getBreadcrumb()
|
||||
);
|
||||
|
||||
const pathCompile = (path: string): string | Object => {
|
||||
const { params } = route;
|
||||
var toPath = pathToRegexp.compile(path);
|
||||
return toPath(params);
|
||||
};
|
||||
// const pathCompile = (path: string): string | Object => {
|
||||
// const { params } = route;
|
||||
// var toPath = pathToRegexp.compile(path);
|
||||
// return toPath(params);
|
||||
// };
|
||||
|
||||
const handleLink = (item: RouteLocationMatched): any => {
|
||||
const { redirect, path } = item;
|
||||
@ -72,7 +72,7 @@ export default defineComponent({
|
||||
router.push(redirect.toString());
|
||||
return;
|
||||
}
|
||||
router.push(pathCompile(path));
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
return { levelList, handleLink };
|
||||
|
@ -13,7 +13,6 @@
|
||||
<script>
|
||||
import { computed, defineComponent } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { deviceDetection } from "../../utils/deviceDetection";
|
||||
export default defineComponent({
|
||||
name: "AppMain",
|
||||
setup() {
|
||||
|
@ -2,3 +2,4 @@ export { default as Navbar } from './Navbar.vue'
|
||||
export { default as Sidebar } from './sidebar/index.vue'
|
||||
export { default as AppMain } from './AppMain.vue'
|
||||
export { default as setting } from './setting/index.vue'
|
||||
export { default as tag } from './tag/index.vue'
|
||||
|
@ -9,6 +9,7 @@
|
||||
active-text-color="#409EFF"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
@select="menuSelect"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="route in routes"
|
||||
@ -26,6 +27,7 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { useStore } from "vuex";
|
||||
import SidebarItem from "./SidebarItem.vue";
|
||||
import { algorithm } from "../../../utils/algorithm";
|
||||
import { useDynamicRoutesHook } from "../tag/tagsHook";
|
||||
|
||||
export default defineComponent({
|
||||
name: "sidebar",
|
||||
@ -45,10 +47,22 @@ export default defineComponent({
|
||||
return path;
|
||||
});
|
||||
|
||||
const { dynamicRouteTags } = useDynamicRoutesHook();
|
||||
|
||||
const menuSelect = (indexPath: string): void => {
|
||||
let parentPath = "";
|
||||
let parentPathIndex = indexPath.lastIndexOf("/");
|
||||
if (parentPathIndex > 0) {
|
||||
parentPath = indexPath.slice(0, parentPathIndex);
|
||||
}
|
||||
dynamicRouteTags(indexPath, parentPath);
|
||||
};
|
||||
|
||||
return {
|
||||
routes: computed(() => algorithm.increaseIndexes(router)),
|
||||
activeMenu,
|
||||
isCollapse: computed(() => !store.getters.sidebar.opened),
|
||||
menuSelect,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -1,51 +1,117 @@
|
||||
<template>
|
||||
<div class="tags">
|
||||
<el-tag
|
||||
size="medium"
|
||||
v-for="tag in tags"
|
||||
:key="tag.name"
|
||||
closable
|
||||
:type="tag.type"
|
||||
>{{ tag.name }}</el-tag
|
||||
>
|
||||
<div class="tags-view">
|
||||
<el-scrollbar :vertical="false" class="scroll-container">
|
||||
<div
|
||||
v-for="(item, index) in dynamicTagList"
|
||||
:key="index"
|
||||
:class="['scroll-item', $route.path === item.path ? 'active' : '']"
|
||||
>
|
||||
<router-link :to="item.path">{{ $t(item.meta.title) }}</router-link>
|
||||
<span v-if="index !== 0 " class="el-icon-close" @click="deleteMenu(item)"></span>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts'>
|
||||
import { ref, defineComponent, onUnmounted, onMounted } from "vue";
|
||||
export default defineComponent({
|
||||
name: "tag",
|
||||
<script>
|
||||
import { useDynamicRoutesHook } from "./tagsHook";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ref, watchEffect } from "vue";
|
||||
export default {
|
||||
setup() {
|
||||
let flag = ref(true);
|
||||
const route = useRoute();
|
||||
const { deleteDynamicTag, dRoutes } = ref(useDynamicRoutesHook()).value;
|
||||
|
||||
const tags = ref([
|
||||
{ name: "首页", type: "info" },
|
||||
{ name: "基础管理", type: "info" },
|
||||
]);
|
||||
function deleteMenu(item) {
|
||||
deleteDynamicTag(item, route.path);
|
||||
}
|
||||
|
||||
const { dynamicRouteTags } = useDynamicRoutesHook();
|
||||
|
||||
// 初始化页面刷新保证当前路由tabview存在
|
||||
let stop = watchEffect(() => {
|
||||
let parentPath = route.path.slice(0, route.path.lastIndexOf("/"));
|
||||
dynamicRouteTags(route.path, parentPath);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
// 监听只执行一次,但获取不到当前路由,需要下一个事件轮询中取消监听
|
||||
stop();
|
||||
});
|
||||
|
||||
return {
|
||||
tags,
|
||||
flag,
|
||||
dynamicTagList: dRoutes,
|
||||
deleteMenu,
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tags {
|
||||
height: 32px;
|
||||
float: right;
|
||||
border: 1px solid #f0f0f0;
|
||||
.tags-view {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: 0.18s;
|
||||
}
|
||||
:deep(.el-tag) {
|
||||
background-color: #fff;
|
||||
border: 1px solid #d0d7e7;
|
||||
margin-left: 4px;
|
||||
&:first-child {
|
||||
margin-left: 8px;
|
||||
justify-content: flex-start;
|
||||
.scroll-item {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 3px;
|
||||
padding: 2px 8px;
|
||||
display: inline-block;
|
||||
margin-right: 2px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #666;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
.el-icon-close {
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
padding: 1px;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
.el-icon-close:hover {
|
||||
background: #b4bccc;
|
||||
}
|
||||
.scroll-container {
|
||||
text-align: left;
|
||||
padding: 5px 0;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
:deep(.el-scrollbar__bar) {
|
||||
bottom: 0px;
|
||||
}
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
height: 49px;
|
||||
}
|
||||
:deep(.el-scrollbar__wrap::-webkit-scrollbar) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
background: #409EFF;
|
||||
position: relative;
|
||||
color: #fff;
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.active::before {
|
||||
content: "";
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 5px;
|
||||
margin-top: -4px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
</style>
|
||||
|
74
src/layout/components/tag/tagsHook.ts
Normal file
74
src/layout/components/tag/tagsHook.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { reactive, toRefs, nextTick } from "vue"
|
||||
import { useRouter } from "vue-router"
|
||||
|
||||
interface InterDynamic {
|
||||
dRoutes: object[],
|
||||
[propName: string]: any
|
||||
}
|
||||
|
||||
// 默认显示首页tag
|
||||
let dynamic: InterDynamic = reactive({
|
||||
dRoutes: [
|
||||
{
|
||||
path: "/welcome", meta: {
|
||||
title: "home",
|
||||
icon: 'el-icon-s-home',
|
||||
showLink: true,
|
||||
savedPosition: false,
|
||||
}
|
||||
}]
|
||||
})
|
||||
|
||||
export function useDynamicRoutesHook() {
|
||||
const router = useRouter()
|
||||
/**
|
||||
* @param value string 当前menu对应的路由path
|
||||
* @param parentPath string 当前路由中父级路由
|
||||
*/
|
||||
function dynamicRouteTags(value: string, parentPath: string): void {
|
||||
const hasValue = dynamic.dRoutes.some((item: any) => {
|
||||
return item.path === value
|
||||
})
|
||||
function concatPath(arr: object[], value: string, parentPath: string) {
|
||||
if (!hasValue) {
|
||||
arr.forEach((arrItem: any) => {
|
||||
let pathConcat = parentPath + '/' + arrItem.path
|
||||
if (arrItem.path === value || pathConcat === value) {
|
||||
dynamic.dRoutes.push({ path: value, meta: arrItem.meta })
|
||||
console.log(dynamic.dRoutes)
|
||||
} else {
|
||||
if (arrItem.children && arrItem.children.length > 0) {
|
||||
concatPath(arrItem.children, value, parentPath)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
concatPath(router.options.routes, value, parentPath)
|
||||
}
|
||||
/**
|
||||
* @param value any 当前删除tag路由
|
||||
* @param current objct 当前激活路由对象
|
||||
*/
|
||||
const deleteDynamicTag = async (obj: any, current: object): Promise<any> => {
|
||||
let valueIndex: number = dynamic.dRoutes.findIndex((item: any) => {
|
||||
return item.path === obj.path
|
||||
})
|
||||
// 从当前匹配到的路径中删除
|
||||
await dynamic.dRoutes.splice(valueIndex, 1)
|
||||
if (current === obj.path) { // 如果删除当前激活tag就自动切换到最后一个tag
|
||||
let newRoute: any = dynamic.dRoutes.slice(-1)
|
||||
nextTick(() => {
|
||||
router.push({
|
||||
path: newRoute[0].path
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(dynamic),
|
||||
dynamicRouteTags,
|
||||
deleteDynamicTag
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<!-- 顶部导航栏 -->
|
||||
<navbar />
|
||||
<!-- tabs标签页 -->
|
||||
<tag />
|
||||
</div>
|
||||
<!-- 主体内容 -->
|
||||
<app-main />
|
||||
@ -21,7 +23,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Navbar, Sidebar, AppMain, setting } from "./components";
|
||||
import { Navbar, Sidebar, AppMain, setting, tag } from "./components";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
@ -48,6 +50,7 @@ export default {
|
||||
Sidebar,
|
||||
AppMain,
|
||||
setting,
|
||||
tag
|
||||
},
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
@ -220,6 +220,7 @@ const whiteList = ["/login", "/register"]
|
||||
|
||||
router.beforeEach((to, _from, next) => {
|
||||
NProgress.start()
|
||||
// @ts-ignore
|
||||
document.title = to.meta.title // 动态title
|
||||
whiteList.indexOf(to.path) !== -1 || storageSession.getItem("info") ? next() : next("/login") // 全部重定向到登录页
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user