diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue index 2a7e6116..65399990 100644 --- a/src/layout/components/TagsView/ScrollPane.vue +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -1,5 +1,5 @@ @@ -143,7 +142,7 @@ const showScreenfull = computed(() => { } .el-scrollbar { flex: 1; - // 横向超出窗口长度时,显示滚动条 + // 防止换行(超出宽度时,显示滚动条) white-space: nowrap; .scrollbar-content { display: inline-block; diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue index a9d796d7..4a6bd261 100644 --- a/src/layout/components/TagsView/index.vue +++ b/src/layout/components/TagsView/index.vue @@ -13,26 +13,35 @@ const route = useRoute() const tagsViewStore = useTagsViewStore() const permissionStore = usePermissionStore() +/** 标签页组件元素的引用数组 */ const tagRefs = ref[]>([]) +/** 右键菜单的状态 */ const visible = ref(false) +/** 右键菜单的 top 位置 */ const top = ref(0) +/** 右键菜单的 left 位置 */ const left = ref(0) +/** 当前正在右键操作的标签页 */ const selectedTag = ref({}) +/** 固定的标签页 */ let affixTags: TagView[] = [] +/** 判断标签页是否激活 */ const isActive = (tag: TagView) => { return tag.path === route.path } +/** 判断标签页是否固定 */ const isAffix = (tag: TagView) => { return tag.meta?.affix } +/** 筛选出固定标签页 */ const filterAffixTags = (routes: RouteRecordRaw[], basePath = "/") => { - let tags: TagView[] = [] + const tags: TagView[] = [] routes.forEach((route) => { - if (route.meta?.affix) { + if (isAffix(route)) { const tagPath = path.resolve(basePath, route.path) tags.push({ fullPath: tagPath, @@ -43,24 +52,22 @@ const filterAffixTags = (routes: RouteRecordRaw[], basePath = "/") => { } if (route.children) { const childTags = filterAffixTags(route.children, route.path) - if (childTags.length >= 1) { - tags = tags.concat(childTags) - } + tags.push(...childTags) } }) return tags } +/** 初始化标签页 */ const initTags = () => { affixTags = filterAffixTags(permissionStore.routes) for (const tag of affixTags) { // 必须含有 name 属性 - if (tag.name) { - tagsViewStore.addVisitedView(tag) - } + tag.name && tagsViewStore.addVisitedView(tag) } } +/** 添加标签页 */ const addTags = () => { if (route.name) { tagsViewStore.addVisitedView(route) @@ -68,40 +75,43 @@ const addTags = () => { } } +/** 刷新当前正在右键操作的标签页 */ const refreshSelectedTag = (view: TagView) => { tagsViewStore.delCachedView(view) router.replace({ path: "/redirect" + view.path, query: view.query }) } +/** 关闭当前正在右键操作的标签页 */ const closeSelectedTag = (view: TagView) => { tagsViewStore.delVisitedView(view) tagsViewStore.delCachedView(view) - if (isActive(view)) { - toLastView(tagsViewStore.visitedViews, view) - } + isActive(view) && toLastView(tagsViewStore.visitedViews, view) } +/** 关闭其他标签页 */ const closeOthersTags = () => { - if (selectedTag.value.fullPath !== route.path && selectedTag.value.fullPath !== undefined) { - router.push(selectedTag.value.fullPath) + const fullPath = selectedTag.value.fullPath + if (fullPath !== route.path && fullPath !== undefined) { + router.push(fullPath) } tagsViewStore.delOthersVisitedViews(selectedTag.value) tagsViewStore.delOthersCachedViews(selectedTag.value) } +/** 关闭所有标签页 */ const closeAllTags = (view: TagView) => { tagsViewStore.delAllVisitedViews() tagsViewStore.delAllCachedViews() - if (affixTags.some((tag) => tag.path === route.path)) { - return - } + if (affixTags.some((tag) => tag.path === route.path)) return toLastView(tagsViewStore.visitedViews, view) } +/** 跳转到最后一个标签页 */ const toLastView = (visitedViews: TagView[], view: TagView) => { const latestView = visitedViews.slice(-1)[0] - if (latestView !== undefined && latestView.fullPath !== undefined) { - router.push(latestView.fullPath) + const fullPath = latestView?.fullPath + if (fullPath !== undefined) { + router.push(fullPath) } else { // 如果 TagsView 全部被关闭了,则默认重定向到主页 if (view.name === "Dashboard") { @@ -113,26 +123,26 @@ const toLastView = (visitedViews: TagView[], view: TagView) => { } } +/** 打开右键菜单面板 */ const openMenu = (tag: TagView, e: MouseEvent) => { const menuMinWidth = 105 - // container margin left + // 当前组件距离浏览器左端的距离 const offsetLeft = instance!.proxy!.$el.getBoundingClientRect().left - // container width + // 当前组件宽度 const offsetWidth = instance!.proxy!.$el.offsetWidth - // left boundary + // 面板的最大左边距 const maxLeft = offsetWidth - menuMinWidth - // 15: margin right + // 面板距离鼠标指针的距离 const left15 = e.clientX - offsetLeft + 15 - if (left15 > maxLeft) { - left.value = maxLeft - } else { - left.value = left15 - } + left.value = left15 > maxLeft ? maxLeft : left15 top.value = e.clientY + // 显示面板 visible.value = true + // 更新当前正在右键操作的标签页 selectedTag.value = tag } +/** 关闭右键菜单面板 */ const closeMenu = () => { visible.value = false } @@ -148,11 +158,7 @@ watch( ) watch(visible, (value) => { - if (value) { - document.body.addEventListener("click", closeMenu) - } else { - document.body.removeEventListener("click", closeMenu) - } + value ? document.body.addEventListener("click", closeMenu) : document.body.removeEventListener("click", closeMenu) }) onMounted(() => { @@ -168,10 +174,10 @@ onMounted(() => { ref="tagRefs" v-for="tag in tagsViewStore.visitedViews" :key="tag.path" - :class="isActive(tag) ? 'active' : ''" - :to="{ path: tag.path, query: tag.query }" + :class="{ active: isActive(tag) }" class="tags-view-item" - @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''" + :to="{ path: tag.path, query: tag.query }" + @click.middle="!isAffix(tag) && closeSelectedTag(tag)" @contextmenu.prevent="openMenu(tag, $event)" > {{ tag.meta?.title }} @@ -180,7 +186,7 @@ onMounted(() => { -