表单是后台项目业务中的经常使用组件,此次重构了登陆功能以知足登陆方式可配置的需求,在此记录和分享一下。javascript
在以前,项目只支持手机号+密码登陆,前端是直接把表单写死的,后来有客户但愿能支持验证码登陆,有的客户还但愿能有手机号+验证码+密码的登陆方式...因此登陆方式的灵活性须要可配置的表单支持,因而我把登陆组件作了拆分。html
以表单元素为粒度,分离出了手机号、密码、短信验证码这几个组件,它们内部都有本身的表单验证方法,经过组合能够快速完成登陆、注册、找回密码等表单组件。高内聚低耦合、高内聚低耦合...跟着念十遍~前端
.
├ common #拆分的表单元素组件
| ├ captcha.vue #短信验证码
| ├ password.vue #密码
| └ phone.vue #手机号
├ login #登陆组件
| └ index.vue
├ register #注册组件
| └ index.vue
└ resetPassword #找回密码组件
└ index.vue
复制代码
这里咱们将login做为父组件,读取服务端返回的登陆配置并在模板作条件渲染,登陆时调用子组件内部的表单验证,最后经过Vuex拿到数据调用接口。整个可配置登陆表单的逻辑就是酱子,接下来上代码。vue
请求服务端配置数据:java
/* 参数说明: * 'password': 密码登陆 * 'captcha': 短信验证码登陆 * 'password_or_captcha': 密码或短信登陆 * 'password_with_captcha': 密码+短信登陆 */
config: {
login_methods: 'password'
}
复制代码
登陆组件的核心渲染代码(pug):vuex
.login-card
.login-header
h3 登陆
.login-content
//- 手机号做为通用的帐户名
phone(ref="phone")
//- 密码组件
password(
v-if="isPasswordMode"
ref="password"
)
//- 短信验证码组件
captcha(
v-if="isCaptchaMode"
ref="captcha"
)
//- 短信验证码 和 密码
template(v-if="isPasswordWithCaptchaMode")
captcha(ref="captcha")
password(ref="password")
//- 短信验证码 或 密码
template(v-if="isPasswordOrCaptchaMode")
...
el-button(@click="login") 登陆
复制代码
登陆时须要三个步骤:表单验证、组装数据、调用接口:element-ui
async login () {
if (!this.validate()) return // 表单验证
const loginData = this.getLoginData() // 组装数据
await this.postLogin(loginData) // 调用接口(vuex actions)
...
}
复制代码
登陆的表单验证实际上是对当前登陆方式中全部组件的 validate()
方法进行逻辑判断:bash
/** * @return {Boolean} isPass */
validate () {
const phone = this.$refs.phone.validate()
let isPass = false
if (this.isPasswordMode) {
if (this.$refs.password) isPass = this.$refs.password.validate()
}
if (this.isCaptchaMode) {
if (this.$refs.captcha) isPass = this.$refs.captcha.validate()
}
if (this.isPasswordWithCaptchaMode) ...
if (this.isPasswordOrCaptchaMode) ...
isPass = phone && isPass
return isPass
}
复制代码
因为使用了element-ui,这里子组件的 validate()
方法是由 el-form
组件所提供的。async
能够从下面的password组件模板看到,咱们将每一个子组件当作一个完整的表单(当时这样作是为了应对部分子组件内部又有多个表单元素,它们之间的交互和验证逻辑比较复杂):post
.login-password
el-form(
:model="form"
:rules="rules"
ref="form"
@submit.native.prevent=""
)
el-form-item(prop="password")
el-input(
v-model="form.password"
type="password"
name="password"
)
复制代码
W3C: When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.
须要注意,根据W3C标准, 当一个form元素中只有一个输入框时,在该输入框中按下回车会自动提交表单。经过在 <el-form>
添加 @submit.native.prevent
能够阻止这一默认行为。
password组件的表单验证(使用element-ui表单):
/** * @return {Boolean} res */
validate () {
let res = false
this.$refs.form.validate((valid) => {
res = valid
})
return res
}
复制代码
最后从Vuex里拿到全部表单数据,进行组装:
computed: {
...mapState('login', {
phone: state => state.phone,
password: state => state.password,
captcha: state => state.captcha
}),
},
methods: {
...
/** * @return {Object} data */
getLoginData () {
let mode = ''
const phone = this.phone
...
const data = { phone }
if (this.isPasswordMode) {
mode = 'password'
data.password = password
}
if (this.isCaptchaMode) {
mode = 'captcha'
data.captcha = captcha
}
if (this.isPasswordWithCaptchaMode) ...
if (this.isPasswordOrCaptchaMode) ...
data.mode = mode
return data
}
}
复制代码
调用接口:
const loginData = this.getLoginData()
await this.postLogin(loginData)
...
复制代码