Skip to content

自定义 picker

内置 picker 无法满足多列联动、复杂选项或自定义操作区时,可以组合 root-portalpage-containerpicker-viewpicker-view-column 实现底部选择器。

组件作用
root-portal将浮层提升到页面根节点,避免祖先布局和层叠上下文影响
page-container提供遮罩、底部进出场动画和返回行为
picker-view管理各列当前选中项的索引
picker-view-column定义一列可滚动选项

结构

xml
<root-portal enable>
  <page-container
    show="{{showPicker}}"
    position="bottom"
    round
    overlay
    bind:clickoverlay="closePicker"
  >
    <view class="picker-sheet">
      <view class="picker-sheet__header">
        <button class="picker-sheet__action" bindtap="closePicker">取消</button>
        <text>选择地区</text>
        <button class="picker-sheet__action" bindtap="confirmPicker">确定</button>
      </view>

      <picker-view
        class="picker-sheet__body"
        value="{{draftValue}}"
        indicator-style="height: 88rpx;"
        bindchange="handlePickerChange"
      >
        <picker-view-column>
          <view
            wx:for="{{provinces}}"
            wx:key="id"
            class="picker-sheet__item"
          >
            {{item.name}}
          </view>
        </picker-view-column>

        <picker-view-column>
          <view wx:for="{{cities}}" wx:key="id" class="picker-sheet__item">
            {{item.name}}
          </view>
        </picker-view-column>
      </picker-view>
    </view>
  </page-container>
</root-portal>
css
.picker-sheet {
  padding-bottom: env(safe-area-inset-bottom);
  background: #fff;
}

.picker-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 96rpx;
  padding: 0 24rpx;
}

.picker-sheet__action {
  margin: 0;
  padding: 0;
  color: #576b95;
  background: transparent;
}

.picker-sheet__action::after {
  border: 0;
}

.picker-sheet__body {
  width: 100%;
  height: 440rpx;
}

.picker-sheet__item {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 88rpx;
}

picker-viewvalue 是索引数组,数组位置对应列,数组值对应该列的选项索引。列数据变化后,应同步修正越界索引。

js
Page({
  data: {
    showPicker: false,
    value: [0, 0],
    draftValue: [0, 0],
    provinces: [],
    cities: [],
  },

  openPicker() {
    this.setData({
      showPicker: true,
      draftValue: [...this.data.value],
    })
  },

  handlePickerChange(event) {
    const draftValue = event.detail.value

    // 第一列变化时,在此更新第二列数据,并将无效索引重置为 0。
    this.setData({ draftValue })
  },

  closePicker() {
    this.setData({ showPicker: false })
  },

  confirmPicker() {
    this.setData({
      value: [...this.data.draftValue],
      showPicker: false,
    })
  },
})

状态与兼容性

  • 将已确认值与弹层内的临时值分开;取消时丢弃临时值,确认时再提交。
  • bindchange 在滚动停止后触发。多列联动应根据最新索引更新后续列,并处理空数组。
  • picker-view 必须具有明确高度,列中的每个选项也应保持一致高度。
  • 同一页面最多使用一个 page-container。关闭动作统一修改 show,避免视图状态与业务状态不一致。
  • root-portal 需要基础库 2.25.2 及以上,page-container 需要基础库 2.16.0 及以上;使用前确认最低基础库版本,并为低版本准备普通定位浮层。

参考:

基于 MIT 许可发布