路由修正

This commit is contained in:
2025-11-14 19:17:49 +08:00
parent 9e4cfc1200
commit cb401eebe1
8 changed files with 54 additions and 98 deletions

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M17 2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H7V0H9V2H15V0H17V2ZM17 4V6H15V4H9V6H7V4H5V20H19V4H17ZM7 8H17V10H7V8ZM7 12H17V14H7V12Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -8,6 +8,7 @@ import type { Router, NavigationGuardNext, RouteLocationNormalized } from 'vue-r
import type { Store } from 'vuex';
import { AuthState } from '@/store/modules/auth';
import { systemOverviewApi } from '@/apis/system/overview';
import { routes as staticRoutes } from '@/router';
/**
* 白名单路由 - 无需登录即可访问
@@ -50,8 +51,9 @@ export function setupRouterGuards(router: Router, store: Store<any>) {
// 设置页面标题
setPageTitle(to.meta?.title as string);
// 统计PV对path以"/"开头的路由进行统计
if (to.path.startsWith('/')) {
// 统计PV对path以"/"开头的路由进行统计 非静态路由
const isStaticRoute = staticRoutes.some(route => route.path === to.matched[0]?.path);
if (!isStaticRoute && to.path.startsWith('/')) {
systemOverviewApi.trackVisit().catch(() => {
// 统计失败不影响正常页面使用
});

View File

@@ -186,9 +186,12 @@ function generateRouteFromMenu(menu: SysMenu, isTopLevel = true): RouteRecordRaw
}
});
// 当前菜单指定了页面组件时,即使存在子菜单也应当渲染该页面
// 但如果 component 是布局组件,则不应该创建 path: '' 的子路由(因为布局组件已经是父路由的组件了)
if (menu.component && !isComponentLayout) {
// 当前菜单指定了页面组件时,如果存在“普通子菜单”(非 PAGE 类型)
// 则需要创建一个默认子路由来承载当前菜单的页面组件,
// 这样父级既能作为分组,又能渲染自己的页面。
// 如果只有 PAGE 类型子菜单,则直接使用当前路由组件,而不再包一层 `_page`
// 避免多出一层嵌套导致 matched 结构过深。
if (menu.component && !isComponentLayout && normalChildren.length > 0) {
route.children!.push({
path: '',
name: `${menu.menuID}_page`,

View File

@@ -1,33 +1,40 @@
import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router';
export function getParentChildrenRoutes(route: RouteLocationNormalized): RouteRecordRaw[] {
// 判断是否有父节点至少需要2个匹配路由
if (route.matched.length < 2) {
// 没有任何匹配路由,直接返回空
if (!route.matched || route.matched.length === 0) {
return [];
}
// 获取倒数第二个匹配的路由(父路由)
const parentRoute = route.matched[route.matched.length - 2];
// 检查父路由是否有子路由
if (!parentRoute?.children || parentRoute.children.length === 0) {
return [];
// 从当前路由向上查找,找到第一个“有可见子路由”的父级
for (let i = route.matched.length - 2; i >= 0; i--) {
const parentRoute = route.matched[i];
const children = parentRoute?.children || [];
if (!children || children.length === 0) {
continue;
}
const visibleChildren = children.filter((child: RouteRecordRaw) => {
// 必须有 title
if (!child.meta?.title) {
return false;
}
// 排除 path 为空字符串的子路由(通常是默认子路由)
if (child.path === '' || child.path === undefined) {
return false;
}
// 排除 name 以 _page 结尾的子路由(父路由的默认子路由)
if (typeof child.name === 'string' && child.name.endsWith('_page')) {
return false;
}
return true;
});
if (visibleChildren.length > 0) {
return visibleChildren;
}
}
// 返回有 title 的子路由但排除父路由本身path 为空字符串或 name 以 _page 结尾的子路由)
return parentRoute.children.filter((child: RouteRecordRaw) => {
// 必须有 title
if (!child.meta?.title) {
return false;
}
// 排除 path 为空字符串的子路由(代表父路由本身)
if (child.path === '' || child.path === undefined) {
return false;
}
// 排除 name 以 _page 结尾的子路由(父路由的默认子路由)
if (typeof child.name === 'string' && child.name.endsWith('_page')) {
return false;
}
return true;
});
return [];
}

View File

@@ -49,11 +49,12 @@ defineProps<{
}>();
const route = useRoute();
console.log(route.path);
// 获取兄弟路由(计算属性,确保响应式更新)
const menus = computed(() => {
return getParentChildrenRoutes(route);
});
console.log(menus.value);
// 获取完整路径
function getFullPath(menu: RouteRecordRaw): string {

View File

@@ -1,56 +0,0 @@
<template>
<AdminLayout
title="资源管理"
subtitle="管理文章、资源、数据等内容"
>
<div class="data-records">
<el-tabs v-model="activeTab">
<el-tab-pane label="菜单管理" name="menu">
<!-- 菜单管理已在manage/system中实现这里可以引用或重新实现 -->
<div class="redirect-info">
<p>菜单管理功能已在系统管理模块中实现</p>
<el-button type="primary" @click="goToMenuManage">前往菜单管理</el-button>
</div>
</el-tab-pane>
</el-tabs>
</div>
</AdminLayout>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElTabs, ElTabPane, ElButton } from 'element-plus';
import { AdminLayout } from '@/views/admin';
defineOptions({
name: 'DataRecordsView'
});
const router = useRouter();
const activeTab = ref('menu');
function goToMenuManage() {
router.push('/admin/system/menu');
}
</script>
<style lang="scss" scoped>
.data-records {
background: #FFFFFF;
padding: 24px;
border-radius: 14px;
}
.redirect-info {
padding: 40px;
text-align: center;
p {
font-size: 16px;
color: #666;
margin-bottom: 20px;
}
}
</style>

View File

@@ -1,11 +1,10 @@
<template>
<AdminLayout
title="资源管理"
subtitle="管理文章、资源、数据等内容"
title="数据采集"
subtitle="管理新闻采集数据"
>
<div class="resource-management">
<div class="header">
<h2>数据采集管理</h2>
<div class="header-actions">
<el-button type="primary" @click="handleRefresh">
<el-icon><Refresh /></el-icon>
@@ -710,7 +709,7 @@ onMounted(() => {
.header {
display: flex;
justify-content: space-between;
justify-content: flex-end;
align-items: center;
margin-bottom: 20px;