chore: menu permissions & plugins setting permissions (#3822)
* chore: menu permissions * chore: plugins setting permissions * chore: plugins setting permissions * chore: plugins setting permissions
This commit is contained in:
parent
d04a60d386
commit
d3bd0c71dd
@ -5,11 +5,12 @@ import { Checkbox, message } from 'antd';
|
|||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
import React, { useContext, useMemo } from 'react';
|
import React, { useContext, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { RolesManagerContext } from '../RolesManagerProvider';
|
import { RolesManagerContext } from '../RolesManagerProvider';
|
||||||
import { StrategyActions } from './StrategyActions';
|
import { StrategyActions } from './StrategyActions';
|
||||||
import { useACLTranslation } from '../locale';
|
import { useACLTranslation } from '../locale';
|
||||||
import { uid } from '@formily/shared';
|
import { PluginPermissions } from './PluginPermissions';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
|
||||||
|
|
||||||
const SnippetCheckboxGroup = connect((props) => {
|
const SnippetCheckboxGroup = connect((props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -61,25 +62,8 @@ export const GeneralPermissions: React.FC<{
|
|||||||
const { role, setRole } = useContext(RolesManagerContext);
|
const { role, setRole } = useContext(RolesManagerContext);
|
||||||
const { t } = useACLTranslation();
|
const { t } = useACLTranslation();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { data } = useRequest(
|
const pm = role?.snippets?.includes('pm.*');
|
||||||
() =>
|
|
||||||
api
|
|
||||||
.resource('roles')
|
|
||||||
.get({
|
|
||||||
filterByTk: role?.name,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
const record = res?.data?.data;
|
|
||||||
record.snippets?.forEach((key: string) => {
|
|
||||||
record[key] = true;
|
|
||||||
});
|
|
||||||
return record;
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
ready: active,
|
|
||||||
refreshDeps: [role?.name],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const update = useMemoizedFn(async (form: Form) => {
|
const update = useMemoizedFn(async (form: Form) => {
|
||||||
await api.resource('roles').update({
|
await api.resource('roles').update({
|
||||||
filterByTk: role.name,
|
filterByTk: role.name,
|
||||||
@ -90,37 +74,48 @@ export const GeneralPermissions: React.FC<{
|
|||||||
});
|
});
|
||||||
const form = useMemo(() => {
|
const form = useMemo(() => {
|
||||||
return createForm({
|
return createForm({
|
||||||
values: data,
|
values: role,
|
||||||
effects() {
|
effects() {
|
||||||
onFormValuesChange(async (form) => {
|
onFormValuesChange(async (form) => {
|
||||||
await update(form);
|
await update(form);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [data, update]);
|
}, [role, update]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
components={{ SnippetCheckboxGroup, StrategyActions }}
|
components={{ SnippetCheckboxGroup, StrategyActions, PluginPermissions }}
|
||||||
|
scope={{ pm }}
|
||||||
schema={{
|
schema={{
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: uid(),
|
name: uid(),
|
||||||
'x-component': 'FormV2',
|
'x-component': 'div',
|
||||||
'x-component-props': {
|
|
||||||
form,
|
|
||||||
},
|
|
||||||
properties: {
|
properties: {
|
||||||
snippets: {
|
general: {
|
||||||
title: t('Configure permissions'),
|
'x-component': 'FormV2',
|
||||||
type: 'boolean',
|
'x-component-props': {
|
||||||
'x-decorator': 'FormItem',
|
form,
|
||||||
'x-component': 'SnippetCheckboxGroup',
|
},
|
||||||
|
properties: {
|
||||||
|
snippets: {
|
||||||
|
title: t('Configure permissions'),
|
||||||
|
type: 'boolean',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'SnippetCheckboxGroup',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
allowNewMenu: {
|
plugins: {
|
||||||
title: t('Menu permissions'),
|
'x-component': 'FormV2',
|
||||||
'x-decorator': 'FormItem',
|
properties: {
|
||||||
'x-component': 'Checkbox',
|
pluginSettings: {
|
||||||
'x-content': t('New menu items are allowed to be accessed by default.'),
|
title: t('Plugin settings'),
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'PluginPermissions',
|
||||||
|
'x-visible': '{{pm}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { Checkbox, message, Table } from 'antd';
|
import { Checkbox, message, Table } from 'antd';
|
||||||
|
import { onFormValuesChange, createForm, Form } from '@formily/core';
|
||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import { useAPIClient, SchemaComponent, useRequest } from '@nocobase/client';
|
||||||
import { useStyles } from './style';
|
import { useStyles } from './style';
|
||||||
import { useAPIClient, useCollectionRecord, useRequest } from '@nocobase/client';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { RolesManagerContext } from '../RolesManagerProvider';
|
import { RolesManagerContext } from '../RolesManagerProvider';
|
||||||
import { useMenuItems } from './MenuItemsProvider';
|
import { useMenuItems } from './MenuItemsProvider';
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ export const MenuPermissions: React.FC<{
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
}> = ({ active }) => {
|
}> = ({ active }) => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const { role } = useContext(RolesManagerContext);
|
const { role, setRole } = useContext(RolesManagerContext);
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { items } = useMenuItems();
|
const { items } = useMenuItems();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -107,51 +110,89 @@ export const MenuPermissions: React.FC<{
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const update = useMemoizedFn(async (form: Form) => {
|
||||||
|
await api.resource('roles').update({
|
||||||
|
filterByTk: role.name,
|
||||||
|
values: form.values,
|
||||||
|
});
|
||||||
|
setRole({ ...role, ...form.values });
|
||||||
|
message.success(t('Saved successfully'));
|
||||||
|
});
|
||||||
|
const form = useMemo(() => {
|
||||||
|
return createForm({
|
||||||
|
values: role,
|
||||||
|
effects() {
|
||||||
|
onFormValuesChange(async (form) => {
|
||||||
|
await update(form);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [role, update]);
|
||||||
return (
|
return (
|
||||||
<Table
|
<>
|
||||||
className={styles}
|
<SchemaComponent
|
||||||
loading={loading}
|
schema={{
|
||||||
rowKey={'uid'}
|
type: 'void',
|
||||||
pagination={false}
|
name: uid(),
|
||||||
expandable={{
|
'x-component': 'FormV2',
|
||||||
defaultExpandAllRows: true,
|
'x-component-props': {
|
||||||
}}
|
form,
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
dataIndex: 'title',
|
|
||||||
title: t('Menu item title'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'accessible',
|
|
||||||
title: (
|
|
||||||
<>
|
|
||||||
<Checkbox
|
|
||||||
checked={allChecked}
|
|
||||||
onChange={async (value) => {
|
|
||||||
if (allChecked) {
|
|
||||||
await resource.set({
|
|
||||||
values: [],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await resource.set({
|
|
||||||
values: allUids,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
refresh();
|
|
||||||
message.success(t('Saved successfully'));
|
|
||||||
}}
|
|
||||||
/>{' '}
|
|
||||||
{t('Accessible')}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
render: (_, schema) => {
|
|
||||||
const checked = uids.includes(schema.uid);
|
|
||||||
return <Checkbox checked={checked} onChange={() => handleChange(checked, schema)} />;
|
|
||||||
},
|
},
|
||||||
},
|
properties: {
|
||||||
]}
|
allowNewMenu: {
|
||||||
dataSource={translateTitle(items)}
|
title: t('Menu permissions'),
|
||||||
/>
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Checkbox',
|
||||||
|
'x-content': t('New menu items are allowed to be accessed by default.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
className={styles}
|
||||||
|
loading={loading}
|
||||||
|
rowKey={'uid'}
|
||||||
|
pagination={false}
|
||||||
|
expandable={{
|
||||||
|
defaultExpandAllRows: true,
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
dataIndex: 'title',
|
||||||
|
title: t('Menu item title'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'accessible',
|
||||||
|
title: (
|
||||||
|
<>
|
||||||
|
<Checkbox
|
||||||
|
checked={allChecked}
|
||||||
|
onChange={async (value) => {
|
||||||
|
if (allChecked) {
|
||||||
|
await resource.set({
|
||||||
|
values: [],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await resource.set({
|
||||||
|
values: allUids,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
message.success(t('Saved successfully'));
|
||||||
|
}}
|
||||||
|
/>{' '}
|
||||||
|
{t('Accessible')}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
render: (_, schema) => {
|
||||||
|
const checked = uids.includes(schema.uid);
|
||||||
|
return <Checkbox checked={checked} onChange={() => handleChange(checked, schema)} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
dataSource={translateTitle(items)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useApp } from '@nocobase/client';
|
import { useApp, useRequest, useAPIClient } from '@nocobase/client';
|
||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
import React, { useContext, useEffect, useMemo } from 'react';
|
import React, { useContext, useEffect, useMemo } from 'react';
|
||||||
import { RolesManagerContext } from '../RolesManagerProvider';
|
import { RolesManagerContext } from '../RolesManagerProvider';
|
||||||
@ -7,7 +7,6 @@ import { AvailableActionsProvider } from './AvailableActions';
|
|||||||
import { GeneralPermissions } from './GeneralPermissions';
|
import { GeneralPermissions } from './GeneralPermissions';
|
||||||
import { MenuItemsProvider } from './MenuItemsProvider';
|
import { MenuItemsProvider } from './MenuItemsProvider';
|
||||||
import { MenuPermissions } from './MenuPermissions';
|
import { MenuPermissions } from './MenuPermissions';
|
||||||
import { PluginPermissions } from './PluginPermissions';
|
|
||||||
|
|
||||||
const TabLayout: React.FC = (props) => {
|
const TabLayout: React.FC = (props) => {
|
||||||
return <div style={{ maxHeight: '60vh', overflowY: 'auto' }}>{props.children}</div>;
|
return <div style={{ maxHeight: '60vh', overflowY: 'auto' }}>{props.children}</div>;
|
||||||
@ -16,7 +15,7 @@ const TabLayout: React.FC = (props) => {
|
|||||||
export const Permissions: React.FC<{ active: boolean }> = ({ active }) => {
|
export const Permissions: React.FC<{ active: boolean }> = ({ active }) => {
|
||||||
const { t } = useACLTranslation();
|
const { t } = useACLTranslation();
|
||||||
const [activeKey, setActiveKey] = React.useState('general');
|
const [activeKey, setActiveKey] = React.useState('general');
|
||||||
const { role } = useContext(RolesManagerContext);
|
const { role, setRole } = useContext(RolesManagerContext);
|
||||||
const pm = role?.snippets?.includes('pm.*');
|
const pm = role?.snippets?.includes('pm.*');
|
||||||
const app = useApp();
|
const app = useApp();
|
||||||
const DataSourcePermissionManager = app.getComponent('DataSourcePermissionManager');
|
const DataSourcePermissionManager = app.getComponent('DataSourcePermissionManager');
|
||||||
@ -25,7 +24,7 @@ export const Permissions: React.FC<{ active: boolean }> = ({ active }) => {
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
key: 'general',
|
key: 'general',
|
||||||
label: t('General'),
|
label: t('System'),
|
||||||
children: (
|
children: (
|
||||||
<TabLayout>
|
<TabLayout>
|
||||||
<GeneralPermissions active={activeKey === 'general' && active} />
|
<GeneralPermissions active={activeKey === 'general' && active} />
|
||||||
@ -43,19 +42,6 @@ export const Permissions: React.FC<{ active: boolean }> = ({ active }) => {
|
|||||||
</TabLayout>
|
</TabLayout>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...(pm
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
key: 'plugin',
|
|
||||||
label: t('Plugin settings'),
|
|
||||||
children: (
|
|
||||||
<TabLayout>
|
|
||||||
<PluginPermissions active={activeKey === 'plugin' && active} />
|
|
||||||
</TabLayout>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(DataSourcePermissionManager
|
...(DataSourcePermissionManager
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -74,10 +60,33 @@ export const Permissions: React.FC<{ active: boolean }> = ({ active }) => {
|
|||||||
],
|
],
|
||||||
[pm, activeKey, active, t],
|
[pm, activeKey, active, t],
|
||||||
);
|
);
|
||||||
|
const api = useAPIClient();
|
||||||
|
const { data } = useRequest(
|
||||||
|
() =>
|
||||||
|
api
|
||||||
|
.resource('roles')
|
||||||
|
.get({
|
||||||
|
filterByTk: role?.name,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const record = res?.data?.data;
|
||||||
|
record.snippets?.forEach((key: string) => {
|
||||||
|
record[key] = true;
|
||||||
|
});
|
||||||
|
return record;
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
ready: active && !!role,
|
||||||
|
refreshDeps: [role?.name],
|
||||||
|
},
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveKey('general');
|
setActiveKey('general');
|
||||||
}, [role?.name]);
|
}, [role?.name]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRole(data);
|
||||||
|
}, [data]);
|
||||||
return (
|
return (
|
||||||
<AvailableActionsProvider>
|
<AvailableActionsProvider>
|
||||||
<Tabs type="card" activeKey={activeKey} onChange={(key) => setActiveKey(key)} items={items} />
|
<Tabs type="card" activeKey={activeKey} onChange={(key) => setActiveKey(key)} items={items} />
|
||||||
|
@ -38,7 +38,7 @@ export const PluginPermissions: React.FC<{
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const settings = app.pluginSettingsManager.getList(false);
|
const settings = app.pluginSettingsManager.getList(false);
|
||||||
const allAclSnippets = app.pluginSettingsManager.getAclSnippets();
|
const allAclSnippets = app.pluginSettingsManager.getAclSnippets();
|
||||||
const [snippets, setSnippets] = useState<string[]>([]);
|
const [snippets, setSnippets] = useState<string[]>(role?.snippets || []);
|
||||||
const flatPlugins = useMemo(() => {
|
const flatPlugins = useMemo(() => {
|
||||||
return flatMap(settings, (item) => {
|
return flatMap(settings, (item) => {
|
||||||
if (item.children) {
|
if (item.children) {
|
||||||
@ -52,12 +52,11 @@ export const PluginPermissions: React.FC<{
|
|||||||
() => snippets.includes('pm.*') && snippets.every((item) => !item.startsWith('!pm.')),
|
() => snippets.includes('pm.*') && snippets.every((item) => !item.startsWith('!pm.')),
|
||||||
[snippets],
|
[snippets],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { loading, refresh } = useRequest(
|
const { loading, refresh } = useRequest(
|
||||||
{
|
{
|
||||||
resource: 'roles.snippets',
|
resource: 'roles.snippets',
|
||||||
resourceOf: role.name,
|
resourceOf: role?.name,
|
||||||
action: 'list',
|
action: 'list',
|
||||||
params: {
|
params: {
|
||||||
paginate: false,
|
paginate: false,
|
||||||
@ -66,6 +65,7 @@ export const PluginPermissions: React.FC<{
|
|||||||
{
|
{
|
||||||
ready: !!role && active,
|
ready: !!role && active,
|
||||||
refreshDeps: [role?.name],
|
refreshDeps: [role?.name],
|
||||||
|
manual: true,
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
setSnippets(
|
setSnippets(
|
||||||
data?.data.filter((v) => {
|
data?.data.filter((v) => {
|
||||||
@ -75,7 +75,10 @@ export const PluginPermissions: React.FC<{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const resource = api.resource('roles.snippets', role.name);
|
if (!role) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resource = api.resource('roles.snippets', role?.name);
|
||||||
const handleChange = async (checked, record) => {
|
const handleChange = async (checked, record) => {
|
||||||
const childrenKeys = getChildrenKeys(record?.children, []);
|
const childrenKeys = getChildrenKeys(record?.children, []);
|
||||||
const totalKeys = childrenKeys.concat(record.aclSnippet);
|
const totalKeys = childrenKeys.concat(record.aclSnippet);
|
||||||
@ -129,7 +132,7 @@ export const PluginPermissions: React.FC<{
|
|||||||
refresh();
|
refresh();
|
||||||
message.success(t('Saved successfully'));
|
message.success(t('Saved successfully'));
|
||||||
}}
|
}}
|
||||||
/>{' '}
|
/>
|
||||||
{t('Accessible')}
|
{t('Accessible')}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user