input 封装
// components/ValidateInput.vue
<template>
<div class="validate-input-container pb-3">
<input
v-if="tag !== 'textarea'"
class="form-control"
:class="{'is-invalid': inputRef.error}"
@blur="validateInput"
v-model="inputRef.val"
v-bind="$attrs"
>
<textarea
v-else
class="form-control"
:class="{'is-invalid': inputRef.error}"
@blur="validateInput"
v-model="inputRef.val"
v-bind="$attrs"
>
</textarea>
<span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, computed } from 'vue'
import { emitter } from './ValidateForm.vue'
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
interface RuleProp {
type: 'required' | 'email' | 'custom';
message: string;
validator?: () => boolean;
}
export type RulesProp = RuleProp[]
export type TagType = 'input' | 'textarea'
export default defineComponent({
props: {
rules: Array as PropType<RulesProp>,
modelValue: String,
tag: {
type: String as PropType<TagType>,
default: 'input'
}
},
inheritAttrs: false,
setup(props, context) {
const inputRef = reactive({
val: computed({
get: () => props.modelValue || '',
set: val => {
context.emit('update:modelValue', val)
}
}),
error: false,
message: ''
})
const validateInput = () => {
if (props.rules) {
const allPassed = props.rules.every(rule => {
let passed = true
inputRef.message = rule.message
switch (rule.type) {
case 'required':
passed = (inputRef.val.trim() !== '')
break
case 'email':
passed = emailReg.test(inputRef.val)
break
case 'custom':
passed = rule.validator ? rule.validator() : true
break
default:
break
}
return passed
})
inputRef.error = !allPassed
return allPassed
}
return true
}
onMounted(() => {
emitter.emit('form-item-created', validateInput)
})
return {
inputRef,
validateInput
}
}
})
</script>
form封装
// components/ValidateForm.vue
<template>
<form class="validate-form-container">
<slot name="default"></slot>
<div class="submit-area" @click.prevent="submitForm">
<slot name="submit">
<button type="submit" class="btn btn-primary">提交</button>
</slot>
</div>
</form>
</template>
<script lang="ts">
import { defineComponent, onUnmounted } from 'vue'
import mitt from 'mitt'
type ValidateFunc = () => boolean
export const emitter = mitt()
export default defineComponent({
emits: ['form-submit'],
setup(props, context) {
let funcArr: ValidateFunc[] = []
const submitForm = () => {
const result = funcArr.map(func => func()).every(result => result)
context.emit('form-submit', result)
}
const callback = (func?: ValidateFunc) => {
if (func) {
funcArr.push(func)
}
}
emitter.on('form-item-created', callback)
onUnmounted(() => {
emitter.off('form-item-created', callback)
funcArr = []
})
return {
submitForm
}
}
})
</script>
在登录页面中使用
<template>
<div class="login-page mx-auto p-3 w-330">
<validate-form @form-submit="onFormSubmit">
<div class="mb-3">
<label class="form-label">邮箱地址</label>
<validate-input
:rules="emailRules" v-model="emailVal"
placeholder="请输入邮箱地址"
type="text"
ref="inputRef"
/>
</div>
<div class="mb-3">
<label class="form-label">密码</label>
<validate-input
type="password"
placeholder="请输入密码"
:rules="passwordRules"
v-model="passwordVal"
/>
</div>
<template #submit>
<button type="submit" class="btn btn-primary btn-block btn-large">登录</button>
</template>
</validate-form>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
import ValidateInput, { RulesProp } from '../components/ValidateInput.vue'
import ValidateForm from '../components/ValidateForm.vue'
import createMessage from '../components/createMessage'
export default defineComponent({
name: 'Login',
components: {
ValidateInput,
ValidateForm
},
setup() {
const emailVal = ref('')
const router = useRouter()
const store = useStore()
const emailRules: RulesProp = [
{ type: 'required', message: '电子邮箱地址不能为空' },
{ type: 'email', message: '请输入正确的电子邮箱格式' }
]
const passwordVal = ref('')
const passwordRules: RulesProp = [
{ type: 'required', message: '密码不能为空' }
]
const onFormSubmit = (result: boolean) => {
if (result) {
const payload = {
email: emailVal.value,
password: passwordVal.value
}
store.dispatch('loginAndFetch', payload).then(data => {
createMessage('登录成功 2秒后跳转首页', 'success')
setTimeout(() => {
router.push('/')
}, 2000)
}).catch(e => {
console.log(e)
})
}
}
return {
emailRules,
emailVal,
passwordVal,
passwordRules,
onFormSubmit
}
}
})
</script>