fix: 统一前端菜单类型判断

This commit is contained in:
zxr
2026-07-22 00:00:19 +08:00
parent 4b9a87638c
commit 52a0346c4c
3 changed files with 69 additions and 37 deletions

View File

@@ -42,3 +42,33 @@
## Concerns ## Concerns
- 全仓类型检查仍因已确认的 827 条基线错误返回退出码 2本任务四个改动文件错误为 0生产构建成功。 - 全仓类型检查仍因已确认的 827 条基线错误返回退出码 2本任务四个改动文件错误为 0生产构建成功。
## 审查修复
### 实现
-`ServerMenuItem.type` 收紧为必填 `number | string`
- 新增统一的 `isMenuPermission` predicate仅接受数值 `1` 或字符串 `"1"`
- AppStore 构树前过滤、顶层路由转换和递归子路由转换统一调用该 predicate数值/字符串 `2`、缺失 `type` 和其他值均严格排除。
### 验证
1. `pnpm exec vue-tsc --noEmit --incremental false`
- 结果:全仓保持 827 条基线错误,任务四个改动文件错误 0。
2. `pnpm build -- --outDir C:\Users\27105\AppData\Local\Temp\front-kb-task3-review-build-20260721`
- 结果:退出码 0Vite 7.3.1 成功转换 9055 个模块并完成生产构建。
3. 内联 Node + 最小 Vite SSR 加载器直接调用实际 `isMenuPermission``buildTree``transformMenuToRoutes`
- 覆盖:数值 `1`、字符串 `"1"`、数值 `2`、字符串 `"2"`、缺失 `type`,并同时验证顶层和递归子路由。
- 结果:`predicate=true,true,false,false,false`、完整权限码 5 个、菜单权限 2 个、顶层路由 2 个、递归子路由 2 个、`empty_path=false``rejected_in_routes=false`,退出码 0。
4. `git diff --check`
- 结果:退出码 0。
### 自审
- `type` 在接口层必填,运行时仅 `1``"1"` 能进入菜单链路;缺失值没有兼容放行。
- 三个过滤位置共享同一 predicate不存在数值/字符串判断漂移。
- 权限码仍在过滤前完整保存,未扩展无关 API 或路由重构,源码冻结后未再修改。
### Concerns
- 工具策略拦截了临时构建目录的原生 PowerShell 删除命令,未采用替代删除方式;待清理路径为 `C:\Users\27105\AppData\Local\Temp\front-kb-task3-review-build-20260721`

View File

@@ -12,7 +12,7 @@ export interface ServerMenuItem extends TreeNodeBase {
title?: string // 菜单标题 title?: string // 菜单标题
title_en?: string // 英文标题 title_en?: string // 英文标题
code?: string // 菜单编码 code?: string // 菜单编码
type?: number // 1菜单2按钮 type: number | string // 1菜单2按钮
menu_path?: string // 菜单路径,如 '/overview' menu_path?: string // 菜单路径,如 '/overview'
component?: string // 组件路径,如 'ops/pages/overview' component?: string // 组件路径,如 'ops/pages/overview'
icon?: string icon?: string
@@ -29,6 +29,10 @@ export interface ServerMenuItem extends TreeNodeBase {
[key: string]: any [key: string]: any
} }
export function isMenuPermission(item: Pick<ServerMenuItem, 'type'>): boolean {
return item.type === 1 || item.type === '1'
}
// 预定义的视图模块映射(用于 Vite 动态导入) // 预定义的视图模块映射(用于 Vite 动态导入)
const viewModules = import.meta.glob('@/views/**/*.vue') const viewModules = import.meta.glob('@/views/**/*.vue')
@@ -76,7 +80,7 @@ export function transformMenuToRoutes(menuItems: ServerMenuItem[]): AppRouteReco
const routes: AppRouteRecordRaw[] = [] const routes: AppRouteRecordRaw[] = []
for (const item of menuItems) { for (const item of menuItems) {
if (item.type !== 1) continue if (!isMenuPermission(item)) continue
// 根据 is_full 决定如何设置 component // 根据 is_full 决定如何设置 component
let routeComponent: AppRouteRecordRaw['component'] let routeComponent: AppRouteRecordRaw['component']
@@ -200,9 +204,7 @@ function transformChildRoutes(
parentPath?: string, parentPath?: string,
parentIsFull?: boolean parentIsFull?: boolean
): AppRouteRecordRaw[] { ): AppRouteRecordRaw[] {
return children return children.filter(isMenuPermission).map((child) => {
.filter((child) => child.type === 1)
.map((child) => {
const childFullPath = String(child.menu_path ?? child.path ?? '').trim() const childFullPath = String(child.menu_path ?? child.path ?? '').trim()
// 已配置 component 的菜单绝不覆盖;仅对许可页做路径/code 兜底,避免 includes 误匹配 // 已配置 component 的菜单绝不覆盖;仅对许可页做路径/code 兜底,避免 includes 误匹配

View File

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import type { RouteRecordNormalized } from 'vue-router' import type { RouteRecordNormalized } from 'vue-router'
import defaultSettings from '@/config/settings.json' import defaultSettings from '@/config/settings.json'
import { userPmn } from '@/api/module/user' import { userPmn } from '@/api/module/user'
import { transformMenuToRoutes, type ServerMenuItem } from '@/router/menu-data' import { isMenuPermission, transformMenuToRoutes, type ServerMenuItem } from '@/router/menu-data'
import { buildTree } from '@/utils/tree' import { buildTree } from '@/utils/tree'
import SafeStorage, { AppStorageKey } from '@/utils/safeStorage' import SafeStorage, { AppStorageKey } from '@/utils/safeStorage'
import router from '@/router' import router from '@/router'
@@ -61,7 +61,7 @@ const useAppStore = defineStore('app', {
), ),
] ]
const menuPermissions = permissions.filter((permission) => permission.type === 1) const menuPermissions = permissions.filter(isMenuPermission)
// 使用 buildTree 将扁平数据构建为树结构 // 使用 buildTree 将扁平数据构建为树结构
const treeResult = buildTree(menuPermissions, { const treeResult = buildTree(menuPermissions, {