Unverified 提交 19dc88b4 作者: Jim 提交者: GitHub

docs: 添加注释, 修复 (#1927)

* docs: 添加注释

* docs: 添加注释

* fix: 后台动态权限changePermissionCode加上await

* docs: 添加注释

* docs: 添加注释

* docs: 添加注释

* docs: 添加注释

* docs: 添加注释
上级 cfbd52bb
......@@ -27,4 +27,7 @@ pnpm-debug.log*
*.njsproj
*.sln
*.sw?
/package-lock.json
package-lock.json
pnpm-lock.yaml
......@@ -30,14 +30,18 @@ export enum SessionTimeoutProcessingEnum {
*/
export enum PermissionModeEnum {
// role
// 角色权限
ROLE = 'ROLE',
// black
// 后端
BACK = 'BACK',
// route mapping
// 路由映射
ROUTE_MAPPING = 'ROUTE_MAPPING',
}
// Route switching animation
// Route switching animation
// 路由切换动画
export enum RouterTransitionEnum {
ZOOM_FADE = 'zoom-fade',
ZOOM_OUT = 'zoom-out',
......
......@@ -39,6 +39,7 @@ export function usePermission() {
/**
* Reset and regain authority resource information
* 重置和重新获得权限资源信息
* @param id
*/
async function resume() {
......
......@@ -11,14 +11,18 @@ export function getAllParentPath<T = Recordable>(treeData: T[], path: string) {
return (menuList || []).map((item) => item.path);
}
// 路径处理
function joinParentPath(menus: Menu[], parentPath = '') {
for (let index = 0; index < menus.length; index++) {
const menu = menus[index];
// https://next.router.vuejs.org/guide/essentials/nested-routes.html
// Note that nested paths that start with / will be treated as a root path.
// 请注意,以 / 开头的嵌套路径将被视为根路径。
// This allows you to leverage the component nesting without having to use a nested URL.
// 这允许你利用组件嵌套,而无需使用嵌套 URL。
if (!(menu.path.startsWith('/') || isUrl(menu.path))) {
// path doesn't start with /, nor is it a url, join parent path
// 路径不以 / 开头,也不是 url,加入父路径
menu.path = `${parentPath}/${menu.path}`;
}
if (menu?.children?.length) {
......@@ -37,14 +41,18 @@ export function transformMenuModule(menuModule: MenuModule): Menu {
return menuList[0];
}
// 将路由转换成菜单
export function transformRouteToMenu(routeModList: AppRouteModule[], routerMapping = false) {
// 借助 lodash 深拷贝
const cloneRouteModList = cloneDeep(routeModList);
const routeList: AppRouteRecordRaw[] = [];
// 对路由项进行修改
cloneRouteModList.forEach((item) => {
if (routerMapping && item.meta.hideChildrenInMenu && typeof item.redirect === 'string') {
item.path = item.redirect;
}
if (item.meta?.single) {
const realItem = item?.children?.[0];
realItem && routeList.push(realItem);
......@@ -52,6 +60,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
routeList.push(item);
}
});
// 提取树指定结构
const list = treeMap(routeList, {
conversion: (node: AppRouteRecordRaw) => {
const { meta: { title, hideMenu = false } = {} } = node;
......@@ -66,6 +75,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
};
},
});
// 路径处理
joinParentPath(list);
return cloneDeep(list);
}
......@@ -74,6 +84,7 @@ export function transformRouteToMenu(routeModList: AppRouteModule[], routerMappi
* config menu with given params
*/
const menuParamRegex = /(?::)([\s\S]+?)((?=\/)|$)/g;
export function configureDynamicParamsMenu(menu: Menu, params: RouteParams) {
const { path, paramPath } = toRaw(menu);
let realPath = paramPath ? paramPath : path;
......
......@@ -68,6 +68,7 @@ function dynamicImport(
}
// Turn background objects into routing objects
// 将背景对象变成路由对象
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => {
const component = route.component as string;
......@@ -94,35 +95,46 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
/**
* Convert multi-level routing to level 2 routing
* 将多级路由转换为 2 级路由
*/
export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) {
const modules: AppRouteModule[] = cloneDeep(routeModules);
for (let index = 0; index < modules.length; index++) {
const routeModule = modules[index];
// 判断级别是否 多级 路由
if (!isMultipleRoute(routeModule)) {
// 声明终止当前循环, 即跳过此次循环,进行下一轮
continue;
}
// 路由等级提升
promoteRouteLevel(routeModule);
}
return modules;
}
// Routing level upgrade
// 路由等级提升
function promoteRouteLevel(routeModule: AppRouteModule) {
// Use vue-router to splice menus
// 使用vue-router拼接菜单
// createRouter 创建一个可以被 Vue 应用程序使用的路由实例
let router: Router | null = createRouter({
routes: [routeModule as unknown as RouteRecordNormalized],
history: createWebHashHistory(),
});
// getRoutes: 获取所有 路由记录的完整列表。
const routes = router.getRoutes();
// 将所有子路由添加到二级路由
addToChildren(routes, routeModule.children || [], routeModule);
router = null;
// omit lodash的函数 对传入的item对象的children进行删除
routeModule.children = routeModule.children?.map((item) => omit(item, 'children'));
}
// Add all sub-routes to the secondary route
// 将所有子路由添加到二级路由
function addToChildren(
routes: RouteRecordNormalized[],
children: AppRouteRecordRaw[],
......@@ -145,7 +157,9 @@ function addToChildren(
}
// Determine whether the level exceeds 2 levels
// 判断级别是否超过2级
function isMultipleRoute(routeModule: AppRouteModule) {
// Reflect.has 与 in 操作符 相同, 用于检查一个对象(包括它原型链上)是否拥有某个属性
if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) {
return false;
}
......
......@@ -36,6 +36,7 @@ export function resetRouter() {
}
// config router
// 配置路由器
export function setupRouter(app: App<Element>) {
app.use(router);
}
......@@ -6,10 +6,11 @@ import { mainOutRoutes } from './mainOut';
import { PageEnum } from '/@/enums/pageEnum';
import { t } from '/@/hooks/web/useI18n';
// import.meta.globEager() 直接引入所有的模块 Vite 独有的功能
const modules = import.meta.globEager('./modules/**/*.ts');
const routeModuleList: AppRouteModule[] = [];
// 加入到路由集合中
Object.keys(modules).forEach((key) => {
const mod = modules[key].default || {};
const modList = Array.isArray(mod) ? [...mod] : [mod];
......@@ -18,6 +19,7 @@ Object.keys(modules).forEach((key) => {
export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
// 根路由
export const RootRoute: AppRouteRecordRaw = {
path: '/',
name: 'Root',
......
......@@ -26,26 +26,37 @@ import { PageEnum } from '/@/enums/pageEnum';
interface PermissionState {
// Permission code list
// 权限代码列表
permCodeList: string[] | number[];
// Whether the route has been dynamically added
// 路由是否动态添加
isDynamicAddedRoute: boolean;
// To trigger a menu update
// 触发菜单更新
lastBuildMenuTime: number;
// Backstage menu list
// 后台菜单列表
backMenuList: Menu[];
// 菜单列表
frontMenuList: Menu[];
}
export const usePermissionStore = defineStore({
id: 'app-permission',
state: (): PermissionState => ({
// 权限代码列表
permCodeList: [],
// Whether the route has been dynamically added
// 路由是否动态添加
isDynamicAddedRoute: false,
// To trigger a menu update
// 触发菜单更新
lastBuildMenuTime: 0,
// Backstage menu list
// 后台菜单列表
backMenuList: [],
// menu List
// 菜单列表
frontMenuList: [],
}),
getters: {
......@@ -96,6 +107,8 @@ export const usePermissionStore = defineStore({
const codeList = await getPermCode();
this.setPermCodeList(codeList);
},
// 构建路由
async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n();
const userStore = useUserStore();
......@@ -105,16 +118,21 @@ export const usePermissionStore = defineStore({
const roleList = toRaw(userStore.getRoleList) || [];
const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
// 路由过滤器 在 函数filter 作为回调传入遍历使用
const routeFilter = (route: AppRouteRecordRaw) => {
const { meta } = route;
// 抽出角色
const { roles } = meta || {};
if (!roles) return true;
// 进行角色权限判断
return roleList.some((role) => roles.includes(role));
};
const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
const { meta } = route;
// ignoreRoute 为true 则路由仅用于菜单生成,不会在实际的路由表中出现
const { ignoreRoute } = meta || {};
// arr.filter 返回 true 表示该元素通过测试
return !ignoreRoute;
};
......@@ -124,6 +142,7 @@ export const usePermissionStore = defineStore({
const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
if (!routes || routes.length === 0) return;
let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME;
function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
if (parentPath) parentPath = parentPath + '/';
routes.forEach((route: AppRouteRecordRaw) => {
......@@ -140,6 +159,7 @@ export const usePermissionStore = defineStore({
children && children.length > 0 && patcher(children, currentPath);
});
}
try {
patcher(routes);
} catch (e) {
......@@ -149,29 +169,44 @@ export const usePermissionStore = defineStore({
};
switch (permissionMode) {
// 角色权限
case PermissionModeEnum.ROLE:
// 对非一级路由进行过滤
routes = filter(asyncRoutes, routeFilter);
// 对一级路由根据角色权限过滤
routes = routes.filter(routeFilter);
// Convert multi-level routing to level 2 routing
// 将多级路由转换为 2 级路由
routes = flatMultiLevelRoutes(routes);
break;
// 路由映射, 默认进入该case
case PermissionModeEnum.ROUTE_MAPPING:
// 对非一级路由进行过滤
routes = filter(asyncRoutes, routeFilter);
// 对一级路由再次根据角色权限过滤
routes = routes.filter(routeFilter);
// 将路由转换成菜单
const menuList = transformRouteToMenu(routes, true);
// 移除掉 ignoreRoute: true 的路由 非一级路由
routes = filter(routes, routeRemoveIgnoreFilter);
// 移除掉 ignoreRoute: true 的路由 一级路由;
routes = routes.filter(routeRemoveIgnoreFilter);
// 对菜单进行排序
menuList.sort((a, b) => {
return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0);
});
// 设置菜单列表
this.setFrontMenuList(menuList);
// Convert multi-level routing to level 2 routing
// 将多级路由转换为 2 级路由
routes = flatMultiLevelRoutes(routes);
break;
// If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
// 如果确定不需要做后台动态权限,请在下方评论整个判断
case PermissionModeEnum.BACK:
const { createMessage } = useMessage();
......@@ -181,23 +216,28 @@ export const usePermissionStore = defineStore({
});
// !Simulate to obtain permission codes from the background,
// 模拟从后台获取权限码,
// this function may only need to be executed once, and the actual project can be put at the right time by itself
// 这个功能可能只需要执行一次,实际项目可以自己放在合适的时间
let routeList: AppRouteRecordRaw[] = [];
try {
this.changePermissionCode();
await this.changePermissionCode();
routeList = (await getMenuList()) as AppRouteRecordRaw[];
} catch (error) {
console.error(error);
}
// Dynamically introduce components
// 动态引入组件
routeList = transformObjToRoute(routeList);
// Background routing to menu structure
// 后台路由到菜单结构
const backMenuList = transformRouteToMenu(routeList);
this.setBackMenuList(backMenuList);
// remove meta.ignoreRoute item
// 删除 meta.ignoreRoute 项
routeList = filter(routeList, routeRemoveIgnoreFilter);
routeList = routeList.filter(routeRemoveIgnoreFilter);
......@@ -214,6 +254,7 @@ export const usePermissionStore = defineStore({
});
// Need to be used outside the setup
// 需要在设置之外使用
export function usePermissionStoreWithOut() {
return usePermissionStore(store);
}
......@@ -3,15 +3,19 @@ interface TreeHelperConfig {
children: string;
pid: string;
}
// 默认配置
const DEFAULT_CONFIG: TreeHelperConfig = {
id: 'id',
children: 'children',
pid: 'pid',
};
// 获取配置。 Object.assign 从一个或多个源对象复制到目标对象
const getConfig = (config: Partial<TreeHelperConfig>) => Object.assign({}, DEFAULT_CONFIG, config);
// tree from list
// 列表中的树
export function listToTree<T = any>(list: any[], config: Partial<TreeHelperConfig> = {}): T[] {
const conf = getConfig(config) as TreeHelperConfig;
const nodeMap = new Map();
......@@ -123,18 +127,24 @@ export function findPathAll(tree: any, func: Fn, config: Partial<TreeHelperConfi
export function filter<T = any>(
tree: T[],
func: (n: T) => boolean,
// Partial 将 T 中的所有属性设为可选
config: Partial<TreeHelperConfig> = {},
): T[] {
// 获取配置
config = getConfig(config);
const children = config.children as string;
function listFilter(list: T[]) {
return list
.map((node: any) => ({ ...node }))
.filter((node) => {
// 递归调用 对含有children项 进行再次调用自身函数 listFilter
node[children] = node[children] && listFilter(node[children]);
// 执行传入的回调 func 进行过滤
return func(node) || (node[children] && node[children].length);
});
}
return listFilter(tree);
}
......@@ -157,6 +167,7 @@ export function forEach<T = any>(
/**
* @description: Extract tree specified structure
* @description: 提取树指定结构
*/
export function treeMap<T = any>(treeData: T[], opt: { children?: string; conversion: Fn }): T[] {
return treeData.map((item) => treeMapEach(item, opt));
......@@ -164,6 +175,7 @@ export function treeMap<T = any>(treeData: T[], opt: { children?: string; conver
/**
* @description: Extract tree specified structure
* @description: 提取树指定结构
*/
export function treeMapEach(
data: any,
......
......@@ -61,7 +61,7 @@ export class VAxios {
}
/**
* @description: Interceptor configuration
* @description: Interceptor configuration 拦截器配置
*/
private setupInterceptors() {
const transform = this.getTransform();
......
......@@ -204,6 +204,7 @@ const transform: AxiosTransform = {
function createAxios(opt?: Partial<CreateAxiosOptions>) {
return new VAxios(
// 深度合并
deepMerge(
{
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
......
......@@ -32,6 +32,7 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string {
return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
}
// 深度合并
export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
let key: string;
for (key in target) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论