uniapp微信小程序基于wu-input二次封装TInput组件(支持点击下拉选择、支持整数、电话、小数、身份证、小数点位数控制功能)

news2025/5/17 16:44:25

一、 最终效果

在这里插入图片描述

二、实现了功能

1、支持输入正整数---设置`specifyType=integer`
2、支持输入数字(含小数点)---设置`specifyType=decimal`,可设置`decimalLimit`来调整小数点位数
3、支持输入手机号--设置`specifyType=phone`
4、支持输入身份证号---设置`specifyType=idCard`
4、支持失焦后校验错误提示
5、支持title自定义或者slotLabel插槽
6、支持inputType输入框类型可选值:input/select 
7、支持自带下拉选择

三、TInput 参数配置

1、代码示例:

<TInput v-model="value" titlt="标题" />

2. 配置参数(Attributes)继承wu-input及wu-popup的所有参数事件

参数说明类型默认值
v-modelinput绑定值String /number-
v-model:select-valueinputType:select 的绑定值String /number-
titlelabel标题String-
inputType输入框类型可选值:input/selectStringinput
required是否必填红点标识booleanfalse
decimalLimit小数点后保留几位number2
specifyType指定输入类型"text"/“decimal”/“phone” /“integer” / “idCard”Stringtext
showThousands是否显示千分位booleanfalse
isShowErrorTip输入框失焦是否提示错误信息booleanfalse
isLink是否展示右侧箭头(inputType=select自动展示)booleanfalse
iconArrow右侧箭头图标String‘arrow-right’
iconArrowColor右侧箭头颜色String#969799
isColonlabel标题是否显示冒号booleantrue
isShowBorder是否显示下边框booleanfalse
listinputType=select自带下拉数据源array[]
customKey列表项自定义keyStringkey
customLabel列表项自定义labelstringlabel
popupTitle自带下拉框标题显示string-
isPopupTitleBorder自带下拉框标题与列表边框显示booleanfalse

3. Events

事件名说明返回值
clickInputlist没有设置时点击input触发-

4. Slot

事件名说明
slotInput右侧input框插槽
slotLabel左侧label插槽
prefixinput前缀插槽
suffixinput后缀插槽

四、源码

<template>
  <view class="t_input">
    <view class="list-call" :class="{ is_border: isShowBorder }">
      <view class="t_input_title" v-if="isShow('slotLabel')">
        <slot name="slotLabel" />
      </view>
      <view class="t_input_title" v-else>
        <view class="t_input_required" v-if="required">*</view>
        {{ title }}<text v-if="isColon">: </text>
      </view>
      <view class="t_input_value_slot" v-if="isShow('slotInput')">
        <slot name="slotInput" />
      </view>
      <view class="t_input_value" v-else @click="openPopup">
        <wu-input v-bind="fieldAttrs" v-model="selectValue" @blur="handleBlur" v-if="inputType === 'input'">
          <template #prefix>
            <slot name="prefix" />
          </template>
          <template #suffix>
            <slot name="suffix" />
          </template>
        </wu-input>
        <wu-input v-bind="fieldAttrs" v-model="finallySelectLabel" v-else>
          <template #prefix>
            <slot name="prefix" />
          </template>
          <template #suffix>
            <slot name="suffix" />
          </template>
        </wu-input>
        <wu-icon :name="iconArrow" :color="iconArrowColor" v-if="isLink || inputType === 'select'"></wu-icon>
      </view>
    </view>
    <wu-popup ref="popup" v-bind="{ closeable: true, mode: 'bottom', round: 15, ...attrs }">
      <view class="t_input_popup-list">
        <view class="t_input_popup-title" :class="{ 't_input_popup-title-border': isPopupTitleBorder }">
          {{ popupTitle }}
        </view>
        <view
          class="t_input_popup-item"
          v-for="(item, index) in list"
          :key="index"
          @click="selectItem(item)"
          :class="{ is_active: selectLabel === item[props.customLabel] }"
        >
          {{ item[props.customLabel] }}
        </view>
      </view>
    </wu-popup>
  </view>
</template>

<script lang="ts" setup>
import { ref, computed, useAttrs, useSlots } from "vue";
// 定义 ListItem 接口
interface ListItem {
  [key: string]: string | number; // 支持任意字符串键
  customKey: string | number;
  customLabel: string;
}
interface TInputProps {
  title?: string;
  inputType: "input" | "select";
  decimalLimit?: number; // 小数点后保留几位
  specifyType?: "text" | "decimal" | "phone" | "integer" | "idCard"; // 指定输入类型
  showThousands?: boolean; // 是否显示千分位
  isShowErrorTip?: boolean; // 是否显示错误提示
  required?: boolean; // 是否必填
  isLink?: boolean; // 是否展示右侧箭头
  iconArrow?: string; // 右侧箭头图标
  iconArrowColor?: string; // 右侧箭头颜色
  isColon?: boolean; // 是否显示冒号
  isShowBorder?: boolean; // 是否显示下边框
  list?: ListItem[]; // 列表项
  customKey?: string; // 列表项的key
  customLabel?: string; // 列表项的label
  popupTitle?: string; // 弹出框标题
  isPopupTitleBorder?: boolean; // 是否显示弹出框title的边框
  modelValue?: string | number; // type:input 的绑定值
  selectValue?: string | number; // type:select 的绑定值
}
const props = withDefaults(defineProps<TInputProps>(), {
  title: "",
  inputType: "input",
  specifyType: "text",
  showThousands: false,
  isShowErrorTip: false,
  decimalLimit: 2,
  required: false,
  list: () => [] as ListItem[],
  customKey: "key",
  customLabel: "label",
  popupTitle: "",
  isPopupTitleBorder: false,
  isLink: false,
  isShowBorder: true,
  isColon: true,
  iconArrow: "arrow-right",
  iconArrowColor: "#969799",
  modelValue: "",
  selectValue: ""
});

const emit = defineEmits<{
  (event: "update:modelValue", value: string | number): void;
  (event: "update:selectValue", value: string | number): void;
  (event: "clickInput"): void;
}>();
const attrs = useAttrs();
const popup = ref<null | any>(null);
const slots = useSlots();

const isShow = (name: string) => {
  return Object.keys(slots).includes(name);
};

const selectLabel = ref<string | number>("");

const selectValue = computed({
  get: () => props.modelValue,
  set: (val: string | number) => {
    emit("update:modelValue", val);
  }
});
const selectModelLabel = computed({
  get: () => props.selectValue,
  set: (val: string | number) => {
    emit("update:selectValue", val);
  }
});
const finallySelectLabel = computed(() => {
  if (props?.list?.length > 0) {
    return selectLabel.value;
  } else {
    return selectModelLabel.value;
  }
});
const handleBlur = () => {
  let formattedValue = selectValue.value;

  const formatValue = (value: any, formatter: (val: any) => any) => {
    if (formatter) {
      return formatter(value);
    }
    return value;
  };

  switch (props.specifyType) {
    case "decimal": // 小数点后保留几位
      formattedValue = formatValue(Number(selectValue.value), value =>
        formatDecimal(value, props.decimalLimit)
      );
      break;
    case "phone": // 手机号码
      formattedValue = formatValue(selectValue.value.toString(), validatePhone);
      break;
    case "integer": // 整数
      formattedValue = formatValue(selectValue.value.toString(), validateInteger);
      break;
    case "idCard": // 身份证号码
      formattedValue = formatValue(selectValue.value.toString(), validateIdCard);
      break;
    default: // 默认处理
      formattedValue = selectValue.value;
  }

  selectValue.value = formattedValue;
};
// 手机号码校验
const validatePhone = (value: string) => {
  const phoneReg = /^1[3456789]\d{9}$/;
  if (phoneReg.test(value)) {
    return value;
  } else {
    props.isShowErrorTip &&
      uni.showToast({
        title: "请输入正确的手机号码",
        icon: "none"
      });
    return "";
  }
};
// 身份证号码校验
const validateIdCard = (value: string) => {
  const idCardReg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
  if (idCardReg.test(value)) {
    return value;
  } else {
    props.isShowErrorTip &&
      uni.showToast({
        title: "请输入正确的身份证号码",
        icon: "none"
      });
    return "";
  }
};
// 整数校验
const validateInteger = (value: string) => {
  const integerReg = /^\d+$/;
  if (integerReg.test(value)) {
    return value;
  } else {
    props.isShowErrorTip &&
      uni.showToast({
        title: "请输入正确的整数",
        icon: "none"
      });
    return "";
  }
};
// 小数转换
const formatDecimal = (value: number, decimalLimit: number) => {
  if (!value) {
    props.isShowErrorTip &&
      uni.showToast({
        title: "请输入正确的数字",
        icon: "none"
      });
    return "";
  }
  // 格式化千分号
  if (props.showThousands) {
    const val = value
      .toFixed(decimalLimit)
      .toString()
      .replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    return val;
  } else {
    return value.toFixed(decimalLimit);
  }
};
// 计算属性 fieldAttrs
const fieldAttrs = computed(() => ({
  border: "none",
  placeholder: props.inputType === "select" ? `请选择${props.title}` : `请输入${props.title}`,
  readonly: props.inputType === "select",
  type: "text",
  inputAlign: "right",
  clearable: true,
  ...attrs
}));

// 选择列表项
const selectItem = (item: ListItem) => {
  if (props.customLabel && item[props.customLabel]) {
    selectLabel.value = item[props.customLabel];
    emit("update:modelValue", item[props.customKey]);
    popup.value?.close();
  } else {
    console.error("Invalid customLabel or item:", props.customLabel, item);
  }
};

// 打开弹窗
const openPopup = () => {
  if (props.inputType === "select" && props.list.length > 0) {
    popup.value?.open();
  } else {
    emit("clickInput");
  }
};
</script>

<style lang="scss" scoped>
.t_input {
  .list-call {
    display: flex;
    height: 54px;
    align-items: center;
    padding: 0 10px;
    &.is_border {
      border-bottom: 1px solid #eee;
    }
    .t_input_title {
      display: flex;
      height: inherit;
      align-items: center;
      .t_input_required {
        color: red;
      }
    }
    .t_input_value {
      flex: 1;
      display: flex;
      height: inherit;
      align-items: center;
      justify-content: flex-end;
    }
    .t_input_value_slot {
      display: flex;
      height: inherit;
      align-items: center;
      justify-content: flex-end;
    }
  }

  .t_input_popup-list {
    .t_input_popup-title {
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 16px;
      color: #101010;
      height: 44px;
      font-weight: bold;
      &.t_input_popup-title-border {
        border-bottom: 1px solid #eee;
      }
    }
    .t_input_popup-item {
      text-align: center;
      line-height: 45px;
      &.is_active {
        background-color: #f0f0f0;
      }
    }
  }
}
</style>



相关文章

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


Vue3+Vite+Ts+Pinia+Qiankun后台管理系统


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode-419.棋盘上的战舰

leetcode-419.棋盘上的战舰 文章目录 leetcode-419.棋盘上的战舰一.题目描述二.第一次代码提交三.第二次代码提交 一.题目描述 二.第一次代码提交 class Solution { public:int countBattleships(vector<vector<char>>& board) {int m board.size(); //列数i…

使用uglifyjs对静态引入的js文件进行压缩

前言 因为有时候js文件没有npm包&#xff0c;或者需要修改&#xff0c;只能引入静态的js&#xff0c;那么这个时候就可以对js进行压缩了。我其实想通过vite、webpack等插件进行压缩的&#xff0c;可是他都不能定位到public目录下面的文件&#xff0c;所以我只能自己压缩了。编…

程序加壳脱壳原理和实现

理论 一个可运行的执行文件&#xff0c;至少会有一个代码段&#xff0c;程序的入口点指向代码段&#xff0c;程序运行的时候&#xff0c;从入口点开始执行代码段指令 为了将一个正常的程序进行加壳保护&#xff0c;至少要三部分逻辑配合 1、待加壳保护的程序 2、加壳逻辑 3…

【数据分析实战】使用 Matplotlib 绘制折线图

1、简述 在日常的数据分析、科研报告、项目可视化展示中&#xff0c;折线图是一种非常常见且直观的数据可视化方式。本文将带你快速上手 Matplotlib&#xff0c;并通过几个实际例子掌握折线图的绘制方法。 Matplotlib 是 Python 中最常用的数据可视化库之一&#xff0c;它能够…

数据仓库标准库模型架构相关概念浅讲

数据仓库与模型体系及相关概念 数据仓库与数据库的区别可参考&#xff1a;数据库与数据仓库的区别及关系_数据仓库和数据库-CSDN博客 总之&#xff0c;数据库是为捕获数据而设计&#xff0c;数据仓库是为分析数据而设计 数据仓库集成工具 在一些大厂中&#xff0c;其会有自…

亚洲区域健康人群免疫细胞marker

最近发现一篇文献&#xff0c;作者来自新加坡基因研究所&#xff0c;这篇文章大概是整理了619个亚洲人群的免疫多样性图集&#xff08;AIDA&#xff09;&#xff0c;跨越了7个国家&#xff0c;最终使用了1,265,624个免疫细胞的单细胞数据&#xff0c;并最终确定了8种主要的免疫…

三极管以及mos管

三极管与mos管的高低电平导通判断 &#xff08;1&#xff09;三极管的高低电平导通判断 三极管中有2个PN结&#xff0c;分别称为发射结和集电极结&#xff0c;按材料划分为硅材料三极管&#xff08;硅管&#xff09;&#xff0c;锗材料三极管&#xff08;锗管&#xff09;&am…

PPT模板之--个人简历

还在为制作 PPT 时毫无头绪、对着空白页面抓耳挠腮而烦恼吗&#xff1f;别担心&#xff0c;这里就是你的 PPT 灵感补给站&#xff01;在这个快节奏的信息时代&#xff0c;一份吸睛又高效的 PPT 至关重要&#xff0c;它能在商务汇报中助你赢得先机&#xff0c;在课堂展示时让你脱…

springboot--页面的国际化

今天来实现页面中的国际化 首先&#xff0c;需要创建一个新的spring boot项目&#xff0c;导入前端模板&#xff0c;在我的博客中可以找到&#xff0c;然后将HTML文件放在templates包下&#xff0c;将其他的静态资源放在statics包下&#xff0c;如下图结构 页面的国际化主要在首…

前端学习10—Ajax

1 AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大优势为&#xff1a;无刷新获取数据 AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组合在一起使用的新方…

list的常见接口使用

今天&#xff0c;我们来讲解一下C关于STL标准库中的一个容器list的常见接口。 在我们之前c语言数据结构中&#xff0c;我们已经了解过了关于链表的知识点了&#xff0c;那么对于现在理解它也是相对来说比较容易的了。 数据结构--双向循环链表-CSDN博客 1. 定义与包含头文件 …

一维差分数组

2.一维差分 - 蓝桥云课 问题描述 给定一个长度为 n 的序列 a。 再给定 m 组操作&#xff0c;每次操作给定 3 个正整数 l, r, d&#xff0c;表示对 a_{l} 到 a_{r} 中的所有数增加 d。 最终输出操作结束后的序列 a。 ​​Update​​: 由于评测机过快&#xff0c;n, m 于 20…

再次重拾jmeter之踩坑

1.添加“csv数据文件设置”&#xff0c;运行时提示 java.lang.IllegalArgumentException: Filename must not be null or empty检查多次后才发现因为我运行的是整个线程组&#xff0c;所以对应http请求下不能包括空的csv文件 2. 填写ip时不能加/&#xff0c;要在路径里加&…

4-6记录(B树)

找左边右下或者右边左下 转化成了前驱后继的删除 又分好几种情况&#xff1a; 1. 只剩25&#xff0c;小于2&#xff0c;所以把父亲拉到25旁边&#xff0c;兄弟的70顶替父亲 对于25&#xff0c;25的后继就是70&#xff0c;25后继的后继是71&#xff08;中序遍历) 2. 借左子树…

06软件测试需求分析案例-添加用户

给职业顾问部的老师添加用户密码后&#xff0c;他们才能登录使用该软件。只有admin账户具有添加用户、修改用户信息、删除用户的权利。admin是经理或团队的第一个人的账号&#xff0c;后面招一个教师就添加一个账号。 通读需求是提取信息&#xff0c;提出问题&#xff0c;输出…

Nacos服务发现和配置管理

目录 一、Nacos概述 1. Nacos 简介 2. Nacos 特性 2.1 服务发现与健康监测 2.2 动态配置管理 2.3 动态DNS服务 2.4 其他关键特性 二、 服务注册和发现 2.1 核心概念 2.2 Nacos注册中心 2.3 Nacos单机模式 2.4 案例——服务注册与发现 2.4.1 父工程 2.4.2 order-p…

操作系统 3.1-内存使用和分段

如何简单使用内存 这张幻灯片展示了计算机如何开始执行程序的基本过程&#xff0c;涉及到存储器、指令寄存器&#xff08;IR&#xff09;、运算器和控制器等计算机组件。 存储器&#xff1a;程序被加载到内存中。图中显示了一个指令 mov ax, [100]&#xff0c;它的作用是将内存…

禅道MCP Server开发实践与功能全解析

一、简介 1、MCP Server核心定义 MCP Server&#xff08;Meta Command Protocol Server&#xff09;是一种基于客户端-服务器架构的轻量级服务程序&#xff0c;采用统一的mcp协议格式&#xff0c;通过连接多样化数据源和工具为AI应用提供扩展能力。它作为中间层&#xff0c;实…

GNSS静态数据处理

1 安装数据处理软件&#xff1a;仪器之星&#xff08;InStar &#xff09;和 Trimble Business Center 做完控制点静态后&#xff0c;我们需要下载GNSS数据&#xff0c;对静态数据进行处理。在处理之前需要将相关软件在自己电脑上安装好&#xff1a; 仪器之星&#xff08;InS…

java家政APP源码,家政预约平台源码,家电上门维修、家电上门清洗

家政上门预约服务APP源码&#xff0c;开发功能涵盖了用户注册与登录、家政服务分类与选择、预约管理、支付与交易、地图与导航、评价与反馈、个人信息管理、消息通知、营销工具以及数据分析等多个方面。这些功能的实现不仅提高了家政服务的便捷性和效率&#xff0c;还为用户提供…