【vue3组件封装】含校验的Input

这篇博客介绍了Vue组件SInput的用法,它是一个带有自定义校验规则的输入框。当失去焦点时,SInput会进行校验,并在输入值不符合规则时显示错误信息。校验规则通过数组形式传递,每个规则包含类型和错误提示。博客中还展示了如何定义和使用各种校验函数,如检查是否为空、是否为IP地址等。此外,还展示了组件的完整代码实现,包括模板、脚本和样式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码:https://gitee.com/chenxiangzhi/components.git
历史文章:

  1. 复选框和单选框
  2. Form和FormItem

效果

  • 失去焦点时会进行校验
  • 更新数据后会传回一个对象,包含namevalue
    在这里插入图片描述

使用

<template>
    <s-input 
        @update="updateForm" 
        name="ip" 
        :value="state.ip"
        :rules="[
           { type: 'required', message: 'ip不能为空' },
           { type: 'ip', message: 'ip格式不对' },
        ]"
    />
</template>


<script lang="ts" setup>
import { reactive } from "vue"
import { SInput } from "@/components"
const state = reactive({
  ip: ""
})
const updateForm = (param) => {
  state[param.name] = param.value
}
</script>

校验规则传的是一个数组,里面每个元素包含type校验种类与message校验失败时的提示说明。每个type校验种类将对应于一个校验函数,这由自己自定义。

Types

// 属性
type InputProp={
  type: string,				// 输入框类型
  name: string,
  value: string|number|boolean,
  placeholder?: string,
  rules?: RuleProp[],			// 校验规则
  inputStyle: Object			// 输入框样式
}

// 校验类型:每个校验类型会对应一个校验函数。
type RuleKeys = "required" | "ip" | "port" | "range"; 
interface IRuleStrategies {
   [index in RuleKeys]: (...params: any[]) => boolean;
   [index: string]: (...params: any[]) => boolean;
}

// 校验对象
interface RuleProp {
   type: RuleKeys;
   message: string;
}

SInput

<template>
    <div class="input-container">
        <input
            :type="type"
            class="input-control"
            @blur="testError"
            :value="value"
            @input="input"
            :class="{ 'is-invalid': inputRef.error }"
            :placeholder="placeholder"
            :style="{...inputStyle }"
        />
        <div v-if="inputRef.error" class="error-text">
            {{ inputRef.message }}
        </div>
    </div>
</template>

<script lang="ts" setup>
import { PropType, reactive } from "vue"
import { RuleProp } from "@types"
import { ruleStrategies } from "@/utils"

const emitFn = defineEmits(["update"])
const props = defineProps({
  type: {
    type: String,
    default: "text"
  },
  name: String,
  value: [String, Number, Boolean],
  placeholder: String,
  rules: Array as PropType<RuleProp[]>,
  inputStyle: Object
})

// 响应式错误信息
const inputRef = reactive({
  error: false,
  message: ""
})

// 检查错误
const testError = () => {
  if (props.rules) {
   // 对所有校验规则进行校验,只要有一个不通过就为不通过
    const allPassed = props.rules.every((rule: RuleProp) => {
      let passed = true
      // 首先将错误说明赋值给响应式对象inputRef
      inputRef.message = rule.message
     	// 接着在校验函数中找到对应的校验函数进行校验
      passed = ruleStrategies[rule.type](props.value) // 根据校验类型判断是否通过
      return passed
    })
    // 主要有不通过的,就会显示错误信息
    inputRef.error = !allPassed
  }
}

// 传回数据
const input = (e: Event) => {
  const targetValue = (e.target as HTMLInputElement).value
  emitFn("update", { name: props.name, value: targetValue })  // 传回一个对象
}
</script>

<style lang="less">
.input-container {
   .input-control{
      padding: 4px 10px;
      border-radius: @smallRadius;
      border: 1px solid @borderColor;
      align-items: center;
      font-size: 1em;
      &:focus{
         border: 1px solid @activeColor;
         outline: 2px solid @mainColor;
      }
   }
   .error-text{
      padding:6px 0 0 1px;
      color:@danger;
      text-align:left;
   }
}
</style>

校验函数

import {IRuleStrategies} from '@types'

// 判断是否为空
const isRequired=(val:string):boolean=>{
    return val!== ""
}

// 判断是否为整数
const isInteger=(value:any)=>{
    return Number.isInteger(Number(value))
}

// 判断是否为ip
const isIp=(ip:string):boolean=>{
    var rep = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
    return rep.test(ip)
}

// 判断端口是否合法
const isPort=(port:number):boolean=>{
    if(!isInteger(port)||65535<port||port<1){
        return false;
    }  
    return true;
}

// 判断整数范围
const isInRange:number,max:number=20,min:number=-10):boolean=>{
    if(!isInteger(value)||value>max||min<-10){
        return false
    }
    return true
}

// 导出校验策略
export const ruleStrategies:IRuleStrategies={
    "required":isRequired,
    "ip":isIp,
    "port":isPort,
    "range":isInRange
}
<think>好的,我现在需要帮助用户了解如何使用Ant Design Vue封装一个可复用的Input组件。首先,我得回忆一下Ant Design Vue的基本用法,特别是表单组件的部分。用户可能已经用过原生的a-input,但想要进行二次封装以增加功能或统一样式。 根据用户提供的引用内容,特别是引用[2]提到的Vue3组件的二次封装,我需要考虑如何利用Ant Design Vue的现有组件进行扩展。首先,确定用户的需求:可复用、支持验证、可定制样式和功能。可能需要使用v-model来处理双向绑定,这是Vue组件通信的关键部分。 接下来,我应该考虑如何暴露Ant Design Vue的原有属性,比如placeholder、allowClear等,这样用户在使用封装后的组件时可以灵活配置。使用v-bind="$attrs"可以传递这些属性,同时避免在props中逐个声明,这样更高效。 然后,验证功能是表单组件的重要部分。Ant Design Vue的Form组件通常配合使用验证规则,所以封装后的Input需要能够集成这些规则。可能需要使用provide/inject或者context.slots来传递验证状态,比如是否校验成功或失败,并显示相应的图标。 另外,用户可能希望自定义前缀和后缀,比如添加搜索图标或清除按钮。这可以通过插槽来实现,比如在组件中定义prefix和suffix插槽,让父组件可以插入自定义内容。 样式统一也是一个重点,比如定义全局的class,这样所有封装Input组件都能保持一致的样式。同时,暴露class和style的prop,允许用户在特定情况下覆盖样式。 在代码结构方面,应该使用setup语法糖,因为用户提到的是Vue3,这样代码更简洁。同时,使用defineProps和defineEmits来定义props和事件,确保类型检查和正确的父子通信。 最后,需要考虑组件的使用示例,展示如何传递属性、验证规则以及使用插槽。这帮助用户更好地理解如何在实际项目中应用封装后的组件。 可能还需要考虑性能优化,比如避免不必要的渲染,使用v-model的修饰符处理输入值,比如.trim自动去除首尾空格。同时,处理事件,如blur和change,确保与表单验证的整合。 总结步骤:定义props,处理v-model,暴露属性,集成验证状态,使用插槽,统一样式,提供使用示例和相关问题。同时,参考用户提供的引用内容中的思路,比如全局挂载或配置式封装,但这里可能不需要全局挂载,而是局部封装以便更灵活地复用。</think>### 基于 Ant Design Vue 封装可复用 Input 组件的最佳实践 #### 1. **组件基础结构设计** - **核心目标**:继承 Ant Design Vue 的 `a-input` 基础功能,增加统一校验规则、样式定制、扩展插槽等能力 - **关键技术点**: - 使用 `v-model` 实现双向绑定 - 通过 `v-bind="$attrs"` 继承原生属性 - 集成表单验证状态反馈 - 提供可扩展插槽 ```vue <template> <div class="custom-input-wrapper"> <a-input v-model="innerValue" v-bind="$attrs" :class="[customClass, { &#39;error-state&#39;: isError }]" :style="customStyle" @blur="handleBlur" > <template v-if="$slots.prefix" #prefix> <slot name="prefix" /> </template> <template v-if="$slots.suffix" #suffix> <slot name="suffix" /> </template> </a-input> <div v-if="errorMessage" class="error-message"> {{ errorMessage }} </div> </div> </template> ``` #### 2. **逻辑实现** ```vue <script setup> import { ref, watch, defineProps, defineEmits } from &#39;vue&#39; const props = defineProps({ modelValue: { type: [String, Number], default: &#39;&#39; }, rules: { type: Array, default: () => [] }, // 校验规则数组 customClass: String, // 自定义类名 customStyle: Object // 自定义样式对象 }) const emit = defineEmits([&#39;update:modelValue&#39;, &#39;validate&#39;]) const innerValue = ref(props.modelValue) const isError = ref(false) const errorMessage = ref(&#39;&#39;) // 同步外部值变化 watch(() => props.modelValue, (newVal) => { innerValue.value = newVal }) // 实现双向绑定 watch(innerValue, (newVal) => { emit(&#39;update:modelValue&#39;, newVal) }) // 校验逻辑 const validate = () => { for (const rule of props.rules) { if (rule.required && !innerValue.value?.trim()) { isError.value = true errorMessage.value = rule.message || &#39;该字段为必填项&#39; return false } if (rule.pattern && !rule.pattern.test(innerValue.value)) { isError.value = true errorMessage.value = rule.message || &#39;格式不正确&#39; return false } } isError.value = false errorMessage.value = &#39;&#39; return true } const handleBlur = () => { const isValid = validate() emit(&#39;validate&#39;, isValid) } </script> ``` #### 3. **样式优化** ```css <style scoped> .custom-input-wrapper { position: relative; margin-bottom: 8px; } .error-state :deep(.ant-input) { border-color: #ff4d4f; box-shadow: 0 0 0 2px rgba(255,77,79,.2); } .error-message { color: #ff4d4f; font-size: 12px; margin-top: 4px; } </style> ``` #### 4. **使用示例** ```vue <template> <CustomInput v-model="searchValue" placeholder="请输入搜索内容" allow-clear :rules="[ { required: true, message: &#39;搜索内容不能为空&#39; }, { pattern: /^[a-zA-Z0-9]+$/, message: &#39;仅支持英文数字&#39; } ]" > <template #prefix> <search-outlined /> </template> </CustomInput> </template> ``` ### 关键设计原则[^2] 1. **属性继承**:通过 `v-bind="$attrs"` 继承所有原生 Input 属性 2. **验证解耦**:将校验规则通过 props 传入,保持组件通用性 3. **样式隔离**:使用 `:deep()` 穿透作用域样式,保持 Ant Design 原有样式体系 4. **插槽扩展**:保留原组件插槽机制,支持自定义前后缀内容 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值