鼓捣一下文字,讲一讲工做中遇到的趣事,今日来聊一聊步骤管理器。话说一日,产品经理带着和善的眼神缓步踱来,对着前端的页面这样分析道,前端同窗啊,你看,咱们的页面操做对用户来说是零散的,用起来有好多不便,缺少一个有效的引导,应该怎么去解决呢?哈哈,心想这问题不大。就说,我看不如这样吧,咱们能够把这些零散又颇有必要的操做集中起来,作成一个操做引导页,这样用户就无需跳转不一样的页面去完成那些零散的操做了,是对用户体验的一个很大提高。与此同时,朋友A说他也想要用这样的方案去提高用户体验。ok,那么一个便捷可配置,通用性良好的步骤管理组件应该怎样去设计呢?css
如下均已React为例,但用Vue,Angular的实现大同小异。前端
静下心来想想,要知足可灵活配置,高通用性,高可用性可并不容易,灵活配置和使用便捷是两个对立的面,如何去平衡好是一件重要的事情。例如我想要灵活配置,就免不了要多写代码,这样组件使用起来就会麻烦,参数容易忘记,要时不时看文档。git
咱们先从保证通用性的角度出发,一步步去解决这个问题。github
通常来讲一个步骤管理组件由三个部分组成,分别是给与每一步步骤信息的标题部分,内容展现部分以及操控上/下一步的控制部分。sql
根据组成来拆分,咱们能够这样去配置每个步骤。后端
// demo.jsx
<Steps stepset={[{
title: '1',//标题
content: <p>1</p>,//内容
handleNext: () => { return true;}//控制true or false
}, {
title: '2',
content: <p>2</p>,
handleNext: () => { return true;}
}, {
title: '3',
content: <p>3</p>,
handleNext: () => { return true;}
}]} />
复制代码
嗯,这很直观。sass
每个步骤都有三种状态,待选状态,当前状态,已选状态。咱们能够轻易地经过当前步骤currentStep
去判断每一步究竟属于哪一种状态。bash
// steps.jsx
...
handleTitleStyle(stepIndex, currentStep) {
if (stepIndex > currentStep) {
return 'step-sign-default';
}
if (stepIndex < currentStep) {
return 'step-sign-ed';
}
return 'step-sign-active';
}
...
<div className={['orther-style', this.handleTitleStyle(index,currentStep)].join(' ')}>{item.title}</div>
...
复制代码
布局的要点在于步骤标题栏的自适应和通用性上。数据结构
使用flex布局令标题栏上的‘零件’整齐地自适应异步
// steps.scss
.steps-box{
display: flex;
}
.steps-item{
flex: 1;
}
...
复制代码
使用相邻兄弟选择符巧妙地识别出首个步骤,令横杠不在第一个步骤前显示。
// steps.scss
...
.step-item+.step-item .sign-box {
&:before {
content: '';
position: absolute;
display: inline-block;
border-style: solid;
border-width: 1px;
border-color: #cdcdcd;
width: 100%;
height: 0;
left: -50%;
top: 46%;
transition: all .2s;
}
}
...
复制代码
除当前步骤外,其余步骤使用position:absolute;
脱出文档流,仅留下当前步骤所占据的空间。
// steps.jsx
...
this.props.stepset.map((step, index) => {
return (
<div key={index} className="p-r">
<div className="step-content" style={index == this.state.currentStep ? { position: 'relative', visibility: 'visible', zIndex: 1, left: 0 } : {}}>
{
step.content
}
</div>
</div>
)
})
...
复制代码
没错,这里就是灵活配置与方便使用的平衡点。若是为了最大灵活配置考虑,控制栏的存在是没有必要的,彻底能够将上一步or下一步每个时刻的控制权交给开发者。但这样未免要开发者写不少控制代码,因此笔者认为这些控制代码能够交由组件去完成,咱们只要在合适的地方提供相关的API,开发者也能彻底控制步骤组件的每个时刻。这样的折中方案即考虑到了灵活配置,又兼顾到了易用性。
一般在处理下一步的响应时,常常有异步的场景,例如点击下一步时须要远程校验一下验证码。嗯,咱们能够用async/await去构建一个支持异步的处理程序。
// steps.jsx
...
async handelNextStep() {
const len = this.props.stepset.length;
if (this.props.stepset[this.state.currentStep].handleNext) {
// 支持异步操做 return Promise
const AllowNext = await this.props.stepset[this.state.currentStep].handleNext(this.props.stepset[this.state.currentStep], this.state.currentStep);
if (AllowNext) {
if (this.state.currentStep + 1 < len) {
this.setState({
currentStep: this.state.currentStep + 1
});
}
}
} else {
if (this.state.currentStep + 1 < len) {
this.setState({
currentStep: this.state.currentStep + 1
});
}
}
}
handelPreStep() {
if (this.state.currentStep - 1 >= 0) {
this.setState({
currentStep: this.state.currentStep - 1
}, () => {
this.props.stepset[this.state.currentStep].handlePre && this.props.stepset[this.state.currentStep].handlePre(this.props.stepset[this.state.currentStep], this.state.currentStep);
});
}
}
...
复制代码
朋友A这时又问了,那我想在content中去控制步骤怎么办呢?嗯,这不是问题,只要将相关的操做API暴露出来,经过props传入便可。就像这样:
// demo.jsx
<Steps stepset={[{
title: '1',//标题
content: (handelNextStep, handelPreStep) => {
<p onClick={() => handelNextStep()}>hello</p>
},
isCustomCtrl: true//是否自定义控制栏
}, {
title: '2',
content: <p>2</p>,
handleNext: () => { return new Promise((resolve) => { resolve(true); }) }//支持异步操做
}]} />
复制代码
ok,一个步骤管理组件就成型了。
源码已放到GitHub 组件&&样式,能够在SluckyUI中的‘步骤管理Steps’标签下查看在线Demo。
这个步骤管理组件的故事到此就结束啦,产品经理与朋友A都很高兴。这个步骤管理组件现已集成在SluckyUI For React中,SluckyUI的理念是打造一个组件库种子,让其余开发者可以进行快速二次开发,减小没必要要的造轮子,但当中的编写任重而道远,还有不少还没有完善的地方,欢迎多多交流加入SluckyUI。