背景:十月十二日进入了新公司.负责PC端.使用技术:AntDesign,React,dva.分配的项目模块是财务回款.AntDesign中涉及到的组件有:
Button按钮 Icon图标 Text文本 Title标题 Grid栅格 Form表单 Pagination分页 Steps步骤条 Checkbox多选框 DatePicker日期选择框 Input输入框 InputNumber数字输入框 Radio单选框 Select选择器 Badge徽标数 List列表 Tag标签, Table表格 Drawer抽屉 Message全局提示 Result结果 Spin加载中 Search搜索 Divider分割线 Descriptions描述列表
历经一个月.现在已开发联调完毕.成功上线.今天刚好有时间,整理下自己遇到的难点和解决思路.
开发第一步,先看UI设计图和项目文档,了解客户需求与页面逻辑.
开发第二步,开始绘制静态页面.
因为是新的项目,所以项目小组长会给一个git地址,上面有一个空的antd的架子.直接从git上拉下来在这个空架子里开发就好.如果不会从git上拉取项目的可以看这里git命令
,因为一个项目有多个模块,这样大家每次都是在同一个环境下开发的.所以用一个git地址.每个人每次写完自己的页面后就提交代码合并就好了.
这个是antd pro 的项目目录:
简单介绍下常用的文件夹:
config里面是路由
mock是模拟接口的
public里面放图片
页面在src下面写.
三层结构.services,models,pages.
services里面是连接后端接口的
models是把拿到的接口数据转换为使用的数据
pages里面是页面层,画页面,调接口,跳转页面等操作.
util里面是工具.
基本上常用的就是这些.上家公司是把所有的model都统一写在model层里,所有的services都写在services里.现在这家公司.我发现他们都是在一个文件夹里面.写自己的model和services.就像这样:
.js文件里面就是写页面和逻辑的地方,页头引入组件,class上面引入model层,引入Form,里面写你的state,还有方法,render里面写return.
.less里面是页面样式,就像这样,直接写.
model层里面是接口返回的值.
关注的点有:
namespace.就是你定义的你的model层的名字
effcts里面就是你获取的接口的值.可以在这里处理接口调成功后的逻辑.callback.
reducers里面就是你的异步方法返回值.
srvice里面是连接后端接口的.
基本上开发一个页面,文件夹下就是这四个文件.
在这里,我遇到了第一个问题,就是页面较为复杂,比如有步骤条的页面,代码写着写着就1000行了.整个页面代码很多,不利于管理.后面请教了同事.学会了分割页面,把一个步骤条作为一个子组件,放在一个父组件里面.这时候,目录就变成了这样:
当父组件页面需要展示子组件的时候,把子组件引用过来.就像这样:
import StepsOne from './components/stepsOne';
import StepsTwo from './components/stepsTwo';
import StepsThree from './components/stepsThree';
import StepsFour from './components/stepsFour';
使用的时候:
{steps.map(item => (
))}
{current == 0 && (
this.getOneListData(listData)}
val={this.state.val}
/>
)}
{current == 1 && (
this.getTwoListData(listDataTwo, data1)}
/>
)}
{current == 2 && (
this.stepThreeRef(ref)}
aClaimAmounts={aClaimAmounts}
getThreeNote={note => this.getThreeNote(note)}
/>
)}
{current == 3 && }
{current == 4 && (
this.newApplication()}>
填写新的申请
,
,
]}
/>
)}
可能我写的比较多.看起来不是很顺.大概这样理解:
把子组件引入后,在需要使用的地方,直接用标签 这样使用就可以得到子组件的内容了.至于我的datalist={datalist} 这个意义是向子组件传值.{datalist}这里面的datalist是我把父组件里的state里的datalist解析出来的值.
getTwoListData={(listDataTwo, data1) => this.getTwoListData(listDataTwo, data1)}
这个意义是子组件向父组件传值.
onRef={ref => this.stepThreeRef(ref)}
这个意义是父组件可以拿到子组件内所有的值,包括state里面的.
在使用onRef方法的时候,需要先在子组件内的componentDidMount方法里面定义:this.props.onRef(this);然后在父组件内才可以拿到.父组件拿值:
我是在父组件的state里面定义了一个变量,用来存储第三个步骤条所有的值.
stepThreeRef = ref => {
this.setState({ threeRef: ref });
};
这个很绕很绕,静态页面我一个礼拜就写完了.联调接口的时候,这几个步骤条加上传值取值赋值花了我联调一半的时间,哈哈哈哈.女生嘛,逻辑差了那么一点点~ 理解起来就会有难度~写的就慢了~我也是查了N个百度又问了好几次同事才解决了这些问题.以后多写写应该就顺了就快了.
其他的组件都比较简单,引入后直接使用就好了.但是步骤条和抽屉是有逻辑在里面的,这两个组件之前都没有用过.所以这个项目写起来还是花了点时间的.
比如这次我写项目使用的步骤条逻辑(记录一下,哈哈哈哈):
第一个页面有一个单选列表.使用的组件有Table表格,Tag标签,Search搜索,Radio单选框
点击下一步去第二个页面的时候,需要把单选后的列表内容全部传过去.并且这个单选是必选.如果没有选,就要用Message全局提示弹框.
第二个页面,分为两部分,展示公司名称和回款单还有发票.使用的组件有:
点击回款详情是打开一个抽屉,里面有回款单信息:
点击上一步是返回第一个步骤条页面,并且第一个页面之前选中的数据要保持选中的状态.点击取消也是回到第一个步骤条页面,不过选中的状态要取消.点击下一步去第三个步骤条页面.需要把第二个页面的单选回款单和多选发票带过去.这两个必须判断两个都必选一个,否则弹框,页面也停留在当前页.就像这样:
两个都选中后点击下一步就可以进入第三个页面了.
第三个页面展示的就是第二个页面传过来的回款单和发票,并且每个发票下面有很多个行项目,截图的是一个,后续会有很多.所以这里要遍历展示发票列表.且每个行项目都可以输入金额匹配.右下方的本次匹配金额动态增加或减少.好了,在这里我遇到了我的第二个问题:页面下方的本次匹配金额如何动态相加减输入框的金额.输入框的金额是有方法可以拿到的.e.target.value.相信一般大家都知道.但是页面上可能有N个输入框.这个是不确定的.后面想到,这个输入框我是放在Form下面的..可能我写的复杂了.但是那时候就是这样想的.后面同事就给我推荐了Form表单的一个方法,叫这个!!!this.props.form.getFieldsValue();贼好用.一次性给我把页面的输入框内容全部获取到了.而且我还踩坑了,开始绑定的是onChange方法,后面发现有点bug,因为是onChange方法是输入框值改变的时候才触发的,如果用户点击输入框后没有做改变,但是你总值又相加减了,就会有问题.后面用的是onBlur方法.失焦的时候触发.这样的就可以避免用户点击输入框后不做操作也不会影响最终值了.因为我是在方法内把所有的值相加了~ 好吧~ 我觉得稍微有点点蠢,但是没想到好方法.就用的这个了.最终结果就是本次匹配金额随着输入框的值动态相加减.还是实现了,哈哈哈哈哈.点击上一步回到第二个页面,第二个页面要选中当时来第三个页面的时候选中的值.第三个页面用的组件也介绍下:
判断逻辑也有很多,比如输入框值不能大于未匹配,本次匹配总金额不能大于未匹配金额.否则弹框,停留
全部都符合条件后,点击下一步成功来到第四个页面.
这个页面和第三个页面不一样的点就是输入框变成了展示,备注也变成了只读.点击上一步回到第三个页面,第三个页面输入框要显示之前输入的数字,备注也是一样.
第四个页面点击提交来到第五个页面.这里点击提交的时候是不用做逻辑判断的,但是要发接口.把所有的参数传给后端.如果接口返回错误了,页面也会停留.
这里介绍了我模块中最复杂的一个页面.
其他的四个页面就是一些展示数据,搜索,抽屉详情,跳转抽屉等等没啥难度了.
说下跳转页面我用方法:
goDetails = () => {
const {id} = this.state
router.push({
pathname: '/receivableManagement/receivableDetails',
query: {
id,
},
});
};
pathname就是页面路径,query里面是传参.把参数带到跳转后的页面去.取参数就是用query取:
// 获取回款单内容
getListCompletedMatch = () => {
const {
location: { query },
} = this.props;
this.setState({ loading: true });
this.props.dispatch({
type: 'receivableDetailsModel/getListCompletedMatch',
data: { paymentId: query.id },
callback:data =>{
this.setState({ loading: false });
}
});
};
还有一些小坑坑就不一一诉说了.在这个模块中收获最多的就是动态得到输入框相加减后的值,还有步骤条父组件子组件传值.以及抽屉,多个抽屉跳转.好用的方法有:
给金额后保留两位小数.Number(amount).toFixed(2);
这个toFixed()方法一定要是Number类型的值,括号里就是保留的小数位.
输入框失焦的时候触发的onBlur方法.可以取到最后一个值.
获取Form表单下的全部输入框值:this.props.form.getFieldsValue()
放几张写好的页面~