feat: add tags

This commit is contained in:
xiaoxian521 2021-04-08 17:44:23 +08:00
parent 651ac333ee
commit 1eed20ebce
8 changed files with 199 additions and 41 deletions

View File

@ -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 };

View File

@ -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() {

View File

@ -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'

View File

@ -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,
};
},
});

View File

@ -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>

View 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
}
}

View File

@ -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();

View File

@ -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") // 全部重定向到登录页
})