- Implement PermissionGuard component for route-level permission enforcement - Add tree-based permission checkbox group in role management popup - Gate SSE connection, unread count fetch, and rule edit actions on user permissions - Add skip401Refresh option and auto token refresh on 401 response - Improve alert table with fixed columns, explicit widths, and text ellipsis - Add i18n entries for permission guide and rule menu descriptions (en/zh) - Add expo-api nginx proxy configuration - Add .claude to .gitignore Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
154 lines
4.5 KiB
TypeScript
154 lines
4.5 KiB
TypeScript
import {
|
|
ModalForm,
|
|
ProForm,
|
|
ProFormDependency,
|
|
// ProFormDigit,
|
|
ProFormText,
|
|
} from '@ant-design/pro-components';
|
|
import { ColorPicker, Form, Space } from 'antd';
|
|
import { useMemo } from 'react';
|
|
import MyTag from '@/components/MyTag';
|
|
import useUserInfo from '@/hooks/useUserInfo';
|
|
import { getRoleDetail } from '@/services/api';
|
|
import { $t } from '@/utils/i18n';
|
|
// import { preventScientificNotation } from '@/utils';
|
|
import useCardList from './cardList';
|
|
import PermissionCheckboxGroup from './components/PermissionCheckboxGroup';
|
|
|
|
function flattenAllIds(nodes: API.MenuListItem[]): (string | number)[] {
|
|
const ids: (string | number)[] = [];
|
|
const walk = (items: API.MenuListItem[]) => {
|
|
items.forEach((item) => {
|
|
ids.push(item.id);
|
|
if (item.children?.length) walk(item.children);
|
|
});
|
|
};
|
|
walk(nodes);
|
|
return ids;
|
|
}
|
|
|
|
type RolesListItem = Partial<API.RolesListItem>;
|
|
export default (props: {
|
|
action: EX.Action;
|
|
visible: boolean;
|
|
formValues: Partial<RolesListItem | undefined>;
|
|
onSubmit: (values: RolesListItem) => void;
|
|
onDone: () => void;
|
|
menuTree?: API.MenuListItem[];
|
|
}) => {
|
|
const {
|
|
visible,
|
|
formValues,
|
|
onSubmit,
|
|
onDone,
|
|
action,
|
|
menuTree: propMenuTree,
|
|
} = props;
|
|
|
|
const { userInfo } = useUserInfo();
|
|
// const { isGlobalCompany } = useAccess();
|
|
// const userRoleInfo = userInfo?.roleList?.[0];
|
|
// const level = userRoleInfo?.level || (isGlobalCompany ? 100 : 80);
|
|
|
|
const { getMenuMetaByPermissions } = useCardList();
|
|
|
|
const menuTree = useMemo(() => {
|
|
const source = propMenuTree?.length
|
|
? propMenuTree
|
|
: getMenuMetaByPermissions(userInfo?.menuList || []);
|
|
return source as API.MenuListItem[];
|
|
}, [propMenuTree, userInfo?.menuList]);
|
|
|
|
const actionMap = {
|
|
add: $t('table.action.add'),
|
|
view: $t('table.action.view'),
|
|
edit: $t('table.action.edit'),
|
|
};
|
|
return (
|
|
<ModalForm
|
|
disabled={action === 'view'}
|
|
title={`${actionMap[action]} ${$t('form.role.title')}`}
|
|
initialValues={formValues?.id ? formValues : { color: '#3b82f6' }}
|
|
open={visible}
|
|
onFinish={async (values: any) => {
|
|
values.menuIds = (values.menuIds || []).join(',') || '';
|
|
return onSubmit(values);
|
|
}}
|
|
modalProps={{
|
|
maskClosable: action === 'view',
|
|
onCancel: () => onDone(),
|
|
destroyOnHidden: true,
|
|
}}
|
|
request={async () => {
|
|
if (formValues?.id) {
|
|
const {
|
|
data: { data },
|
|
} = await getRoleDetail({
|
|
id: formValues.id,
|
|
});
|
|
const allSelectedIds = flattenAllIds(data?.menuList || []);
|
|
return {
|
|
...formValues,
|
|
menuIds: allSelectedIds,
|
|
};
|
|
}
|
|
return formValues || {};
|
|
}}
|
|
>
|
|
<ProFormText
|
|
name="name"
|
|
label={$t('form.role.roleName')}
|
|
placeholder={$t('form.role.roleName.placeholder')}
|
|
rules={[{ required: true, message: $t('form.required') }]}
|
|
/>
|
|
{/* <ProFormDigit
|
|
min={1}
|
|
max={level - 1}
|
|
extra={$t('form.role.level.placeholder', { level: level - 1 })}
|
|
precision={0}
|
|
name="level"
|
|
label={$t('form.role.level')}
|
|
fieldProps={{
|
|
type: 'number',
|
|
onKeyDown: preventScientificNotation,
|
|
}}
|
|
rules={[{ required: true, message: $t('form.required') }]}
|
|
/> */}
|
|
<ProForm.Item label={$t('form.role.badgeColor')} required>
|
|
<Space>
|
|
<Form.Item
|
|
name="color"
|
|
noStyle
|
|
rules={[{ required: true, message: $t('form.required') }]}
|
|
getValueFromEvent={(color: { toHexString: () => any }) =>
|
|
typeof color === 'string' ? color : color.toHexString()
|
|
}
|
|
>
|
|
<ColorPicker format="hex" placement="top" showText disabledAlpha />
|
|
</Form.Item>
|
|
|
|
<ProFormDependency name={['color', 'name']}>
|
|
{({ color, name }) => (
|
|
<Space>
|
|
<span>{$t('form.role.preview')}: </span>
|
|
<MyTag color={color || '#3b82f6'}>{name || 'Example'}</MyTag>
|
|
</Space>
|
|
)}
|
|
</ProFormDependency>
|
|
</Space>
|
|
</ProForm.Item>
|
|
<ProForm.Item
|
|
name="menuIds"
|
|
label={$t('form.role.menuIds')}
|
|
rules={[{ required: true, message: $t('form.required') }]}
|
|
>
|
|
<PermissionCheckboxGroup
|
|
menuTree={menuTree}
|
|
disabled={action === 'view'}
|
|
/>
|
|
</ProForm.Item>
|
|
<ProFormText hidden name="id" label="ID" />
|
|
</ModalForm>
|
|
);
|
|
};
|