美文网首页APP & program
微信小程序组件:下拉框

微信小程序组件:下拉框

作者: 牛会骑自行车 | 来源:发表于2022-05-01 15:29 被阅读0次

    需求:
    1.调取接口获取下拉框数据
    2.下拉框分页
    3.可以通过输入框进行数据搜索
    4.输入框可以清空
    5.可选是否展示第一条数据
    6.根据输入框所在位置判断下拉框出现的位置
    7.聚焦时的样式变化

    思路:
    1.将接口所需参数传值进组件
    2.与上篇文章的列表方式一致
    3.bindinput时取输入框的值传入接口参数中同时调取数据
    4.没啥好说的
    5.同上
    6.输入框位于整个页面中线以上就让下拉框出现在输入框下面,反之则。。反之
    7.依旧没啥好说的。。

    效果图 👇

    那啥我感到非常抱歉哈哈哈哈哈哈
    这个组件有个小小的问题,我使用了animation,用opacity来控制的下拉框显隐。很明显会存在一个问题就是当它看不到的时候还是会存在啊哈哈哈哈哈哈哈哈真是智障但是我懒得粘代码了。。遇到这个问题的时候再多一个变量加在z-index上就可以啦
    真的很抱歉哟

    上代码 👇
    组件html ↓

    <!--components/form/select.wxml-->
    
    <view class="f_r_b select-con form-con" style="border: 2rpx solid {{active ? '#416BFF' : '#EEEEEE'}};" id="select">
    
      <input class="input" type="text" value="{{value}}" bindinput="onChange" bindfocus="onFocus" bindblur="onBlur"
        disabled="{{disabled}}" />
      <!-- 清空按钮的小叉 -->
      <image src="/images/common/icon_close.png" class="icon" catchtap="onClear" wx:if="{{clearShow}}"></image>
      <!-- 右侧的小箭头 -->
      <image src="{{active ? '/images/common/icon_form_arrow_up_active.png' : '/images/common/icon_arrow_down.png'}}" class="icon" wx:if="{{disabled?false:true}}"></image>
      <!-- 下拉框 -->
      <scroll-view class="option-con" style="top: {{optionTop}};bottom: {{optionBottom}};opacity: 0;" scroll-y
        animation="{{listShow}}" show-scrollbar="{{false}}" enhanced bindscrolltolower="moreData" id="list">
        <view class="item" wx:for="{{options}}" wx:key="index" bindtap="clickRow" data-item="{{item}}">
          {{item[showName]}}
        </view>
        <view class="loading">
          {{bottomText}}
        </view>
      </scroll-view>
    
    </view>
    

    组件js ↓

    // components/form/select.js
    
    Component({
      properties: {
        options: Array,
        // 接口相关的参数 ↓
        interface: {
          type: Object,
            value: () => {
              return {
                url: "", // 路径
                method: "", // 方法
                params: {} // 参数
              }
            }
        },
        // 搜索框的参数名  ↓
        inputName: String,
        // 选中展示的参数名  ↓
        showName: {
          type: String,
          value: "name"
        },
        // 是否展示第一条数据  ↓
        showFirstData: {
          type: Boolean,
          value: false
        },
        // 调取接口的信号??大概就这意思。。重置也用这个参数来着哈哈哈哈哈
        signal: Boolean,
      },
      data: {
        clearShow: false, // 清除数据的icon小×
        bottomText: "加载中...", // 下拉框底部的文字 ‘加载中’、‘到底啦~’
        currentCount: 0,
        value: "", // 搜索框的值
        firstGetData: false, // 是否第一次调取数据
        active: false
      },
      observers: {
        "signal": function (v) {
          if (!v) return;
          this.setData({
            value: "",
            ['interface.params.pageNum']: 1,
            [`interface.params.${this.data.inputName}`]: '',
            firstGetData: false
          })
          this.getData();
        },
        "value": function (v) {
          // 写不动注释了。。。。。。自己对比html就能懂这句
          this.setData({
            clearShow: v === '' ? false : true
          })
        },
      },
      methods: {
        getData() {
          // 接收接口需要的参数
          const {
            url,
            params,
            method
          } = this.data.interface;
          // 赋值是否第一次调取数据
          let gotFirstData = this.data.firstGetData;
    
          this.setData({
            // 给底部的文字赋值
            bottomText: "加载中..."
          })
          // 已经封装好的调取接口的方法,对应参数用wx.request也可
          wx.httpWithoutToken(url, params, method).then(res => {
            // 判断数据是否为空
            let hasData = res.content.length > 0 || res.content != '[]';
    
            if (res.code === 200 && res.success && hasData) {
              this.setData({
                // 页码最大值
                maxPage: res.totalCount / this.data.interface.params.pageSize,
                // 第一次调取数据赋值
                firstGetData: true
              })
    
              let params = this.data.interface.params;
              // 取值:已经调取过几页
              let used = (params.pageNum - 1) * params.pageSize;
              // 当前数据数
              let currentCount;
              // 赋值:下拉框数据
              res.content.forEach((option, index) => {
                this.setData({
                  [`options[${used + index}]`]: option
                })
                currentCount = used + index + 1;
              })
              if (!gotFirstData) this.setFirstData(); // 第一次获取数据
              // 赋值:底部文字 --> 如果当前数据 <= 总数据数,则为‘加载中’
              this.setData({
                bottomText: currentCount < res.totalCount ? '加载中...' : '暂无更多数据'
              })
            } else {
              this.setData({
                bottomText: "暂无更多数据"
              })
            }
          })
        },
        moreData() {
          // 写不动注释了。。。。这里的代码可以参考“列表组件”
          let num = this.data.interface.params.pageNum;
          if (num >= this.data.maxPage) {
            this.setData({
              bottomText: "暂无更多数据"
            })
            return;
          }
          num++;
          this.setData({
            ['interface.params.pageNum']: num
          })
          this.getData();
        },
        // 如果需要设置默认选中第一条数据才会用到这个方法  ↓
        setFirstData() {
          // 多条数据  设置第一条数据
          if (this.data.showFirstData) {
            // value show 
            this.setData({
              value: this.data.options[0][this.data.showName]
            })
            // send value to page
            this.triggerEvent("showDefaultValue", this.data.options[0]);
          }
        },
        onChange(e) {
          // input的change事件
          let val = e.detail.value;
          this.setData({
            value: e.detail.value,
            [`interface.params.${this.data.inputName}`]: val,
            [`interface.params.pageNum`]: 1,
            options: []
          })
          this.getData();
        },
        onFocus() {
          this.setData({
            active: true
          })
          // 获取input-con的位置,判断drop-con的弹出方向
          wx.getDomInfo(this, 'select').then(res => {
            let containerHeight = res[1].scrollHeight;
            let offsetTop = res[0].top;
            let num = offsetTop / containerHeight;
    
            if (num * 10 <= 5) {
              this.setData({
                optionTop: "72rpx",
                optionBottom: ""
              })
            } else {
              this.setData({
                optionBottom: "73rpx",
                optionTop: ""
              })
            }
          })
    
          this.handleListShow(true);
        },
        onBlur() {
          this.setData({
            active: false
          })
          setTimeout(() => {
            this.handleListShow(false);
          }, 1);
        },
        clickRow(e) {
          // 取值:点击每行
          let item = e.currentTarget.dataset.item;
          this.setData({
            value: item.name,
            [`interface.params.pageNum`]: 1,
            [`interface.params.${this.data.inputName}`]: item[`${this.data.showName}`],
            options: []
          })
          this.getData();
    
          this.triggerEvent("select", item);
        },
        // 点击输入框右侧的清空按钮  ↓
        onClear() {
          this.setData({
            value: '',
            [`interface.params.${this.data.inputName}`]: '',
            [`interface.params.pageNum`]: 1,
            options: []
          })
          this.triggerEvent("select", "");
          this.getData();
        },
        // drop-con show situation:下拉框展开或收起时的方法。
        // 有动画的这么写,如果只是控制显隐就给wx:if赋值true or false即可
        handleListShow(show) {
          let opacity = show ? 1 : 0;
          this.listShow.opacity(opacity).step()
          this.setData({
            listShow: this.listShow.export()
          })
        },
      },
      lifetimes: {
        attached() {
          // 下拉框显隐的动画:我这个是用透明度来做的
          this.listShow = wx.createAnimation({
            duration: 100
          })
        },
      },
    })
    

    使用页面 👇 (JSON文件先引用这个组件昂,代码懒得粘了)
    html ↓

      <select interface="{{interface}}" signal="{{signal}}" inputName="name"  showFirstData 
      bindselect="handleChooseAgency" bindshowDefaultValue="agencyFirstValue"></select>
    

    js ↓

      data: {
        interface: {},
        signal: false
      },
      initSelect() {
        this.setData({
          'interface': {
            url: "selectMechanismByCode",
            method: "GET",
            params: {
              pageNum: 1,
              pageSize: 8,
              name: "",
            }
          },
          signal: true
        })
      },
      handleChooseAgency(e) {
        let item = e.detail;
        console.log(item,'取值每行数据')
      },
      agencyFirstValue(e) {
        let firstAgency = e.detail;
        console.log(firstAgency,'取值默认赋值第一个的数据')
      },
      onLoad: function () {
        this.initSelect();
      },
    

    微信小程序与vue很像,不过在监听数据这方面,vue的watch只会在监听到变化的时候才会执行,但小程序好像只要赋值,就算与以前的值一样,也会执行。
    在这个case中,signal的设置就是运用了这一点。不论是初始化页面,还是刷新后重置页面数据,只要将signal赋值true就可以执行。
    真的是太方便啦~~~~

    相关文章

      网友评论

        本文标题:微信小程序组件:下拉框

        本文链接:https://www.haomeiwen.com/subject/pfssyrtx.html