uview picker添加搜索

uview实现全功能自定义picker


1:先上效果图
在这里插入图片描述
2:组件代码

  • 代码没有什么难点,我就不过多赘述了
  • 代码贴的是全部的,可以选择自己需要的部分进行删减
  • 使用方式参考第3步
<template>
  <view class="SPicker">
    <u-popup :show="visible" @close="handleClose" mode="bottom">
      <view class="picker-container">
        <view class="picker-header">
          <u-search
            v-model="searchText"
            @search="handleSearch"
            @custom="handleSearch"
            @change="handleSearch"
            :actionStyle="{ height: '100%', background: 'rgba(255, 158, 21, 1)', color: 'white', borderRadius: '100px', width: '118rpx', height: '32px', lineHeight: '32px', position: 'absolute', right: '24rpx', zIndex: '9999' }"
            :show-action="true"
            placeholder="测站名称"
          >
          </u-search>
        </view>

        <!-- 选择器主体 -->
        <view class="picker-body" v-if="!searchText">
          <picker-view
            :indicator-style="indicatorStyle"
            :value="pickerValue"
            @change="handleChange"
            class="picker-view"
          >
            <!-- 第一列:行政区划 -->
            <picker-view-column>
              <view class="picker-item" :class="{ 'picker-item-selected': index === pickerValue[0] }" v-for="(item, index) in columnData" :key="index">
                {{ item.customLocation }}
              </view>
            </picker-view-column>
            <!-- 第二列:测站 -->
            <picker-view-column>
              <view class="picker-item" :class="{ 'picker-item-selected': index === pickerValue[1] }" v-for="(item, index) in currentStations" :key="index">
                {{ item.name }}
              </view>
            </picker-view-column>
          </picker-view>
        </view>

        <!-- 搜索结果列表 -->
        <scroll-view v-else scroll-y class="search-result">
          <div v-if="searchResults && searchResults.length">
            <view 
              class="search-item"
              v-for="(item, index) in searchResults"
              :key="index"
              @click="handleSearchItemClick(item)"
            >
              {{ item.name }}
            </view>
          </div>
          <u-empty mode="data" marginTop="120rpx" v-else></u-empty>
        </scroll-view>
        <!-- 底部按钮 -->
        <view class="picker-footer">
          <view class="cancel-button" @click="handleClose">取消</view>
          <view class="confirm-button" @click="handleConfirm">确定</view>
        </view>
      </view>
    </u-popup>
  </view>
</template>

<script>
import { result } from 'lodash'

export default {
  name: 'SPicker',
  props: {
    // 选择器数据,现在需要是包含 stations 的数组结构
    pickerList: {
      type: Array,
      default: () => []
    },
    pickerTittle: {
      type: String,
      default: '请选择'
    }
  },
  data() {
    return {
      visible: false,
      searchText: '',
      pickerValue: [0, 0], // 改为两列的索引值
      columnData: [], // 第一列数据
      currentStations: [], // 第二列数据
      currentSelected: null,
      indicatorStyle: 'height: 80rpx',
      searchResults: []
    }
  },
  watch: {
    pickerList: {
      handler(val) {
        this.initPickerData()
      },
      immediate: true
    }
  },
  methods: {
    // 显示选择器
    handleShow() {
      this.visible = true
      this.searchText = ''
    },
    
    // 关闭选择器
    handleClose() {
      this.visible = false
      this.searchText = ''
    },
    
    // 确认选择
    handleConfirm() {
      this.$emit('change', {
        value: this.currentSelected,
        index: this.pickerValue
      })
      this.handleClose()
    },
    // 初始化选择器数据
    initPickerData() {
      this.columnData = this.pickerList
      this.currentStations = this.pickerList[0]?.stations || []
      this.pickerValue = [0, 0]
      this.currentSelected = this.currentStations[0] || null
    },
    
    // 处理选择器值改变
    handleChange(e) {
      const values = e.detail.value
      // 如果第一列变化,需要更新第二列数据
      if (values[0] !== this.pickerValue[0]) {
        this.currentStations = this.columnData[values[0]]?.stations || []
        // 重置第二列选择索引
        values[1] = 0
      }
      this.pickerValue = values
      this.currentSelected = this.currentStations[values[1]]
    },
    
    // 搜索功能现在需要在所有测站中搜索
    handleSearch(value) {
      if (!value) {
        this.searchResults = []
        return
      }
      const results = []
      this.pickerList.forEach(region => {
        if (region.stations) {
          const matchedStations = region.stations.filter(station =>
            station.name.toLowerCase().includes(value.toLowerCase())
          )
          results.push(...matchedStations);
        }
      })
      this.searchResults = results
    },
    
    // 点击搜索结果
    handleSearchItemClick(item) {
      // 找到对应的行政区划和测站索引
      for (let i = 0; i < this.pickerList.length; i++) {
        const stationIndex = this.pickerList[i].stations?.findIndex(s => s.id === item.id)
        if (stationIndex > -1) {
          this.pickerValue = [i, stationIndex]
          this.currentStations = this.pickerList[i].stations
          this.currentSelected = item
          this.searchText = ''
          this.handleConfirm()
          break
        }
      }
    },

    handleCancelSearch() {
      this.searchText = null;
    }
  }
}
</script>
<style lang="scss" scoped>

.SPicker {
  .picker-container {
    background-color: #fff;
    border-radius: 24rpx 24rpx 0 0;
    overflow: hidden;
    
    .picker-header {
      position: relative;
      height: 100rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      border-bottom: 2rpx solid #f5f5f5;
      padding: 0 24rpx;
      margin-bottom: 20rpx;
    }
    
    .picker-body {
      height: 500rpx;
      
      .picker-view {
        height: 100%;
        
        .picker-item {
          height: 68rpx;
          line-height: 68rpx;
          text-align: center;
          color: #141414;
          font-size: 32rpx;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          padding: 0 20rpx;
          box-sizing: border-box;
          &.picker-item-selected {
            color: #141414;
            font-weight: bold;
          }
        }
      }
    }
    
    .search-result {
      height: 500rpx;
      
      .search-item {
        height: 88rpx;
        line-height: 88rpx;
        padding: 0 32rpx;
        font-size: 32rpx;
        color: #141414;
        border-bottom: 2rpx solid #f5f5f5;
        
        &:active {
          background-color: #f5f5f5;
        }
      }
    }
    
    .picker-footer {
      height: 120rpx;
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 0 32rpx;
      .cancel-button, .confirm-button {
        height: 80rpx;
        line-height: 80rpx;
        text-align: center;
        border-radius: 8rpx;
        font-size: 30rpx;
      }
      
      .cancel-button {
        width: 196rpx;
        margin-right: 24rpx;
        background-color: #f5f5f5;
        color: #141414;
      }
      
      .confirm-button {
        flex: 1;
        background-color: #FFAE09;
        color: #fff;
      }
    }
  }
}
</style>

3:在你的父组件中需要这样使用

数据结构示例, 可以随意更改,key变了组件里的key相应也要改变,根据自己需求来

regionList = [
  {
    customLocation: "区划1",
    stations: [
      { id: 1, name: "测站1", ... },
      { id: 2, name: "测站2", ... }
    ]
  },
  {
    customLocation: "区划2",
    stations: [
      { id: 3, name: "测站3", ... },
      { id: 4, name: "测站4", ... }
    ]
  }
]
// 替换成你自己的路径
import SPicker from '@/components/Picker.vue'
<template>
    <!-- 使用 s-picker 组件 -->
    <s-picker 
      ref="lzcPicker"
      :pickerList="regionList"
      pickerTittle="选择测站"
      @change="handleStationConfirm"
    />
  </div>
</template>

<script>
import SPicker from '@/components/SPicker.vue'

export default {
  components: {
    lzcPicker
  },
  data() {
    return {
      regionList: [], // 存储行政区划和测站数据,数据格式参考第四步
    }
  },
  methods: {

    // 打开选择器
    handleClick() {
      this.$refs.lzcPicker.handleShow()
    },

    // 处理选择确认
    handleStationConfirm(result) {
      // result就是选中的信息
    }
  }
}
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值