Antd样式覆盖
项目中肯定需要部分页面对原有的antd样式做修改,刚开始弄的时候遇到不少坑,后来查了方法和本项目中的写法,主要修改的思路是:
- 用chrome的开发者工具定位到要修改的div,查看class名
- 在对应的less样式下,增加 :global 字段,再写入对应的class名,做样式修改
- :global 需要包在自己页面的 module 类内,以防污染其他页面的全局样式
// 这里采用了less作为css预处理器
:global {
// Tab 样式修改
.ant-tabs-nav .ant-tabs-tab {
line-height: 20px;
padding: 13px 0 17px;
margin: 0 47px 0 0;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #4a4a4a;
text-align: center;
}
}
单页面多表单取值
当负责的页面涉及到需要很多表单的时候,例如绑定邮箱、手机的步骤验证或修改密码等,会出现单页面多表单
这种情况。一般会分为两种:
- 一种是两个以上表单同时独立存在
- 一种是一个表单出现会销毁上一个表单
情况1的处理
以手机/邮箱注册为例
因为在Antd中,Tabs组件是带缓存的,所以在包含Tabs的页面中,“手机注册”和“邮箱注册”的表单是同时存在的,这时候要对提交按钮的方法做一下处理,防止出现提交当前页面的值去校验另一个页面的表单
的情况。
可以利用Form的getFieldsValue
方法的第一个参数,自定义获取几组控件的值。
例如:
handleSubmitStepOne = e => {
e.preventDefault();
const { form, dispatch } = this.props;
if (isSelectedEmail) {
form.validateFields(['email', 'captchaEmail'], (errors, values) => {
if (!errors) {
// 你的业务逻辑
}
});
} else {
form.validateFields(['phone', 'country', 'captchaPhone'], (errors, values) => {
if (!errors) {
// 你的业务逻辑
}
});
}
};
同时需要搭配tabs获取当前页面key的做法,来判断当前页面是哪个。
情况2的处理
以按照步骤条注册的表单为例
针对这种场景有两种思路:
- 用变量控制FormItem
- 采用组件化的实现,抽离表单组件
这里以带验证码按钮的抽离表单组件为例
子组件需要提供如下方法:
方法/参数 | 说明 | 类型 |
---|---|---|
type | 可以让表单对应不同的校验规则或发送提示等,增加表单复用的可能性 | “email”/“phone” |
getCaptcha | 父组件需要传入的验证码发送接口,需要返回true或false表示发送成功或失败
|
Function |
getItemsValue | 向父组件返回表单的值,父组件调用此方法 | Function |
子组件写法
import React, {Component} from 'react';
import {Button, Col, Form, Input, message, Row} from "antd";
import style from "./index.less";
const FormItem = Form.Item;
@Form.create()
class VerifiedForm extends Component {
state = {
count: 0,
captchaLoading: false,
};
componentWillUnmount() {
clearInterval(this.interval);
}
// 向父组件传form表单的值
getItemsValue = () => {
let value = '';
this.props.form.validateFields((err, values) => {
if (!err) {
value = values;
}
});
return value;
};
// 获取验证码按钮点击
onGetCaptcha = (type) => {
this.setState({captchaLoading: true});
// 获取到父组件传过来的验证码发送接口
this.props.getCaptcha()
.then(res => {
if (res === true) {
// 开始倒计时
message.success(type === 'email' ? '邮箱验证码已发送' : '手机号验证码已发送');
let count = 59;
this.setState({count, captchaLoading: false});
this.interval = setInterval(() => {
count -= 1;
this.setState({count});
if (count === 0) {
clearInterval(this.interval);
}
}, 1000);
} else {
this.setState({captchaLoading: false});
}
})
};
render() {
const {form: {getFieldDecorator}, type} = this.props;
const {count, captchaLoading} = this.state;
return (
<Form onSubmit={this.handleSubmit}>
<FormItem>
<Row gutter={16}>
<Col span={16}>
{getFieldDecorator('captcha', {
rules: [
{
required: true,
message: '验证码不能为空',
},
],
})(<Input size="large" placeholder="请输入验证码" />)}
</Col>
<Col span={8}>
<Button
size="large"
style={{width: '100%'}}
disabled={count}
className={style.getCaptcha}
onClick={(e) => this.onGetCaptcha(type, e)}
loading={captchaLoading}
>
{count ? `${count} s` : '获取验证码'}
</Button>
</Col>
</Row>
</FormItem>
</Form>
)
}
}
export default VerifiedForm;
“获取验证码”按钮的倒计时和恢复,以及表单校验的逻辑都可以在表单组件中完成。只把需要的验证码发送接口和获取表单值的方法暴露给父组件。
父组件写法
<div>
<VerifiedForm
// antd提供的方法获取到表单组件的ref
wrappedComponentRef={form => {
this.form = form;
}}
// sendEmailCaptcha方法中包含验证码发送接口
getCaptcha={this.sendEmailCaptcha}
type="email"
/>
<Button
size="large"
type="primary"
loading={loadingCheckBindEmailCaptcha}
style={{ width: '100%', marginTop: 8 }}
onClick={this.handleClick}
disabled={!hasSendCaptcha}
>
下一步
</Button>
</div>
提交表单的按钮需放在父组件,通过按钮handleClick
方法调用表单组件的values值
handleClick = () => {
// 此处获取表单值
const values = this.form.getItemsValue();
// 业务逻辑
}
响应式布局
这里用到了媒体查询
,它有两种方式。
- 直接写在link中,根据设备尺寸的不同引入不同的CSS文件
// 意思是当屏幕的宽度 大于等于400px的时候,应用styleA.css
<link rel="stylesheet" type="text/css" href="styleA.css" media="screen and (min-width: 400px)">
- 写在<style>样式里
@media screen and (max-width: 600px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
.class {
background: #ccc;
}
}
项目中用的是方式二,值得注意的是@media screen
要放在对应的样式尾部,有层级关系!
关键字
类型 | 解释 |
---|---|
and | 并关系,用于连接逻辑 |
not | 排除某种设备 |
all | 所有设备 |
aural | 听觉设备 |
braille | 点字触觉设备 |
handled | 便携设备,如手机、平板电脑 |
打印预览图等 | |
projection | 投影设备 |
screen |
显示器、笔记本、移动端等设备 |
tty | 如打字机或终端等设备 |
tv | 电视机等设备类型 |
embossed | 盲文打印机 |
出现滚动条的高度计算
首先明确
- window.innerHeight: 获取浏览器窗口的高度
-
document.body.clientHeight: 获取body的高度
出现这两种,一部分是为了解决不同浏览器的兼容性问题,还有移动web宽高的问题。在一般情况下两个值是相等的,但是在页面出现滚动条的情况下,innerHeight的计算会加入滚动条的height。
所以在动态计算页面最小高度时,需要两者相减。参考如下代码:
minHeight: `calc(100vh - 198px - ${window.innerHeight - document.body.clientHeight}px)`,
多条件筛选
针对react中使用多组按钮来控制筛选,可采用如下方法进行扩展。
-
全部
按钮的value值为空字符 - 其余各个筛选按钮的value值对应后端传过来的值
以“全部”、“iOS开发专家”、“Android开发专家”......这几个按钮为例。
<RadioGroup
value={identityStatus}
onChange={e => this.handleChangeDataStatus(e)}
>
<RadioButton value="">全部</RadioButton>
<RadioButton value="iOS开发专家">iOS开发专家</RadioButton>
<RadioButton value="Android开发专家">Android开发专家</RadioButton>
<RadioButton value="H5开发专家">H5开发专家</RadioButton>
<RadioButton value="部署专家">部署专家</RadioButton>
<RadioButton value="后端开发专家">后端开发专家</RadioButton>
</RadioGroup>
筛选方法
// 两组按钮各自的选择状态
const { reviewStatus, identityStatus } = this.state;
// 过滤方法
tempDataSource = tempDataSource.filter(item => {
// 设定初始flag状态
let flagReview = !reviewStatus;
let flagIdentity = !identityStatus;
// 第一组按钮过滤
if (reviewStatus && item.reviewStatus) {
flagReview = true;
}
// 第二组按钮过滤
if (identityStatus && identityStatus === item.type) {
flagIdentity = true;
}
return flagReview && flagIdentity;
});
this.setState({ dataSource: tempDataSource });