npm i @ant-design/pro-cli -g
pro create cms-front
cd cms-front
package.json
- "prepare": "husky install",
npm install
npm run start:no-mock
/login/account
{
"username": "字符串",
"password": "字符串"
}
{
"status": "字符串",
"type": "字符串",
"token": "字符串",
"currentAuthority": "字符串"
}
用户名和密码都是必填项。
用户名或密码无效。
/login/outLogin
{
"data": {},
"success": true
}
/currentUser
authorization
: Bearer <token>
{
"success": true,
"data": {
"name": "字符串",
"avatar": "字符串",
...
}
}
未提供令牌。
无效的令牌。
未找到用户。
/user
current
: 整数pageSize
: 整数sorter
: JSON对象filter
: JSON对象name
: 字符串username
: 字符串email
: 字符串access
: 字符串{
"data": [ ... ],
"total": 整数,
"success": true,
"pageSize": 整数,
"current": 整数
}
/user
{
"name": "字符串",
...
}
创建用户时出错
/user
{
"userid": 整数,
...
}
{
"success": true
}
未找到用户
更新用户时出错
/user
{
"userid": 整数
}
{
"success": true
}
未找到用户
删除用户时出错
{
+ "strict": false,
+ "noImplicitAny": false,
}
src\services\ant-design-pro\typings.d.ts
type LoginResult = {
status?: string;
type?: string;
currentAuthority?: string;
+ token?: string;
};
src\pages\User\Login\index.tsx
const handleSubmit = async (values: API.LoginParams) => {
try {
// 登录
const msg = await login({ ...values, type });
if (msg.status === 'ok') {
const defaultLoginSuccessMessage = intl.formatMessage({
id: 'pages.login.success',
defaultMessage: '登录成功!',
});
message.success(defaultLoginSuccessMessage);
+ localStorage.setItem('token', msg.token || '');
await fetchUserInfo();
const urlParams = new URL(window.location.href).searchParams;
history.push(urlParams.get('redirect') || '/');
return;
}
console.log(msg);
// 如果失败去设置用户错误信息
setUserLoginState(msg);
} catch (error) {
const defaultLoginFailureMessage = intl.formatMessage({
id: 'pages.login.failure',
defaultMessage: '登录失败,请重试!',
});
console.log(error);
message.error(defaultLoginFailureMessage);
}
};
src\requestErrorConfig.ts
requestInterceptors: [
(config: RequestOptions) => {
+ const token = localStorage.getItem('token');
+ const headers = {
+ ...config.headers,
+ authorization: `Bearer ${token}`
+ };
+ return { ...config, headers };
},
],
config\routes.ts
[
+ {
+ name: 'user.list',
+ icon: 'user',
+ path: '/users',
+ component: './Users',
+ },
]
src\locales\zh-CN\menu.ts
{
+ 'menu.user.list': '用户管理',
}
src\locales\zh-CN\pages.ts
{
+ 'pages.user.createForm.newUser': '新建用户',
+ 'pages.user.updateForm.userUsername.usernameLabel': '用户名',
+ 'pages.user.updateForm.userUsername.usernameRules': '请输入用户名!',
+ 'pages.user.updateForm.userPassword.passwordLabel': '密码',
+ 'pages.user.updateForm.userPassword.passwordRules': '请输入密码!',
+ 'pages.user.updateForm.userName.nameLabel': '姓名',
+ 'pages.user.updateForm.userName.nameRules': '请输入姓名!',
+ 'pages.user.updateForm.userEmail.emailLabel': '邮箱',
+ 'pages.user.updateForm.userEmail.emailRules': '请输入邮箱!',
+ 'pages.user.updateForm.access.accessLabel': '角色',
+ 'pages.user.updateForm.access.accessRules': '请选择角色!',
+ 'pages.user.titleOption': '操作',
+ 'pages.user.update': '更新',
+ 'pages.user.new': '新建',
+ 'pages.user.chosen': '已选择',
+ 'pages.user.item': '项',
+ 'pages.user.title': '用户管理',
+ 'pages.user.updateForm.updateUser': '修改用户',
+ 'pages.user.batchDeletion': '批量删除',
+ 'pages.user.access.admin': '管理员',
+ 'pages.user.access.user': '普通用户'
}
src\services\ant-design-pro\api.ts
+export async function user(
+ params: {
+ current?: number;
+ pageSize?: number;
+ [key: string]: any;
+ },
+ sort: { [key: string]: 'ascend' | 'descend' },
+ filter: { [key: string]: string[] }
+) {
+ if (sort && Object.keys(sort).length > 0) {
+ params.sorter = JSON.stringify(sort);
+ }
+ if (filter && Object.keys(filter).length > 0) {
+ params.filter = JSON.stringify(filter);
+ }
+ return request<API.UserList>('/api/user', {
+ method: 'GET',
+ params
+ });
+}
+/** 更新用户 PUT /api/user */
+export async function updateUser(options?: { [key: string]: any }) {
+ return request<API.UserItem>('/api/user', {
+ method: 'PUT',
+ ...(options || {}),
+ });
+}
+
+/** 新建用户 POST /api/user */
+export async function addUser(options?: { [key: string]: any }) {
+ return request<API.UserItem>('/api/user', {
+ method: 'POST',
+ ...(options || {}),
+ });
+}
+
+/** 删除用户 DELETE /api/user */
+export async function removeUser(options?: { [key: string]: any }) {
+ return request<Record<string, any>>('/api/user', {
+ method: 'DELETE',
+ ...(options || {}),
+ });
+}
src\services\ant-design-pro\typings.d.ts
type UserItem = {
userid?: number; // 用户ID,自增主键
username?: string; // 用户名
password?: string; // 密码
name: string; // 姓名
avatar?: string; // 头像
email?: string; // 邮箱
signature?: string; // 签名
title?: string; // 标题
group?: string; // 组
notifyCount?: number; // 通知计数
unreadCount?: number; // 未读计数
country?: string; // 国家
access?: string; // 访问权限
province?: string; // 省份
city?: string; // 城市
address?: string; // 地址
phone?: string; // 电话
createdAt?: Date; // 创建日期
updatedAt?: Date; // 更新日期
};
type UserList = {
data?: UserItem[];
/** 列表的内容总数 */
total?: number;
success?: boolean;
};
src\pages\Users\index.tsx
import { addUser, removeUser, user, updateUser } from '@/services/ant-design-pro/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import {
FooterToolbar,
ModalForm,
PageContainer,
ProDescriptions,
ProFormText,
ProTable,ProFormSelect
} from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Button, Drawer, message } from 'antd';
import React, { useRef, useState } from 'react';
import type { FormValueType } from './components/UpdateForm';
import UpdateForm from './components/UpdateForm';
const handleAdd = async (fields: API.UserItem) => {
const hide = message.loading('正在添加');
try {
await addUser({ data: fields });
hide();
message.success('Added successfully');
return true;
} catch (error) {
hide();
message.error('Adding failed, please try again!');
return false;
}
};
const handleUpdate = async (fields: FormValueType) => {
const hide = message.loading('Updating');
try {
await updateUser({ data: fields });
hide();
message.success('Update is successful');
return true;
} catch (error) {
hide();
message.error('Update failed, please try again!');
return false;
}
};
const handleRemove = async (selectedRows: API.UserItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
try {
await removeUser({ data: { userid: selectedRows.map((row) => row.userid) } });
hide();
message.success('Deleted successfully and will refresh soon');
return true;
} catch (error) {
hide();
message.error('Delete failed, please try again');
return false;
}
};
const TableList: React.FC = () => {
const [createModalOpen, handleModalOpen] = useState<boolean>(false);
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [showDetail, setShowDetail] = useState<boolean>(false);
const actionRef = useRef<ActionType>();
const [currentRow, setCurrentRow] = useState<API.UserItem>();
const [selectedRowsState, setSelectedRows] = useState<API.UserItem[]>([]);
const intl = useIntl();
const columns: ProColumns<API.UserItem>[] = [
{
title: (
<FormattedMessage
id="pages.user.updateForm.userUsername.usernameLabel"
defaultMessage="Username"
/>
),
dataIndex: 'username',
sorter: true,
valueType: 'text',
},
{
title: (
<FormattedMessage
id="pages.user.updateForm.userName.nameLabel"
defaultMessage="name"
/>
),
dataIndex: 'name',
tip: 'The user name is the unique key',
render: (dom, entity: API.UserItem) => {
return (
<a
onClick={() => {
setCurrentRow(entity);
setShowDetail(true);
}}
>
{dom}
</a>
);
},
},
{
title: (
<FormattedMessage
id="pages.user.updateForm.userEmail.emailLabel"
defaultMessage="Email"
/>
),
dataIndex: 'email',
sorter: true,
valueType: 'text',
},
{
title: <FormattedMessage
id="pages.user.updateForm.access.accessLabel"
defaultMessage="access"
/>,
dataIndex: 'access',
sorter: true,
filters: true,
valueType: 'select',
valueEnum: {
'admin': {
text: <FormattedMessage
id="pages.user.access.admin"
defaultMessage="admin"
/>
},
'user': {
text: <FormattedMessage
id="pages.user.access.user"
defaultMessage="user"
/>
}
},
},
{
title: <FormattedMessage id="pages.user.titleOption" defaultMessage="Operating" />,
dataIndex: 'option',
valueType: 'option',
render: (_, record) => [
<a
key="update"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
<FormattedMessage id="pages.user.update" defaultMessage="Update" />
</a>
],
},
];
return (
<PageContainer>
<ProTable<API.UserItem, API.PageParams>
headerTitle={intl.formatMessage({
id: 'pages.user.title',
defaultMessage: 'Enquiry form',
})}
actionRef={actionRef}
rowKey="userid"
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
handleModalOpen(true);
}}
>
<PlusOutlined /> <FormattedMessage id="pages.user.new" defaultMessage="New" />
</Button>,
]}
request={user}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => {
setSelectedRows(selectedRows);
},
}}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
<FormattedMessage id="pages.user.chosen" defaultMessage="Chosen" />{' '}
<a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
<FormattedMessage id="pages.user.item" defaultMessage="项" />
</div>
}
>
<Button
onClick={async () => {
await handleRemove(selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}}
>
<FormattedMessage
id="pages.user.batchDeletion"
defaultMessage="Batch deletion"
/>
</Button>
</FooterToolbar>
)}
<ModalForm
title={intl.formatMessage({
id: 'pages.user.createForm.newUser',
defaultMessage: 'New user',
})}
width="400px"
open={createModalOpen}
onOpenChange={handleModalOpen}
onFinish={async (value) => {
const success = await handleAdd(value as API.UserItem);
if (success) {
handleModalOpen(false);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
>
<ProFormText
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userUsername.usernameRules"
defaultMessage="username is required"
/>
),
},
]}
width="md"
name="username"
label={
intl.formatMessage({
id: 'pages.user.updateForm.userUsername.usernameLabel',
defaultMessage: 'Enquiry form',
})
}
/>
<ProFormText
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userPassword.passwordRules"
defaultMessage="password is required"
/>
),
},
]}
width="md"
name="password"
label={
intl.formatMessage({
id: 'pages.user.updateForm.userPassword.passwordLabel',
defaultMessage: 'Enquiry form',
})
}
/>
<ProFormText
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userName.nameRules"
defaultMessage="name is required"
/>
),
},
]}
width="md"
name="name"
label={
intl.formatMessage({
id: 'pages.user.updateForm.userName.nameLabel',
defaultMessage: 'Enquiry form',
})
}
/>
<ProFormText
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userEmail.emailRules"
defaultMessage="email is required"
/>
),
},
]}
width="md"
name="email"
label={
intl.formatMessage({
id: 'pages.user.updateForm.userEmail.emailLabel',
defaultMessage: 'Enquiry form',
})
}
/>
<ProFormSelect
name="access"
label={intl.formatMessage({
id: 'pages.user.updateForm.access.accessLabel',
defaultMessage: 'access',
})}
width="md"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.access.accessRules"
defaultMessage="please choose access"
/>
),
},
]}
options={[
{
value: 'admin',
label: <FormattedMessage
id="pages.user.access.admin"
defaultMessage="admin"
/>
},
{
value: 'user',
label: <FormattedMessage
id="pages.user.access.user"
defaultMessage="user"
/>
}
]}
/>
</ModalForm>
<UpdateForm
onSubmit={async (value) => {
const success = await handleUpdate({ userid: currentRow.userid, ...value });
if (success) {
handleUpdateModalOpen(false);
setCurrentRow(undefined);
if (actionRef.current) {
actionRef.current.reload();
}
}
}}
onCancel={() => {
handleUpdateModalOpen(false);
if (!showDetail) {
setCurrentRow(undefined);
}
}}
updateModalOpen={updateModalOpen}
values={currentRow || {}}
/>
<Drawer
width={600}
open={showDetail}
onClose={() => {
setCurrentRow(undefined);
setShowDetail(false);
}}
closable={false}
>
{currentRow?.name && (
<ProDescriptions<API.UserItem>
column={2}
title={currentRow?.name}
request={async () => ({
data: currentRow || {},
})}
params={{
id: currentRow?.name,
}}
columns={columns as ProDescriptionsItemProps<API.UserItem>[]}
/>
)}
</Drawer>
</PageContainer>
);
};
export default TableList;
src\pages\Users\components\UpdateForm.tsx
import { ProFormText, ProFormSelect } from '@ant-design/pro-components';
import { FormattedMessage, useIntl } from '@umijs/max';
import { Modal, Form } from 'antd';
import React, { useEffect } from 'react';
export type FormValueType = {} & Partial<API.UserItem>;
export type UpdateFormProps = {
onCancel: (flag?: boolean, formVals?: FormValueType) => void,
onSubmit: (values: FormValueType) => Promise<void>,
updateModalOpen: boolean,
values: Partial<API.UserItem>,
};
const UpdateForm: React.FC<UpdateFormProps> = (props) => {
const intl = useIntl();
const [form] = Form.useForm();
useEffect(() => {
form.setFieldsValue({
username: props.values.username,
name: props.values.name,
access: props.values.access,
});
}, [form, props.values]);
return (
<Modal
width={640}
bodyStyle={{ padding: '32px 40px 48px' }}
destroyOnClose={true}
title={intl.formatMessage({
id: 'pages.user.updateForm.updateUser',
defaultMessage: '修改用户',
})}
open={props.updateModalOpen}
onOk={() => {
form
.validateFields()
.then((values) => {
form.resetFields();
props.onSubmit(values);
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
onCancel={() => {
props.onCancel();
}}
>
<Form
form={form}
initialValues={{
username: props.values.username,
name: props.values.name,
}}
>
<ProFormText
name="username"
label={intl.formatMessage({
id: 'pages.user.updateForm.userUsername.usernameLabel',
defaultMessage: '用户名',
})}
width="md"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userUsername.usernameRules"
defaultMessage="请输入用户名!"
/>
),
},
]}
/>
<ProFormText
name="name"
label={intl.formatMessage({
id: 'pages.user.updateForm.userName.nameLabel',
defaultMessage: '姓名',
})}
width="md"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.userName.nameRules"
defaultMessage="请输入姓名!"
/>
),
},
]}
/>
<ProFormSelect
name="access"
label={intl.formatMessage({
id: 'pages.user.updateForm.access.accessLabel',
defaultMessage: 'access',
})}
width="md"
rules={[
{
required: true,
message: (
<FormattedMessage
id="pages.user.updateForm.access.accessRules"
defaultMessage="please choose access"
/>
),
},
]}
options={[
{
value: 'admin',
label: <FormattedMessage id="pages.user.access.admin" defaultMessage="admin" />,
},
{
value: 'user',
label: <FormattedMessage id="pages.user.access.user" defaultMessage="user" />,
},
]}
/>
</Form>
</Modal>
);
};
export default UpdateForm;