relation/entity 改动 支持单节点授权
This commit is contained in:
parent
cbe25c7c3c
commit
b6e0a24d28
|
|
@ -0,0 +1,8 @@
|
|||
/// <reference types="react" />
|
||||
import { EntityDict } from "oak-domain/lib/types";
|
||||
declare const _default: (props: import("../../..").ReactComponentProps<EntityDict & import("oak-domain/lib/base-app-domain").EntityDict, "actionAuth", true, {
|
||||
path: string;
|
||||
openTip: boolean;
|
||||
entity: string | number;
|
||||
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
exports.default = OakComponent({
|
||||
entity: 'actionAuth',
|
||||
projection: {
|
||||
id: 1,
|
||||
relationId: 1,
|
||||
path: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
isList: true,
|
||||
properties: {
|
||||
path: '',
|
||||
openTip: false,
|
||||
entity: '',
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter: function () {
|
||||
var _a = this.props, path = _a.path, entity = _a.entity;
|
||||
return {
|
||||
destEntity: entity,
|
||||
path: path,
|
||||
};
|
||||
},
|
||||
'#name': 'path',
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
pageSize: 1000,
|
||||
currentPage: 0,
|
||||
},
|
||||
formData: function (_a) {
|
||||
var rows = _a.data;
|
||||
console.log(this.props.path);
|
||||
return {
|
||||
rows: rows
|
||||
};
|
||||
},
|
||||
data: {
|
||||
relations: [],
|
||||
actions: [],
|
||||
},
|
||||
listeners: {
|
||||
path: function (prev, next) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var _a, path, entity;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
if (prev.path !== next.path) {
|
||||
_a = this.props, path = _a.path, entity = _a.entity;
|
||||
this.getRelationAndActions();
|
||||
this.addNamedFilter({
|
||||
filter: {
|
||||
path: path,
|
||||
destEntity: entity,
|
||||
},
|
||||
'#name': 'path'
|
||||
}, true);
|
||||
}
|
||||
return [2 /*return*/];
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
lifetimes: {
|
||||
ready: function () {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
this.getRelationAndActions();
|
||||
return [2 /*return*/];
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getRelationAndActions: function () {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var _a, path, entity, entities, sourceEntity, source, relations, actions;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_a = this.props, path = _a.path, entity = _a.entity;
|
||||
entities = path.split('.');
|
||||
sourceEntity = entities[(entities === null || entities === void 0 ? void 0 : entities.length) - 1];
|
||||
source = sourceEntity.includes('$') ? sourceEntity.split('$')[0] : sourceEntity;
|
||||
return [4 /*yield*/, this.features.cache.refresh('relation', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
name: 1,
|
||||
display: 1,
|
||||
},
|
||||
filter: {
|
||||
entity: source,
|
||||
entityId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
})];
|
||||
case 1:
|
||||
relations = (_b.sent()).data;
|
||||
actions = this.features.relationAuth.getActions(entity);
|
||||
this.setState({
|
||||
relations: relations,
|
||||
actions: actions,
|
||||
});
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/// <reference types="react" />
|
||||
import { WebComponentProps } from '../../../types/Page';
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
declare type ED = EntityDict & BaseEntityDict;
|
||||
declare type TableData = {
|
||||
relationId: string;
|
||||
relation: string;
|
||||
actions: string[];
|
||||
};
|
||||
export default function render(props: WebComponentProps<ED, 'actionAuth', true, {
|
||||
relations: EntityDict['relation']['Schema'][];
|
||||
actions: string[];
|
||||
datasource: TableData[];
|
||||
rows: EntityDict['actionAuth']['Schema'][];
|
||||
path: string;
|
||||
entity: keyof ED;
|
||||
openTip: boolean;
|
||||
}, {}>): JSX.Element;
|
||||
export {};
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
var jsx_runtime_1 = require("react/jsx-runtime");
|
||||
var react_1 = require("react");
|
||||
var antd_1 = require("antd");
|
||||
var antd_2 = require("antd");
|
||||
var Title = antd_2.Typography.Title, Text = antd_2.Typography.Text;
|
||||
function render(props) {
|
||||
var data = props.data, methods = props.methods;
|
||||
var rows = data.rows, relations = data.relations, actions = data.actions, path = data.path, entity = data.entity, openTip = data.openTip, oakExecutable = data.oakExecutable;
|
||||
var _a = tslib_1.__read((0, react_1.useState)([]), 2), datasource = _a[0], setDatasource = _a[1];
|
||||
(0, react_1.useEffect)(function () {
|
||||
var tableRows = relations.map(function (ele) { return ({
|
||||
relationId: ele.id,
|
||||
relation: ele.name,
|
||||
actions: actions,
|
||||
}); });
|
||||
setDatasource(tableRows);
|
||||
}, [relations]);
|
||||
return ((0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ direction: "vertical", style: { width: '100%' } }, { children: [(0, jsx_runtime_1.jsxs)(antd_1.Space, { children: [(0, jsx_runtime_1.jsx)(Text, tslib_1.__assign({ style: { fontSize: 16 } }, { children: "\u6388\u6743" })), (0, jsx_runtime_1.jsx)(antd_1.Tooltip, tslib_1.__assign({ title: "点击保存", open: openTip, placement: "right" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "link", disabled: !oakExecutable, onClick: function () { return methods.execute(); } }, { children: "\u4FDD\u5B58" })) }))] }), (0, jsx_runtime_1.jsx)(antd_1.Table, { rowKey: "relationId", dataSource: datasource, columns: [
|
||||
{
|
||||
dataIndex: 'relation',
|
||||
title: '角色'
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
title: '操作权限',
|
||||
render: function (value, row) {
|
||||
var options = value.map(function (ele) { return ({
|
||||
label: ele,
|
||||
value: ele,
|
||||
}); });
|
||||
var actionAuth = rows.find(function (ele) { return ele.relationId === row.relationId; });
|
||||
var defaultValue = actionAuth ? actionAuth.deActions : [];
|
||||
return ((0, jsx_runtime_1.jsx)(antd_1.Checkbox.Group, { options: options, defaultValue: defaultValue, onChange: function (checkedArr) {
|
||||
if (!actionAuth) {
|
||||
methods.addItem({
|
||||
relationId: row.relationId,
|
||||
path: path,
|
||||
deActions: checkedArr,
|
||||
destEntity: entity,
|
||||
});
|
||||
}
|
||||
else {
|
||||
methods.updateItem({
|
||||
deActions: checkedArr,
|
||||
}, actionAuth.id);
|
||||
}
|
||||
if (!checkedArr.length && actionAuth) {
|
||||
methods.removeItem(actionAuth.id);
|
||||
}
|
||||
} }));
|
||||
}
|
||||
}
|
||||
], pagination: false })] })));
|
||||
}
|
||||
exports.default = render;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/// <reference types="react" />
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
declare const _default: (props: import("../../..").ReactComponentProps<EntityDict & BaseEntityDict, "actionAuth", true, {
|
||||
entity: string | number;
|
||||
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
exports.default = OakComponent({
|
||||
entity: 'actionAuth',
|
||||
projection: {
|
||||
id: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
destEntity: 1,
|
||||
deActions: 1,
|
||||
},
|
||||
isList: true,
|
||||
filters: [
|
||||
{
|
||||
filter: function () {
|
||||
var entity = this.props.entity;
|
||||
return {
|
||||
path: {
|
||||
$includes: '$',
|
||||
entity: entity,
|
||||
}
|
||||
};
|
||||
},
|
||||
'#name': 'path',
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
entity: '',
|
||||
},
|
||||
formData: function (_a) {
|
||||
var rows = _a.data;
|
||||
// 查看path为actionAuthList-cpn 是否有未提交的操作,如果有提示先提交
|
||||
var operations = this.features.runningTree.getOperations('$actionAuthList-cpn');
|
||||
var showExecuteTip = false;
|
||||
if (operations && operations.length) {
|
||||
showExecuteTip = true;
|
||||
}
|
||||
console.log(operations);
|
||||
return {
|
||||
rows: rows,
|
||||
showExecuteTip: showExecuteTip,
|
||||
};
|
||||
},
|
||||
data: {
|
||||
links: [],
|
||||
entityDNode: [],
|
||||
entitySNode: [],
|
||||
},
|
||||
lifetimes: {
|
||||
ready: function () {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var entity, links;
|
||||
var _this = this;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
entity = this.props.entity;
|
||||
links = this.features.relationAuth.getEntityGraph().links;
|
||||
this.setState({
|
||||
links: links,
|
||||
}, function () {
|
||||
_this.getNodes(entity);
|
||||
});
|
||||
return [2 /*return*/];
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNodes: function (entity) {
|
||||
var links = this.state.links;
|
||||
// 以entity为source 查找entity所指向的实体
|
||||
var entityDNode = links.filter(function (ele) { return ele.source === entity; }).map(function (ele) { return ele.target; });
|
||||
// 以entity为target 查找指向entity的实体
|
||||
var entitySNode = links.filter(function (ele) { return ele.target === entity; }).map(function (ele) { return ele.source; });
|
||||
this.setState({
|
||||
entityDNode: entityDNode,
|
||||
entitySNode: entitySNode,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/// <reference types="react" />
|
||||
import { EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { WebComponentProps } from '../../../types/Page';
|
||||
declare type ED = EntityDict & BaseEntityDict;
|
||||
export default function render(props: WebComponentProps<ED, keyof ED, false, {
|
||||
rows: EntityDict['actionAuth']['Schema'][];
|
||||
entity: keyof ED;
|
||||
entityDNode: string[];
|
||||
entitySNode: string[];
|
||||
showExecuteTip: boolean;
|
||||
}, {
|
||||
getNodes: (entity: keyof ED) => void;
|
||||
}>): JSX.Element;
|
||||
export {};
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
var jsx_runtime_1 = require("react/jsx-runtime");
|
||||
var antd_1 = require("antd");
|
||||
var antd_2 = require("antd");
|
||||
var Title = antd_2.Typography.Title, Text = antd_2.Typography.Text;
|
||||
var react_1 = require("react");
|
||||
var actionAuthList_1 = tslib_1.__importDefault(require("../actionAuthList"));
|
||||
function render(props) {
|
||||
var methods = props.methods, data = props.data;
|
||||
var entity = data.entity, entityDNode = data.entityDNode, entitySNode = data.entitySNode, oakFullpath = data.oakFullpath, showExecuteTip = data.showExecuteTip, rows = data.rows;
|
||||
var getNodes = methods.getNodes;
|
||||
var _a = tslib_1.__read((0, react_1.useState)(false), 2), open = _a[0], setOpen = _a[1];
|
||||
var _b = tslib_1.__read((0, react_1.useState)(false), 2), openTip = _b[0], setOpenTip = _b[1];
|
||||
var _c = tslib_1.__read((0, react_1.useState)([]), 2), breadcrumbItems = _c[0], setBreadcrumbItems = _c[1];
|
||||
(0, react_1.useEffect)(function () {
|
||||
if (!showExecuteTip) {
|
||||
setOpenTip(false);
|
||||
}
|
||||
}, [showExecuteTip]);
|
||||
var checkExecute = function () {
|
||||
if (showExecuteTip) {
|
||||
methods.setMessage({
|
||||
content: '有未保存的权限设置,请先保存',
|
||||
type: 'warning',
|
||||
});
|
||||
setOpenTip(true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return ((0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ direction: "vertical", style: { width: '100%' } }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ onClick: function () { return setOpen(true); } }, { children: "\u8BBE\u7F6E" })), (0, jsx_runtime_1.jsx)(antd_1.Modal, tslib_1.__assign({ title: "\u6743\u9650\u8BBE\u7F6E(".concat(entity, ")"), open: open, destroyOnClose: true, footer: ((0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ onClick: function () { return setOpen(false); } }, { children: "\u53D6\u6D88" }))), onCancel: function () { return setOpen(false); }, width: 1000 }, { children: (0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ direction: "vertical", style: { width: '100%', marginTop: 16 }, size: 16 }, { children: [(0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ direction: "vertical" }, { children: [(0, jsx_runtime_1.jsx)(Text, tslib_1.__assign({ style: { fontSize: 16 } }, { children: "\u8DEF\u5F84" })), (0, jsx_runtime_1.jsx)(antd_1.Space, tslib_1.__assign({ style: { width: '100%' }, wrap: true }, { children: (breadcrumbItems && breadcrumbItems.length > 0) ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(antd_1.Breadcrumb, { items: breadcrumbItems.map(function (ele, index) { return ({
|
||||
title: ((0, jsx_runtime_1.jsx)("a", tslib_1.__assign({ onClick: function () {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
var newItems = breadcrumbItems.slice(0, index + 1);
|
||||
setBreadcrumbItems(newItems);
|
||||
var entity = ele.includes('$') ? ele.split('$')[0] : ele;
|
||||
getNodes(entity);
|
||||
} }, { children: ele })))
|
||||
}); }) }), (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ size: "small", type: "link", onClick: function () {
|
||||
setBreadcrumbItems([]);
|
||||
getNodes(entity);
|
||||
} }, { children: "\u6E05\u7A7A" }))] })) : ((0, jsx_runtime_1.jsx)(Text, tslib_1.__assign({ type: "secondary" }, { children: "\u8BF7\u5148\u9009\u62E9\u7ED3\u70B9" }))) }))] })), (0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ direction: "vertical" }, { children: [(0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ align: "center" }, { children: [(0, jsx_runtime_1.jsx)(Text, tslib_1.__assign({ style: { fontSize: 16 } }, { children: "\u7ED3\u70B9" })), (0, jsx_runtime_1.jsx)(antd_1.Badge, { color: "var(--oak-color-primary)", text: "\u5916\u952E", style: { color: 'rgba(0, 0, 0, 0.45)' } }), (0, jsx_runtime_1.jsx)(antd_1.Badge, { color: "cyan", text: "\u53CD\u6307\u7ED3\u70B9", style: { color: 'rgba(0, 0, 0, 0.45)' } })] })), (0, jsx_runtime_1.jsxs)(antd_1.Space, tslib_1.__assign({ wrap: true }, { children: [entityDNode.map(function (ele) { return ((0, jsx_runtime_1.jsx)(antd_1.Tag, tslib_1.__assign({ style: { cursor: 'pointer' }, color: "processing", bordered: false, onClick: function () {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
breadcrumbItems.push(ele);
|
||||
setBreadcrumbItems(breadcrumbItems);
|
||||
getNodes(ele);
|
||||
} }, { children: ele }))); }), entitySNode.map(function (ele) { return ((0, jsx_runtime_1.jsx)(antd_1.Tag, tslib_1.__assign({ style: { cursor: 'pointer' }, color: "cyan", bordered: false, onClick: function () {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
breadcrumbItems.push("".concat(ele, "$").concat(entity));
|
||||
setBreadcrumbItems(breadcrumbItems);
|
||||
getNodes(ele);
|
||||
} }, { children: ele }))); })] }))] })), (0, jsx_runtime_1.jsx)(actionAuthList_1.default, { oakPath: "$actionAuthList-cpn", entity: entity, path: breadcrumbItems.join('.'), openTip: openTip })] })) })), (0, jsx_runtime_1.jsx)(antd_1.Table, { rowKey: "relationId", dataSource: rows, columns: [
|
||||
{
|
||||
width: 400,
|
||||
dataIndex: 'path',
|
||||
title: '路径',
|
||||
},
|
||||
{
|
||||
dataIndex: 'relation',
|
||||
title: '角色',
|
||||
render: function (value, row) { var _a; return (_a = row.relation) === null || _a === void 0 ? void 0 : _a.name; },
|
||||
},
|
||||
{
|
||||
dataIndex: 'deActions',
|
||||
title: '权限',
|
||||
render: function (value) {
|
||||
return value.map(function (ele) { return ((0, jsx_runtime_1.jsx)(antd_1.Tag, { children: ele })); });
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
render: function (value, row, index) {
|
||||
return ((0, jsx_runtime_1.jsx)("a", tslib_1.__assign({ onClick: function () {
|
||||
var path = row === null || row === void 0 ? void 0 : row.path;
|
||||
var items = path.split('.');
|
||||
setBreadcrumbItems(items);
|
||||
setOpen(true);
|
||||
} }, { children: "\u7F16\u8F91" })));
|
||||
}
|
||||
}
|
||||
], pagination: false })] })));
|
||||
}
|
||||
exports.default = render;
|
||||
|
|
@ -8,6 +8,7 @@ var Title = antd_2.Typography.Title, Text = antd_2.Typography.Text;
|
|||
var actionAuth_1 = tslib_1.__importDefault(require("../actionAuth"));
|
||||
var relationAuth_1 = tslib_1.__importDefault(require("../relationAuth"));
|
||||
var react_1 = require("react");
|
||||
var single_1 = tslib_1.__importDefault(require("../../../components/relation/single"));
|
||||
function render(props) {
|
||||
var _a = props.data, oakFullpath = _a.oakFullpath, entity = _a.entity, actions = _a.actions, checkedActions = _a.checkedActions, hasDirectActionAuth = _a.hasDirectActionAuth, hasDirectRelationAuth = _a.hasDirectRelationAuth, dras = _a.dras, daas = _a.daas, relationIds = _a.relationIds, relations = _a.relations, deduceRelationAttr = _a.deduceRelationAttr;
|
||||
var _b = props.methods, onActionsSelected = _b.onActionsSelected, onRelationsSelected = _b.onRelationsSelected, t = _b.t;
|
||||
|
|
@ -87,6 +88,11 @@ function render(props) {
|
|||
], dataSource: daas, pagination: false }))
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
label: '特定授权',
|
||||
key: 'special',
|
||||
children: ((0, jsx_runtime_1.jsx)(single_1.default, { oakPath: oakFullpath ? "".concat(oakFullpath, ".actionAuths:2") : undefined, entity: entity }))
|
||||
});
|
||||
var ActionSelector = actions && ((0, jsx_runtime_1.jsxs)(antd_1.Row, tslib_1.__assign({ style: { width: '100%' }, justify: "center", align: "middle" }, { children: [(0, jsx_runtime_1.jsxs)(Text, tslib_1.__assign({ strong: true }, { children: [t('action'), ":"] })), (0, jsx_runtime_1.jsx)(antd_1.Row, tslib_1.__assign({ style: { flex: 1, marginLeft: 10 }, justify: "start", align: "middle", wrap: true }, { children: (0, jsx_runtime_1.jsx)(antd_1.Checkbox.Group, { options: actions, value: checkedActions, onChange: function (value) { return onActionsSelected(value); }, style: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import assert from "assert";
|
||||
import { AuthCascadePath, EntityDict } from "oak-domain/lib/types";
|
||||
import { ED } from "../../../types/AbstractComponent";
|
||||
|
||||
export default OakComponent({
|
||||
entity: 'actionAuth',
|
||||
projection: {
|
||||
id: 1,
|
||||
relationId: 1,
|
||||
path: 1,
|
||||
deActions: 1,
|
||||
destEntity: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
isList: true,
|
||||
properties: {
|
||||
path: '',
|
||||
openTip: false,
|
||||
entity: '' as keyof ED,
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
filter() {
|
||||
const { path, entity } = this.props;
|
||||
return {
|
||||
destEntity: entity as string,
|
||||
path,
|
||||
};
|
||||
},
|
||||
'#name': 'path',
|
||||
}
|
||||
],
|
||||
pagination: {
|
||||
pageSize: 1000,
|
||||
currentPage: 0,
|
||||
},
|
||||
formData({ data: rows }) {
|
||||
console.log(this.props.path);
|
||||
return {
|
||||
rows
|
||||
};
|
||||
},
|
||||
data: {
|
||||
relations: [] as Partial<EntityDict['relation']['Schema']>[],
|
||||
actions: [] as string[],
|
||||
},
|
||||
listeners: {
|
||||
async path(prev, next) {
|
||||
if (prev.path !== next.path) {
|
||||
const { path, entity } = this.props;
|
||||
this.getRelationAndActions();
|
||||
this.addNamedFilter({
|
||||
filter: {
|
||||
path,
|
||||
destEntity: entity as string,
|
||||
},
|
||||
'#name': 'path'
|
||||
}, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
this.getRelationAndActions();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getRelationAndActions() {
|
||||
const { path, entity } = this.props;
|
||||
const entities = path!.split('.');
|
||||
const sourceEntity = entities[entities?.length - 1];
|
||||
const source = sourceEntity.includes('$') ? sourceEntity.split('$')[0] : sourceEntity;
|
||||
// 获取relation
|
||||
const { data: relations } = await this.features.cache.refresh('relation', {
|
||||
data: {
|
||||
id: 1,
|
||||
entity: 1,
|
||||
entityId: 1,
|
||||
name: 1,
|
||||
display: 1,
|
||||
},
|
||||
filter: {
|
||||
entity: source,
|
||||
entityId: {
|
||||
$exists: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
// 获取actions
|
||||
const actions = this.features.relationAuth.getActions(entity!);
|
||||
this.setState({
|
||||
relations,
|
||||
actions,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
import React, {useState, useEffect} from 'react';
|
||||
import { Table, Checkbox, Button, Row, Space, Tooltip, } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
const { Title, Text } = Typography;
|
||||
import { RowWithActions, WebComponentProps } from '../../../types/Page';
|
||||
import { AuthCascadePath, EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import assert from 'assert';
|
||||
|
||||
type ED = EntityDict & BaseEntityDict;
|
||||
type TableData = {
|
||||
relationId: string;
|
||||
relation: string;
|
||||
actions: string[];
|
||||
}
|
||||
|
||||
export default function render(
|
||||
props: WebComponentProps<
|
||||
ED,
|
||||
'actionAuth',
|
||||
true,
|
||||
{
|
||||
relations: EntityDict['relation']['Schema'][],
|
||||
actions: string[];
|
||||
datasource: TableData[];
|
||||
rows: EntityDict['actionAuth']['Schema'][];
|
||||
path: string;
|
||||
entity: keyof ED;
|
||||
openTip: boolean;
|
||||
},
|
||||
{
|
||||
|
||||
}
|
||||
>
|
||||
) {
|
||||
const { data, methods } = props;
|
||||
const { rows, relations, actions, path, entity, openTip, oakExecutable } = data;
|
||||
const [datasource, setDatasource] = useState<TableData[]>([]);
|
||||
useEffect(() => {
|
||||
const tableRows: TableData[] = relations.map((ele) => ({
|
||||
relationId: ele.id,
|
||||
relation: ele.name,
|
||||
actions,
|
||||
}))
|
||||
setDatasource(tableRows);
|
||||
}, [relations])
|
||||
return (
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<Text style={{fontSize: 16}}>授权</Text>
|
||||
<Tooltip title={"点击保存"} open={openTip} placement="right">
|
||||
<Button type="link" disabled={!oakExecutable} onClick={() => methods.execute()}>保存</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
<Table
|
||||
rowKey={"relationId"}
|
||||
dataSource={datasource}
|
||||
columns={[
|
||||
{
|
||||
dataIndex: 'relation',
|
||||
title: '角色'
|
||||
},
|
||||
{
|
||||
dataIndex: 'actions',
|
||||
title: '操作权限',
|
||||
render: (value: string[], row: TableData) => {
|
||||
const options = value.map((ele) => ({
|
||||
label: ele,
|
||||
value: ele,
|
||||
}))
|
||||
const actionAuth = rows.find((ele) => ele.relationId === row.relationId);
|
||||
const defaultValue = actionAuth ? actionAuth.deActions : [];
|
||||
return (
|
||||
<Checkbox.Group
|
||||
options={options}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(checkedArr) => {
|
||||
if (!actionAuth) {
|
||||
methods.addItem({
|
||||
relationId: row.relationId,
|
||||
path,
|
||||
deActions: checkedArr as string[],
|
||||
destEntity: entity as string,
|
||||
})
|
||||
}
|
||||
else {
|
||||
methods.updateItem({
|
||||
deActions: checkedArr as string[],
|
||||
}, actionAuth.id)
|
||||
}
|
||||
if (!checkedArr.length && actionAuth) {
|
||||
methods.removeItem(actionAuth.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
]}
|
||||
pagination={false}
|
||||
></Table>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
|
||||
import { AuthCascadePath, EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
|
||||
type ED = EntityDict & BaseEntityDict;
|
||||
|
||||
export default OakComponent({
|
||||
entity: 'actionAuth',
|
||||
projection: {
|
||||
id: 1,
|
||||
path: 1,
|
||||
relationId: 1,
|
||||
relation: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
destEntity: 1,
|
||||
deActions: 1,
|
||||
|
||||
},
|
||||
isList: true,
|
||||
filters: [
|
||||
{
|
||||
filter(){
|
||||
const { entity } = this.props;
|
||||
return {
|
||||
path: {
|
||||
$includes: '$',
|
||||
entity,
|
||||
}
|
||||
}
|
||||
},
|
||||
'#name': 'path',
|
||||
}
|
||||
],
|
||||
properties: {
|
||||
entity: '' as keyof ED,
|
||||
},
|
||||
formData({ data: rows }) {
|
||||
// 查看path为actionAuthList-cpn 是否有未提交的操作,如果有提示先提交
|
||||
const operations = this.features.runningTree.getOperations('$actionAuthList-cpn');
|
||||
let showExecuteTip = false;
|
||||
if (operations && operations.length) {
|
||||
showExecuteTip = true;
|
||||
}
|
||||
console.log(operations)
|
||||
return {
|
||||
rows,
|
||||
showExecuteTip,
|
||||
};
|
||||
},
|
||||
data: {
|
||||
links: [] as {
|
||||
source: string;
|
||||
target: string;
|
||||
value: number;
|
||||
}[],
|
||||
entityDNode: [] as string[],
|
||||
entitySNode: [] as string[],
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
const { entity } = this.props;
|
||||
const { links } = this.features.relationAuth.getEntityGraph();
|
||||
this.setState({
|
||||
links,
|
||||
}, () => {
|
||||
this.getNodes(entity);
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNodes(entity: keyof ED) {
|
||||
const { links } = this.state;
|
||||
// 以entity为source 查找entity所指向的实体
|
||||
const entityDNode = links.filter((ele) => ele.source === entity).map((ele) => ele.target);
|
||||
// 以entity为target 查找指向entity的实体
|
||||
const entitySNode = links.filter((ele) => ele.target === entity).map((ele) => ele.source);
|
||||
this.setState({
|
||||
entityDNode,
|
||||
entitySNode,
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
import { AuthCascadePath, EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Row, Badge, Col, Tabs, Checkbox, Table, Space, Button, Modal, Card, Breadcrumb, Tag } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
const { Title, Text } = Typography;
|
||||
import { WebComponentProps } from '../../../types/Page';
|
||||
import { useState, useEffect } from 'react';
|
||||
import ActionAuthList from '../actionAuthList';
|
||||
|
||||
|
||||
type ED = EntityDict & BaseEntityDict;
|
||||
|
||||
export default function render(props: WebComponentProps<ED, keyof ED, false, {
|
||||
rows: EntityDict['actionAuth']['Schema'][];
|
||||
entity: keyof ED;
|
||||
entityDNode: string[];
|
||||
entitySNode: string[];
|
||||
showExecuteTip: boolean;
|
||||
}, {
|
||||
getNodes: (entity: keyof ED) => void;
|
||||
}>) {
|
||||
const { methods, data } = props;
|
||||
const { entity, entityDNode, entitySNode, oakFullpath, showExecuteTip, rows } = data;
|
||||
const { getNodes } = methods;
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openTip, setOpenTip] = useState(false);
|
||||
const [breadcrumbItems, setBreadcrumbItems] = useState<string[]>([])
|
||||
useEffect(() => {
|
||||
if (!showExecuteTip) {
|
||||
setOpenTip(false);
|
||||
}
|
||||
}, [showExecuteTip]);
|
||||
const checkExecute = () => {
|
||||
if (showExecuteTip) {
|
||||
methods.setMessage({
|
||||
content: '有未保存的权限设置,请先保存',
|
||||
type: 'warning',
|
||||
})
|
||||
setOpenTip(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Space direction="vertical" style={{width: '100%'}}>
|
||||
<Button onClick={() => setOpen(true)}>设置</Button>
|
||||
<Modal
|
||||
title={`权限设置(${entity})`}
|
||||
open={open}
|
||||
destroyOnClose={true}
|
||||
footer={(
|
||||
<Button onClick={() => setOpen(false)}>
|
||||
取消
|
||||
</Button>
|
||||
)}
|
||||
onCancel={() => setOpen(false)}
|
||||
width={1000}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%', marginTop: 16 }} size={16}>
|
||||
<Space direction="vertical">
|
||||
<Text style={{fontSize: 16}}>路径</Text>
|
||||
<Space style={{ width: '100%' }} wrap>
|
||||
{(breadcrumbItems && breadcrumbItems.length > 0) ? (
|
||||
<>
|
||||
<Breadcrumb
|
||||
items={breadcrumbItems.map((ele, index) => ({
|
||||
title: (
|
||||
<a
|
||||
onClick={() => {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
const newItems = breadcrumbItems.slice(0, index + 1)
|
||||
setBreadcrumbItems(newItems);
|
||||
const entity = ele.includes('$') ? ele.split('$')[0] : ele;
|
||||
getNodes(entity);
|
||||
}}
|
||||
>
|
||||
{ele}
|
||||
</a>
|
||||
)
|
||||
}))}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
onClick={() => {
|
||||
setBreadcrumbItems([]);
|
||||
getNodes(entity);
|
||||
}}
|
||||
>
|
||||
清空
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Text type="secondary">请先选择结点</Text>
|
||||
)}
|
||||
</Space>
|
||||
</Space>
|
||||
<Space direction="vertical">
|
||||
<Space align="center">
|
||||
<Text style={{ fontSize: 16 }}>结点</Text>
|
||||
<Badge color={"var(--oak-color-primary)"} text="外键" style={{color: 'rgba(0, 0, 0, 0.45)'}} />
|
||||
<Badge color="cyan" text="反指结点" style={{color: 'rgba(0, 0, 0, 0.45)'}} />
|
||||
</Space>
|
||||
<Space wrap>
|
||||
{entityDNode.map((ele) => (
|
||||
<Tag
|
||||
style={{cursor: 'pointer'}}
|
||||
color="processing"
|
||||
bordered={false}
|
||||
onClick={() => {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
breadcrumbItems.push(ele);
|
||||
setBreadcrumbItems(breadcrumbItems)
|
||||
getNodes(ele)
|
||||
}}
|
||||
>
|
||||
{ele}
|
||||
</Tag>
|
||||
))}
|
||||
{entitySNode.map((ele) => (
|
||||
<Tag
|
||||
style={{cursor: 'pointer'}}
|
||||
color={"cyan"}
|
||||
bordered={false}
|
||||
onClick={() => {
|
||||
if (checkExecute()) {
|
||||
return;
|
||||
}
|
||||
breadcrumbItems.push(`${ele}$${entity}`);
|
||||
setBreadcrumbItems(breadcrumbItems)
|
||||
getNodes(ele)
|
||||
}}
|
||||
>
|
||||
{ele}
|
||||
</Tag>
|
||||
))}
|
||||
</Space>
|
||||
</Space>
|
||||
<ActionAuthList
|
||||
oakPath="$actionAuthList-cpn"
|
||||
entity={entity}
|
||||
path={breadcrumbItems.join('.')}
|
||||
openTip={openTip}
|
||||
/>
|
||||
</Space>
|
||||
</Modal>
|
||||
<Table
|
||||
rowKey={"relationId"}
|
||||
dataSource={rows}
|
||||
columns={[
|
||||
{
|
||||
width: 400,
|
||||
dataIndex: 'path',
|
||||
title: '路径',
|
||||
},
|
||||
{
|
||||
dataIndex: 'relation',
|
||||
title: '角色',
|
||||
render: (value, row) => row.relation?.name,
|
||||
},
|
||||
{
|
||||
dataIndex: 'deActions',
|
||||
title: '权限',
|
||||
render: (value: string[]) => {
|
||||
return value.map((ele) => (
|
||||
<Tag>{ele}</Tag>
|
||||
))
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
render: (value, row, index) => {
|
||||
return (
|
||||
<a
|
||||
onClick={() => {
|
||||
const path = row?.path as string;
|
||||
const items = path.split('.');
|
||||
setBreadcrumbItems(items);
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</a>
|
||||
)
|
||||
}
|
||||
}
|
||||
]}
|
||||
pagination={false}
|
||||
></Table>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { AuthCascadePath, EntityDict } from 'oak-domain/lib/types/Entity';
|
||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { Row, Radio, Col, Tabs, Checkbox, Table } from 'antd';
|
||||
import { Row, Radio, Col, Tabs, Checkbox, Table, Space, Button } from 'antd';
|
||||
import { Typography } from 'antd';
|
||||
const { Title, Text } = Typography;
|
||||
import ActionAuth from '../actionAuth';
|
||||
import RelationAuth from '../relationAuth';
|
||||
import { WebComponentProps } from '../../../types/Page';
|
||||
import { useState } from 'react';
|
||||
|
||||
import ActionAuthSingle from '../../../components/relation/single';
|
||||
|
||||
type ED = EntityDict & BaseEntityDict;
|
||||
|
||||
|
|
@ -146,6 +146,16 @@ export default function render(props: WebComponentProps<ED, keyof ED, false, {
|
|||
}
|
||||
);
|
||||
}
|
||||
items.push({
|
||||
label: '特定授权',
|
||||
key: 'special',
|
||||
children: (
|
||||
<ActionAuthSingle
|
||||
oakPath={oakFullpath ? `${oakFullpath}.actionAuths:2` : undefined}
|
||||
entity={entity}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const ActionSelector = actions && (
|
||||
<Row style={{ width: '100%' }} justify="center" align="middle">
|
||||
|
|
|
|||
Loading…
Reference in New Issue