zhitan-vue/src/assets/styles/page.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/assets/styles/sidebar.scss | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/components/Breadcrumb/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/layout/components/TagsView/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/permission.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/store/modules/permission.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
zhitan-vue/src/views/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
zhitan-vue/src/assets/styles/page.scss
@@ -163,9 +163,9 @@ display: flex; .page-container-left { width: 280px; width: 220px; min-height: calc(100vh - 148px); border-right: 1px solid #fff; border-right: 1px solid #e8e8e8; background: #f1f4fa; .el-tree { @@ -194,8 +194,8 @@ } .tree { height: calc(100vh - 170px) !important; max-height: calc(100vh - 170px) !important; height: calc(100vh - 170px); max-height: calc(100vh - 170px); overflow-y: auto; } } zhitan-vue/src/assets/styles/sidebar.scss
@@ -307,6 +307,7 @@ } .main-container { background-color: #f7f8fa; height: 100%; transition: margin-left 0.28s; margin-left: $base-sidebar-width; @@ -390,7 +391,7 @@ .menu-title { overflow: hidden !important; font-weight: 400 !important; font-size: 16px !important; font-size: 14px !important; } // @media (min-width: 1440px) { zhitan-vue/src/components/Breadcrumb/index.vue
@@ -19,20 +19,29 @@ let matched = route.matched.filter(item => item.meta && item.meta.title); const first = matched[0] // 不自动添加首页到面包屑中 // if (!isDashboard(first)) { // matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched) // } // 添加调试日志 console.log('Current route path:', route.path); console.log('Route matched:', route.matched); console.log('Filtered matched routes:', matched); // 如果是首页看板路由,确保它被添加到面包屑中 if (route.path === '/index' || route.path === '/index/index') { matched = [{ path: '/index', meta: { title: '首页看板' } }].concat(matched) console.log('Added index route to matched:', matched); } levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) console.log('Final breadcrumb items:', levelList.value); } function isDashboard(route) { const name = route && route.name if (!name) { return false } return name.trim() === 'Index' return name.trim().toLowerCase() === 'index' } function handleLink(item) { const { redirect, path } = item if (redirect) { zhitan-vue/src/layout/components/Sidebar/SidebarItem.vue
@@ -84,65 +84,169 @@ if (e.target.closest('.el-sub-menu__title')) { // 按照正确的路径构建层级 let currentNode = props.item; let pathSegments = []; // 首先添加当前节点的路径 if (currentNode.path) { pathSegments.push(currentNode.path); console.log('当前点击的菜单项:', JSON.stringify(currentNode, null, 2)); console.log('basePath:', props.basePath); // 获取第一个可见子菜单,如果没有可见子菜单,不进行跳转 if (!currentNode.children || currentNode.children.length === 0) { return; } // 逐层添加子路径 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; const firstVisibleChild = currentNode.children.find(child => !child.hidden); if (!firstVisibleChild) { return; } // 普通节点处理 currentNode = firstChild; // 如果路径不是以/开头,则添加到路径片段中 if (!firstChild.path.startsWith('/')) { pathSegments.push(firstChild.path); console.log('第一个可见子菜单:', JSON.stringify(firstVisibleChild, null, 2)); // 日志管理等三级菜单特殊处理 // 检查是否有预先写入的完整路径,如果有则直接使用 if (firstVisibleChild.fullPath) { console.log('使用预先设置的完整路径:', firstVisibleChild.fullPath); router.push({ path: firstVisibleChild.fullPath }); return; } // 判断是否是系统/日志管理类型的三级菜单(例如,/system/log/operlog) // 这种情况下,直接跳转到第一个子菜单的完整路径 if (firstVisibleChild.component === 'ParentView' || (typeof firstVisibleChild.component === 'object' && firstVisibleChild.component.name === 'ParentView')) { console.log('检测到ParentView组件,处理三级菜单'); // 是有三级菜单的情况 if (firstVisibleChild.children && firstVisibleChild.children.length > 0) { const grandChild = firstVisibleChild.children.find(child => !child.hidden); if (grandChild) { console.log('找到第三级菜单:', JSON.stringify(grandChild, null, 2)); // 判断是否应该使用parentPath if (firstVisibleChild.parentPath && grandChild.path.startsWith('/')) { console.log('使用parentPath属性:', firstVisibleChild.parentPath); // 如果子菜单是绝对路径,但有parentPath,则应该使用parentPath作为基础 let fullPath = firstVisibleChild.parentPath; if (!fullPath.startsWith('/')) { fullPath = '/' + fullPath; } // 第二级路径基于根路径 if (firstVisibleChild.path.startsWith('/')) { // 第二级已经是绝对路径,截取最后部分 const pathParts = firstVisibleChild.path.split('/'); const lastPart = pathParts[pathParts.length - 1]; fullPath = fullPath + '/' + lastPart; } else { // 如果是绝对路径,则替换之前所有路径 pathSegments = [firstChild.path]; fullPath = buildFullPath(fullPath, firstVisibleChild.path); } console.log('二级路径:', fullPath); // 如果到达叶子节点,则结束查找 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; // 第三级路径基于二级路径 if (grandChild.path.startsWith('/')) { // 第三级是绝对路径,截取最后部分 const pathParts = grandChild.path.split('/'); const lastPart = pathParts[pathParts.length - 1]; fullPath = fullPath + '/' + lastPart; } else { return `${fullPath}/${segment}`; fullPath = buildFullPath(fullPath, grandChild.path); } }); console.log('三级路径 (最终):', fullPath); // 导航到目标路由,如果有查询参数则添加 if (currentNode.query) { router.push({ path: targetPath, query: currentNode.query }); // 导航到第三级菜单 if (grandChild.query) { router.push({ path: fullPath, query: grandChild.query }); } else { router.push({ path: targetPath }); router.push({ path: fullPath }); } return; } // 常规路径构建 let fullPath; // 第一级路径必须是完整的(例如/system) if (currentNode.path.startsWith('/')) { fullPath = currentNode.path; } else { fullPath = '/' + currentNode.path; } console.log('一级路径:', fullPath); // 第二级路径必须基于第一级路径(例如/system/log) fullPath = buildFullPath(fullPath, firstVisibleChild.path); console.log('二级路径:', fullPath); // 第三级路径必须基于二级路径(例如/system/log/operlog) fullPath = buildFullPath(fullPath, grandChild.path); console.log('三级路径 (最终):', fullPath); // 导航到第三级菜单 if (grandChild.query) { console.log('跳转到:', fullPath, '带参数:', grandChild.query); router.push({ path: fullPath, query: grandChild.query }); } else { console.log('跳转到:', fullPath); router.push({ path: fullPath }); } return; } } } console.log('处理标准二级菜单'); // 检查是否需要使用parentPath if (firstVisibleChild.parentPath && firstVisibleChild.path.startsWith('/')) { console.log('使用parentPath属性:', firstVisibleChild.parentPath); // 如果子菜单是绝对路径,但有parentPath,则应该使用parentPath作为基础 let fullPath = firstVisibleChild.parentPath; if (!fullPath.startsWith('/')) { fullPath = '/' + fullPath; } // 构建完整路径 if (firstVisibleChild.path.startsWith('/')) { // 截取子路径的最后部分 const pathParts = firstVisibleChild.path.split('/'); const lastPart = pathParts[pathParts.length - 1]; fullPath = fullPath + '/' + lastPart; } else { fullPath = buildFullPath(fullPath, firstVisibleChild.path); } console.log('构建的最终路径:', fullPath); // 导航到目标路由 if (firstVisibleChild.query) { router.push({ path: fullPath, query: firstVisibleChild.query }); } else { router.push({ path: fullPath }); } return; } // 标准的二级菜单处理 // 构建正确的路径 let fullPath; // 处理第一级路径(例如/system)- 必须是完整的路径 if (currentNode.path.startsWith('/')) { fullPath = currentNode.path; } else { fullPath = '/' + currentNode.path; } console.log('一级路径:', fullPath); // 处理第二级路径(例如/system/user)- 必须基于第一级路径 fullPath = buildFullPath(fullPath, firstVisibleChild.path); console.log('二级路径 (最终):', fullPath); // 导航到目标路由 if (firstVisibleChild.query) { console.log('跳转到:', fullPath, '带参数:', firstVisibleChild.query); router.push({ path: fullPath, query: firstVisibleChild.query }); } else { console.log('跳转到:', fullPath); router.push({ path: fullPath }); } } } @@ -202,6 +306,20 @@ return getNormalPath(props.basePath + '/' + routePath) } // 正确构建路径 function buildFullPath(base, segment) { // 如果segment是绝对路径,直接返回 if (segment.startsWith('/')) { return segment; } // 确保base有正确的开头斜杠 const normalizedBase = base.startsWith('/') ? base : '/' + base; // 拼接路径,避免双斜杠 return normalizedBase.endsWith('/') ? normalizedBase + segment : normalizedBase + '/' + segment; } function hasTitle(title){ if (title.length > 5) { return title; zhitan-vue/src/layout/components/TagsView/index.vue
@@ -1,5 +1,5 @@ <template> <div id="tags-view-container" class="tags-view-container"> <div id="tags-view-container" class="tags-view-container" :class="{'theme-dark': sideTheme === 'theme-dark', 'theme-light': sideTheme === 'theme-light'}"> <scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll"> <router-link v-for="tag in visitedViews" @@ -52,6 +52,7 @@ const visitedViews = computed(() => useTagsViewStore().visitedViews) const routes = computed(() => usePermissionStore().routes) const theme = computed(() => useSettingsStore().theme) const sideTheme = computed(() => useSettingsStore().sideTheme) watch(route, () => { addTags() @@ -238,10 +239,37 @@ 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); &.theme-dark { background: #0A3465; .tags-view-item { color: #fff; border: 1px solid #0c4685; background: rgba(10, 52, 101, .48); border-radius: 5px; &.active { background-color: var(--el-color-primary) !important; color: #fff !important; } } } &.theme-light { background: #fff; .tags-view-item { color: #495060; background: #fff; border: 1px solid #d8dce5; &.active { background-color: var(--el-color-primary) !important; color: #fff !important; } } } .tags-view-wrapper { .tags-view-item { display: inline-block; @@ -249,10 +277,8 @@ cursor: pointer; height: 26px; line-height: 26px; border: 1px solid #d8dce5; color: #495060; background: #fff; padding: 0 8px; border-radius: 3px; padding: 0 10px; font-size: 12px; margin-left: 5px; margin-top: 4px; @@ -278,6 +304,7 @@ } } } } .contextmenu { margin: 0; background: #fff; @@ -296,7 +323,6 @@ cursor: pointer; &:hover { background: #eee; } } } } zhitan-vue/src/permission.js
@@ -35,9 +35,16 @@ // 跳过ParentView类型的中间节点 if (firstChild.component === 'ParentView' || (typeof firstChild.component === 'object' && firstChild.component.name === 'ParentView')) { (typeof firstChild.component === 'object' && firstChild.component.name === 'ParentView')) { currentNode = firstChild; // 如果路径不是以/开头,则添加到路径片段中 if (!firstChild.path.startsWith('/')) { pathSegments.push(firstChild.path); } else { // 如果是绝对路径,则替换之前所有路径 pathSegments = [firstChild.path]; } continue; } @@ -72,7 +79,10 @@ } else if (index === 0) { return segment; } else { return `${fullPath}/${segment}`; // 确保路径之间不会出现重复的斜杠 const base = fullPath.endsWith('/') ? fullPath.slice(0, -1) : fullPath; const part = segment.startsWith('/') ? segment : '/' + segment; return `${base}${part}`; } }); } zhitan-vue/src/store/modules/permission.js
@@ -120,7 +120,18 @@ // 设置父路由引用 c.parent = el; c.path = el.path + '/' + c.path // 确保路径格式正确拼接 if (el.path) { if (c.path.startsWith('/')) { // 绝对路径保持不变 // 但也设置原始父路径用于菜单导航 c.parentPath = el.path; } else { // 相对路径需要拼接 c.path = el.path.endsWith('/') ? el.path + c.path : el.path + '/' + c.path; } } if (c.children && c.children.length) { children = children.concat(filterChildren(c.children, c)) return @@ -134,7 +145,18 @@ // 设置父路由引用 el.parent = lastRouter; el.path = lastRouter.path + '/' + el.path // 确保路径格式正确拼接 if (lastRouter.path) { if (el.path.startsWith('/')) { // 绝对路径保持不变 // 但也设置原始父路径用于菜单导航 el.parentPath = lastRouter.path; } else { // 相对路径需要拼接 el.path = lastRouter.path.endsWith('/') ? lastRouter.path + el.path : lastRouter.path + '/' + el.path; } } if (el.children && el.children.length) { children = children.concat(filterChildren(el.children, el)) return zhitan-vue/src/views/index.vue
@@ -911,18 +911,20 @@ margin-top: 14px; display: flex; width: 100%; flex-wrap: wrap; flex-wrap: nowrap; justify-content: space-between; gap: 15px; &:after { content: ""; flex: auto; flex: 0 0 0; } .card-list-item { width: 320px; height: 127px; width: 0; flex: 1 1 320px; max-width: 320px; height: 135px; background: rgba(242, 246, 250, 0.1); box-sizing: border-box; padding: 16px; @@ -939,7 +941,7 @@ } .item-left { margin-right: 16px; margin-right: 20px; .top-icon { width: 73px; @@ -965,9 +967,12 @@ font-size: 14px; font-family: OPPOSans-Regular; color: rgba(255, 255, 255, 0.65); letter-spacing: 0.5px; margin-bottom: 4px; .unit { color: rgba(255, 255, 255, 0.65); margin-left: 2px; margin-left: 4px; font-size: 12px; font-weight: normal; } @@ -976,29 +981,31 @@ .right-value { font-weight: 500; font-size: 26px; margin-top: 4px; margin-top: 6px; font-family: OPPOSans-Medium; color: #fff; line-height: 1; line-height: 1.2; letter-spacing: 0.5px; } } .item-bottom { display: flex; justify-content: space-between; margin-top: 14px; margin-top: 18px; font-family: OPPOSans, OPPOSans; font-weight: normal; font-size: 12px; color: rgba(255, 255, 255, 0.5); line-height: 1; line-height: 1.2; .bottom-left, .bottom-right { display: flex; align-items: center; letter-spacing: 0.3px; :deep(.el-icon) { margin-left: 4px; margin-left: 6px; font-size: 12px; } } @@ -1080,7 +1087,7 @@ .themeLight { .page { padding: 20px; background: #f7f8fa; background: #fff; .card-title { width: 132px; @@ -1094,18 +1101,20 @@ margin-top: 14px; display: flex; width: 100%; flex-wrap: wrap; flex-wrap: nowrap; justify-content: space-between; gap: 15px; &:after { content: ""; flex: auto; flex: 0 0 0; } .card-list-item { width: 320px; height: 127px; width: 0; flex: 1 1 320px; max-width: 320px; height: 135px; background: #fff; box-sizing: border-box; padding: 16px; @@ -1117,7 +1126,7 @@ align-items: center; .item-left { margin-right: 16px; margin-right: 20px; .top-icon { width: 73px; @@ -1143,9 +1152,12 @@ font-size: 14px; font-family: OPPOSans-Regular; color: rgba(0, 0, 0, 0.65); letter-spacing: 0.5px; margin-bottom: 4px; .unit { color: rgba(0, 0, 0, 0.65); margin-left: 2px; margin-left: 4px; font-size: 12px; font-weight: normal; } @@ -1154,29 +1166,31 @@ .right-value { font-weight: 500; font-size: 26px; margin-top: 4px; margin-top: 6px; font-family: OPPOSans-Medium; color: #333; line-height: 1; line-height: 1.2; letter-spacing: 0.5px; } } .item-bottom { display: flex; justify-content: space-between; margin-top: 14px; margin-top: 18px; font-family: OPPOSans, OPPOSans; font-weight: normal; font-size: 12px; color: rgba(0, 0, 0, 0.5); line-height: 1; line-height: 1.2; .bottom-left, .bottom-right { display: flex; align-items: center; letter-spacing: 0.3px; :deep(.el-icon) { margin-left: 4px; margin-left: 6px; font-size: 12px; } }