当前位置:   article > 正文

vite4+vue3:从0到1搭建vben后台管理系统(五)-封装属于自己的form组件_vben从零搭建

vben从零搭建

日常业务开发中的,避免不了的要接触到表单开发,那么我们有必要了解表单的深入使用方法,及封装出适合自己业务的自定义表单组件,效果如下:

不是用ele和antdvue框架,实现自己的表单组件!!!
请添加图片描述

ValidateForm组件封装

<template>
  <div class="validate-form-container">
    <!-- 表单项的区域 -->
    <slot></slot>
    <!-- 底部提交按钮区域 -->

    <div class="sublit-area" @click="formSubmit">
      <slot name="submit">
        <!-- 后备按钮 -->
        <button class="btn primary-btn">提交</button>
      </slot>
    </div>
  </div>
</template>

<script lang="ts" setup>
const formSubmit = () => {
  console.log('test....')
}
</script>
<style scoped></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
 <ValidateForm> </ValidateForm>
  • 1

页面效果
在这里插入图片描述

实现离开input后立即验证有效性

在这里插入图片描述
validateForm组件

<template>
  <div class="validate-form-container">
    <!-- 表单项的区域 -->
    <slot></slot>
    <!-- 底部提交按钮区域 -->

    <div class="sublit-area" @click="formSubmit">
      <slot name="submit">
        <!-- 后备按钮 -->
        <button class="btn btn-primary">提交</button>
      </slot>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { defineEmits } from 'vue'
const emitts = defineEmits(['form-submit'])
const formSubmit = () => {
  console.log('test....')
  emitts('form-submit')
}
</script>
<style scoped></style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

validateInput

<template>
  {{ inputRef.error }}
  <input
    :class="['form-control', { 'is-invalid': inputRef.error }]"
    @blur="validateInput"
    v-bind="$attrs"
    v-model="inputRef.val"
  />
  <div v-if="inputRef.error" class="invalid-feedback">
    {{ inputRef.message }}
  </div>
</template>
<script lang="ts" setup>
import { reactive, computed, defineProps, defineEmits, PropType } from 'vue'
import { RulesHelp } from '../App.vue'
const props = defineProps({
  modelValue: String,
  rules: Array as PropType<RulesHelp>
})

const emitts = defineEmits(['update:modelValue'])

const inputRef = reactive({
  val: computed({
    get: () => props.modelValue || '',
    set: (val) => {
      emitts('update:modelValue', val)
    }
  }),
  error: false,
  message: ''
})
const regRules = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$/
const validateInput = () => {
  if (props.rules) {
    const allPassed = props.rules.every((ele) => {
      let passed = true
      inputRef.message = ele.message
      switch (ele.type) {
        case 'required':
          passed = !!inputRef.val
          break
        case 'email':
          passed = regRules.test(inputRef.val)
          break
        case 'password':
          passed = inputRef.val.length >= 6
          break
        default:
          break
      }
      return passed
    })
    inputRef.error = !allPassed
    return allPassed
  }
  return true
}
</script>
<style lang="less" scoped></style>

## 点击提交按钮后,验证所有的输入框


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

请添加图片描述
完整的源码

app.vue

<template>
  <div class="container-xxl">
    <ValidateForm @form-submit="onFormSubmit">
      <div class="mb-3">
        <label class="form-label">Email address</label>
        <ValidateInput
          type="email"
          :rules="emalRules"
          placeholder="hello email"
          class="py-3"
          v-model="emailVal"
        ></ValidateInput>
      </div>
      <div class="mb-3">
        <label class="form-label">Password</label>
        <ValidateInput
          :rules="passwordRules"
          type="password"
          placeholder="hello pwd"
          class="py-3"
          v-model="passwordVal"
        ></ValidateInput>
      </div>
      <template #submit>
        <button class="btn btn-primary">Submit</button>
      </template>
    </ValidateForm>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, reactive, onMounted } from 'vue'
import ValidateForm from './components/ValidateForm1.vue'
import ValidateInput from './components/ValidateInput1.vue'
import mitt from 'mitt'
interface RuleHelp {
  type: 'required' | 'email' | 'password'
  message: string
}

export const mitts = mitt()
export type RulesHelp = RuleHelp[]
export default defineComponent({
  components: { ValidateForm, ValidateInput },
  setup() {
    const emalRules: RulesHelp = [
      {
        type: 'required',
        message: '邮箱不能为空'
      },
      {
        type: 'email',
        message: '电子邮件格式不正确'
      }
    ]
    const emailVal = ref('')
    const passwordVal = ref()
    const passwordRules: RulesHelp = [
      {
        type: 'required',
        message: '密码不能为空'
      },
      {
        type: 'password',
        message: '密码长度不能少于6位'
      }
    ]
    /*     const regRules = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$/
     */
    const onFormSubmit = (value: boolean) => {
      console.log('999')
      if (value) {
        alert('验证成功')
      } else {
        alert('验证失败')
      }
    }

    return {
      onFormSubmit,
      emalRules,
      emailVal,
      passwordVal,
      passwordRules
    }
  }
})
</script>

<style lang="scss">
@import '~bootstrap/scss/bootstrap';
* {
  margin: 0;
  padding: 0;
}
ul {
  list-style: none;
}
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  // text-align: center;
  color: #2c3e50;
  // margin-top: 60px;
}
.form-text-tip {
  color: red;
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111

validateForm.vue

<template>
  <div class="validate-form-container">
    <!-- 表单项的区域 -->
    <slot></slot>
    <!-- 底部提交按钮区域 -->

    <div class="sublit-area" @click="formSubmit">
      <slot name="submit">
        <!-- 后备按钮 -->
        <button class="btn btn-primary">提交</button>
      </slot>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { defineEmits, onMounted, onUnmounted } from 'vue'
import { mitts } from '../App.vue'
const emitts = defineEmits(['form-submit'])
type FuncType = () => boolean
const formSubmit = () => {
  console.log('test....', funcArr)
  const value = funcArr.map((ele) => ele()).every((result) => result)
  emitts('form-submit', value)
}
let funcArr: FuncType[] = []
const cb = (func: FuncType) => {
  console.log('mitts')
  if (func) {
    funcArr.push(func)
  }
}
/* on事件必须在emit事件之前注册好 */
//子组件是在onMounted挂在
//那么父组件必须在onMounted之前进行监听
mitts.on('form-item-create', cb as FuncType)

onUnmounted(() => {
  mitts.off('form-item-create')
  funcArr = []
})
</script>
<style scoped></style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

validateInput.vue

<template>
  <input
    :class="['form-control', { 'is-invalid': inputRef.error }]"
    @blur="validateInput"
    v-bind="$attrs"
    v-model="inputRef.val"
  />
  <div v-if="inputRef.error" class="invalid-feedback">
    {{ inputRef.message }}
  </div>
</template>

<script lang="ts" setup>
import {
  reactive,
  computed,
  defineProps,
  defineEmits,
  PropType,
  onMounted,
  onUnmounted
} from 'vue'
import { mitts } from '../App.vue'
import { RulesHelp } from '../App.vue'
const props = defineProps({
  modelValue: String,
  rules: Array as PropType<RulesHelp>
})

const emitts = defineEmits(['update:modelValue'])

const inputRef = reactive({
  val: computed({
    get: () => props.modelValue || '',
    set: (val) => {
      emitts('update:modelValue', val)
    }
  }),
  error: false,
  message: ''
})
const regRules = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$/
const validateInput = () => {
  if (props.rules) {
    const allPassed = props.rules.every((ele) => {
      let passed = true
      inputRef.message = ele.message
      switch (ele.type) {
        case 'required':
          passed = !!inputRef.val
          break
        case 'email':
          passed = regRules.test(inputRef.val)
          break
        case 'password':
          passed = inputRef.val.length >= 6
          break
        default:
          break
      }
      return passed
    })
    inputRef.error = !allPassed
    return allPassed
  }
  return true
}

onMounted(() => {
  mitts.emit('form-item-create', validateInput)
})
</script>
<style lang="less" scoped></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

这样就基本实现了一个简单的自定义组件,大家可以尝试下。。。


扩展一个多行文本,textarea

validateInput.vue完整代码

<template>
  {{ inputRef.error }}
  <input
    v-if="tagType === 'input'"
    :class="['form-control', { 'is-invalid': inputRef.error }]"
    @blur="validateInput"
    v-bind="$attrs"
    v-model="inputRef.val"
  />
  <textarea
    v-if="tagType === 'textarea'"
    :class="['form-control', { 'is-invalid': inputRef.error }]"
    @blur="validateInput"
    v-bind="$attrs"
    v-model="inputRef.val"
  />
  <div v-if="inputRef.error" class="invalid-feedback">
    {{ inputRef.message }}
  </div>
</template>

<script lang="ts" setup>
import {
  reactive,
  computed,
  defineProps,
  defineEmits,
  PropType,
  onMounted,
  onUnmounted
} from 'vue'
import { mitts } from '../App.vue'
import { RulesHelp } from '../App.vue'
type TagType = 'input' | 'textarea'
const props = defineProps({
  modelValue: String,
  rules: Array as PropType<RulesHelp>,
  tagType: {
    type: String as PropType<TagType>,
    default: 'input'
  }
})

const emitts = defineEmits(['update:modelValue'])

const inputRef = reactive({
  val: computed({
    get: () => props.modelValue || '',
    set: (val) => {
      emitts('update:modelValue', val)
    }
  }),
  error: false,
  message: ''
})
const regRules = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$/
const validateInput = () => {
  if (props.rules) {
    const allPassed = props.rules.every((ele) => {
      let passed = true
      inputRef.message = ele.message
      switch (ele.type) {
        case 'required':
          passed = !!inputRef.val
          break
        case 'email':
          passed = regRules.test(inputRef.val)
          break
        case 'password':
          passed = inputRef.val.length >= 6
          break
        default:
          break
      }
      return passed
    })
    inputRef.error = !allPassed
    return allPassed
  }
  return true
}

onMounted(() => {
  mitts.emit('form-item-create', validateInput)
})
</script>
<style lang="less" scoped></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

关注我的个人公众号,获取更多前后端开发经验知识

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/862453
推荐阅读
相关标签
  

闽ICP备14008679号