鼓搗一下文字,講一講工做中遇到的趣事,今日來聊一聊步驟管理器。話說一日,產品經理帶着和善的眼神緩步踱來,對着前端的頁面這樣分析道,前端同窗啊,你看,咱們的頁面操做對用戶來說是零散的,用起來有好多不便,缺少一個有效的引導,應該怎麼去解決呢?哈哈,心想這問題不大。就說,我看不如這樣吧,咱們能夠把這些零散又頗有必要的操做集中起來,作成一個操做引導頁,這樣用戶就無需跳轉不一樣的頁面去完成那些零散的操做了,是對用戶體驗的一個很大提高。與此同時,朋友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> ... 複製代碼
佈局的要點在於步驟標題欄的自適應和通用性上。markdown
使用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。