contextMenuFactory

This commit is contained in:
Xu Chang 2023-06-19 21:17:34 +08:00
parent e88b843f0a
commit 911b22ccd5
4 changed files with 82 additions and 22 deletions

View File

@ -10,6 +10,7 @@ interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
entity: T;
action: ED[T]['Action'];
data?: ED[T]['Update']['data'];
paths?: string[];
}
interface IMenuWrapper<ED extends EntityDict & BaseEntityDict, T extends keyof ED> extends IMenu<ED, T> {
filtersMaker: (entity: keyof ED, entityId: string) => Array<ED[T]['Selection']['filter']>;
@ -21,6 +22,6 @@ export declare class ContextMenuFactory<ED extends EntityDict & BaseEntityDict,
private makeMenuWrappers;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD>, cascadePathGraph: AuthCascadePath<ED>[]);
setMenus(menus: IMenu<ED, keyof ED>[]): void;
getMenusByContext(entity: keyof ED, entityId: string): IMenu<ED, keyof ED>[];
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string): OMenu[];
}
export {};

View File

@ -6,6 +6,7 @@ var assert_1 = tslib_1.__importDefault(require("assert"));
var lodash_1 = require("oak-domain/lib/utils/lodash");
var filter_1 = require("oak-domain/lib/store/filter");
var Feature_1 = require("../types/Feature");
var relation_1 = require("oak-domain/lib/store/relation");
;
;
var ContextMenuFactory = /** @class */ (function (_super) {
@ -17,6 +18,7 @@ var ContextMenuFactory = /** @class */ (function (_super) {
return _this;
}
ContextMenuFactory.prototype.makeMenuWrappers = function (menus) {
var _this = this;
var destEntities = (0, lodash_1.uniq)(menus.map(function (ele) { return ele.entity; }));
var pathMap = {};
this.cascadePathGraph.forEach(function (path) {
@ -33,16 +35,41 @@ var ContextMenuFactory = /** @class */ (function (_super) {
}
});
return menus.map(function (menu) {
var destEntity = menu.entity;
var destEntity = menu.entity, cascadePaths = menu.paths;
var filtersMaker = function (sourceEntity, entityId) {
// 在cascadePathMap中找到可能的路径并构建对应的filter
var paths = pathMap[destEntity].filter(function (ele) { return ele[2] === sourceEntity; });
var paths = pathMap[destEntity].filter(function (ele) {
if (cascadePaths) {
(0, assert_1.default)(cascadePaths.length > 0);
return ele[2] === sourceEntity && cascadePaths.includes(ele[1]);
}
return ele[2] === sourceEntity;
});
return paths.map(function (path) {
var p = path[1];
var p2 = p.concat('Id');
var filter = {};
(0, lodash_1.set)(filter, p2, entityId);
return filter;
var ps = p.split('.');
var makeFilterInner = function (entity, idx) {
var _a, _b;
var attr = ps[idx];
var rel = (0, relation_1.judgeRelation)(_this.cache.getSchema(), entity, attr);
if (idx === ps.length - 1) {
if (rel === 2) {
return {
entity: attr,
entityId: entityId,
};
}
(0, assert_1.default)(typeof rel === 'string');
return _a = {},
_a["".concat(attr, "Id")] = entityId,
_a;
}
var e = rel === 2 ? attr : rel;
return _b = {},
_b[attr] = makeFilterInner(e, idx + 1),
_b;
};
return makeFilterInner(destEntity, 0);
});
};
return Object.assign({}, menu, {
@ -60,9 +87,12 @@ var ContextMenuFactory = /** @class */ (function (_super) {
var menus = this.menuWrappers.filter(function (wrapper) {
var destEntity = wrapper.entity, data = wrapper.data, filtersMaker = wrapper.filtersMaker, action = wrapper.action;
var filters = filtersMaker(entity, entityId);
var filter = (0, filter_1.combineFilters)(filters);
var allow = _this.cache.checkOperation(destEntity, action, data, filter);
return allow;
if (filters.length > 0) {
var filter = (0, filter_1.combineFilters)(filters);
var allow = _this.cache.checkOperation(destEntity, action, data, filter);
return allow;
}
return false;
}).map(function (wrapper) { return (0, lodash_1.omit)(wrapper, ['filtersMaker']); });
return menus;
};

View File

@ -81,7 +81,7 @@
"copy-xml": "copyfiles -u 1 src/**/*.xml lib/ & copyfiles -u 1 src/**/*.wxml lib/",
"build": "tsc && npm run copy-js && npm run copy-less && npm run copy-wxs && npm run copy-svg && npm run copy-xml",
"test": "ts-node ./test/test2.ts",
"prepare": "rimraf node_modules/react & rimraf node_modules/react-dom & rimraf node_modules/react-router"
"prepare": "rimraf node_modules/react & rimraf node_modules/react-dom & rimraf node_modules/react-router & rimraf node_modules/react-router-dom"
},
"main": "lib/index"
}

View File

@ -8,12 +8,14 @@ import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
import { combineFilters } from 'oak-domain/lib/store/filter';
import { Cache } from './cache';
import { Feature } from '../types/Feature';
import { judgeRelation } from 'oak-domain/lib/store/relation';
interface IMenu<ED extends EntityDict & BaseEntityDict, T extends keyof ED> {
name: string;
entity: T;
action: ED[T]['Action'];
data?: ED[T]['Update']['data'];
paths?: string[];
};
interface IMenuWrapper<ED extends EntityDict & BaseEntityDict, T extends keyof ED> extends IMenu<ED, T> {
@ -57,19 +59,43 @@ export class ContextMenuFactory<
return menus.map(
(menu) => {
const { entity: destEntity } = menu;
const { entity: destEntity, paths: cascadePaths } = menu;
const filtersMaker = (sourceEntity: keyof ED, entityId: string) => {
// 在cascadePathMap中找到可能的路径并构建对应的filter
const paths = pathMap[destEntity]!.filter(
(ele) => ele[2] === sourceEntity
(ele) => {
if (cascadePaths) {
assert(cascadePaths.length > 0);
return ele[2] === sourceEntity && cascadePaths.includes(ele[1]);
}
return ele[2] === sourceEntity;
}
);
return paths.map(
(path) => {
const p = path[1];
const p2 = p.concat('Id');
const filter: ED[keyof ED]['Selection']['filter'] = {};
set(filter, p2, entityId);
return filter;
const ps = p.split('.');
const makeFilterInner = (entity: keyof ED, idx: number): ED[keyof ED]['Selection']['filter'] => {
const attr = ps[idx];
const rel = judgeRelation(this.cache.getSchema(), entity, attr);
if (idx === ps.length - 1) {
if (rel === 2) {
return {
entity: attr,
entityId,
};
}
assert(typeof rel === 'string');
return {
[`${attr}Id`]: entityId,
};
}
const e = rel === 2 ? attr : rel as string;
return {
[attr]: makeFilterInner(e, idx + 1),
};
}
return makeFilterInner(destEntity, 0);
}
)
};
@ -91,19 +117,22 @@ export class ContextMenuFactory<
this.menuWrappers = this.makeMenuWrappers(menus);
}
getMenusByContext(entity: keyof ED, entityId: string) {
getMenusByContext<OMenu extends IMenu<ED, keyof ED>>(entity: keyof ED, entityId: string) {
assert(this.menuWrappers, '应当先调用setMenus才能动态判定菜单');
const menus = this.menuWrappers.filter(
(wrapper) => {
const { entity: destEntity, data, filtersMaker, action } = wrapper;
const filters = filtersMaker(entity, entityId);
const filter = combineFilters(filters);
const allow = this.cache.checkOperation(destEntity, action, data, filter);
return allow;
if (filters.length > 0) {
const filter = combineFilters(filters);
const allow = this.cache.checkOperation(destEntity, action, data, filter);
return allow;
}
return false;
}
).map(
(wrapper) => omit(wrapper, ['filtersMaker'])
) as IMenu<ED, keyof ED>[];
) as OMenu[];
return menus;
}