自动化测试平台(八):列表组件公共化封装和用例项目管理功能开发

x33g5p2x  于2021-12-23 转载在 其他  
字(6.1k)|赞(0)|评价(0)|浏览(505)

一、前言

上一章我们完成了整个用户管理模块的功能,能够正确的增、删、改、查用户。但其中有很多判断实际上是其他类似的模块也会有的,例如:

  1. 创建用户后回到首页刷新列表;
  2. 删除次页最后一条数据,回到前一页刷新列表;
  3. 查询条件的格式化;

难道我们每写一个类似的模块,都要去写一遍这些重复的逻辑代码吗?

显然是没必要的,所以我们需要将其抽离成公共列表组件提供给其他模块使用,避免大量的做重复的事情,并让代码更容易维护。本章还将完成用例项目管理功能,它主要用于管理不同类型(API、UI),不同项目、项目状态等。

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

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

实现效果如下:

二、实现公共列表组件

1. 公共Table组件

src/components下创建ComTable文件夹,并在其下创建index.tsx文件,它的代码如下:

  1. import React, { useImperativeHandle, useRef, useState } from 'react';
  2. import ProTable from '@ant-design/pro-table';
  3. import { Button } from 'antd';
  4. import { PlusOutlined } from '@ant-design/icons';
  5. import { reqList, reqDelete } from '@/globalEnum';
  6. interface ActionType {
  7. reload: () => void;
  8. fetchMore: () => void;
  9. reset: () => void;
  10. }
  11. const Table: React.FC<any> = ({
  12. actionRef,
  13. func,
  14. headerTitle,
  15. showModal,
  16. columns,
  17. }) => {
  18. const ref = useRef<ActionType | any>();
  19. const [params, setParams] = useState<any>({ page: 1, page_size: 10 }); //默认请求第一页,10条数据
  20. const [dataLength, setDataLength] = useState<number>(0); //列表返回数据的长度(行数)
  21. const handlePagination: any = {
  22. pageSize: params.page_size,
  23. showSizeChanger: false,
  24. page: params.page,
  25. onChange: (current: number, size: number) => {
  26. setParams({ page: current, page_size: size });
  27. },
  28. };
  29. useImperativeHandle(actionRef, () => ({
  30. tableRef: ref,
  31. comDeleteData: (func: any, record_id: any) => {
  32. func(record_id, reqDelete).then((res: any) => {
  33. if (params.page > 1 && dataLength <= 1) {
  34. //在非首页的最后一条数据被删除时,改变页码为前一页进行请求
  35. setParams({ ...params, ...{ page: params.page - 1 } });
  36. }
  37. ref.current.reload();
  38. });
  39. }
  40. }));
  41. return (
  42. <ProTable
  43. actionRef={ref}
  44. columns={columns}
  45. scroll={{ y: 'calc(100vh - 300px)' }}
  46. request={(...tableParams) => {
  47. var filters: object = tableParams[2];
  48. for (let key in filters) {
  49. //删除查询条件为空的
  50. if (!filters[key]) {
  51. delete filters[key];
  52. }
  53. }
  54. setParams({ ...params, ...filters });
  55. return func({ ...params, ...filters }, reqList).then((res: any) => {
  56. setDataLength(res.data.length);
  57. return res;
  58. });
  59. }}
  60. rowKey="id"
  61. pagination={handlePagination}
  62. size="middle"
  63. headerTitle={< h3 style={{ fontWeight: 'bold' }}> {headerTitle}</h3 >}
  64. search={false}
  65. toolBarRender={() => [
  66. <Button
  67. type="primary"
  68. icon={<PlusOutlined />}
  69. onClick={() => showModal('create')}
  70. >
  71. 新建
  72. </Button>,
  73. ]}
  74. dateFormatter="string"
  75. />
  76. );
  77. };
  78. export default React.memo<any>(Table);

这里只是简单封装,可以根据自己的需求进行diy

2. 用户列表接入公共Table组件

公共Table完成后,可以将我们之前用户模块代码进行接入,完整代码如下:

  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { ProColumns } from '@ant-design/pro-table';
  3. import { message } from 'antd';
  4. import { reqCreate, reqUpdate } from '@/globalEnum';
  5. import { UserAll } from '@/services/users';
  6. import Form from './form';
  7. import ComTable from '@/components/ComTable';
  8. const User: React.FC = () => {
  9. const ref = useRef<any>();
  10. const [tableRef, setTableRef] = useState<any>({});
  11. const [formData, setFormData] = useState<any>({}); //传递给弹窗显示的数据
  12. const [formVisible, setFormVisible] = useState<boolean>(false); //控制弹窗显示还是隐藏
  13. useEffect(() => {
  14. setTableRef(ref.current.tableRef)
  15. }, []);
  16. const columns: ProColumns[] = [
  17. {
  18. title: '姓名',
  19. dataIndex: 'name',
  20. key: 'name',
  21. width: 100,
  22. render: (v: any, record: any) => (
  23. <span>{`${record.last_name}${record.first_name}`}</span>
  24. ),
  25. },
  26. {
  27. title: '用户名',
  28. key: 'username',
  29. width: 100,
  30. dataIndex: 'username',
  31. },
  32. {
  33. title: '邮箱地址',
  34. key: 'email',
  35. dataIndex: 'email',
  36. width: 200,
  37. },
  38. {
  39. title: '加入时间',
  40. key: 'date_joined',
  41. dataIndex: 'date_joined',
  42. width: 200,
  43. valueType: 'dateTime',
  44. },
  45. {
  46. title: '状态',
  47. dataIndex: 'is_active',
  48. width: 100,
  49. filters: true,
  50. filterMultiple: false,
  51. valueEnum: {
  52. true: { text: '启用', status: 'Success' },
  53. false: { text: '禁用', status: 'Error' },
  54. },
  55. },
  56. {
  57. title: '操作',
  58. key: 'option',
  59. dataIndex: 'option',
  60. width: 120,
  61. valueType: 'option',
  62. render: (v: any, record: any) => [
  63. <a key={record.id} onClick={() => showModal('update', record)}>
  64. 修改
  65. </a>,
  66. <a
  67. key={record.id}
  68. onClick={() => { ref.current.comDeleteData(UserAll, record.id) }}
  69. >
  70. 删除
  71. </a>,
  72. ],
  73. },
  74. ];
  75. //点击新建/修改,执行的代码
  76. const showModal = (type: string, values = {}) => {
  77. values['formType'] = type;
  78. setFormData(values);
  79. setFormVisible(true);
  80. };
  81. // 弹窗点确认按钮执行的方法
  82. const onFormFinish = async (values: any, formType: string) => {
  83. var reqType: string;
  84. if (formType === 'create') {
  85. reqType = reqCreate;
  86. } else {
  87. reqType = reqUpdate;
  88. values.id = formData.id;
  89. }
  90. return await UserAll(values, reqType).then((res) => {
  91. message.success('操作成功!');
  92. setFormVisible(false);
  93. formType === 'create' ? tableRef.current.reloadAndRest() : tableRef.current.reload();
  94. });
  95. };
  96. return (
  97. <>
  98. <Form
  99. formData={formData}
  100. onFinish={(values: object, formType: string) =>
  101. onFormFinish(values, formType)
  102. }
  103. cancel={() => setFormVisible(false)}
  104. visible={formVisible}
  105. />
  106. <ComTable
  107. actionRef={ref}
  108. columns={columns}
  109. func={UserAll}
  110. showModal={showModal}
  111. headerTitle="用户列表"
  112. />
  113. </>
  114. );
  115. };
  116. export default React.memo(User);

三、实现项目管理功能

1. 后台接口

首先在代码项目下执行创建项目模块的命令:

  1. django-admin startapp project

为了让后面创建的表都有创建时间结束时间这两个字段,需要先在comFunc文件夹中创建一个comModel.py的文件,用于放置公共的数据库模型,这样需要拥有公共模型字段的模型继承公共模型即可。
拥有创建时间结束时间的公共模型代码如下:

  1. from django.db import models
  2. class ComModel(models.Model):
  3. """ 公共模型 """
  4. created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
  5. updated = models.DateTimeField(auto_now=True, verbose_name='修改时间', null=True, blank=True)
  6. class Meta:
  7. abstract = True

然后在model.py中添加项目的数据库模型代码:

  1. from django.db import models
  2. from comFunc.comModel import ComModel
  3. class Project(ComModel):
  4. id = models.AutoField(primary_key=True)
  5. name = models.CharField(max_length=32, verbose_name="项目名称")
  6. remark = models.TextField(blank=True, null=True, verbose_name="备注")
  7. status = models.IntegerField(verbose_name="项目状态")
  8. type = models.IntegerField(verbose_name="项目类型")
  9. class Meta:
  10. verbose_name = '项目表'
  11. db_table = 'project'

再在settings.py配置里的INSTALLED_APPS中加入project进行注册,然后执行模型迁移的命令即可:

  1. python manage.py makemigrations
  2. python manage.py migrate

这样我们就把项目模块的数据库表建立成功了,再按照user模块中的代码、代码结构并结合之前的教程进行代码开发即可。

2. 前端页面

1)在前端项目下,执行创建project模块文件的命令:

  1. npx umi g page project/index --typescript

2)在.umirc.ts文件配置路由中加入project的跳转:

  1. routes: [
  2. ...
  3. {
  4. name: '项目管理',
  5. path: '/project',
  6. icon: 'users',
  7. component: '@/pages/project',
  8. },
  9. ...
  10. ],

这样我们就把项目模块的基础页面建立好了,再按照user模块的代码并结合之前的教程进行代码开发即可,实现后的效果如下:

需要注意的是,在用例类型的筛选中,自定义了proTable的valueType实现,教程文档地址:自定义valueType

四、总结

这一章主要涉及技术的教学只有一个公共列表组件的抽离和使用,其他都是重复的CURD了,所以只给了数据库表模型,没有直接贴代码。

需要源码的可以点击下方的演示地址进行获取:

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

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

相关文章