自动化测试平台(七):头像展示、下拉菜单及用户管理模块增删改功能实现

x33g5p2x  于2021-12-20 转载在 其他  
字(6.7k)|赞(0)|评价(0)|浏览(616)

一、前言

上一章节我们完成了前端菜单和用户列表的功能,以及后端通过中间件来封装响应结果、通过封装DRF分页组件来实现分页的功能。

这一章节将实现页面头部用户头像展示和下拉退出系统的功能、用户管理模块的增删改功能。

由于博主本身也才开始使用ts,很多类型都是用any来定义的,后续会逐渐完善。

实现效果如下:

在线演示地址:http://121.43.43.59/ (帐号:admin 密码:123456)

二、实现页面头部用户头像展示和下拉菜单

1. 实现效果展示

2. 开发讲解

1)后端接口调整

为了实现上述图片中的效果(头像中为用户的姓名),我们需要在登录接口成功时返回用户姓名的数据,所以需要修改登录接口来增加返回字段,如下图红框所示(增加了用户姓名、用户名的返回):

2)前端登录代码调整

接口调整完成后,我们需要修改前端的登录完成后的代码,将用户信息保存起来备用,在登录方法中加入红框中的代码即可:

然后为了代码结构分类清晰,因为头像这个功能实际上后面每个页面都会用到,它不属于任何一个页面,所以这里我在src文件夹下新建了文件夹components,以后类似这样的公共组件我们都可以存放在这里。

然后在components下面新建RightContent文件夹,这就是我们的右侧头部(头像)组件了,再在它下面新建index.tsx文件用于开发我们的代码,目录结果如下:

index.tsx代码为:

  1. import {message, Dropdown, Menu, Avatar } from 'antd';
  2. import { history } from 'umi';
  3. import React from 'react';
  4. const menu = (
  5. <Menu>
  6. <Menu.Item key="1" onClick={() => message.warning('待实现!')}>个人中心</Menu.Item>
  7. <Menu.Item key="2" onClick={() => { localStorage.setItem('token', ''); history.push('/login'); }}>退出</Menu.Item>
  8. </Menu >
  9. );
  10. const GlobalHeaderRight: React.FC = () => {
  11. const userInfo = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo') as any) : {}
  12. return (
  13. <Dropdown
  14. placement="bottomRight"
  15. overlay={menu}
  16. >
  17. <Avatar style={{ backgroundColor: '#7265e6' }} size={36}>{userInfo.name}</Avatar>
  18. </Dropdown>
  19. );
  20. };
  21. export default GlobalHeaderRight;

最后在layouts\index.tsx中的ProLayout指定我们的右侧头部组件即可(ProLayout中增加下面的代码):

  1. rightContentRender={() => <RightContent />}

记得在文件头部import该组件:

  1. import RightContent from '@/components/RightContent';

有疑问可查看ProLayout文档学习,也可以在演示地址联系博主进行解答。

这样我们就完成了头像和下拉菜单的功能:

三、用户增删改功能实现

1. 删除功能

删除功能除了删除对应的数据外,还需要重新刷新列表,刚好ProTalbe提供了对应的方法,所以在我们的用户模块代码中(pages/user/index.tsx文件)里的columns操作里修改对应的代码即可

1)首先头部新增引入userRef

  1. import React, { useState, useRef } from 'react';

2)定义格式:

  1. interface ActionType {
  2. reload: () => void;
  3. fetchMore: () => void;
  4. reset: () => void;
  5. }

3)在User组件中声明:

  1. const ref = useRef<ActionType | any>();

4)在ProTable组件中增加如下配置:

  1. actionRef={ref}

5)修改操作中的删除请求代码,完整的操作代码如下:

  1. {
  2. title: '操作',
  3. key: 'option',
  4. dataIndex: 'option',
  5. width: 120,
  6. valueType: 'option',
  7. render: (v: any, record: any) => [
  8. <a key={record.id}>修改</a>,
  9. <a key={record.id} onClick={() => {
  10. services.UserAll(record.id, reqDelete).then(res => {
  11. ref.current.reload(); //刷新代码
  12. })
  13. }}> 删除</a >
  14. ],
  15. }

虽然这样实现了删除功能并且刷新了列表,但在删除非第一页数据的最后一条时(比如存在两页数据,删除第二页最后一条)会保持第二页的参数进行请求,会导致列表数据为空:

所以在非首页的最后一条数据被删除时,我们应该改变页码为前一页进行请求。

1)声明获取数据的长度字段:

  1. const [dataLength, setDataLength] = useState<number>(0);

2)修改请求ProTable中的request方法,为了获取返回数据的长度,完整的request代码如下:

  1. request={(...tableParams) => {
  2. var filters: object = tableParams[2];
  3. for (let key in filters) {
  4. //删除查询条件为空的
  5. if (!filters[key]) {
  6. delete filters[key];
  7. }
  8. }
  9. setParams({ ...params, ...filters });
  10. return UserAll({ ...params, ...filters }, reqList).then((res) => {
  11. setDataLength(res.data.length);
  12. return res;
  13. });
  14. }}

3)修改操作中删除请求代码如下:

  1. <a key={record.id} onClick={() => {
  2. UserAll(record.id, reqDelete).then(
  3. res => {
  4. if (params.page > 1 && dataLength <= 1) {//在非首页的最后一条数据被删除时,改变页码为前一页进行请求
  5. setParams({ ...params, ...{ page: params.page - 1 } })
  6. }
  7. ref.current.reload();
  8. })
  9. }}> 删除</a >

这样我们的删除功能就大功告成了!

2. 修改/创建功能

对于修改和创建功能我们通过弹窗的来做,因为需要有地方填写用户的信息。

这里我们使用的弹窗是ProForm这个组件,官方文档:https://procomponents.ant.design/components/form

1)实现思路

  1. 首先要在user文件夹下新建form.tsx文件,并编写弹窗页面的代码,代码为具体的弹窗样式和逻辑交互,使用proForm组件能省不少事。

  2. 然后在入口index.tsx文件增加控制弹窗显示、弹窗数据管理的代码;

  3. 最后就是控制弹窗请求接口进行数据保存的方法封装了。

2)具体代码

1.user/form.tsx弹窗完整代码:

  1. import React from 'react';
  2. import ProForm, {
  3. ModalForm,
  4. ProFormText,
  5. ProFormRadio,
  6. } from '@ant-design/pro-form';
  7. const Form: React.FC<any> = ({ visible, cancel, formData, onFinish }) => {
  8. return (
  9. <ModalForm
  10. title={formData.formType === 'create' ? "创建用户" : "修改用户"}
  11. initialValues={{
  12. username: formData.username || null,
  13. first_name: formData.first_name || null,
  14. last_name: formData.last_name || null,
  15. email: formData.email || null,
  16. is_active: formData.is_active || false,
  17. is_superuser: formData.is_superuser || false
  18. }}
  19. visible={visible}
  20. autoFocusFirstInput
  21. modalProps={{
  22. destroyOnClose: true,
  23. onCancel: () => cancel()
  24. }}
  25. onFinish={async (values) => {
  26. onFinish(values, formData.formType).then(() => { //成功保存则返回true
  27. return true;
  28. }, () => { //保存失败则返回false
  29. return false;
  30. })
  31. }}
  32. >
  33. <ProFormText
  34. width="md"
  35. name="username"
  36. rules={[{ required: true, message: "用户名必填" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
  37. label="用户名"
  38. placeholder="请输入用户名" />
  39. <ProForm.Group>
  40. <ProFormText
  41. width="md"
  42. name="first_name"
  43. rules={[{ required: true, message: "必填!" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
  44. label="用户名字"
  45. placeholder="请输入名称"
  46. />
  47. <ProFormText
  48. width="md"
  49. name="last_name"
  50. rules={[{ required: true, message: "必填!" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
  51. label="用户姓氏"
  52. placeholder="请输入名称"
  53. />
  54. </ProForm.Group>
  55. <ProFormText width="md" name="email" label="邮箱号" placeholder="请输入邮箱号" />
  56. <ProFormRadio.Group
  57. name="is_active"
  58. label="用户状态"
  59. options={[
  60. {
  61. label: '启用',
  62. value: true,
  63. },
  64. {
  65. label: '禁用',
  66. value: false,
  67. }
  68. ]}
  69. />
  70. <ProFormRadio.Group
  71. name="is_superuser"
  72. label="是否为管理员"
  73. options={[
  74. {
  75. label: '是',
  76. value: true,
  77. },
  78. {
  79. label: '否',
  80. value: false,
  81. }
  82. ]}
  83. />
  84. </ModalForm>
  85. );
  86. };
  87. export default React.memo<any>(Form);

2.user/index.tsx 相关代码:

1)User组件类定义控制弹窗的属性:

  1. const [formData, setFormData] = useState<any>({});//传递给弹窗显示的数据
  2. const [formVisible, setFormVisible] = useState<boolean>(false); //控制弹窗显示还是隐藏

2)点击列表的新建/修改按钮以及弹窗确认按钮后执行的方法代码:

  1. //点击新建/修改,执行的代码
  2. const showModal = (type: string, values = {}) => {
  3. values['formType'] = type
  4. setFormData(values)
  5. setFormVisible(true)
  6. };
  7. // 弹窗点确认按钮执行的方法
  8. const onFormFinish = async (values: any, formType: string) => {
  9. var reqType: string
  10. if (formType === 'create') {
  11. reqType = reqCreate
  12. }
  13. else {
  14. reqType = reqUpdate
  15. values.id = formData.id
  16. }
  17. return await UserAll(values, reqType).then((res) => {
  18. message.success('操作成功!')
  19. setFormVisible(false)
  20. formType === 'create' ? ref.current.reloadAndRest() : ref.current.reload();
  21. });
  22. };

3)return中弹窗Form组件的代码:

  1. <Form
  2. formData={formData}
  3. onFinish={(values: object, formType: string) => onFormFinish(values, formType)}
  4. cancel={() => setFormVisible(false)}
  5. visible={formVisible}
  6. />

4)列表columns中,操作栏的代码:

  1. {
  2. title: '操作',
  3. key: 'option',
  4. dataIndex: 'option',
  5. width: 120,
  6. valueType: 'option',
  7. render: (v: any, record: any) => [
  8. <a key={record.id} onClick={() => showModal('update', record)}>
  9. 修改
  10. </a>,
  11. <a
  12. key={record.id}
  13. onClick={() => {
  14. UserAll(record.id, reqDelete).then((res) => {
  15. if (params.page > 1 && dataLength <= 1) {
  16. //在非首页的最后一条数据被删除时,改变页码为前一页进行请求
  17. setParams({ ...params, ...{ page: params.page - 1 } });
  18. }
  19. ref.current.reload();
  20. });
  21. }}
  22. >
  23. 删除
  24. </a>,
  25. ],
  26. },

四、总结

这一章用到了ProForm这个组件,可以结合下面两个官方文档进行学习:

  1. profrom文档:https://procomponents.ant.design/components/form
  2. antd-form表单:https://ant.design/components/form-cn/#header

当然这个过程中会存在不少疑问,可以点击下方在线演示地址进行反馈。

在线演示地址:http://121.43.43.59/ (帐号:admin 密码:123456)

欢迎在文章头部右上角订阅本专栏,及时获取最新教程分享!

相关文章