Appearance
表单组件字段校验
日常开发在使用表单组件的时候,应该根据实际的业务场景,对字段是否必填、字段长度,字段所包含的字符类型做一些校验,同时在调用接口的时候,相关数据接口需要对请求的数据进行字段验证
代码示例:
vue
<!-- 修改密码弹出框 -->
<template>
<!-- 容器 -->
<el-dialog :visible.sync="visible" width="500px" v-draggable :close-on-click-modal="false" :modal-append-to-body="false" @close="onClose">
<template v-if="visible">
<span slot="title">
<i class="fa fa-pencil-square-o b"> 修改密码</i>
</span>
<!-- 表单信息 -->
<el-form ref="form" :model="form" :rules="formRules" label-width="120px" size="small">
<el-form-item label="原密码:" prop="oldPassword">
<el-input type="password" :show-password="true" v-model.trim="form.oldPassword" :maxlength="20" placeholder="输入原密码"></el-input>
</el-form-item>
<el-form-item label="新密码:" prop="newPassword">
<el-input type="password" :show-password="true" v-model.trim="form.newPassword" :maxlength="20" placeholder="输入新密码"></el-input>
<!-- 密码级别 -->
<v-password-level :value="form.newPassword" />
</el-form-item>
<el-form-item label="确认新密码:" prop="confirmPassword">
<el-input type="password" :show-password="true" v-model.trim="form.confirmPassword" :maxlength="20" placeholder="再次输入新密码"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="onCancel" size="small">取消</el-button>
<el-button type="primary" @click="onSave" size="small">保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
const jsBase64 = () =>
import(/* webpackChunkName: 'async_vendors/js-base64' */ 'js-base64');
import blueimpMd5 from 'blueimp-md5';
import store from '@/store/index';
import { GET_APP_INFO } from '@/store/system';
import { transPassword } from '@/mixin/page';
import fetch from '@/config/fetch';
import validate from '@/assets/js/validate';
/**
* 校验密码
* 密码强度级别
* '1': 纯数字(6-20位)
* '2': 字母和数字必需同时存在(6-20位)
* '3': 至少为1位,只要不为空即可
* '4': 大小写字母、数字、特殊字符必需同时存在(8-20位)
* '5': 大小写字母、数字、特殊字符必需同时存在(8-20位),不能包含3个及以上相同或字典连续字符、不能包含3个及以上键盘连续字符
* @export
* @param {*} rule
* @param {*} value
* @param {*} callback
*/
function validatePassword(rule, value, callback) {
let level = store.getters[GET_APP_INFO].pwdLevel || '1';
switch (level) {
case '1':
if (validate.validateNumberPassword(value)) {
return callback();
} else {
return callback(new Error('密码应为6 - 20位数字'));
}
case '2':
if (validate.validatePassword(value)) {
return callback();
} else {
return callback(new Error('密码应为6 - 20位字符,含有字母与数字'));
}
case '3':
if (util.isNotEmpty(value)) {
return callback();
} else {
return callback(new Error('密码至少为1位'));
}
case '4':
if (validate.validateStrongPassword(value)) {
return callback();
} else {
return callback(
new Error('密码应为8 - 20位字符,含有大小写字母、数字、特殊字符')
);
}
case '5':
/**
* 是否包含3个及以上相同或字典连续字符
* @param {*} value
* @returns
*/
function hasContinuousChar(value) {
value = value || '';
if (value === '') {
return false;
}
value = String(value);
for (let i = 0; i < value.length - 2; i++) {
// 转ASCII
let n1 = value[i].charCodeAt();
let n2 = value[i + 1].charCodeAt();
let n3 = value[i + 2].charCodeAt();
// 判断重复字符
if (n1 == n2 && n1 == n3) {
return true;
}
// 判断连续字符: 正序 + 倒序
if (
(n1 + 1 == n2 && n1 + 2 == n3) ||
(n1 - 1 == n2 && n1 - 2 == n3)
) {
return true;
}
}
return false;
}
/**
* 是否包含3个及以上键盘连续字符
* @param {*} value
* @returns
*/
function hasKeyBoardContinuousChar(value) {
value = value || '';
if (value === '') {
return false;
}
value = String(value);
/**
* 键盘字符表(小写)
*/
const normalKeyboard = [
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\0'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '\0', '\0'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', '\0', '\0', '\0'],
];
/**
* shift键盘的字符表
*/
const shiftKeyboard = [
['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\0'],
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'],
['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"', '\0', '\0'],
['z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?', '\0', '\0', '\0'],
];
value = value.toLowerCase().replace(/[\s\uFEFF\xa0\u3000]/g, '');
// 获取字符串长度
let nStrLen = value.length;
// 定义位置数组:row - 行,col - column 列
let pRowCharPos = [];
let pColCharPos = [];
for (let i = 0; i < nStrLen; i++) {
let chLower = value[i];
pColCharPos[i] = -1; //-1为~/`键
// 检索在表normalKeyboard中的位置,构建位置数组
for (let j = 0; j < 4; j++) {
for (let k = 0; k < 13; k++) {
if (chLower == normalKeyboard[j][k]) {
pRowCharPos[i] = j;
pColCharPos[i] = k;
}
}
}
// 在表normalKeyboard中没找到,到表shiftKeyboard中去找,找到则continue
if (pColCharPos[i] >= 0) {
continue;
}
// 检索在表shiftKeyboard中的位置,构建位置数组
for (let j = 0; j < 4; j++) {
for (let k = 0; k < 13; k++) {
if (chLower == shiftKeyboard[j][k]) {
pRowCharPos[i] = j;
pColCharPos[i] = k;
}
}
}
}
// 匹配坐标连线
for (let j = 1; j <= nStrLen - 2; j++) {
//同一行
if (
pRowCharPos[j - 1] == pRowCharPos[j] &&
pRowCharPos[j] == pRowCharPos[j + 1]
) {
// 键盘行正向连续(asd)或者键盘行反向连续(dsa)
if (
(pColCharPos[j - 1] + 1 == pColCharPos[j] &&
pColCharPos[j] + 1 == pColCharPos[j + 1]) ||
(pColCharPos[j + 1] + 1 == pColCharPos[j] &&
pColCharPos[j] + 1 == pColCharPos[j - 1])
) {
return true;
}
}
//同一列
if (
pColCharPos[j - 1] == pColCharPos[j] &&
pColCharPos[j] == pColCharPos[j + 1]
) {
//键盘列连续(qaz)或者键盘列反向连续(zaq)
if (
(pRowCharPos[j - 1] + 1 == pRowCharPos[j] &&
pRowCharPos[j] + 1 == pRowCharPos[j + 1]) ||
(pRowCharPos[j - 1] - 1 == pRowCharPos[j] &&
pRowCharPos[j] - 1 == pRowCharPos[j + 1])
) {
return true;
}
}
}
return false;
}
if (validate.validateStrongPassword(value)) {
if (!hasContinuousChar(value)) {
if (!hasKeyBoardContinuousChar(value)) {
return callback();
} else {
return callback(new Error('密码不能包含3个及以上键盘连续字符'));
}
} else {
return callback(new Error('密码不能包含3个及以上相同或字典连续字符'));
}
} else {
return callback(
new Error('密码应为8 - 20位字符,含有大小写字母、数字、特殊字符')
);
}
default:
return callback();
}
}
export default {
data() {
const validatorConfirmPassword = (rule, value, callback) => {
if (this.form.confirmPassword === this.form.newPassword) {
callback();
} else {
callback(new Error('两次密码输入不一致'));
}
};
return {
visible: false, //显示隐藏状态
form: {
oldPassword: '',
newPassword: '',
confirmPassword: '',
}, //表单对象
formRules: {
oldPassword: [
{ required: true, message: '请输入原密码', trigger: 'blur' },
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
{ validator: validatePassword, trigger: 'blur' },
],
confirmPassword: [
{ required: true, message: '请再次输入新密码', trigger: 'blur' },
{ validator: validatorConfirmPassword, trigger: 'blur' },
],
}, //表单规则
};
},
methods: {
/**
* 打开修改密码弹出框
*/
open() {
this.visible = true;
},
/**
* 构建参数
*/
async buildParams() {
try {
let { Base64 } = await jsBase64();
return {
opassword: transPassword(blueimpMd5(this.form.oldPassword)),
password: transPassword(blueimpMd5(this.form.newPassword)),
basePwd: Base64.encode(this.form.newPassword),
};
} catch (err) {
return Promise.reject(err);
}
},
/**
* 校验表单
* 1、TODO:调用el-form表单校验方法,对表单进行校验,包括必填校验、字段长度校验、字段是否特殊字符等
*/
validateForm() {
return new Promise((resolve) => {
this.$refs.form.validate((valid) => {
return resolve(valid);
});
});
},
/**
* 保存数据
* 1、TODO:保存数据的时候,相关数据接口需要对请求的数据进行字段验证
*/
async save() {
try {
let params = await this.buildParams();
await fetch.post('/pa/user/modifyPassword', params);
} catch(err) {
return Promise.reject(err);
}
},
/**
* 处理保存
* 1、校验表单
* 2、保存数据
*/
async onSave() {
try {
this.$loading();
let valid = await this.validateForm();
if (valid) {
await this.save();
}
this.$message({
type: 'success',
message: '保存成功!',
});
this.visible = false;
} catch (err) {
console.error(err);
this.$message({
type: 'error',
message: err.msg,
});
} finally {
this.$loadingClose();
}
},
/**
* 处理取消
*/
onCancel() {
this.visible = false;
},
/**
* 处理弹出框关闭
*/
onClose() {
this.$refs.form.resetFields();
},
},
};
</script>