relation/entity 改动 支持单节点授权

This commit is contained in:
梁朝伟 2023-07-21 18:51:27 +08:00
parent cbe25c7c3c
commit b6e0a24d28
15 changed files with 910 additions and 2 deletions

View File

@ -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;

View File

@ -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*/];
}
});
});
}
}
});

View File

@ -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 {};

View File

@ -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;

View File

@ -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;

View File

@ -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,
});
}
}
});

View File

@ -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 {};

View File

@ -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;

View File

@ -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',

View File

@ -0,0 +1,3 @@
{
}

View File

@ -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,
})
}
}
})

View File

@ -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>
);
}

View File

@ -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,
})
}
}
});

View File

@ -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>
);
}

View File

@ -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">