refactor: ♻️ 项目重构(临时提交)

Former-commit-id: 14e64324f7556c095c38ad289690367cdac0ec56
This commit is contained in:
郝先瑞
2023-11-16 22:46:28 +08:00
parent c1725e0b09
commit 12c78f96d7
34 changed files with 2129 additions and 399 deletions

View File

@@ -5,7 +5,6 @@
</template>
<script setup lang="ts">
import SvgIcon from "@/components/SvgIcon/index.vue";
import { translateRouteTitle } from "@/utils/i18n";
defineProps({

View File

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

View File

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

View File

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

View File

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

View File

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