5.1 界面与流程改造

5.1.1 在列表工具栏放置操作按钮

  • src/pages/MemberList/index.tsx中的ProTable增加一个属性

    1. toolBarRender={() => [
    2. <Button
    3. type="primary"
    4. key="primary"
    5. onClick={() => {
    6. setFormState({ showModal: true, operation: 'create',record: {
    7. //所有需要设置默认值的都可以放在这里
    8. nationality: 0,
    9. education: 6,
    10. degree: 1,
    11. party: 0,
    12. email: '',
    13. }})
    14. }}
    15. >
    16. <PlusOutlined /> 新建
    17. </Button>,
    18. ]}

    这里我们给多数非必填内容都设置了初始默认值(没有给email设置默认值是为了演示下一节的重置效果),这样做有两个好处:一是设置为最常用的值有助于减少用户操作次数,二是默认的情况下,ProForm的属性omiNil的默认值为true,这时所有的值为undefined的变量都不会传递给onFinshed函数,这就增加了对后端逻辑完整性的考验(尽管后端程序无论如何都需要完整的判断逻辑)。

  • 当然,还需要引入使用的图标

    1. import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons';

    完成以后,列表的上方可以看到一个按钮
    image.png
    点击它,应该看到下面的对话框
    image.png

    5.1.2 修正数据重置功能

    在“增加会员”的对话框中,填写一部分内容之后点击“重置”按钮,只会把写在setFormState参数中的字段进行重置,在本例中就是当我们填写了邮件地址之后,其内容不会被重置操作情况(想想看为什么?),为了真正的实现重置(把填写的内容都恢复为初始状态),需要修正MemberDataForm.tsx中的重置功能。

MemberDataForm.tsx中引用FormInstance、定义函数resetFormDate并且修改useEffect的定义

  1. import { message, Form, Button, FormInstance } from "antd";
  1. const resetFormDate = (theForm: FormInstance | undefined) => {
  2. if(operation == 'edit')
  3. theForm?.setFieldsValue(record);
  4. else
  5. theForm?.resetFields();
  6. }
  7. useEffect(() => {
  8. if (form && visible)
  9. resetFormDate(form)
  10. }, [visible]);

然后给ModalForm增加初始化属性

  1. <ModalForm
  2. ...
  3. + initialValues={record}

最后修改重置按钮的响应函数

  1. onClick={() => {
  2. const { form } = props
  3. - form?.setFieldsValue(record)
  4. + resetFormDate(form)
  5. setDataChanged(false)
  6. }}

5.2 完成网络请求和数据模拟

5.2.1 增加网络请求函数

api/member.ts中增加新的函数

  1. export async function addMember(data: TYPE.Member) {
  2. return request('/api/member/add', {
  3. method: 'POST',
  4. data: {
  5. ...data
  6. }
  7. });
  8. }

MemberDataForm.tsx中引入该函数

  1. import { addMember, updateMember } from "@/services/api/member";

修改MemberDataForm.tsxonFinishtry .. catch部分代码

  1. try {
  2. if(operation === 'edit')
  3. await updateMember(result);
  4. else
  5. await addMember(result);
  6. message.success('保存成功,即将刷新',1);
  7. handleResponse(true);
  8. } catch (error) {
  9. handleResponse(false);
  10. }

5.2.2 模拟添加数据

在mock中增加如下的函数

  1. function create(req: Request, res: Response, u: string) {
  2. const record = {...req.body}
  3. let max = 0
  4. for( let member of memberListDataSource){
  5. const { id=0 } = member;
  6. max = (max < id)? id : max
  7. }
  8. record.id = max + 99
  9. memberListDataSource.reverse()
  10. memberListDataSource.push(record)
  11. memberListDataSource.reverse()
  12. const result = {
  13. success: true,
  14. }
  15. return res.json(result);
  16. }
  17. export default {
  18. 'POST /api/member/add': create,

5.3 实现对话框数据间的依赖联动

  • 首先在utils/utils.ts中增加一个从身份证号中提取出生日期的函数

    1. import moment from 'moment';
    1. export function getBirthdayFromId(idNumber: string) {
    2. let dateString = ''
    3. if(idNumber != undefined) {
    4. dateString = (idNumber as string).substr(6,8)
    5. switch(dateString.length) {
    6. case 8:
    7. break;
    8. case 7:
    9. dateString += '1'
    10. break;
    11. case 6:
    12. dateString += '01'
    13. break;
    14. case 5:
    15. dateString += '101'
    16. break;
    17. case 4:
    18. dateString += '0101'
    19. break;
    20. case 0:
    21. dateString = ''
    22. break;
    23. default:
    24. dateString = dateString.padEnd(4,'0') + '0101'
    25. }
    26. }
    27. return (dateString!='')? moment(dateString) : null;
    28. }
  • 然后在对话框中引用它

    1. import { getOptionsFormValueEnum, getBirthdayFromId } from '@/utils/utils'
  • 官方文档中认为在实现组件联动的时候,“ProFormDependency绝对是最好的选择”,但实际上,这种方式比较适合用于控制某些组件是否出现,以及控制纯文字(label)的内容。如果需要控制组件字段的实际值,不要使用**ProFormDependency**,应该在onValuesChange中实现数据间的依赖逻辑。

    1. onValuesChange={(changedValus) => {
    2. if(!dataChanged)
    3. setDataChanged(true)
    4. if(operation == 'create' && changedValus['identityNumber'] != undefined) {
    5. const identityNumber = changedValus['identityNumber']
    6. form.setFieldsValue({
    7. birthday: getBirthdayFromId(identityNumber),
    8. gender: ((identityNumber as string)?.length > 16)? (2 - parseInt((identityNumber as string)[16]) % 2) : 0,
    9. })
    10. }
    11. }}

    关于的ProFormDependency具体用法可见官方代码示例

  • 给被动改变数值的组件增加disabled属性,禁止用户直接操作其内容 ```diff

    1. <ProFormDatePicker name="birthday" label="出生日期" width="sm"
  • disabled={operation === ‘create’} … … <ProFormSelect name=”gender” label=”性别” placeholder=”选择性别” width={64} options={getOptionsFormValueEnum(genderEnum)}
  • disabled={operation === ‘create’} ``` 这里有两个重要提醒:
  1. 使用disabled属性而不是readonly属性,因为前者会保留组件完整的外观合大小,后者则会把组件变成纯文本的形式。
  2. 我们这里设计的逻辑是在创建数据时执行生日、性别与身份证号的强逻辑联系,但在修改数据时完全放开,给用户随意修改的权力,这里除了展示不同的软件功能以外,还因为项目实践中确实存在因身份证编码出错而不匹配的情况,此时确实需要有修改的手段。

下图就是实现了联动功能的对话框界面
image.png

5.4 小结

本章涉及的的内容也非常多,所以一定要按照本章的内容逐步完成每个步骤,并且理解每条语句的作用,一定不要简单的复制粘贴

版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。