目录
1. 回顾antd pro 结构
(1)重要菜单:
config菜单下routes.ts里可以设置其他相关目录的结构,添加新页面的内容。
即:src/pages/下添加新文件夹,对应新页面
(2)useState, useRef, useEffect:
hooks使得函数式组件中用useState, useRef, useEffect
(3)const [count, setCount] = useState(0):第一个参数是变量,第二个参数是异步函数。
!!!重要说明!!!
1) setCount函数是一个异步的过程,不是同步的。
即:setCount不会阻塞它后面的命令的执行,有可能后面的命令在它之前执行,后面命令用用到的值可能就是setCount更新之前的值。
2)setCount函数第一个参数是count, 还可以有第二个参数 (回调函数?)…..待研究
针对这个问题,运用到的知识点:setState, setCount还可以传第二个参数。
3)setCount有两种写法:第一种写法是第二种写法的语法糖。
第一种写法:
可以用第二个参数,即回调函数:
类似:this.setState({todos: newTodos}, () => {console.log(this.state.todos)})
这样的话,就能让其顺序执行,先更新,再打印
第二种写法:
this.setState中的函数,可以只需要一个回调函数(最常用这个),传一个参数(原始的state),然后修改这个参数,返回修改之后的参数。
即如果setState只有一个参数,那这个参数是它的第二个参数,是一个回调函数。
为什么这个地方讲到这个问题?
是因为在函数式组件中,这是setCount里可用一个回调函数的原因。setCount(count => count+1)。跟之前的例子对应上了:
(4)const myRef = useRef(0) //定义useRef对象
在某个标签中<BBB ref={myRef}> //用useRef对象标识组件标签
最后在需要的地方使用:myRef.current.value //通过useRef,调用标签的值
注意跟JS原生的event从input里取值的区别:event.target.value
(5)useEffect
第一个参数:回调函数(参数),在componentDidmount和componentWillUnmount阶段都会执行。
第二个参数是限制在什么方法下/情况下才执行这个useEffect.
2. 用antd pro实现todolist
说明:
(1)模态框:避免过多的页面跳转,新页面会以一个弹窗的形式弹出来显示。
(2)相比于原生的table, 更多的使用Protable
(3)VScode 里创建函数组件快捷方式: rfc。类式组件快捷方式:rcc。
(4)常规操作:需要用到什么组件,先拷贝官网上相应组件的代码信息,后面没有用的再清掉。
(5)一般我们用<PageContainer>,会帮我们把路径和标题信息加载好。也可以用<ProCard>。
(6)定义一些字段,以及它们的类型,常用的是string和number。类似Java的接口
Type DataType = {
id: string;
name: string;
age: number;
todo: string
}
(7)定义表头(第一列)的信息,以及每一列的取值/从传入数据中的匹配项
const columes: ProColumnType<DataType>[ ] = [ //这个columes是一个数组,切记
{
title: ‘Name’,
dataIndex: ‘name’,
…
}, //数组元素1
{
title: ‘Age’,
dataIndex: ‘age’,
…
}, //数组元素2
{
title: ‘Todo’,
dataindex: ‘todo’,
…
}, //数组元素3
{
title: ‘Action’,
…
render:() =>{}
} //数组元素4
]
(8)数据源
const data: DateType[ ] = [
{
id: ‘1’,
name: ‘Mike’,
age: ’30’,
todo: ‘吃饭’,
},
{
id: ‘2’,
name: ‘Kate’,
age: ’40’,
todo: ‘睡觉’,
}]
(9)return里的Protable, 将官网上的示例代码拷过去之后,再根据情况删除一些我们不需要的功能,模块,简化代码。 在<ProTable />标签里面加表头columns,footer等内容。
(10)<ProTable />标签里,最重要的是columns (表头字段), dataSource
columns = {columns}
dataSource = {data}
(11)把data放到函数组件里的useState
(12)要实现delete操作:
把columns放到函数里。
columns里action字段的render方法的回调,它自己可以获取一些参数,其中第二个参数record就是当前操作的某个todo项,根据这个信息,就能判断要删除确切的哪一项。
遍历每一条todo时,到action这一块,发现没有dataIndex, 就不像上面那样直接赋值action, 而是等点击的时候,它就去执行render里的回调方法。(每次都渲染render, 执不执行回调方法看有没有监听到动作)
columns里面就很像之前的<Item />组件。
{
title: 'Action',
key: 'action',
sorter: true,
valueType: 'option',
render: (_, record) => [
<a key="delete" onClick={
() => {
const newTodos = todos.filter(todoObj => {
return todoObj.id !== record.id
})
setTodos(newTodos)
}
}> Delete </a>,
],
}, //语法说明:render: (text, record, _, action) => {}
(13)要实现新增操作:使用模态框
根据ProTable,加一个新增按钮,这里的<Button>是常规的,但真正使用时,用ProComponents里的Modal表单,新建表单ModalForm,ProForm.Group等。
toolBarRender = {( ) => [ //这里箭头指向的是一个数组,意思是这个地方可以包含多个按钮
<Button type=“primary” onClick={ ( ) => { }}>
新增
</Button>
]}
实例:
toolBarRender={ //放在<ProTable />标签中
() => [
<ModalForm<DataType>
title="新建TODO"
formRef={formRef}
trigger={ //触发表单
<Button type="primary">
<PlusOutlined />
新建Todo
</Button>
}
submitter={{ // 实现了重置按钮
searchConfig: {
resetText: '重置',
},
resetButtonProps: {
onClick: () => {
formRef.current?.resetFields(); //提交后清空模态框
// setModalVisible(false);
},
},
}}
onFinish={async (values) => { //提交表单时的操作
const { name, age, todo } = values; //解构赋值
const newTodos = [{ id: nanoid(), name, age, todo }, ...todos] //扩展运算符
setTodos(newTodos);
message.success('提交成功');
//formRef.current?.resetFields(); //提交后清空模态框
return true;
}}
> //ModalForm的前一个标签在这里结束,下面是ModalForm要展示的内容,最后是结束标签
<ProForm.Group>
<ProFormText width="sm" name="name" label="姓名" placeholder="请输入姓名" />
<ProFormText width="sm" name="age" label="年龄" placeholder="请输入年龄" />
<ProFormText width="sm" name="todo" label="todo" placeholder="请输入todo" />
</ProForm.Group>
</ModalForm>
]
}
补充解析:
(1)onFinish中,用异步方法,values传的是下方ProForm中输入的新建内容
(2)const newTodos = [{ id: nanoid(), name, age, todo }, ...todos] 这里用了语法糖
(3)提交后清空模态框:formRef.current?.resetFields();
(4)安装nanoid, yarn add nanoid
(5)实现新增页面的重置/清空操作
具体参考 Modal/Drawer中重置表单:通formRef重置。
上面代码的submitter部分,实现了重置按钮。
如果想要在提交新todo时同步更新,则无需上面的submitter部分,只需要在onFinish触发部分,添加:formRef.current?.resetFields();
3. 从接口获取数据,而不是页面上直接获取
(1)request从接口获取的数据(JSON格式等)和自定义的dataSource
(2)拿到request里的内容(数组)之后,它也会自动去跟columns遍历匹配,dataindex对得上的话,就会显示在页面上。
(3)之后如果需要调用后端信息的话,通常会编写一个service.ts,存放各种方法,请求,接口,跟后端联调。
(4)因为这里后端的还没写好,没起来,所以这里依赖一下mock接口,在listTableList.ts里改
(5)在TodoList里也新建一个service.ts,然后在TodoList的index.ts里修改request部分:
request = {(params, sorter, filter) => queryTodos({...params, sorter, filter})} //不再用dataSource
1)mock里的listTavleList.ts: //模拟后端接口数据
这里最好用data,不要用别的变量。如果用别的变量的话,需要额外处理。
export default { //将这些接口和里面的内容暴露出来,供service.ts调用,service.ts中的接口,再供前端调用
'GET /api/rule': getRule,
'POST /api/rule': postRule,
'GET /api/todos': {
data:[
{
id: '001',
name: 'Kate',
age: 18,
todo: '吃饭'
},
{
id: '002',
name: 'Ellie',
age: 19,
todo: '睡觉'
},
{
id: '003',
name: 'Joe',
age: 20,
todo: '上班'
},
]
}
};
2)TodoList里新建service.ts:
import request from '@/utils/request';
export async function queryTodos(params?: any){
return request('/api/todos', {
params,
});
}
3)TodoList里的index.tsx:
request={(params, sorter, filter) => queryTodos({ ...params, sorter, filter })}
// queryTodos对应service.ts里的函数
(6)每次启动页面,或者搜索(默认自己带的,自己写的render之类的在这里不算)之后,相当于都重新调request一次。 所以request赋值的那个方法,通常是获取页面显示内容的方法。
(7)request的params中,会带多个参数,current当前页,pageSize, name, todo…(打印看看)
(8)补充:ProTable里加: rawKey=“id". 避免了报错:因此每个子项都要有一个key.
4. 搜索功能
(1)函数组件里的ProTable里:search(true)
(2)在columns中某个字段可设置 hideInSearch,来隐藏该字段,使之不作为搜索项。
5. 删除功能
针对接口接收数据的情况的delete操作——这里还未实现,后续在其他文章中会有说明
什么是Promise?
6. 页面分页
data中,加total, pageSize, current
1)mock里的listTavleList.ts:
这里最好用data,不要用别的变量,框架分页所致。如果用别的变量的话,需要额外处理,需要在service.ts中修改。比如用了data1
export default {
'GET /api/rule': getRule,
'POST /api/rule': postRule,
'GET /api/todos': {
data:[
{
id: '001',
name: 'Kate',
age: 18,
todo: '吃饭'
},
{
id: '002',
name: 'Ellie',
age: 19,
todo: '睡觉'
},
{
id: '003',
name: 'Joe',
age: 20,
todo: '上班'
},
],
total: 101,
success: true,
pageSize: "20",
current: 1
}
};
处理如下:TodoList里的index.tsx:
request={async (params) => {
const response = await queryTodos({ ...params })
console.log(responce)
return {
data: response.data1,
success: response.success,
total: response.total
}
}
}
7.删除?刷新?
在ProTable中加一个actionRef, reload actionRef
8. 测试平台
(1)用例列表(id, casename, method, url...)
自动化列表
前后端技术方案,接口约定和定义等等。
然后分开开发,最后联调起来。
(2)接口列表:
用例列表
老的风格:
/api/todos? pageNum=1&cuttent=1&name=123&todo=456
GET /api/case/delete?id=3
RESTFUL风格接口:
POST /api/case/
DELETE /api/case/3 (数字代表要删哪一个)
接口 名称 path 请求方式 备注
新增 POST /api/case/ 内容放body
删除 DELETE /api/case/{id}
修改 PUT /api/case/{id} 内容放body
列表查询 GET /api/cases?…
单条记录查询 GET /api/case/{id}
(3)定义好接口之后,要开始定义各个接口里的数据个数,现在通常传JSON。
{
"caseName": "String",
"method": "POST/GET?DELETE",
"url": "String",
"headers": [
{"key": "key1", "value": "value1"},
{"key": "key2", "value": "value2"},
{"key": "key3", "value": "value3"}
],
"body": "String"
}
(4)工具swagger,能根据代码的改动而自动更新接口文档。
即:
在后端修改代码之后,就不用逐个改文档的,它会自动修改(页面上可以查看到)。
实现了在写代码的同时,就把接口文档自动写好了。
用BEJSON页面也可以将JSON转成Java实体类