Skip to content

Commit 5290535

Browse files
committed
feat: 完成用户管理的开发
1 parent 5428d51 commit 5290535

File tree

3 files changed

+515
-1
lines changed

3 files changed

+515
-1
lines changed

src/pages/(base)/manage/user/index.tsx

Lines changed: 216 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,220 @@
1+
import { Suspense, lazy } from 'react';
2+
3+
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
4+
import { ATG_MAP } from '@/constants/common';
5+
import { TableHeaderOperation, useTable, useTableOperate, useTableScroll } from '@/features/table';
6+
import { fetchGetUserList } from '@/service/api';
7+
8+
import UserSearch from './modules/UserSearch';
9+
10+
const UserOperateDrawer = lazy(() => import('./modules/UserOperateDrawer'));
11+
12+
const tagUserGenderMap: Record<Api.SystemManage.UserGender, string> = {
13+
1: 'processing',
14+
2: 'error'
15+
};
16+
117
const UserManage = () => {
2-
return <AInput />;
18+
const { t } = useTranslation();
19+
20+
const { scrollConfig, tableWrapperRef } = useTableScroll();
21+
22+
const nav = useNavigate();
23+
24+
const isMobile = useMobile();
25+
26+
const { columnChecks, data, run, searchProps, setColumnChecks, tableProps } = useTable(
27+
{
28+
apiFn: fetchGetUserList,
29+
apiParams: {
30+
current: 1,
31+
nickName: null,
32+
size: 10,
33+
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
34+
// the value can not be undefined, otherwise the property in Form will not be reactive
35+
status: null,
36+
userEmail: null,
37+
userGender: null,
38+
userName: null,
39+
userPhone: null
40+
},
41+
columns: () => [
42+
{
43+
align: 'center',
44+
dataIndex: 'index',
45+
key: 'index',
46+
title: t('common.index'),
47+
width: 64
48+
},
49+
{
50+
align: 'center',
51+
dataIndex: 'userName',
52+
key: 'userName',
53+
minWidth: 100,
54+
title: t('page.manage.user.userName')
55+
},
56+
{
57+
align: 'center',
58+
dataIndex: 'userGender',
59+
key: 'userGender',
60+
render: (_, record) => {
61+
if (record?.userGender === null) {
62+
return null;
63+
}
64+
65+
const label = t(userGenderRecord[record.userGender]);
66+
67+
return <ATag color={tagUserGenderMap[record.userGender]}>{label}</ATag>;
68+
},
69+
title: t('page.manage.user.userGender'),
70+
width: 100
71+
},
72+
{
73+
align: 'center',
74+
dataIndex: 'nickName',
75+
key: 'nickName',
76+
minWidth: 100,
77+
title: t('page.manage.user.nickName')
78+
},
79+
{
80+
align: 'center',
81+
dataIndex: 'userPhone',
82+
key: 'userPhone',
83+
title: t('page.manage.user.userPhone'),
84+
width: 120
85+
},
86+
{
87+
align: 'center',
88+
dataIndex: 'userEmail',
89+
key: 'userEmail',
90+
minWidth: 200,
91+
title: t('page.manage.user.userEmail')
92+
},
93+
{
94+
align: 'center',
95+
dataIndex: 'status',
96+
key: 'status',
97+
render: (_, record) => {
98+
if (record.status === null) {
99+
return null;
100+
}
101+
const label = t(enableStatusRecord[record.status]);
102+
return <ATag color={ATG_MAP[record.status]}>{label}</ATag>;
103+
},
104+
title: t('page.manage.user.userStatus'),
105+
width: 100
106+
},
107+
{
108+
align: 'center',
109+
key: 'operate',
110+
render: (_, record) => (
111+
<div className="flex-center gap-8px">
112+
<AButton
113+
ghost
114+
size="small"
115+
type="primary"
116+
onClick={() => edit(record.id)}
117+
>
118+
{t('common.edit')}
119+
</AButton>
120+
<AButton
121+
size="small"
122+
onClick={() => nav(`/manage/user/${record.id}`)}
123+
>
124+
详情
125+
</AButton>
126+
<APopconfirm
127+
title={t('common.confirmDelete')}
128+
onConfirm={() => handleDelete(record.id)}
129+
>
130+
<AButton
131+
danger
132+
size="small"
133+
>
134+
{t('common.delete')}
135+
</AButton>
136+
</APopconfirm>
137+
</div>
138+
),
139+
title: t('common.operate'),
140+
width: 195
141+
}
142+
]
143+
},
144+
{ showQuickJumper: true }
145+
);
146+
147+
const { checkedRowKeys, generalPopupOperation, handleAdd, handleEdit, onBatchDeleted, onDeleted, rowSelection } =
148+
useTableOperate(data, run, async (res, type) => {
149+
if (type === 'add') {
150+
// add request 调用新增的接口
151+
console.log(res);
152+
} else {
153+
// edit request 调用编辑的接口
154+
console.log(res);
155+
}
156+
});
157+
158+
async function handleBatchDelete() {
159+
// request
160+
console.log(checkedRowKeys);
161+
onBatchDeleted();
162+
}
163+
164+
function handleDelete(id: number) {
165+
// request
166+
console.log(id);
167+
168+
onDeleted();
169+
}
170+
171+
function edit(id: number) {
172+
handleEdit(id);
173+
}
174+
return (
175+
<div className="h-full min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
176+
<ACollapse
177+
bordered={false}
178+
className="card-wrapper"
179+
defaultActiveKey={isMobile ? undefined : '1'}
180+
items={[
181+
{
182+
children: <UserSearch {...searchProps} />,
183+
key: '1',
184+
label: t('common.search')
185+
}
186+
]}
187+
/>
188+
189+
<ACard
190+
className="flex-col-stretch sm:flex-1-hidden card-wrapper"
191+
ref={tableWrapperRef}
192+
title={t('page.manage.user.title')}
193+
variant="borderless"
194+
extra={
195+
<TableHeaderOperation
196+
add={handleAdd}
197+
columns={columnChecks}
198+
disabledDelete={checkedRowKeys.length === 0}
199+
loading={tableProps.loading}
200+
refresh={run}
201+
setColumnChecks={setColumnChecks}
202+
onDelete={handleBatchDelete}
203+
/>
204+
}
205+
>
206+
<ATable
207+
rowSelection={rowSelection}
208+
scroll={scrollConfig}
209+
size="small"
210+
{...tableProps}
211+
/>
212+
<Suspense>
213+
<UserOperateDrawer {...generalPopupOperation} />
214+
</Suspense>
215+
</ACard>
216+
</div>
217+
);
3218
};
4219

5220
export const handle = {
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { useRequest } from '@sa/hooks';
2+
import { Button, Drawer, Flex, Form, Input, Radio, Select } from 'antd';
3+
import type { FC } from 'react';
4+
5+
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
6+
import { useFormRules } from '@/features/form';
7+
import { fetchGetAllRoles } from '@/service/api';
8+
9+
interface OptionsProps {
10+
label: string;
11+
value: string;
12+
}
13+
14+
type Model = Pick<
15+
Api.SystemManage.User,
16+
'nickName' | 'status' | 'userEmail' | 'userGender' | 'userName' | 'userPhone' | 'userRoles'
17+
>;
18+
19+
type RuleKey = Extract<keyof Model, 'status' | 'userName'>;
20+
21+
function getOptions(item: Api.SystemManage.AllRole) {
22+
return {
23+
label: item.roleName,
24+
value: item.roleCode
25+
};
26+
}
27+
28+
const UserOperateDrawer: FC<Page.OperateDrawerProps> = ({ form, handleSubmit, onClose, open, operateType }) => {
29+
const { t } = useTranslation();
30+
31+
const { data, run } = useRequest(fetchGetAllRoles, {
32+
manual: true
33+
});
34+
35+
const { defaultRequiredRule } = useFormRules();
36+
37+
const roleOptions: OptionsProps[] = data ? data.map(getOptions) : [];
38+
39+
const rules: Record<RuleKey, App.Global.FormRule> = {
40+
status: defaultRequiredRule,
41+
userName: defaultRequiredRule
42+
};
43+
44+
useUpdateEffect(() => {
45+
if (open) {
46+
run();
47+
}
48+
}, [open]);
49+
50+
return (
51+
<Drawer
52+
open={open}
53+
title={operateType === 'add' ? t('page.manage.user.addUser') : t('page.manage.user.editUser')}
54+
footer={
55+
<Flex justify="space-between">
56+
<Button onClick={onClose}>{t('common.cancel')}</Button>
57+
<Button
58+
type="primary"
59+
onClick={handleSubmit}
60+
>
61+
{t('common.confirm')}
62+
</Button>
63+
</Flex>
64+
}
65+
onClose={onClose}
66+
>
67+
<Form
68+
form={form}
69+
layout="vertical"
70+
>
71+
<Form.Item
72+
label={t('page.manage.user.userName')}
73+
name="userName"
74+
rules={[rules.userName]}
75+
>
76+
<Input placeholder={t('page.manage.user.form.userName')} />
77+
</Form.Item>
78+
79+
<Form.Item
80+
label={t('page.manage.user.userGender')}
81+
name="userGender"
82+
>
83+
<Radio.Group>
84+
{userGenderOptions.map(item => (
85+
<Radio
86+
key={item.value}
87+
value={item.value}
88+
>
89+
{t(item.label)}
90+
</Radio>
91+
))}
92+
</Radio.Group>
93+
</Form.Item>
94+
95+
<Form.Item
96+
label={t('page.manage.user.nickName')}
97+
name="nickName"
98+
>
99+
<Input placeholder={t('page.manage.user.form.nickName')} />
100+
</Form.Item>
101+
102+
<Form.Item
103+
label={t('page.manage.user.userPhone')}
104+
name="userPhone"
105+
>
106+
<Input placeholder={t('page.manage.user.form.userPhone')} />
107+
</Form.Item>
108+
109+
<Form.Item
110+
label={t('page.manage.user.userEmail')}
111+
name="email"
112+
>
113+
<Input placeholder={t('page.manage.user.form.userEmail')} />
114+
</Form.Item>
115+
116+
<Form.Item
117+
label={t('page.manage.user.userStatus')}
118+
name="status"
119+
rules={[rules.status]}
120+
>
121+
<Radio.Group>
122+
{enableStatusOptions.map(item => (
123+
<Radio
124+
key={item.value}
125+
value={item.value}
126+
>
127+
{t(item.label)}
128+
</Radio>
129+
))}
130+
</Radio.Group>
131+
</Form.Item>
132+
133+
<Form.Item
134+
label={t('page.manage.user.userRole')}
135+
name="roles"
136+
>
137+
<Select
138+
options={roleOptions}
139+
placeholder={t('page.manage.user.form.userRole')}
140+
/>
141+
</Form.Item>
142+
</Form>
143+
</Drawer>
144+
);
145+
};
146+
147+
export default UserOperateDrawer;

0 commit comments

Comments
 (0)