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

一、 最终效果

二、实现了功能

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

三、TInput 参数配置

1、代码示例:

html 复制代码
<TInput v-model="value" titlt="标题" />

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

参数 说明 类型 默认值
v-model input绑定值 String /number -
v-model:select-value inputType:select 的绑定值 String /number -
title label标题 String -
inputType 输入框类型可选值:input/select String input
required 是否必填红点标识 boolean false
decimalLimit 小数点后保留几位 number 2
specifyType 指定输入类型"text"/"decimal"/"phone" /"integer" / "idCard" String text
showThousands 是否显示千分位 boolean false
isShowErrorTip 输入框失焦是否提示错误信息 boolean false
isLink 是否展示右侧箭头(inputType=select自动展示) boolean false
iconArrow 右侧箭头图标 String 'arrow-right'
iconArrowColor 右侧箭头颜色 String #969799
isColon label标题是否显示冒号 boolean true
isShowBorder 是否显示下边框 boolean false
list inputType=select自带下拉数据源 array []
customKey 列表项自定义key String key
customLabel 列表项自定义label string label
popupTitle 自带下拉框标题显示 string -
isPopupTitleBorder 自带下拉框标题与列表边框显示 boolean false

3. Events

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

4. Slot

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

四、源码

html 复制代码
<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再次封装基础组件文档

相关推荐
十年之少7 小时前
异步编程——微信小程序
笔记·学习·微信小程序
山海青风7 小时前
微信小程序实战案例 - 餐馆点餐系统 阶段 4 - 订单列表 & 状态
微信小程序·小程序
菜冬眠。9 小时前
uni-app/微信小程序接入腾讯位置服务地图选点插件
前端·微信小程序·uni-app
竣子好逑12 小时前
uniapp 自定义tabbar
前端·uni-app
Monly2113 小时前
Uniapp:获取当前定位坐标
前端·javascript·uni-app
依辰13 小时前
可观测性升级:小程序错误监控体系实践
前端·javascript·微信小程序
小兔崽子去哪了15 小时前
微信小程序入门
前端·vue.js·微信小程序
七月十二15 小时前
[微信小程序]对接sse接口
前端·微信小程序
mosen86818 小时前
【微信小程序】报错: http://127.0.0.1:7001 不在以下 request 合法域名列表中
微信小程序·小程序
Monly2120 小时前
Uniapp:确认框
开发语言·javascript·uni-app