Mock平台-08开发:项目管理(四)编辑功能和Component抽离

news2024/11/26 10:00:36

【Mock平台】为系列测试开发教程,从0到1编码带你一步步使用Spring Boot 和 Antd React框架完成搭建一个测试工具平台,希望作为一个实战项目对各位的测试开发学习之路有帮助,大奇一个专注测试技术干货原创与分享的家伙。

本篇重点:继续基于项目增加所有用到的Form和Modal两个组件做一些深化,实现项目管理的编辑功能,并将代码进行合并优化,提取第一个自定义的component组件。

1.项目编辑功能

要实现项目编辑对话框,需要拿到对应行的项目详细信息,可以通过在点击行按钮时候将整行信息传入,即之前table配置列信息时候操作列 render: (text, record) => [Link]()中的record数据,但如果你返回的列表不不是所有字段,那就需要拿到行对应ID,通过查询接口拿到详细信息保存到一个定义好的变量中。

我们这里项目信息比较简单,所以采用前者,定义一个详细信息变量、编辑点击事件,并注意需要将之前定义在外部projectColumns挪到Project(...)主体内,让其正确调用内部新定义的方法。

const Project = () => {
  // 迁移列到内部,并添加editAction方法透传record
  const projectColumns = [
      {dataIndex:"id",title:"编号",},
      ...省略...
      {dataIndex:"option",title:"操作",
        render: (text, record) => (
          <Space>
            <a onClick={()=>editAction(record)}>编辑</a>
            <a>删除</a>
          </Space>
        ),
      },
    ]  
    
  // 定义项目编辑操作,并动态赋值到新的详细项目变量
  const [projectInfo, setProjectInfo] = useState({});
  const editAction = (record) => {
    setProjectInfo(record);
  }
}
  

在实现编辑对话框表单之前需要了解如何初始化数据,从 Form 组件官方API可以查看到其initialValues 可以绑定初始信息,值与name直接匹配,也可以在Form.Item 中initialValue独立绑定,好处是可以做些计算、转换再初始化。

参考上篇添加逻辑,拷贝一份Modal+Form修改其文案,并添加一个ID项(禁止编辑状态),再做个整体数据绑定操作,其中先不做保存修改操作,一份去了重的代码参考如下:

  // 控制显示编辑项目对话框表单
  const [projectEditVisible, setProjectEditVisible] = useState(false);

... 省略部分...
    <Modal
        id="p_edit"
        title="项目修改"
        visible={projectEditVisible}
        destroyOnClose="true"
        onCancel={()=>setProjectEditVisible(false)}
        onOk={() => {
           // 暂时不实现接口操作,先看交互是否满足
        }}
      >
        <Form 
          initialValues={projectInfo}
        >
          <Form.Item name='id' label='编号'>
            <Input disabled></Input>
          </Form.Item>
          <Form.Item
            name='name'
            label='名称'
            rules={[
              {
                required: true,
                message: '项目名称为必填项!',
              },
            ]}
          >
            <Input placeholder="请输入项目名称"></Input>
          </Form.Item>
          <Form.Item name='owner' label='负责人'>
            <Input placeholder="项目相负责人"></Input>
          </Form.Item>
          <Form.Item name="desc" label="更多信息">
            <TextArea/>
          </Form.Item>
        </Form>
      </Modal>

几个关键的代码增加后,运行看下效果,并进行测试,编辑的对话框表单的弹出和显示,数据初始化都正常,但是反填的数据只是第一次选中行的数据,即时已经设置了 destroyOnClose关闭对话框销毁子元素也无效。
在这里插入图片描述

经查找官方组件API下边有这样的说明:

你不能用控件的 value 或 defaultValue 等属性来设置表单域的值,默认值可以用 Form 里的 initialValues 来设置。注意 initialValues 不能被 setState 动态更新,你需要用 setFieldsValue 来更新。

也就是要想动态的替换是需要使用 setFieldsValue来实现,在此交互场景下initialValues/initialValue 更适合新增时候一些默认数据绑定。改动给出diff代码块如下。

const [formEditProject] = Form.useForm();
- //const [projectInfo, setProjectInfo] = useState({});

const editAction = (record) => {
- // setProjectInfo(record);
+ formEditProject.setFieldsValue(record);
  setProjectEditVisible(true);
}

<Form
+ form={formEditProject}
- // initialValues={projectInfo}
>

具体改动点涉及:

  • 定义useForm Hook 并绑定编辑Form;
  • 点击编辑按钮无需再赋予projectInfo,直接调用表单setFieldsValue动态赋值。

由于在之实现后端项目保存的时候其增加/修改都是一个接口,唯一的增量是编辑带原始ID,所以保存接口代码参考增加的onOK,请求参数里增加个ID字段,其他对应改成编辑modal绑定的变量。

onOk={() => {
  formEditProject
    .validateFields()
    .then(async (values) => {
    const data = {
      id: values.id,
      name: values.name,
      owner: values.owner,
      desc: values.desc,
      type: 'public',
      operator: '大奇'
    }
    const resp = await saveProduct(data);
    if (resp.success) {
      formEditProject.resetFields(); 
      setProjectEditVisible(false);
      reloadProjectList();
    }
  })
    .catch((info) => {
    console.log('修改项目信息失败', info);
  });
}}

五处改动点分别为formEditProjectid: values.idformEditProject.resetFields();setProjectEditVisible(false);console.log('修改项目信息失败', info);

带上这部分代码,重新编译做个测试,看下效果。
在这里插入图片描述

至此项目管理的增加和修改的数据交互操作,通过最基本Modal+Form组件已经实现了。类似这类字段较少的数据保存交互都可以通过这种方式实现了。

2.编写Component

随着应用的发展,会需要在多个页面分享 UI 元素 (或在一个页面使用多次),或是在一个tsx逻辑代码太多,这种情况下就可以把这部分抽成 component 。

我们来编写一个 UpsertProject component,再将增加和修改合并,这样就将大幅优化代码。

按照之前Template里的例子,新建 src/pages/Project/components/UpsertProject.tsx 文件:

import React, { useState } from 'react';

/* 抽离出来的组件,用于优化页面代码 */
const UpsertProject = (props) => {
  return (
    <>
    </>
  )
}

export default UpsertProject;

2.1 编码index.jsx

为了保留历史参考代码,增改组件的提取将全部定义新变量,源代码中将注释掉,如果是本地测试开发的话完全可以直接删掉。这里再想一个问题,在之前实现的新增和修改Modal+Form其实将近90%代码重复,所以我们可以将其合并,不同的部分通过条件判断进行逻辑操作和动态赋值。因此我们在 src/pages/Project/index.jsx 有如下新定义:

  // Components 需要留在上级的变量(重新定义原有的将注释掉)
  const [upsertVisible, setUpsertVisible] = useState(false); //控制抽离的组件显隐
  const [upsertAction, setUpsertAction] = useState('ADD'); // 标记组件的动作默认增加
  const [upsertDetail, setUpsertDetail] = useState({}); // 编辑动作下选择行详细信息

对应的也需要修改组件引用、涉及到增加和修改方法逻辑:

  • 原有p_add和p_edit可以全部删掉,使用新的组件
  • 新的组件里会用一些变量和方法直接透传
  • 修改addAction添加按钮方法逻辑,标记动作为ADD并设置状态显示
  • 修改editAction表操作列,标记动作为EDIT,赋值编辑行详细信息,同样设置状态显示
import UpsertProject from "@/pages/Project/components/UpsertProject";

const addAction = () => {
    // setProjectVisible(true);
    setUpsertAction("ADD");
    setUpsertVisible(true);
  }

const editAction = (record) => {
    // setProjectInfo(record);
    // formEditProject.setFieldsValue(record);
    // setProjectEditVisible(true);
    setUpsertDetail(record);
    setUpsertAction('EDIT');
    setUpsertVisible(true);
  }

return (
    <>
      <Button
       ...省略...
      >
        项目添加
      </Button>
  
      {/*原来的增加和修改Modal都可以删掉了, 引入抽离的组件*/}

      <UpsertProject
        upsertAction={upsertAction}
        upsertVisible={upsertVisible}
        setUpsertVisible={setUpsertVisible}
        upsertDetail={upsertDetail}
        reloadProjectList={reloadProjectList}
      />
     
      <Table
       ...省略...
      />
    </>
)

如果对比原index.jsx文件,代码量的减少直观让可阅读和逻辑性更好。

2.2 组件UpsertProject

接下来就要真正实现 UpsertProject 组件 内部逻辑了,基本上将p_edit的代码拷贝过来,然后额外进行页面渲染初始化逻辑判断,以及动态判断动作赋值和组件的显示隐藏,详细的解释请参考如下完整代码。其中有两个新知识点说明移步到下文useEffect和hidden学习。

import React, { useEffect, useState } from "react";
import { Form, Input, Modal } from "antd";
const { TextArea } = Input;

import { saveProduct } from "@/pages/Project/service";

/* 抽离出来的组件,用于优化页面代码 */
const UpsertProject = (props) => {
  const [form] = Form.useForm();

  // 副作用钩子,给定一个参数,当props内容有变化是执行此Hook
  useEffect(()=>{
    if(props.upsertAction==='EDIT'){
      form.setFieldsValue(props.upsertDetail);
    } else {
      form.resetFields();
    }
  },[props]) //

  return (
      <Modal
        title={props.upsertAction==='ADD'?'增加项目':'修改项目'} // 动态判断标题
        visible={props.upsertVisible}
        destroyOnClose
        onCancel={()=>props.setUpsertVisible(false)}
        onOk={() => {
          form
            .validateFields()
            .then(async (values) => {
              const data = {
                id: props.upsertAction==='ADD'? undefined: values.id, // 根据增加还是修改给定id值
                name: values.name,
                owner: values.owner,
                desc: values.desc,
                type: 'public',
                operator: '大奇'
              }
              const resp = await saveProduct(data);
              if (resp.success) {
                form.resetFields(); // 表单清除历史
                props.setUpsertVisible(false);
                props.reloadProjectList();
              }
            })
            .catch((info) => {
              console.log('保存项目信息失败', info);
            });
        }}
      >
        <Form form={form}>
          {/*通过hidden属性决定是否隐藏此项目,在新增操作时候隐藏*/}
          <Form.Item hidden={props.upsertAction==='ADD'} name='id' label='编号'>
            <Input disabled></Input>
          </Form.Item>
          <Form.Item
            name='name'
            label='名称'
            rules={[
              {
                required: true,
                message: '项目名称为必填项!',
              },
            ]}
          >
            <Input placeholder="请输入项目名称"></Input>
          </Form.Item>
          <Form.Item name='owner' label='负责人'>
            <Input placeholder="项目相负责人"></Input>
          </Form.Item>
          <Form.Item name="desc" label="更多信息">
            <TextArea/>
          </Form.Item>
        </Form>
      </Modal>
  )
}

export default UpsertProject;

2.3 集成测试

完成所有代码编写后,做个集成集成测试,强调一下笔者在边开发边总结文档的过程是一点点测试过来,非一次编码后才进行测试的,这里受于文章的排版才这样做的。

测试1:对话框显隐和赋值

  • Case1: 验证对话框弹出和关闭正常
  • Case2: 项目添加表单为空
  • Case3: 项目编辑值正确初始化
    在这里插入图片描述

测试2:添加和修改保存操作

  • Case1: 验证项目新增保存成功
  • Case2: 验证项目修改保存成功
  • Case3: 保存成功后关闭对话框不刷新项目Table
    在这里插入图片描述

3. useEffect和hidden

对于上自定义组件代码涉及到几个新出现的知识点,分别简单讲解下。

3.1钩子useEffect

为了实现项目增改组件数据的初始化,引入useEffect钩子, 可看作是React中 componentDidMountcomponentDidUpdatecomponentWillUnmount 几个声明周期的组合,这里主要的目的当透传的值有变化的时候,触发这个周期Hook,实现根据upsertAction判断是新增还是编辑操作,如果编辑使其表单动态初始化值,否则保持表单为空。

https://zh-hans.reactjs.org/docs/hooks-reference.html#useeffect

useEffect传递两个参数,第一个参数是逻辑处理函数,第二个参数是一个数组,其中

  • 如果参数二存放变量,当数组存放变量发生改变时,第一个参数,逻辑处理函数将会被执行;
  • 如果参数二不传,浏览器会无线循环执行逻辑处理函数;
  • 如果参数二传空数组[],相当于挂在完成后执行一次。

3.2组件隐藏Hidden

在React没有像Vue那种v-if的语法糖,在处理组件根据条件是否渲染的时候,比较正规的处理方式是写个函数定义组件,例如如下用法:显示那种按钮和文案根据给定值动态返回。

const showButton= (state) => {
  if (state==='OPEN'){
    return <Button type='link'>CLOSE</Button>
  } else {
    return <Button>OPEN</Button>
  }
}

<div>{showButton("OPEN")}</div>

但对于本篇中的仅是根据某种条件隐藏编号表单项即可,经验证有一种简单的用法,即大部分官方组件有Hidden的属性,所以我们值需要为这个隐藏字段赋予逻辑值true或false就能控制是否显示。比如代码中根据操作动作来确定此项目是否显示。

 <Form.Item hidden={props.upsertAction==='ADD'} name='id' label='编号'>
    <Input disabled></Input>
</Form.Item>

至此通过详细的讲解和演示实践,完成了项目管理的增加和编辑需求开发,学习开发过程成中,是否遇到什么问题呢?如果需要帮助和交流的可以加我,我尽量在时间允许的情况下回复你,一同探讨学习、动手实战,一起踏实成长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/903687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HCIP生成树STP总结

STP生成树 网桥的4个选举 根网桥&#xff1a; 有且仅有一台&#xff0c;且由BPDU中的桥ID来决定 桥ID 网桥优先级&#xff08;0-65535公有&#xff09; 默认32768 MAC地址&#xff08;只有…

湘潭大学 湘大 XTU OJ 1055 整数分类 题解(非常详细)

链接 整数分类 题目 Description 按照下面方法对整数x进行分类&#xff1a;如果x是一个个位数&#xff0c;则x属于x类&#xff1b;否则将x的各位上的数码累加&#xff0c;得到一个新的x&#xff0c;依次迭代&#xff0c;可以得到x的所属类。比如说24&#xff0c;246&#…

漏洞指北-VulFocus靶场专栏-高级01

漏洞指北-VulFocus靶场专栏-高级01 高级001 &#x1f338;骑士cms任意代码执行&#xff08;CVE-2020-35339&#xff09;&#x1f338;step1&#xff1a;进入页面&#xff0c;登入后台step2 系统——网站配置——网站域名step3 中国蚁剑连接 高级002 &#x1f338;Django SQL注入…

安装ps找不到vcruntime140_1.dll怎么回事?有哪些解决方法

安装ps找不到vcruntime140_1.dll怎么回事&#xff1f;这可能是因为您的计算机Visual C Redistributable包损坏&#xff0c;其中包含vcruntime140_1.dll文件。VCRuntime140_1.dll是一个重要的动态链接库文件&#xff0c;它包含了Visual C运行时所需的一些函数和资源。当这个文件…

银行客户关系管理系统springboot财务金融进销存java jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 银行客户关系管理系统springboot 系统有1权限&#x…

周易卦爻解读笔记——未济

第六十四卦未济 火水未济 离上坎下 未济卦由否卦所变&#xff0c;否卦六二与九五换位&#xff0c;象征尚未完成。 天地否 未济卦和既济卦既是错卦又是覆卦&#xff0c;这也是最后一卦&#xff0c;序卦传【物不可穷也&#xff0c;故受之以未济终焉】 未济卦象征尚未完成&…

NSSCTF之Misc篇刷题记录(14)

[SWPUCTF] 2021新生赛之Crypto篇刷题记录① [UUCTF 2022 新生赛]王八快跑[安洵杯 2020]BeCare4[HDCTF 2023]ExtremeMisc NSSCTF平台&#xff1a;https://www.nssctf.cn/ PS&#xff1a;记得所有的flag都改为NSSCTF [UUCTF 2022 新生赛]王八快跑 小游戏 小乌龟跑过线就有 fla…

BDA初级分析——SQL语句应用基础练习题

1、请检查OLYMPIC表中是否存在重复国家? SELECT Team,COUNT(*) AS 重复次数 FROM Olympic GROUP BY Team HAVING COUNT(*) > 1;SELECT COUNT(Team),COUNT(DISTINCT(Team)) FROM Olympic; 2、将OLYMPIC表中Armenia(ARM)的奖牌总数更新为14 UPDATE Olympic SET total_medals …

【C语言】使用C语言,实现九九乘法表(另附Python、Java、JavaScript实现方式)

文章目录 1. C语言实现1.1 思路1.2 代码实现 3.其他语言实现3.1 Python实现3.2 Java实现3.3 JavaScript实现 1. C语言实现 1.1 思路 九九乘法表图示&#xff1a; 思路如下&#xff1a;定义两层for循环即可实现九九乘法表 一共有9层&#xff0c;所以要定义一个变量i&#xff…

记录首次面试2023-08-18

人生第一次面试&#xff0c;大概一个小时左右。没有问我C的&#xff0c;上来一个数据库事务&#xff0c;虽然没有复习&#xff0c;但是还是能够记住一些&#xff0c;主要问的一些事务的隔离级别&#xff0c;以及都有什么作用&#xff0c;我是举例回答的&#xff0c;客户端A和客…

【激光雕刻与DIY Arduino SCARA机器人】

【激光雕刻与DIY Arduino SCARA机器人】 1. 项目概况2. 设计和3D模型3. 安装激光模块4. SCARA机器人激光雕刻机电路图5. 完成装配6. 马林固件,用于使用 SCARA 机器人进行激光雕刻7. 配置 Marlin 固件8. 控制软件 – 主机9. 使用 SCARA 机器人进行激光雕刻10. 生成用于激光雕刻…

PCAP01介绍和STM32模拟SPI驱动

一.芯片介绍 Pcap01是德国acam公司设计的一款革命性的电容测量芯片。该芯片 内部有DSP计算单元&#xff0c;可以直接将电容元件接到Pcap01芯片&#xff0c;然后芯片计算出容值大小&#xff0c;通过SPI总线将电容容值数据传送给CPU&#xff0c;电容测量完全数字化。 二,测量原…

不要着急购买iPhone 15,先看看这5点再做决定吧!

人们对下个月可能推出的iPhone 15感到兴奋,这是有充分理由的——有传言称,新机型正在做出一些重大改变,尤其是在iPhone 15 Pro机型方面。从四款新iPhone都采用USB-C,到iPhone 15 Pro Max采用潜望镜式长焦镜头以实现更好的变焦,听起来有很多功能值得兴奋。 当然,除非你没…

软件综合测试实训室建设方案

一、实训室总体目标 培养目标:培养软件测试的专业技术人才,使学生掌握软件测试的基本理论知识,熟练运用各类测试工具,具备随产品版本迭代进行回归测试的能力。 建设目标:打造一流的软件测试培训基地,建成集教学实训、技术研发、项目实习为一体的软件测试人才培养高地。 二、…

文件上传xxx

本地保存文件 将文件保存到服务器本地硬盘中 max-request-size 多个文件总大小不能大于100M PostMapping("/upload")public Result upload(String username,Integer age,MultipartFile image) throws IOException {log.info("用户名:{},牛叔&#xff1a;{},文件…

Android 多渠道打包及VasDolly使用

目录 1.添加productFlavors的配置buildConfigFieldmanifestPlaceholdersresValue 2.设置apk文件的名称&#xff0c;便于识别3.添加vasdolly、添加gradle脚本&#xff08;windows&#xff09; 作用&#xff1a;一次性可以打多个apk包&#xff0c;名字、包名、logo等可以不相同。…

掌控未知:项目中如何巧妙应对突发与紧急

引言 在项目管理的领域中&#xff0c;每一个项目都伴随着一系列的不确定性和挑战。这些不确定性可能源于外部环境的变化、团队内部的动态或技术的快速迭代。而在这些不确定性中&#xff0c;突发和紧急事件尤为考验项目经理的应变能力和决策智慧。那么&#xff0c;如何在项目中…

【排排站:探索数据结构中的队列奇象】

本章重点 队列的概念及结构 队列的实现方式 链表方式实现栈接口 队列面试题 一、队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#x…

休息刷一下周赛

7004.判断字母缩略词&#xff08;字符串&#xff09; 思路&#xff1a;1.判断字符串个数是否跟缩略词长度相同 2.定位比较 class Solution { public:bool isAcronym(vector<string>& words, string s) {int nwords.size();if(n!s.size()) return false;for(int i0…

solidworks(3)

两个方法 sr&#xff1a;代表半径&#xff08;画图时记得换成直径&#xff09;