refactor: ♻️ 项目重构(临时提交)
Former-commit-id: 14e64324f7556c095c38ad289690367cdac0ec56
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||
import { translateRouteTitle } from "@/utils/i18n";
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import path from "path-browserify";
|
||||
import { isExternal } from "@/utils/index";
|
||||
import AppLink from "./Link.vue";
|
||||
import { RouteRecordRaw } from "vue-router";
|
||||
|
||||
import Item from "./Item.vue";
|
||||
|
||||
@@ -38,15 +39,19 @@ const onlyOneChild = ref(); // 临时变量,唯一子路由
|
||||
* @param children 子路由数组
|
||||
* @param parent 当前路由
|
||||
*/
|
||||
function hasOneShowingChild(children = [], parent: any) {
|
||||
function hasOneShowingChild(
|
||||
children: RouteRecordRaw[] = [],
|
||||
parent: RouteRecordRaw
|
||||
) {
|
||||
// 子路由集合
|
||||
const showingChildren = children.filter((item: any) => {
|
||||
if (item.meta?.hidden) {
|
||||
const showingChildren = children.filter((route: RouteRecordRaw) => {
|
||||
if (route.meta?.hidden) {
|
||||
// 过滤不显示的子路由
|
||||
return false;
|
||||
} else {
|
||||
route.meta!.hidden = false;
|
||||
// 临时变量(多个子路由 onlyOneChild 变量是用不上的)
|
||||
onlyOneChild.value = item;
|
||||
onlyOneChild.value = route;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -87,7 +92,7 @@ function resolvePath(routePath: string) {
|
||||
<!-- 无子路由 || 目录只有一个子路由并配置始终显示为否(alwaysShow=false) -->
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
hasOneShowingChild(item.children, item as RouteRecordRaw) &&
|
||||
(!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
|
||||
!item.meta?.alwaysShow
|
||||
"
|
||||
|
||||
@@ -61,13 +61,6 @@ onMounted(() => {
|
||||
</template>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<!-- <sidebar-item
|
||||
v-for="route in topMenu"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
:is-collapse="false"
|
||||
/> -->
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
|
||||
const tagAndTagSpacing = ref(4);
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
@@ -1,21 +1,76 @@
|
||||
<template>
|
||||
<div class="tags-container">
|
||||
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll">
|
||||
<router-link
|
||||
ref="tagRef"
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.path"
|
||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
|
||||
:data-path="tag.path"
|
||||
:to="{ path: tag.path, query: tag.query }"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openTagMenu(tag, $event)"
|
||||
>
|
||||
{{ translateRouteTitle(tag.title) }}
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="tags-item-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
>
|
||||
<i-ep-close size="10px" />
|
||||
</span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
|
||||
<!-- tag标签操作菜单 -->
|
||||
<ul
|
||||
v-show="tagMenuVisible"
|
||||
class="tag-menu"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="refresh" />
|
||||
刷新
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="close" />
|
||||
关闭
|
||||
</li>
|
||||
<li @click="closeOtherTags">
|
||||
<svg-icon icon-class="close_other" />
|
||||
关闭其它
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<svg-icon icon-class="close_left" />
|
||||
关闭左侧
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<svg-icon icon-class="close_right" />
|
||||
关闭右侧
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<svg-icon icon-class="close_all" />
|
||||
关闭所有
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, ComponentInternalInstance } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
import path from "path-browserify";
|
||||
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useRoute, useRouter, RouteRecordRaw } from "vue-router";
|
||||
import { resolve } from "path-browserify";
|
||||
|
||||
import { translateRouteTitle } from "@/utils/i18n";
|
||||
|
||||
import { usePermissionStore } from "@/store/modules/permission";
|
||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
import { useAppStore } from "@/store/modules/app";
|
||||
|
||||
import ScrollPane from "./ScrollPane.vue";
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -27,11 +82,19 @@ const { visitedViews } = storeToRefs(tagsViewStore);
|
||||
const settingsStore = useSettingsStore();
|
||||
const layout = computed(() => settingsStore.layout);
|
||||
|
||||
const selectedTag = ref({});
|
||||
const selectedTag = ref<TagView>({
|
||||
path: "",
|
||||
fullPath: "",
|
||||
name: "",
|
||||
title: "",
|
||||
affix: false,
|
||||
keepAlive: false,
|
||||
});
|
||||
|
||||
const affixTags = ref<TagView[]>([]);
|
||||
const scrollPaneRef = ref();
|
||||
const left = ref(0);
|
||||
const top = ref(0);
|
||||
const affixTags = ref<TagView[]>([]);
|
||||
|
||||
watch(
|
||||
route,
|
||||
@@ -40,8 +103,7 @@ watch(
|
||||
moveToCurrentTag();
|
||||
},
|
||||
{
|
||||
//初始化立即执行
|
||||
immediate: true,
|
||||
immediate: true, //初始化立即执行
|
||||
}
|
||||
);
|
||||
|
||||
@@ -54,27 +116,31 @@ watch(tagMenuVisible, (value) => {
|
||||
}
|
||||
});
|
||||
|
||||
function filterAffixTags(routes: any[], basePath = "/") {
|
||||
let tags: TagView[] = [];
|
||||
function filterAffixTags(routes: RouteRecordRaw[], basePath = "/") {
|
||||
const processRoute = (route: RouteRecordRaw) => {
|
||||
const fullPath = resolve(basePath, route.path);
|
||||
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta },
|
||||
});
|
||||
const tag: TagView = {
|
||||
path: route.path,
|
||||
fullPath,
|
||||
name: String(route.name),
|
||||
title: route.meta?.title || "no-name",
|
||||
affix: route.meta?.affix,
|
||||
keepAlive: route.meta?.keepAlive,
|
||||
};
|
||||
|
||||
if (tag.affix) {
|
||||
tags.push(tag);
|
||||
}
|
||||
|
||||
if (route.children) {
|
||||
const childTags = filterAffixTags(route.children, route.path);
|
||||
if (childTags.length >= 1) {
|
||||
tags = tags.concat(childTags);
|
||||
}
|
||||
route.children.forEach(processRoute);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let tags: TagView[] = [];
|
||||
routes.forEach(processRoute);
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
@@ -90,19 +156,35 @@ function initTags() {
|
||||
}
|
||||
|
||||
function addTags() {
|
||||
if (route.name) {
|
||||
tagsViewStore.addView(route);
|
||||
if (route.meta.title) {
|
||||
tagsViewStore.addView({
|
||||
name: route.name as string,
|
||||
title: route.meta.title,
|
||||
path: route.path,
|
||||
fullPath: route.fullPath,
|
||||
affix: route.meta?.affix,
|
||||
keepAlive: route.meta?.keepAlive,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function moveToCurrentTag() {
|
||||
// 使用 nextTick() 的目的是确保在更新 tagsView 组件之前,scrollPaneRef 对象已经滚动到了正确的位置。
|
||||
nextTick(() => {
|
||||
for (const r of tagsViewStore.visitedViews) {
|
||||
if (r.path === route.path) {
|
||||
scrollPaneRef.value.moveToTarget(r);
|
||||
for (const tag of visitedViews.value) {
|
||||
if (tag.path === route.path) {
|
||||
scrollPaneRef.value.moveToTarget(tag);
|
||||
// when query is different then update
|
||||
if (r.fullPath !== route.fullPath) {
|
||||
tagsViewStore.updateVisitedView(route);
|
||||
route.query = { ...route.query, ...tag.query };
|
||||
if (tag.fullPath !== route.fullPath) {
|
||||
tagsViewStore.updateVisitedView({
|
||||
name: route.name as string,
|
||||
title: route.meta.title,
|
||||
path: route.path,
|
||||
fullPath: route.fullPath,
|
||||
affix: route.meta?.affix,
|
||||
keepAlive: route.meta?.keepAlive,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,19 +192,18 @@ function moveToCurrentTag() {
|
||||
}
|
||||
|
||||
function isActive(tag: TagView) {
|
||||
return tag.path === route.path;
|
||||
return tag.fullPath === route.fullPath;
|
||||
}
|
||||
|
||||
function isAffix(tag: TagView) {
|
||||
return tag.meta && tag.meta.affix;
|
||||
return tag?.affix;
|
||||
}
|
||||
|
||||
function isFirstView() {
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath === "/dashboard" ||
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
tagsViewStore.visitedViews[1].fullPath
|
||||
selectedTag.value.fullPath === "/dashboard" ||
|
||||
selectedTag.value.fullPath === tagsViewStore.visitedViews[1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
@@ -132,7 +213,7 @@ function isFirstView() {
|
||||
function isLastView() {
|
||||
try {
|
||||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
selectedTag.value.fullPath ===
|
||||
tagsViewStore.visitedViews[tagsViewStore.visitedViews.length - 1].fullPath
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -144,20 +225,18 @@ function refreshSelectedTag(view: TagView) {
|
||||
tagsViewStore.delCachedView(view);
|
||||
const { fullPath } = view;
|
||||
nextTick(() => {
|
||||
router.replace({ path: "/redirect" + fullPath }).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
router.replace({ path: "/redirect" + fullPath });
|
||||
});
|
||||
}
|
||||
|
||||
function toLastView(visitedViews: TagView[], view?: any) {
|
||||
function toLastView(visitedViews: TagView[], view?: TagView) {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView && latestView.fullPath) {
|
||||
router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === "Dashboard") {
|
||||
if (view?.name === "Dashboard") {
|
||||
// to reload home page
|
||||
router.replace({ path: "/redirect" + view.fullPath });
|
||||
} else {
|
||||
@@ -289,63 +368,6 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tags-container">
|
||||
<scroll-pane ref="scrollPaneRef" @scroll="handleScroll">
|
||||
<router-link
|
||||
v-for="tag in visitedViews"
|
||||
:key="tag.path"
|
||||
:class="'tags-item ' + (isActive(tag) ? 'active' : '')"
|
||||
:data-path="tag.path"
|
||||
:to="{ path: tag.path, query: tag.query }"
|
||||
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||
@contextmenu.prevent="openTagMenu(tag, $event)"
|
||||
>
|
||||
{{ translateRouteTitle(tag.meta?.title) }}
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="tags-item-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
>
|
||||
<i-ep-close class="text-[10px]" />
|
||||
</span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
|
||||
<!-- tag标签操作菜单 -->
|
||||
<ul
|
||||
v-show="tagMenuVisible"
|
||||
class="tag-menu"
|
||||
:style="{ left: left + 'px', top: top + 'px' }"
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="refresh" />
|
||||
刷新
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="close" />
|
||||
关闭
|
||||
</li>
|
||||
<li @click="closeOtherTags">
|
||||
<svg-icon icon-class="close_other" />
|
||||
关闭其它
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<svg-icon icon-class="close_left" />
|
||||
关闭左侧
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<svg-icon icon-class="close_right" />
|
||||
关闭右侧
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<svg-icon icon-class="close_all" />
|
||||
关闭所有
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tags-container {
|
||||
width: 100%;
|
||||
|
||||
@@ -30,6 +30,8 @@ const mixLeftMenu = computed(() => {
|
||||
return permissionStore.mixLeftMenu;
|
||||
});
|
||||
const layout = computed(() => settingsStore.layout);
|
||||
const watermarkEnabled = computed(() => settingsStore.watermark.enabled);
|
||||
|
||||
watch(
|
||||
() => activeTopMenu.value,
|
||||
(newVal) => {
|
||||
@@ -78,29 +80,31 @@ function toggleSideBar() {
|
||||
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<!-- 手机设备侧边栏打开遮罩层 -->
|
||||
<div
|
||||
v-if="classObj.mobile && classObj.openSidebar"
|
||||
class="drawer-bg"
|
||||
@click="handleOutsideClick"
|
||||
></div>
|
||||
<el-watermark content="vue3-element-admin">
|
||||
<!-- 手机设备侧边栏打开遮罩层 -->
|
||||
<div
|
||||
v-if="classObj.mobile && classObj.openSidebar"
|
||||
class="drawer-bg"
|
||||
@click="handleOutsideClick"
|
||||
></div>
|
||||
|
||||
<Sidebar class="sidebar-container" />
|
||||
<template v-if="layout === 'mix'">
|
||||
<div class="mix-wrap">
|
||||
<div class="left-wrap">
|
||||
<LeftMenu :menu-list="mixLeftMenu" :base-path="activeTopMenu" />
|
||||
<div class="menu-action">
|
||||
<hamburger
|
||||
:is-active="appStore.sidebar.opened"
|
||||
@toggle-click="toggleSideBar"
|
||||
/>
|
||||
<Sidebar class="sidebar-container" />
|
||||
<template v-if="layout === 'mix'">
|
||||
<div class="mix-wrap">
|
||||
<div class="left-wrap">
|
||||
<LeftMenu :menu-list="mixLeftMenu" :base-path="activeTopMenu" />
|
||||
<div class="menu-action">
|
||||
<hamburger
|
||||
:is-active="appStore.sidebar.opened"
|
||||
@toggle-click="toggleSideBar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Main />
|
||||
</div>
|
||||
<Main />
|
||||
</div>
|
||||
</template>
|
||||
<Main v-else />
|
||||
</template>
|
||||
<Main v-else />
|
||||
</el-watermark>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user