【微信小程序开发】Input 伪装法 picker 组件实现 placeholder 效果,用于未选择之前的友好提示

  • 原创
  • 作者:程序员三丰
  • 发布时间:2026-03-30 23:24
  • 浏览量:8
微信官方的 picker 组件本身没有 placeholder 属性,本文介绍使用 Input 伪装法来实现这个效果,适用于如果你项目中的表单风格希望完全统一为输入框样式。

引子·为什么写这篇文章

首先,在日常开发中下拉选择框select的需求会经常遇到,但是移动端最常见形式的就是底部弹出一个picker,而select更多见于 PC 端的 web 项目。

无论是select还是picker本质上都是从多个选项中选择其中一个,那么当用户还没有选择的时候,我们希望它能显示友好的提示文案,比如“请选择xxxx”,对于select我们使用placeholder属性就可以轻松实现,但是微信官方的picker组件本身没有placeholder属性,在原生微信小程序开发中我们又不希望引入其他 UI 库,或者第三方组件,此时我们自己动手封装一个组件是最佳方式。

实现原理(Input伪装法)

Input 伪装法的原理就是用一个禁用的Input来模拟picker的显示区域,点击输入框时,弹出picker
这种方法利用了input自带的placeholder属性,当input的值不为空时,自动隐藏placeholder,满足需求,并且在表单场景下能获得更好的样式统一性。

代码实现(组件封装)

组件代码结构

components
    |—— input-mock-picker
            |—— index.js
            |—— index.json
            |—— index.wxml
            |—— index.wxss

index.js 文件代码

Component({
  properties: {
    value: {  // options 中选中的项目的 value
      type: String,
    },
    options: { // 选项列表:[ { value: 1, label: '太原' }, { value: 2, label: '北京', } ]
      type: Array,
      value: []
    },
    color: { // input 文字颜色
      type: String,
      value: '#000'
    },
    placeholder: { // placeholder 文字内容
      type: String,
      value: '请选择'
    },
    placeholderColor: { // placeholder 文字颜色
      type: String,
      value: '#808080'
    }
  },

  data: {
    selectedIndex: '', // 选中项的索引值
    arrowRotate: false, // 控制右侧小箭头的动画效果
  },

  lifetimes: {
    attached() {
      this.handleExternalValue();
    },
    detached() {
      this.data.selectedIndex = '';
    }
  },

  methods: {

    // 渲染初始值
    handleExternalValue() {
      console.log(this.properties)
      if (!this.properties.options.length || !this.properties.value) return;

      const index = this.properties.options.findIndex((item) => item.value == this.properties.value);
      if (index != -1) this.setData({
        selectedIndex: index
      });
    },

    onPickerTap() {
      this.setData({ arrowRotate: true });
    },

    onPickerCancel() {
      this.setData({ arrowRotate: false });
    },

    // 监听 picker 确认事件
    bindPickerChange(e) {
      const selectedIndex = e.detail.value;
      const index = this.properties.options.findIndex((_, _index) => _index == selectedIndex);
      if (index == -1) return;
      this.setData({
        selectedIndex: index,
        arrowRotate: false,
      });

      const selected = this.properties.options.find((_, _index) => _index == selectedIndex)
      if (selected !== undefined) {
        this.triggerEvent('change', {
          value: selected.value
        });
      }
    }
  }
});

index.json 文件代码

{
  "component": true,
  "usingComponents": {}
}

index.wxml 文件代码

<picker 
  mode="selector" 
  range="{{options}}" 
  value="{{selectedIndex}}" 
  range-key="label"
  bindtap="onPickerTap"
  bindcancel="onPickerCancel"
  bindchange="bindPickerChange">
  <view class="picker-container">
    <!-- 用一个禁用的输入框来显示内容 -->
    <input 
      class="picker-input"
      value="{{selectedIndex !== '' ? options[selectedIndex].label : ''}}"
      disabled="true"
      placeholder="{{placeholder}}"
      placeholder-style="color: {{placeholderColor}}"
      style="color: {{color}}"
    />
    <!-- 使用实体字符箭头,Unicode ▼ 或 ▾ -->
    <text class="arrow-icon {{arrowRotate ? 'rotate' : ''}}">▾</text>
  </view>
</picker>

index.wxss 文件代码

.picker-container {
  display: flex;
  align-items: center;
  position: relative;
}

.picker-input {
  flex: 1;
  height: 80rpx;
  padding: 0 24rpx;
  border: 2rpx solid #e0e0e0;
  border-radius: 8rpx;
  font-size: 28rpx;
  box-sizing: border-box;
}

.picker-placeholder {
}

/* 箭头样式 */
.arrow-icon {
  position: absolute;
  right: 10rpx;
  display: inline-block;
  font-size: 42rpx;
  color: #999;
  transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1); /* 更平滑的曲线 */
  margin-left: 10rpx;
  line-height: 1;
}

.arrow-icon.rotate {
  transform: rotate(180deg);
}

代码应用(组件应用)

以 页面 page1 为例,下面给出使用组件的代码片段。

在 page1.json 文件中引入组件:

{
  "usingComponents": {
    "InputMockPicker": "/components/input-mock-picker/index"
  }
}

在 page1.js 中增加相关data属性和方法:

Page({
    data: {
        recordType: '',
        recordTypeEnum: [ { value: 1, label: '太原' }, { value: 2, label: '北京', } ],
    },
    methods: {
        bindRecordTypeSelectChange(e) {
          this.setData({ recordType: e.detail.value });
        },
    }
})

在 page1.wxml 中使用组件:

<InputMockPicker value="{{recordType}}" options="{{recordTypeEnum}}" placeholder="请选择记录类型" style="flex: 1" bindchange="bindRecordTypeSelectChange" />

运行效果

声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/117.html

文章归档

推荐文章

buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas vue3 three.js 微信支付 PHP全栈开发 Python AI 人工智能 AI生成 工作经验 实战笔记