BirtYu
2025-04-13 fdd689daf5cf4c2083e6160d932d70e91bad46e1
Merge pull request #59 from zhitan-cloud/jiayu1.0

Jiayu1.0
已修改11个文件
730 ■■■■ 文件已修改
zhitan-vue/src/assets/styles/sidebar.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/components/TopNav/index.vue 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/AppMain.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/Sidebar/index.vue 165 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/TagsView/ScrollPane.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/components/TagsView/index.vue 152 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/layout/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/permission.js 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/router/index.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/store/modules/permission.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
zhitan-vue/src/assets/styles/sidebar.scss
@@ -13,6 +13,7 @@
    .main-container {
      background-color: #110f2e !important;
      height: 100%;
      transition: margin-left 0.28s;
      margin-left: $base-sidebar-width;
@@ -28,7 +29,7 @@
      transition: width 0.28s;
      width: $base-sidebar-width !important;
      background-color: $base-menu-background;
      height: 100%;
      height: calc(100% - 60px) !important;
      position: fixed;
      top: 60px;
      bottom: 0;
@@ -321,7 +322,7 @@
      transition: width 0.28s;
      width: $base-sidebar-width !important;
      background-color: $base-menu-background;
      height: 100%;
      height: calc(100% - 60px) !important;
      position: fixed;
      top: 60px;
      bottom: 0;
zhitan-vue/src/components/TopNav/index.vue
@@ -135,6 +135,28 @@
  setTimeout(updateScrollButtons, 300);
}
/**
 * 查找最深层的子菜单(叶子节点)
 * 递归查找第一个没有children的子菜单
 */
function findDeepestLeafMenu(route) {
  if (!route) return null;
  // 如果没有子菜单或子菜单为空,则返回当前路由
  if (!route.children || route.children.length === 0) {
    return route;
  }
  // 找到第一个非隐藏的子菜单
  const firstVisibleChild = route.children.find(child => !child.hidden);
  if (!firstVisibleChild) {
    return route; // 如果所有子菜单都是隐藏的,返回当前路由
  }
  // 递归查找这个子菜单的最深层子菜单
  return findDeepestLeafMenu(firstVisibleChild);
}
function handleSelect(key, keyPath) {
  currentIndex.value = key;
  const route = routers.value.find(item => item.path === key);
@@ -145,23 +167,76 @@
    return;
  } 
  
  if (key === '/index' || key === '/') {
    // 首页时显示折叠的侧边栏,而不是隐藏
    router.push({ path: key });
    appStore.showCollapsedSidebar();
    return;
  }
  
  // 检查是否有子路由
  if (route && route.children && route.children.length > 0) {
    // 有子路由,显示侧边栏
    activeRoutes(key);
    const firstChild = route.children[0];
    const path = firstChild.path.startsWith('/') ? firstChild.path : `${key}/${firstChild.path}`;
    if (firstChild.query) {
      router.push({ path, query: firstChild.query });
    // 按照正确的路径构建层级,这里是特殊处理
    let targetPath = key; // 从当前点击的菜单路径开始
    let targetQuery = null;
    let currentNode = route;
    let pathSegments = [];
    // 当前路径是第一段
    pathSegments.push(currentNode.path);
    // 逐层添加子路径
    while (currentNode.children && currentNode.children.length > 0) {
      const firstChild = currentNode.children.find(child => !child.hidden);
      if (!firstChild) break;
      // 跳过ParentView类型的中间节点,直接使用其子节点的path
      if (firstChild.component === 'ParentView' || firstChild.component.name === 'ParentView') {
        currentNode = firstChild;
        pathSegments.push(firstChild.path);
        continue;
      }
      // 普通节点处理
      currentNode = firstChild;
      // 如果路径不是以/开头,则添加到路径片段中
      if (!firstChild.path.startsWith('/')) {
        pathSegments.push(firstChild.path);
      } else {
        // 如果是绝对路径,则替换之前所有路径
        pathSegments = [firstChild.path];
      }
      targetQuery = firstChild.query;
      // 如果到达叶子节点(没有子节点),则结束查找
      if (!firstChild.children || firstChild.children.length === 0) {
        break;
      }
    }
    // 构建最终路径
    if (pathSegments.length > 0) {
      // 如果第一段不是以/开头,添加/
      if (!pathSegments[0].startsWith('/')) {
        pathSegments[0] = '/' + pathSegments[0];
      }
      // 组合路径 - 把数组中所有路径拼接起来,如果某段包含完整路径(以/开头)则从该段重新开始
      targetPath = pathSegments.reduce((fullPath, segment, index) => {
        if (segment.startsWith('/')) {
          return segment;
        } else if (index === 0) {
          return segment;
        } else {
          return `${fullPath}/${segment}`;
        }
      });
    }
    // 导航到目标路由
    if (targetQuery) {
      router.push({ path: targetPath, query: targetQuery });
    } else {
      router.push({ path });
      router.push({ path: targetPath });
    }
  } else {
    // 没有子路由,隐藏侧边栏
@@ -178,11 +253,7 @@
function activeRoutes(key) {
  let routes = [];
  if (key === '/index' || key === '/') {
    // 首页时显示折叠的侧边栏,而不是隐藏
    appStore.showCollapsedSidebar();
    return [];
  }
  
  // 查找匹配的路由
  if (childrenMenus.value && childrenMenus.value.length > 0) {
zhitan-vue/src/layout/components/AppMain.vue
@@ -29,6 +29,7 @@
    background: #110f2e;
    padding: 14px;
    box-sizing: border-box;
    padding-top: 8px;
  }
  .fixed-header + .app-main {
@@ -42,7 +43,7 @@
    }
    .fixed-header + .app-main {
      padding-top: 130px;
      padding-top: 108px; /* 60px(navbar) + 34px(tagsview) + 14px(内边距) */
    }
  }
}
@@ -70,7 +71,7 @@
    }
    .fixed-header + .app-main {
      padding-top: 130px;
      padding-top: 108px; /* 60px(navbar) + 34px(tagsview) + 14px(内边距) */
    }
  }
}
zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue
@@ -9,7 +9,7 @@
      </app-link>
    </template>
    <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
    <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported @click="handleSubMenuClick">
      <template v-if="item.meta" #title>
        <svg-icon :icon-class="item.meta && item.meta.icon" />
        <span class="menu-title" :title="hasTitle(item.meta.title)">{{ item.meta.title }}</span>
@@ -31,6 +31,9 @@
import { isExternal } from '@/utils/validate'
import AppLink from './Link'
import { getNormalPath } from '@/utils/ruoyi'
import { useRouter } from 'vue-router'
const router = useRouter();
const props = defineProps({
  // route object
@@ -50,6 +53,100 @@
const onlyOneChild = ref({});
/**
 * 查找最深层的子菜单(叶子节点)
 * 递归查找第一个没有children的子菜单
 */
function findDeepestLeafMenu(route) {
  if (!route) return null;
  // 如果没有子菜单或子菜单为空,则返回当前路由
  if (!route.children || route.children.length === 0) {
    return route;
  }
  // 找到第一个非隐藏的子菜单
  const firstVisibleChild = route.children.find(child => !child.hidden);
  if (!firstVisibleChild) {
    return route; // 如果所有子菜单都是隐藏的,返回当前路由
  }
  // 递归查找这个子菜单的最深层子菜单
  return findDeepestLeafMenu(firstVisibleChild);
}
// 处理子菜单点击
function handleSubMenuClick(e) {
  // 阻止事件冒泡
  e.stopPropagation();
  // 如果点击的是子菜单标题,则自动导航到最深层的子菜单
  if (e.target.closest('.el-sub-menu__title')) {
    // 按照正确的路径构建层级
    let currentNode = props.item;
    let pathSegments = [];
    // 首先添加当前节点的路径
    if (currentNode.path) {
      pathSegments.push(currentNode.path);
    }
    // 逐层添加子路径
    while (currentNode.children && currentNode.children.length > 0) {
      const firstChild = currentNode.children.find(child => !child.hidden);
      if (!firstChild) break;
      // 跳过ParentView类型的中间节点
      if (firstChild.component === 'ParentView' || firstChild.component.name === 'ParentView') {
        currentNode = firstChild;
        pathSegments.push(firstChild.path);
        continue;
      }
      // 普通节点处理
      currentNode = firstChild;
      // 如果路径不是以/开头,则添加到路径片段中
      if (!firstChild.path.startsWith('/')) {
        pathSegments.push(firstChild.path);
      } else {
        // 如果是绝对路径,则替换之前所有路径
        pathSegments = [firstChild.path];
      }
      // 如果到达叶子节点,则结束查找
      if (!firstChild.children || firstChild.children.length === 0) {
        break;
      }
    }
    // 构建最终路径
    if (pathSegments.length > 0) {
      // 如果第一段不是以/开头,添加/
      if (!pathSegments[0].startsWith('/')) {
        pathSegments[0] = '/' + pathSegments[0];
      }
      // 组合路径
      const targetPath = pathSegments.reduce((fullPath, segment, index) => {
        if (segment.startsWith('/')) {
          return segment;
        } else if (index === 0) {
          return segment;
        } else {
          return `${fullPath}/${segment}`;
        }
      });
      // 导航到目标路由,如果有查询参数则添加
      if (currentNode.query) {
        router.push({ path: targetPath, query: currentNode.query });
      } else {
        router.push({ path: targetPath });
      }
    }
  }
}
function hasOneShowingChild(children = [], parent) {
  if (!children) {
    children = [];
@@ -65,10 +162,15 @@
  })
  // When there is only one child router, the child router is displayed by default
  if (showingChildren.length === 1) {
  if (showingChildren.length === 1 && !showingChildren[0].children) {
    return true
  }
  // If the single child also has children, don't treat it as a single showing child
  if (showingChildren.length === 1 && showingChildren[0].children && showingChildren[0].children.length > 0) {
    return false
  }
  // Show parent if there are no child router to display
  if (showingChildren.length === 0) {
    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
zhitan-vue/src/layout/components/Sidebar/index.vue
@@ -5,9 +5,8 @@
    class="sidebar-container-wrapper"
  >
    <el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper" view-class="scrollbar-view">
      <!-- 首页时不显示任何菜单项 -->
      <!-- 始终显示菜单项,不再根据路径判断 -->
      <el-menu
        v-if="!isHomePage"
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="'transparent'"
@@ -18,15 +17,24 @@
        mode="vertical"
        class="custom-menu"
      >
        <sidebar-item
          v-for="(route, index) in sidebarRouters"
          :key="route.path + index"
          :item="route"
          :base-path="route.path"
        />
        <!-- 当前是首页看板子路由时,渲染专用路由 -->
        <template v-if="isIndexSubRoute">
          <sidebar-item
            v-for="(route, index) in indexPageRouters"
            :key="route.path + index"
            :item="route"
            :base-path="route.path"
          />
        </template>
        <template v-else>
          <sidebar-item
            v-for="(route, index) in sidebarRouters"
            :key="route.path + index"
            :item="route"
            :base-path="route.path"
          />
        </template>
      </el-menu>
      <!-- 首页时的空白区域 -->
      <div v-else class="home-empty-menu"></div>
    </el-scrollbar>
    
    <!-- 底部用户区域 -->
@@ -95,19 +103,21 @@
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
// 判断当前是否为首页
const isHomePage = computed(() => {
  return route.path === '/index' || route.path === '/' || route.fullPath.startsWith('/index')
// 判断当前是否为首页子路由(/index/index)
const isIndexSubRoute = computed(() => {
  return route.path === '/index/index'
})
// 首页专用路由,只有首页一个菜单项
const homePageRouters = computed(() => {
  // 从原始路由中筛选出首页路由
  const homeRoute = sidebarRouters.value.find(route => {
    return route.children && route.children.find(child => child.path === '/index')
  })
  return homeRoute ? [homeRoute] : []
// 判断当前是否为主首页路由(/index或/)
const isMainIndexRoute = computed(() => {
  return route.path === '/index' || route.path === '/'
})
// 首页专用路由,首页看板相关菜单
const indexPageRouters = computed(() => {
  // 查找name为Index的路由
  const indexRoute = sidebarRouters.value.find(route => route.name === 'Index')
  return indexRoute ? [indexRoute] : []
})
const showLogo = computed(() => settingsStore.sidebarLogo)
@@ -162,12 +172,13 @@
}
:deep(.scrollbar-wrapper) {
  height: calc(100% - 290px) !important;
  height: calc(100% - 220px) !important;
  overflow-x: hidden !important;
}
:deep(.scrollbar-view) {
  height: 100%;
  padding-bottom: 20px;
}
:deep(.el-scrollbar__bar.is-vertical) {
@@ -200,6 +211,12 @@
    &.is-active {
      background-color: #3883FA !important;
      color: #fff !important;
      font-weight: bold;
      position: relative;
      box-shadow: 0 2px 8px rgba(56, 131, 250, 0.5);
      // 左侧指示条
    }
    
    &:hover {
@@ -221,6 +238,13 @@
      }
    }
    
    &.is-active {
      > .el-sub-menu__title {
        color: #3883FA !important;
        font-weight: bold;
      }
    }
    .el-menu-item {
      padding-left: 45px !important; 
      min-width: auto !important;
@@ -237,6 +261,28 @@
        height: 38px !important;
        line-height: 38px !important;
      }
      // Add styling for deeply nested submenus (level 3+)
      .el-sub-menu {
        .el-menu-item {
          padding-left: 65px !important;
          &.is-active {
            padding-left: 65px !important;
          }
        }
        // Level 4
        .el-menu {
          .el-menu-item {
            padding-left: 85px !important;
            &.is-active {
              padding-left: 85px !important;
            }
          }
        }
      }
    }
  }
}
@@ -250,7 +296,7 @@
// 底部用户区域样式
.sidebar-footer {
  position: absolute;
  bottom: 72px;
  bottom: 0;
  left: 0;
  width: 100%;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
@@ -425,6 +471,62 @@
  }
}
// 添加深色模式专用样式
.theme-dark {
  :deep(.custom-menu) {
    // Override Element Plus menu styles for dark theme
    .el-menu-item {
      &.is-active {
        background-color: #4e77f8 !important;
        color: #ffffff !important;
        font-weight: bold;
        box-shadow: 0 2px 10px rgba(78, 119, 248, 0.6);
        position: relative;
        // 左侧指示条
      }
      &:hover {
        background-color: rgba(78, 119, 248, 0.2) !important;
      }
    }
    .el-sub-menu {
      &.is-active {
        > .el-sub-menu__title {
          color: #4e77f8 !important;
          font-weight: bold;
        }
      }
      .el-sub-menu__title {
        &:hover {
          background-color: rgba(78, 119, 248, 0.2) !important;
        }
      }
      // 嵌套子菜单样式
      .el-menu {
        .el-menu-item {
          &.is-active {
            background-color: #4e77f8 !important;
            color: #ffffff !important;
          }
        }
        .el-sub-menu {
          &.is-active {
            > .el-sub-menu__title {
              color: #4e77f8 !important;
            }
          }
        }
      }
    }
  }
}
// Add global style to override Element Plus defaults
:global(.el-menu--vertical .el-menu-item),
:global(.el-menu--vertical .el-sub-menu__title) {
@@ -475,4 +577,21 @@
    outline: none;
  }
}
// 深色模式下折叠菜单的样式
.theme-dark {
  :deep(.custom-menu.el-menu--collapse) {
    .el-menu-item, .el-sub-menu__title {
      &.is-active {
        background-color: #4e77f8 !important;
        color: #ffffff !important;
        box-shadow: 0 2px 8px rgba(78, 119, 248, 0.6);
      }
      &:hover {
        background-color: rgba(78, 119, 248, 0.2) !important;
      }
    }
  }
}
</style>
zhitan-vue/src/layout/components/TagsView/ScrollPane.vue
@@ -99,7 +99,7 @@
    bottom: 0px;
  }
  :deep(.el-scrollbar__wrap) {
    height: 39px;
    height: 34px !important;
  }
}
</style>
zhitan-vue/src/layout/components/TagsView/index.vue
@@ -232,119 +232,49 @@
</script>
<style lang="scss" scoped>
.themeDark {
  .tags-view-container {
    height: 52px;
    width: 100%;
    background: #1a235d;
    // border-bottom: 1px solid #d8dce5;
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
    .tags-view-wrapper {
      .tags-view-item {
        display: inline-block;
        position: relative;
        cursor: pointer;
        height: 32px;
        line-height: 30px;
        border: 1px solid #5278f5;
        color: #fff;
        // background: #3271eb;
        padding: 0 12px;
        font-size: 14px;
        margin-left: 6px;
        margin-top: 10px;
        border-radius: 4px;
        font-family: OPPOSans, OPPOSans;
        &:first-of-type {
          margin-left: 16px;
        }
        &:last-of-type {
          margin-right: 15px;
        }
        &.active {
          background-color: #4e77f8 !important;
          color: #fff;
          border-color: #4e77f8 !important;
          &::before {
            content: "";
            background: #fff;
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            position: relative;
            margin-right: 5px;
          }
        }
      }
    }
    .contextmenu {
      margin: 0;
.tags-view-container {
  height: 34px;
  width: calc(100% - 42px);
  margin-top: 10px;
  margin-left: 14px;
  box-sizing: border-box;
 // 添加水平内边距与app-main的内边距一致
 background: #0A3465;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
  .tags-view-wrapper {
    .tags-view-item {
      display: inline-block;
      position: relative;
      cursor: pointer;
      height: 26px;
      line-height: 26px;
      border: 1px solid #d8dce5;
      color: #495060;
      background: #fff;
      z-index: 3000;
      position: absolute;
      list-style-type: none;
      padding: 5px 0;
      border-radius: 4px;
      padding: 0 8px;
      font-size: 12px;
      font-weight: 400;
      color: #333;
      box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
      li {
        margin: 0;
        padding: 7px 16px;
        cursor: pointer;
        &:hover {
          background: #eee;
        }
      margin-left: 5px;
      margin-top: 4px;
      &:first-of-type {
        margin-left: 5px;
      }
    }
  }
}
.themeLight {
  .tags-view-container {
    height: 52px;
    width: 100%;
    background: #fff;
    // border-bottom: 1px solid #d8dce5;
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
    .tags-view-wrapper {
      .tags-view-item {
        display: inline-block;
        position: relative;
        cursor: pointer;
        height: 32px;
        line-height: 30px;
        border: 1px solid #d8dce5;
        color: #495060;
        background: #fff;
        padding: 0 12px;
        font-size: 14px;
        margin-left: 6px;
        margin-top: 10px;
        border-radius: 4px;
        font-family: OPPOSans, OPPOSans;
        &:first-of-type {
          margin-left: 16px;
        }
        &:last-of-type {
          margin-right: 15px;
        }
        &.active {
          background-color: #42b983;
          color: #fff;
          border-color: #42b983;
          &::before {
            content: "";
            background: #fff;
            display: inline-block;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            position: relative;
            margin-right: 5px;
          }
      &:last-of-type {
        margin-right: 5px;
      }
      &.active {
        background-color: #42b983;
        color: #fff;
        border-color: #42b983;
        &::before {
          content: "";
          background: #fff;
          display: inline-block;
          width: 8px;
          height: 8px;
          border-radius: 50%;
          position: relative;
          margin-right: 2px;
        }
      }
    }
@@ -401,6 +331,6 @@
}
.scroll-container .el-scrollbar__wrap {
  height: 50px !important;
  height: 34px !important;
}
</style>
zhitan-vue/src/layout/index.vue
@@ -119,21 +119,26 @@
// 监听路由变化,处理首页的侧边栏显示
watchEffect(() => {
  // 检查是否是首页路由
  if (route.path === '/index' || route.path === '/') {
  // 检查是否是首页路由,但排除/index/index子路由
  if ((route.path === '/index' || route.path === '/') && route.path !== '/index/index') {
    // 首页路由,确保侧边栏不隐藏,但状态是折叠的
    appStore.showCollapsedSidebar()
    appStore.toggleSideBarHide(false) // 改为不隐藏侧边栏
  } else if (route.meta && route.meta.showSidebar === false) {
    // 如果路由明确指定隐藏侧边栏
    appStore.toggleSideBarHide(true)
  } else {
    // 其他路由,确保侧边栏可见
    appStore.toggleSideBarHide(false)
  }
})
// 组件挂载时,确保首页侧边栏状态正确
onMounted(() => {
  // 如果当前是首页,确保侧边栏是折叠的而不是隐藏的
  if (route.path === '/index' || route.path === '/') {
    appStore.showCollapsedSidebar()
  // 如果当前是首页子页面,只确保侧边栏不被隐藏,但保持折叠/展开状态不变
  if (route.path === '/index/index') {
    // 只设置不隐藏侧边栏,但不改变其展开/折叠状态
    appStore.toggleSideBarHide(false)
    // 不再强制设置opened为true,保持用户之前的设置
  }
})
@@ -306,13 +311,19 @@
  top: 60px;
  right: 0;
  z-index: 9;
  width: 100%;
  width: calc(100% - #{$base-sidebar-width});
  transition: width 0.28s;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  padding: 0;
}
.hideSidebar .fixed-header {
  width: calc(100% - 54px);
}
.sidebarHide .fixed-header {
  width: 100%;
}
zhitan-vue/src/permission.js
@@ -14,6 +14,75 @@
const whiteList = ['/login', '/register', '/energy']
/**
 * 查找最深层的子菜单并构建完整路径
 */
function findDeepestPath(route) {
  if (!route) return { path: null, query: null };
  // 首先添加当前节点的路径
  let currentNode = route;
  let pathSegments = [];
  if (currentNode.path) {
    pathSegments.push(currentNode.path);
  }
  // 逐层添加子路径
  while (currentNode.children && currentNode.children.length > 0) {
    const firstChild = currentNode.children.find(child => !child.hidden);
    if (!firstChild) break;
    // 跳过ParentView类型的中间节点
    if (firstChild.component === 'ParentView' ||
        (typeof firstChild.component === 'object' && firstChild.component.name === 'ParentView')) {
      currentNode = firstChild;
      pathSegments.push(firstChild.path);
      continue;
    }
    // 普通节点处理
    currentNode = firstChild;
    // 如果路径不是以/开头,则添加到路径片段中
    if (!firstChild.path.startsWith('/')) {
      pathSegments.push(firstChild.path);
    } else {
      // 如果是绝对路径,则替换之前所有路径
      pathSegments = [firstChild.path];
    }
    // 如果到达叶子节点,则结束查找
    if (!firstChild.children || firstChild.children.length === 0) {
      break;
    }
  }
  // 构建最终路径
  let targetPath = '';
  if (pathSegments.length > 0) {
    // 如果第一段不是以/开头,添加/
    if (!pathSegments[0].startsWith('/')) {
      pathSegments[0] = '/' + pathSegments[0];
    }
    // 组合路径
    targetPath = pathSegments.reduce((fullPath, segment, index) => {
      if (segment.startsWith('/')) {
        return segment;
      } else if (index === 0) {
        return segment;
      } else {
        return `${fullPath}/${segment}`;
      }
    });
  }
  return {
    path: targetPath,
    query: currentNode.query
  };
}
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
@@ -45,11 +114,23 @@
              if (topMenus.length > 0) {
                // 跳转到第一个菜单
                const firstMenu = topMenus[0]
                if (firstMenu.children && firstMenu.children.length > 0) {
                  // 有子菜单,跳转到第一个子菜单
                // 查找最深层的子菜单并构建路径
                const { path, query } = findDeepestPath(firstMenu);
                if (path) {
                  // 有最深层子菜单,跳转到该菜单
                  if (query) {
                    next({ path, query, replace: true });
                  } else {
                    next({ path, replace: true });
                  }
                  return;
                } else if (firstMenu.children && firstMenu.children.length > 0) {
                  // 使用原有逻辑
                  const firstChild = firstMenu.children[0]
                  const path = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
                  next({ path: path, replace: true })
                  const childPath = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
                  next({ path: childPath, replace: true })
                  return
                } else {
                  // 没有子菜单,直接跳转
@@ -75,11 +156,23 @@
          if (topMenus.length > 0) {
            // 跳转到第一个菜单
            const firstMenu = topMenus[0]
            if (firstMenu.children && firstMenu.children.length > 0) {
              // 有子菜单,跳转到第一个子菜单
            // 查找最深层的子菜单并构建路径
            const { path, query } = findDeepestPath(firstMenu);
            if (path) {
              // 有最深层子菜单,跳转到该菜单
              if (query) {
                next({ path, query, replace: true });
              } else {
                next({ path, replace: true });
              }
              return;
            } else if (firstMenu.children && firstMenu.children.length > 0) {
              // 使用原有逻辑
              const firstChild = firstMenu.children[0]
              const path = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
              next({ path: path, replace: true })
              const childPath = firstMenu.path.endsWith('/') ? firstMenu.path + firstChild.path : `${firstMenu.path}/${firstChild.path}`
              next({ path: childPath, replace: true })
              return
            } else {
              // 没有子菜单,直接跳转
@@ -88,6 +181,26 @@
            }
          }
        }
        // 自动处理带有重定向的路由
        if (to.matched.length > 0 && to.matched[0].path === to.path) {
          const currentRouteConfig = router.getRoutes().find(r => r.path === to.path);
          if (currentRouteConfig && currentRouteConfig.children && currentRouteConfig.children.length > 0) {
            // 有子路由,自动导航到最深层子菜单
            const { path, query } = findDeepestPath(currentRouteConfig);
            if (path && path !== to.path) {
              if (query) {
                next({ path, query, replace: true });
              } else {
                next({ path, replace: true });
              }
              return;
            }
          }
        }
        next()
      }
    }
zhitan-vue/src/router/index.js
@@ -67,13 +67,7 @@
        path: '/index',
        component: () => import('@/views/index'),
        name: 'Index',
        meta: { title: '首页', icon: 'dashboard', affix: true, showSidebar: true, breadcrumb: false },
        beforeEnter: (to, from, next) => {
          // 获取app store并设置侧边栏为折叠状态
          const appStore = useAppStore()
          appStore.showCollapsedSidebar()
          next()
        }
        meta: { title: '首页', icon: 'dashboard', affix: true, showSidebar: true, breadcrumb: false }
      }
    ]
  },
zhitan-vue/src/store/modules/permission.js
@@ -56,8 +56,27 @@
  })
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false, parentRoute = null) {
  return asyncRouterMap.filter(route => {
    // 不再过滤掉首页看板相关路由
    /*
    // 过滤掉首页看板相关路由
    if (route.name === 'Index' && route.meta && route.meta.title === '首页看板') {
      return false;
    }
    // 如果是首页看板的子菜单,也过滤掉
    if (route.path === '/index' || route.path === 'index' ||
        (route.meta && route.meta.title === '首页看板')) {
      return false;
    }
    */
    // 设置父路由引用
    if (parentRoute) {
      route.parent = parentRoute;
    }
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
@@ -82,7 +101,8 @@
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
      // 将当前路由作为父路由传递给子路由
      route.children = filterAsyncRouter(route.children, route, type, route)
    } else {
      delete route['children']
      delete route['redirect']
@@ -97,6 +117,9 @@
    if (el.children && el.children.length) {
      if (el.component === 'ParentView' && !lastRouter) {
        el.children.forEach(c => {
          // 设置父路由引用
          c.parent = el;
          c.path = el.path + '/' + c.path
          if (c.children && c.children.length) {
            children = children.concat(filterChildren(c.children, c))
@@ -108,6 +131,9 @@
      }
    }
    if (lastRouter) {
      // 设置父路由引用
      el.parent = lastRouter;
      el.path = lastRouter.path + '/' + el.path
      if (el.children && el.children.length) {
        children = children.concat(filterChildren(el.children, el))