当前位置: 首页 > article >正文

vue3+Element-plus el-input 输入框组件二次封装(支持金额、整数、电话、小数、身份证、小数点位数控制,金额显示中文提示等功能)

一、效果图

在这里插入图片描述

二、组件集成了以下功能

1、输入金额--支持千分号显示、可设置`decimalLimit`来调整小数点位数
2、金额鼠标移入提示中文--标签添加`isTip`开启中文提示则不允许开启千分号显示`showThousands`
3、输入手机号--设置`inputType=phone`
4、输入整数---设置`inputType=integer`
5、输入数字(含小数点)---设置`inputType=decimal`
6、输入身份证号---设置`inputType=idCard`
7、格式化输入内容--`formatter`的情况下显示值,我们通常同时使用 `parser`
8、支持el-input的所有功能

三、参数配置

1、代码示例:

<t-input v-model="inputVlaue" />

2、配置参数(Attributes)继承 el-input Attributes

参数说明类型默认值
v-model绑定值string-
placeholderplaceholder提示语string‘请输入’
decimalLimit小数点位数 (小数、金额类型时生效)Number2
appendTitle插槽append显示文案(金额类型时生效)string‘元’
showThousands是否显示千分号(小数、金额类型时生效)Booleanfalse
isTip是否提示金额中文(金额类型时生效)Booleanfalse
inputType特性类型标注(文字:text,金额:amount,电话:phone,整数:integer,小数:decimal,身份证:idCard’)stringtext

3、继承 el-input 事件、插槽、方法

四、源码

<template>
  <el-tooltip effect="dark" placement="bottom-start" v-if="isTip && !showThousands">
    <template #content>
      {{ currencyFilter(modelValue) }}<br />
      {{ digitUppercase(modelValue) }}
    </template>
    <el-input
      v-model="internalValue"
      v-bind="{ placeholder, clearable: true, ...$attrs }"
      @blur="handleBlur"
    >
      <template v-for="(index, name) in slots" v-slot:[name]="data">
        <slot :name="name" v-bind="data" />
      </template>
      <template #append v-if="$slots.append || inputType === 'amount'">
        <span v-if="inputType === 'amount'">{{ appendTitle }}</span>
        <slot v-else name="append" />
      </template>
    </el-input>
  </el-tooltip>
  <el-input
    v-model="internalValue"
    v-bind="{ placeholder, clearable: true, ...$attrs }"
    @blur="handleBlur"
    v-else
  >
    <template v-for="(index, name) in slots" v-slot:[name]="data">
      <slot :name="name" v-bind="data" />
    </template>
    <template #append v-if="$slots.append || inputType === 'amount'">
      <span v-if="inputType === 'amount'">{{ appendTitle }}</span>
      <slot v-else name="append" />
    </template>
  </el-input>
</template>

<script setup lang="ts" name="TInput">
import { ElMessage } from "element-plus"
import { computed, useSlots } from "vue"
const props = defineProps({
  modelValue: {
    type: [String, Number],
    default: ""
  },
  placeholder: {
    type: String,
    default: "请输入"
  },
  // 小数、金额类型时,小数点后最多几位
  decimalLimit: {
    type: Number,
    default: 2
  },
  // inputType含有文字:text、金额:amount、电话:phone、整数:integer、小数:decimal、身份证:idCard类型
  inputType: {
    type: String,
    default: "text"
  },
  appendTitle: {
    type: String,
    default: "元"
  },
  // 是否显示千分号
  showThousands: {
    type: Boolean,
    default: false
  },
  // 是否显示金额中文提示
  isTip: {
    type: Boolean,
    default: false
  }
})
const emits = defineEmits(["update:modelValue"])
const slots = useSlots()
let internalValue = computed({
  get() {
    return props.modelValue
  },
  set(val) {
    // console.log(777, val)
    emits("update:modelValue", val)
  }
})

const handleBlur = () => {
  let formattedValue = internalValue.value

  const formatValue = (value, formatter) => {
    if (formatter) {
      return formatter(value)
    }
    return value
  }

  switch (props.inputType) {
    case "amount":
    case "decimal":
      formattedValue = formatValue(Number(internalValue.value), value =>
        formatAmount(value, props.decimalLimit)
      )
      break
    case "phone":
      formattedValue = formatValue(internalValue.value.toString(), validatePhone)
      break
    case "integer":
      formattedValue = formatValue(internalValue.value.toString(), validateInteger)
      break
    case "idCard":
      formattedValue = formatValue(internalValue.value.toString(), validateIdCard)
      break
    default:
      formattedValue = internalValue.value
  }

  internalValue.value = formattedValue
}

// 手机号码校验
const validatePhone = (value: string) => {
  const phoneReg = /^1[3456789]\d{9}$/
  if (phoneReg.test(value as string)) {
    return value
  } else {
    ElMessage.error("请输入正确的手机号码")
    return ""
  }
}
// 身份证号码校验
const validateIdCard = (value: string) => {
  const idCardReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/
  if (idCardReg.test(value as string)) {
    return value
  } else {
    ElMessage.error("请输入正确的身份证号码")
    return ""
  }
}
// 整数校验
const validateInteger = (value: string) => {
  const integerReg = /^\d+$/
  if (integerReg.test(value as string)) {
    return value
  } else {
    ElMessage.error("请输入正确的整数")
    return ""
  }
}
// 小数、金额类型转换
const formatAmount = (value: number, decimalLimit: number) => {
  if (!value) {
    ElMessage.error(`请输入正确的${props.inputType == "amount" ? "金额" : "数字"}`)
    return ""
  }
  // 格式化千分号
  if (props.showThousands) {
    const val = value
      .toFixed(decimalLimit)
      .toString()
      .replace(/\B(?=(\d{3})+(?!\d))/g, ",")
    return val
  } else {
    return value.toFixed(decimalLimit)
  }
}
/**
 * 数字金额格式过滤 10000 => "¥10,000.00"
 * @param {number} num 被转换数字
 * @param {number} n 保留小数位
 */
const currencyFilter = (num: any, n: number = 2) => {
  const reg = /((^[1-9]\d*)|^0)(\.\d+)?$/
  if (!reg.test(num)) {
    return ""
  } else {
    n = n > 0 && n <= 20 ? n : 2
    if (num || num === 0) {
      num = parseFloat((num + "").replace(/^\d\.-/g, "")).toFixed(n) + ""
      const l = num.split(".")[0].split("").reverse()
      const r = num.split(".")[1]
      let t = ""
      for (let i = 0; i < l.length; i++) {
        t += l[i] + ((i + 1) % 3 === 0 && i + 1 !== l.length ? "," : "")
      }
      return num ? "¥ " + t.split("").reverse().join("") + "." + r : ""
    } else {
      return ""
    }
  }
}
/**
 * 数字金额格式过滤(转汉字大写) 12000.34 => "壹万贰千叁角肆分"
 * @param {number} num 被转换数字
 */
const digitUppercase = (num: any) => {
  const reg = /((^[1-9]\d*)|^0)(\.\d{0,2}){0,1}$/
  if (!reg.test(num)) {
    return "请输入正确的金额格式"
  } else {
    let fraction = ["角", "分"]
    let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"]
    let unit = [
      ["元", "万", "亿", "兆"],
      ["", "拾", "佰", "仟"]
    ]
    let head = num < 0 ? "欠" : ""
    num = Math.abs(num)
    let s = ""
    fraction.forEach((item, index) => {
      s += (digit[Math.floor(num * 10 * Math.pow(10, index)) % 10] + item).replace(/零./, "")
    })
    s = s || "整"
    num = Math.floor(num)
    for (let i = 0; i < unit[0].length && num > 0; i++) {
      let p = ""
      for (let j = 0; j < unit[1].length && num > 0; j++) {
        p = digit[num % 10] + unit[1][j] + p
        num = Math.floor(num / 10)
      }
      s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s
    }
    return (
      head +
      s
        .replace(/(零.)*零元/, "元")
        .replace(/(零.)+/g, "零")
        .replace(/^整$/, "零元整")
    )
  }
}
</script>

四、组件地址

gitHub组件地址

gitee码云组件地址

五、相关文章

基于ElementUi&antdUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档


http://www.kler.cn/a/318160.html

相关文章:

  • React第十八节 useEffect 用法使用技巧注意事项详解
  • VSCode 性能优化指南:提高编码效率,减少资源占用
  • 运动控制卡网络通讯的心跳检测之C#上位机编程
  • Scala_【1】概述
  • MacOS M3源代码编译Qt6.8.1
  • 重生之我在异世界学编程之C语言:深入预处理篇(上)
  • rust属性宏
  • HTML段落,换行,水平线标签与其属性
  • c/c++八股文
  • MySQL 生产环境性能优化
  • 使用分布式调度框架时需要考虑的问题——详解
  • python 实现 P-Series algorithm算法
  • Seamless:Facebook推出的跨语言语音识别/翻译/合成大模型
  • 计算总体方差statistics.pvariance()
  • 通信工程学习:什么是VNF虚拟网络功能
  • 海思Hi3559av100 sdk开发环境搭建
  • 面试金典题2.3
  • 引用和指针的区别
  • canvas绘制线段、矩形、圆形、文字、贝塞尔曲线、图像、视频处理、线性渐变、径向渐变、坐标变化,旋转,缩放,图形移动
  • 使用数据基础描述进行连续变量的特征提取
  • MySQL数据库索引、事务和存储引擎管理
  • Java基础知识扫盲
  • 代码随想录Day 53|题目:110. 字符串接龙、105.有向图的完全可达性、106. 岛屿的周长
  • Taro多端统一开发解决方案
  • 深入理解LLM的可观测性
  • 31. RabbitMQ顺序消费