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

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

一、前言

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

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

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

实现效果如下:

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

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

1. 实现效果展示

2. 开发讲解

1)后端接口调整

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

2)前端登录代码调整

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

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

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

index.tsx代码为:

import {message, Dropdown, Menu, Avatar } from 'antd';
import { history } from 'umi';
import React from 'react';


const menu = (
  <Menu>
    <Menu.Item key="1" onClick={() => message.warning('待实现!')}>个人中心</Menu.Item>
    <Menu.Item key="2" onClick={() => { localStorage.setItem('token', ''); history.push('/login'); }}>退出</Menu.Item>
  </Menu >
);

const GlobalHeaderRight: React.FC = () => {
  const userInfo = localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo') as any) : {}
  return (
    <Dropdown
      placement="bottomRight"
      overlay={menu}
    >
      <Avatar style={{ backgroundColor: '#7265e6' }} size={36}>{userInfo.name}</Avatar>
    </Dropdown>
  );
};

export default GlobalHeaderRight;

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

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

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

import RightContent from '@/components/RightContent';

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

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

三、用户增删改功能实现

1. 删除功能

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

1)首先头部新增引入userRef

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

2)定义格式:

interface ActionType {
  reload: () => void;
  fetchMore: () => void;
  reset: () => void;
}

3)在User组件中声明:

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

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

actionRef={ref}

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

{
      title: '操作',
      key: 'option',
      dataIndex: 'option',
      width: 120,
      valueType: 'option',
      render: (v: any, record: any) => [
        <a key={record.id}>修改</a>,
        <a key={record.id} onClick={() => {
          services.UserAll(record.id, reqDelete).then(res => {
            ref.current.reload(); //刷新代码
          })
        }}> 删除</a >
      ],
    }

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

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

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

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

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

request={(...tableParams) => {
        var filters: object = tableParams[2];
        for (let key in filters) {
          //删除查询条件为空的
          if (!filters[key]) {
            delete filters[key];
          }
        }
        setParams({ ...params, ...filters });
        return UserAll({ ...params, ...filters }, reqList).then((res) => {
          setDataLength(res.data.length);
          return res;
        });
      }}

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

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

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

2. 修改/创建功能

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

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

1)实现思路

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

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

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

2)具体代码

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

import React from 'react';
import ProForm, {
    ModalForm,
    ProFormText,
    ProFormRadio,
} from '@ant-design/pro-form';
const Form: React.FC<any> = ({ visible, cancel, formData, onFinish }) => {
    return (
        <ModalForm
            title={formData.formType === 'create' ? "创建用户" : "修改用户"}
            initialValues={{
                username: formData.username || null,
                first_name: formData.first_name || null,
                last_name: formData.last_name || null,
                email: formData.email || null,
                is_active: formData.is_active || false,
                is_superuser: formData.is_superuser || false
            }}
            visible={visible}
            autoFocusFirstInput
            modalProps={{
                destroyOnClose: true,
                onCancel: () => cancel()
            }}
            onFinish={async (values) => {
                onFinish(values, formData.formType).then(() => { //成功保存则返回true
                    return true;
                }, () => { //保存失败则返回false
                    return false;
                })
            }}
        >
            <ProFormText
                width="md"
                name="username"
                rules={[{ required: true, message: "用户名必填" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
                label="用户名"
                placeholder="请输入用户名" />
            <ProForm.Group>
                <ProFormText
                    width="md"
                    name="first_name"
                    rules={[{ required: true, message: "必填!" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
                    label="用户名字"
                    placeholder="请输入名称"
                />
                <ProFormText
                    width="md"
                    name="last_name"
                    rules={[{ required: true, message: "必填!" }, { type: 'string' }, { max: 18, message: '最多18个字' }]}
                    label="用户姓氏"
                    placeholder="请输入名称"
                />
            </ProForm.Group>
            <ProFormText width="md" name="email" label="邮箱号" placeholder="请输入邮箱号" />
            <ProFormRadio.Group
                name="is_active"
                label="用户状态"
                options={[
                    {
                        label: '启用',
                        value: true,
                    },
                    {
                        label: '禁用',
                        value: false,
                    }
                ]}
            />
            <ProFormRadio.Group
                name="is_superuser"
                label="是否为管理员"
                options={[
                    {
                        label: '是',
                        value: true,
                    },
                    {
                        label: '否',
                        value: false,
                    }
                ]}
            />
        </ModalForm>
    );
};
export default React.memo<any>(Form);

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

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

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

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

//点击新建/修改,执行的代码
  const showModal = (type: string, values = {}) => {
    values['formType'] = type
    setFormData(values)
    setFormVisible(true)
  };

  // 弹窗点确认按钮执行的方法
  const onFormFinish = async (values: any, formType: string) => {
    var reqType: string
    if (formType === 'create') {
      reqType = reqCreate
    }
    else {
      reqType = reqUpdate
      values.id = formData.id
    }
    return await UserAll(values, reqType).then((res) => {
      message.success('操作成功!')
      setFormVisible(false)
      formType === 'create' ? ref.current.reloadAndRest() : ref.current.reload();
    });
  };

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

<Form
        formData={formData}
        onFinish={(values: object, formType: string) => onFormFinish(values, formType)}
        cancel={() => setFormVisible(false)}
        visible={formVisible}
      />

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

{
      title: '操作',
      key: 'option',
      dataIndex: 'option',
      width: 120,
      valueType: 'option',
      render: (v: any, record: any) => [
        <a key={record.id} onClick={() => showModal('update', record)}>
          修改
        </a>,
        <a
          key={record.id}
          onClick={() => {
            UserAll(record.id, reqDelete).then((res) => {
              if (params.page > 1 && dataLength <= 1) {
                //在非首页的最后一条数据被删除时,改变页码为前一页进行请求
                setParams({ ...params, ...{ page: params.page - 1 } });
              }
              ref.current.reload();
            });
          }}
        >
          删除
        </a>,
      ],
    },

四、总结

这一章用到了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)

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

相关文章