var Vue
var checkWhenChange = true //每一個輸入框須要離焦即校驗
// 給一個dom添加class
function addClass(dom, className) {
// if (dom.classList){
// dom.classList.add(className);
// }else{
// dom.className += ' ' + className;
// }
var hasClass = !!dom.className.match(new RegExp('(\\s|^)' + _class + '(\\s|$)'))
if (!hasClass) {
dom.className += ' ' + _class
}
}
//經常使用正則表
var regList = {
ImgCode: /^[0-9a-zA-Z]{4}$/,
SmsCode: /^\d{4}$/,
MailCode: /^\d{4}$/,
UserName: /^[\w|\d]{4,16}$/,
Password: /^[\w!@#$%^&*.]{6,16}$/,
Mobile: /^1[3|4|5|7|8]\d{9}$/,
RealName: /^[\u4e00-\u9fa5|·]{2,16}$|^[a-zA-Z|\s]{2,20}$/,
BankNum: /^\d{10,19}$/,
Money: /^([1-9]\d*|[0-9]\d*\.\d{1,2}|0)$/,
Answer: /^\S+$/,
Mail: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
}
// 斷言函數
function assert(condition, message) {
if (!condition) {
console.error('[va-warn]:' + message)
}
}
// Rule構造器
function Rule(ruleType, ruleValue, errMsg) {
this.ruleType = ruleType
this.ruleValue = ruleValue
this.errMsg = errMsg || ''
}
//VaForm構造器
function VaForm(el, finalRules, modifiers) {
this.ruleOrder = []
this.rules = {}
this.dom = el
this.value = el.value //值的副本
this.validated = false //是否被驗證過
this.tag = el.getAttribute('tag') //提示的字段名
// this.correctMsg = `${this.tag}輸入正確!`
this.correctMsg = ''
this.modifiers = modifiers //一些特殊的配置
this.noCheck = false //爲true則不要校驗
this.ruleOrder = finalRules.map(item => {
this.rules[item.ruleType] = item
return item.ruleType
})
}
//rules中靠前的配置優先級最高
function mergeRule(...rules) {
var mergeResult = []
var combineArr = Array.prototype.concat.apply([], rules)
var hash = {}
combineArr.forEach((rule) => {
if (hash[rule.ruleType] === undefined) {
mergeResult.push(rule)
hash[rule.ruleType] = mergeResult.length - 1
} else {
var index = hash[rule.ruleType]
Object.assign(mergeResult[index], rule)
}
})
return mergeResult
}
//單個規則的驗證結果
function VaResult(ruleType, ruleValue, isPass, errMsg) {
this.ruleType = ruleType
this.ruleValue = ruleValue
this.isPass = isPass
this.errMsg = errMsg
}
// 顯示結果的構造器
function DisplayResult(isPass, message) {
this.isPass = isPass
this.message = message
}
//單個規則的校驗,或者單個表單的校驗
function validate(field, ruleType) {
assert(field, '未輸入要驗證的字段')
var vaForm = this.forms[field]
var { ruleOrder, rules } = vaForm
if (ruleType === undefined) {
return this.checkForm(vaForm)
} else {
var rule = rules[ruleType] //規則
return this.checkRule(vaForm, rule)
}
// vaForm.validated = true
}
// 得到不一樣的報錯信息
function getErrMsg(vaForm, ruleType, ruleValue) {
var tag = vaForm.tag
var errMsgs = {
NonEmpty: `${tag}不能爲空`,
reg: `${tag}格式錯誤`,
limit: `${tag}必須在${ruleValue[0]}與${ruleValue[1]}之間`,
equal: `兩次${tag}不相同`,
length: `${tag}長度必須在${ruleValue[0]}與${ruleValue[1]}之間`,
unique: `${tag}不能相同`
}
return errMsgs[ruleType]
}
//檢測非空
function checkEmpty(ruleValue, vaForm, va) {
return vaForm.value.trim() ? true : false
}
//檢測正則
function checkReg(ruleValue, vaForm, va) {
return ruleValue.test(vaForm.value) ? true : false
}
//檢測數字區間
function checkLimit(ruleValue, vaForm, va) {
var value = vaForm.value
return ((+value >= ruleValue[0]) && (+value <= ruleValue[1])) ? true : false
}
//檢測相等
function checkEqual(ruleValue, vaForm, va) {
var target = va.forms[ruleValue]
return target.value === vaForm.value ? true : false
}
//檢測字符長度
function checkCharLength(ruleValue, vaForm, va) {
var length = vaForm.value.length
return ((+length >= ruleValue[0]) && (+length <= ruleValue[1])) ? true : false
}
//幾個輸入框要各不相同
function checkUnique(ruleValue, vaForm, va) {
var uniqueGroup = va.uniqueGroup[ruleValue]
var values = uniqueGroup.map(field => va.forms[field].value)
var uniqueValues = values.filter((item, index, arr) => arr.indexOf(item) === index)
return values.length === uniqueValues.length ? true : false
}
// 檢測單個規則
function checkRule(vaForm, rule) {
var forms = this.forms
var { ruleType, ruleValue, errMsg } = rule
//若是有自定義報錯就按自定義報錯,沒有就格式化報錯
errMsg = errMsg || getErrMsg(vaForm, ruleType, ruleValue)
var ruleCheckers = {
NonEmpty: checkEmpty,
reg: checkReg,
limit: checkLimit,
equal: checkEqual,
length: checkCharLength,
unique: checkUnique
}
var ruleChecker = ruleCheckers[ruleType]
var isPass = ruleChecker(ruleValue, vaForm, this)
var vaResult = new VaResult(ruleType, ruleValue, isPass, isPass ? null : errMsg)
return vaResult
}
//檢測單個表單
function checkForm(vaForm) {
var results = vaForm.ruleOrder.map(ruleType => {
var rule = vaForm.rules[ruleType]
return this.checkRule(vaForm, rule)
})
var errIndex = null
for (var i = 0; i < results.length; i++) {
var result = results[i]
if (result.isPass === false) {
errIndex = i
break
}
}
if (errIndex === null) {
return new DisplayResult(true, vaForm.correctMsg)
} else {
return new DisplayResult(false, results[errIndex].errMsg)
}
}
//刷新vaForm中的值的數據
function refreshValue(field, newValue) {
this.forms[field].value = newValue + ''
}
//更新全部表單的值
function refreshAllValue() {
this.fieldOrder.forEach(field => {
var vaForm = this.forms[field]
vaForm.value = vaForm.dom.value
})
}
// 校驗全部的表單,並彈出第一個錯誤。考慮能夠爲空的狀況
function checkAll() {
var firstErr = null
this.fieldOrder.forEach(field => {
var vaForm = this.forms[field]
var canNull = vaForm.ruleOrder.every(ruleType => ruleType !== 'NonEmpty') //輸入框能夠爲空
var noCheckEmpty = (vaForm.value === '' && canNull) //該輸入框能夠爲空,且輸入爲空
if (vaForm.noCheck === false && noCheckEmpty === false) {
var result = this.setVmResult(field)
// var result = this.validate(field)
// this.vmResult[field] = result
// vaForm.validated = true
if (firstErr === null && result.isPass === false) {
firstErr = result.message
}
}
})
return firstErr
}
//驗證單個字段,返回值,並彈出報錯
function setVmResult(field) {
var result = this.validate(field) //本輸入框結果
this.vmResult[field] = result //將報錯彈出
this.forms[field].validated = true //校驗過了
return result
}
// 返回各個表單的值對象
function getValue() {
var dataSet = {}
for (var field in this.forms) {
dataSet[field] = this.forms[field].value
}
return dataSet
}
//添加一個規則
function addRule(field, index, Rule) {
var vaForm = this.forms[field]
vaForm.ruleOrder.splice(index, 0, Rule.ruleType)
vaForm.rules[Rule.ruleType] = Rule
}
// function resetAll(){
// this.fieldOrder.forEach(field=>{
// this.refreshValue(field, '')
// })
// }
// 設置不校驗的表單
function setNoCheck(field, bool) {
this.forms[field].noCheck = bool
}
function createVa(vm, field) {
var va = {
vmResult: vm.va,
fieldOrder: [],
forms: {},
group: {
base: [],
},
equalGroup: {}, //必須相等的字段
uniqueGroup: {}, //必須不一樣的字段
Rule: Rule, //Rule構造器
VaForm: VaForm, //VaForm構造器
validate: validate, //暴露的校驗函數
setVmResult: setVmResult, //校驗並報錯
checkRule: checkRule, //內部的校驗單條規則的函數
checkForm: checkForm, //內部的校驗單個表單的函數
refreshValue: refreshValue, //更新某個表單的值
checkAll: checkAll, //檢查全部的函數
getValue: getValue, //獲取全部表單的當前值,獲得一個對象
setNoCheck: setNoCheck, //設置爲不校驗
addRule: addRule, //給一個表單添加一個規則
refreshAllValue: refreshAllValue //更新全部表單的值
// resetAll: resetAll
}
if (vm.$va) {
return vm.$va
} else {
vm.$va = va
return va
}
}
//v-va:Password.canNull = "[{reg:/^\d{4}$/}]"
//arg = Password, modifiers.canNull = true, value爲後面相關的
//arg用來存字段名, modifiers用來存特殊配置, value爲規則, tag是中文提示名, group 爲分組
var main = {}
main.install = function (_Vue, options) {
Vue = _Vue
Vue.directive('va', {
bind: function (el, binding, vnode) {
var vm = vnode.context //當前的vue實例
var field = binding.arg === 'EXTEND' ? el.getAttribute('name') : binding.arg // 當arg爲EXTEND,從name屬性得到值
var option = binding.modifiers //特殊配置(容許非空,編輯新增共用等)
var value = el.value //輸入框的初始值
var group = el.getAttribute('group') || 'base' //分組,一個表單框在多個組呢?這個還沒設,要兼容。 經過相似 'group1 group2 group3 group4'
var tag = el.getAttribute('tag')
var regMsg = el.getAttribute('regMsg') || '' //針對正則的自定義報錯
var baseRule = [] //默認的校驗規則 --不用寫,默認存在的規則(如非空),優先級最高
var customRule = [] //用戶自定義的規則(組件中) --bingding.value
var optionalRule = [] //配置項中引伸出來的規則,優先級最低
assert(tag, '未設置輸入框的tag')
assert(vm.va, '實例的data選項上,未設置va對象') //實例上若是沒有設置結果則報錯。
assert(field, '未設置輸入框字段')
var va = createVa(vm, field) //單例模式建立va,綁定在vm上
va.fieldOrder.push(field) //字段的檢驗順序
va.group[group].push(field) //分組
var NonEmpty = new Rule('NonEmpty', true, '')
//默認非空
if (option.CanNull === undefined) {
baseRule.push(NonEmpty)
}
//若是regList裏有name對應的,直接就加進optionalConfig
if (regList[field]) {
optionalRule.push(new Rule('reg', regList[field], regMsg))
}
//若是modefiers中的字段有在正則表裏,將其加入optionalRule
var regOptions = Object.keys(option);
for (var i = 0; i < regOptions.length; i++) {
var regOption = regOptions[i]
if (regList[regOptions[i]]) {
optionalRule.push(new Rule('reg', regList[regOption], regMsg))
}
}
//用戶自定義的規則
if (binding.value !== undefined) {
customRule = binding.value.map(item => {
var ruleType = Object.keys(item)[0];
var errMsg = ruleType === 'reg' ? regMsg : ''
return new Rule(ruleType, item[ruleType], errMsg)
})
}
var finalRules = mergeRule(baseRule, optionalRule, customRule)
var hasUniqueRule = false
//對聯合校驗的進行預處理
finalRules.forEach(rule => {
var { ruleType, ruleValue } = rule
if (ruleType === 'equal') {
if (va.equalGroup[ruleValue] === undefined) {
va.equalGroup[ruleValue] = [field]
} else {
va.equalGroup[ruleValue].push(field)
}
}
if (ruleType === 'unique') {
hasUniqueRule = ruleValue
if (va.uniqueGroup[ruleValue] === undefined) {
va.uniqueGroup[ruleValue] = [field]
} else {
va.uniqueGroup[ruleValue].push(field)
}
}
})
var vaForm = new VaForm(el, finalRules, option)
va.forms[field] = vaForm
if (checkWhenChange) {
function validateSingle() {
va.refreshValue(field, el.value) //更新值
//若是容許爲空的此時爲空,不校驗
if (vaForm.value === '' && option.CanNull) {
va.vmResult[field] = {} //若是爲空,把界面顯示上面的提示清掉
return
}
if (vaForm.noCheck === false) {
va.setVmResult(field)
}
var isEqualTarget = false
for (var index in va.equalGroup) {
if (index === field) {
isEqualTarget = true
}
}
//相等框的聯合校驗
if (isEqualTarget) {
va.equalGroup[field].forEach(item => { va.setVmResult(item) })
}
//不一樣框的聯合校驗
if (hasUniqueRule) {
va.uniqueGroup[hasUniqueRule].forEach(item => { va.setVmResult(item) })
}
}
//在change和blur上都綁定了處理事件
el.addEventListener('change', validateSingle)
el.addEventListener('blur', validateSingle)
}
},
})
}
export default main