Files
alert/src/pages/role/Popup.tsx
Jhonton Chen 43d6e9207a feat: add permission-based access control and improve alert table UI
- 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>
2026-05-29 18:04:07 +08:00

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