微信小程序自定義日期選擇器

日期選擇器是我們在寫項目的過程中經常遇到的,有需要標題的選擇器,也有不需要標題的選擇器

今天給大傢帶來一個自定義的時間選擇器,廢話不多說,直接上代碼

第一步:先創建一個picker的文件夾

第二步 :在wxml中寫佈局樣式

<!--picker/picker.wxml-->
<view class="full-box {{isOpen?'cur':''}}">
    <!--<view class="modal" bindtap="tapModal"></view>-->
    <view class="picker">
        <view class="picker-header">
            <view bindtap="cancle" >
                <text>{{cancelText}}</text>
            </view>
            <text style="font-weight: bold;">{{titleText}}</text>
            <view bindtap="sure">
                <text   style="color:aqua;">{{sureText}}</text>
            </view>
        </view>
        <picker-view
            value="{{value}}"
            class="picker-content"
            bindpickstart="_bindpickstart"
            bindchange="_bindChange"
            bindpickend="_bindpickend"
            indicator-style="{{indicatorStyle}}"
            mask-style="{{maskStyle}}"
        >
            <picker-view-column wx:for="{{columnsData}}" wx:key="{{index}}">
                <view wx:for="{{item}}" wx:for-item="itemIn" class="picker-line" wx:key="{{index}}">
                    <text class="line1">{{isUseKeywordOfShow?itemIn[keyWordsOfShow]:itemIn}}</text>
                </view>
            </picker-view-column>
        </picker-view>
    </view>
</view>

第三步:wxss中添加樣式

/* picker/picker.wxss */
.full-box{
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    z-index: 9999;
    opacity: 0;
    background:rgba(0,0,0,.4);
    transition:all .4s ease-in-out 0;
    pointer-events:none;
}
.full-box.cur{
    opacity:1;
    pointer-events:auto
}
 
.modal{
    position: absolute;
    width: 100%;
    height: 50%;
    bottom:-50%;
    left: 0;
    background: transparent;
    transition:all .4s ease-in-out 0;
}
 
.picker{
    position: absolute;
    width: 100%;
    height: 235px;
    bottom: -235px;
    left: 0;
    background: #fff;
    display: flex;
    flex-direction: column;
    transition:all .4s ease-in-out 0;
}
 
.cur .picker{
    bottom:0;
}
.cur .modal{
    bottom:50%;
}
.picker-line{
    display: flex;
    justify-content: center;
    align-items: center;
}
.picker-header {
    height: 20%;
    box-sizing: border-box;
    padding: 0 20rpx;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #eeeeee;
}
.picker-header view {
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}
.picker-header view text{
    font-size: 36rpx;
}
.picker-content {
    flex-grow: 1;
}
.line1{
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    lines:1
}

第四步:在js中寫組件的屬性

// picker/picker.js
import { isPlainObject } from './tool'
 
Component({
  /**
   * 組件的屬性列表
   */
  properties: {
    scrollType: {
      type: String,
      value: 'normal'// "link": scroll間聯動  "normal": scroll相互獨立
    },
    listData: {
      type: Array,
      value: [],
      observer: function(newVal) {
        if (newVal.length === 0 || this._compareDate()) return
        this._setTempData()
        const tempArr = [...new Array(newVal.length).keys()].map(() => 0)
        this.data.lastValue = this.data.tempValue = tempArr
        this._setDefault()
 
        // let {defaultPickData} = this.properties;
        // if(newVal.length === 0) return;
        //
        // this._setDefault(newVal, defaultPickData)
      }
    },
    defaultPickData: {
      type: Array,
      value: [],
      observer: function(newVal) {
        if (newVal.length === 0 || this._compareDate()) return
        this._setTempData()
        this._setDefault()
      }
    },
    keyWordsOfShow: {
      type: String,
      value: 'name'
    },
    isShowPicker: {
      type: Boolean,
      value: false,
      observer: function(newVal) {
        if (newVal) {
          this._openPicker()
        } else {
          this._closePicker()
        }
      }
    },
    titleText: {// 標題文案
      type: String,
      value: '請選擇到館日期'
    },
    cancelText: {// 取消按鈕文案
      type: String,
      value: '取消'
    },
    sureText: {// 確定按鈕文案
      type: String,
      value: '確定'
    },
  },
 
  /**
   * 組件的初始數據
   */
  data: {
    columnsData: [],
    value: [],
    backData: [],
    height: 0,
    isOpen: false,
    isUseKeywordOfShow: false,
    scrollEnd: true, // 滾動是否結束
    lastValue: [], // 上次各個colum的選擇索引
    tempValue: [],
    isFirstOpen: true,
    onlyKey: '',
    defaultPickDataTemp: '',
    listDataTemp: ''
  },
  /**
   * 組件的方法列表
   */
  methods: {
    tapModal() {
      this.properties.isShowPicker = false
      this._closePicker()
    },
    cancle() {
      this.triggerEvent('cancle')
      this._closePicker()
    },
    sure() {
      const { scrollEnd, tempValue } = this.data
      if (!scrollEnd) return
      const backData = this._getBackDataFromValue(tempValue)
      this.setData({
        backData
      })
      this.triggerEvent('sure', {
        choosedData: backData,
        choosedIndexArr: tempValue
      })
      this._closePicker()
    },
    _bindChange(e) {
      const { scrollType } = this.properties
      const { lastValue } = this.data
      let val = e.detail.value
      switch (scrollType) {
        case 'normal':
          this.data.tempValue = val.concat()
          this.data.tempValue = val.concat()
          break
        case 'link':
          // let column_02 = this._getColumnData(this.properties.listData[val[0]].children);
          // let column_03 = this._getColumnData(this.properties.listData[val[0]].children[val[1]].children);
          var tempArray = []
          if (val.length > 1) {
            val.slice(0, val.length - 1).reduce((t, c, i) => {
              const v = t[c].children
              tempArray.push(this._getColumnData(v))
              return v
            }, this.properties.listData)
          }
          // let columnsData = [this.data.columnsData[0],column_02,column_03];
          var columnsData = [this.data.columnsData[0], ...tempArray]
 
          // 設置value關聯
          var compareIndex = this._getScrollCompareIndex(lastValue, val)
          if (compareIndex > -1) {
            let tempI = 1
            while (val[compareIndex + tempI] !== undefined) {
              val[compareIndex + tempI] = 0
              tempI++
            }
          }
          val = this._validate(val)
          this.data.lastValue = val.concat()
          this.data.tempValue = val.concat()
          this.setData({
            columnsData
            // value: val
          })
      }
    },
    _validate(val) {
      const { columnsData } = this.data
      columnsData.forEach((v, i) => {
        if (columnsData[i].length - 1 < val[i]) {
          val[i] = columnsData[i].length - 1
        }
      })
      this.setData({
        value: val
      })
      return val
    },
    _bindpickend() {
      this.data.scrollEnd = true
    },
    _bindpickstart() {
      this.data.scrollEnd = false
    },
    _openPicker() {
      if (!this.data.isFirstOpen) {
        if (this.properties.listData.length !== 0) {
          this._setDefault(this._computedBackData(this.data.backData))
        }
      }
      this.data.isFirstOpen = false
      this.setData({
        isOpen: true
      })
    },
    _closePicker() {
      this.setData({
        isOpen: false
      })
    },
    _getColumnData(arr) {
      return arr.map((v) => this._fomateObj(v))
    },
    _fomateObj(o) {
      const tempO = {}
      for (const k in o) {
        k !== 'children' && (tempO[k] = o[k])
      }
      return tempO
    },
    _getScrollCompareIndex(arr1, arr2) {
      let tempIndex = -1
      for (let i = 0, len = arr1.length; i < len; i++) {
        if (arr1[i] !== arr2[i]) {
          tempIndex = i
          break
        }
      }
      return tempIndex
    },
    // 根據id獲取索引
    _getIndexByIdOfObject(listData, idArr, key, arr) {
      if (!Array.isArray(listData)) return
      for (let i = 0, len = listData.length; i < len; i++) {
        if (listData[i][key] === idArr[arr.length][key]) {
          arr.push(i)
          return this._getIndexByIdOfObject(listData[i].children, idArr, key, arr)
        }
      }
    },
    _setDefault(inBackData) {
      const { scrollType } = this.properties
      let { listData, defaultPickData } = this.properties
 
      const { lastValue } = this.data
      if (inBackData) {
        defaultPickData = inBackData
      }
      let backData = []
      switch (scrollType) {
        case 'normal':
          if (isPlainObject(listData[0][0])) {
            this.setData({
              isUseKeywordOfShow: true
            })
          }
          if (Array.isArray(defaultPickData) && defaultPickData.length > 0) {
            backData = listData.map((v, i) => v[defaultPickData[i]])
            this.data.tempValue = defaultPickData
            this.data.lastValue = defaultPickData
          } else {
            backData = listData.map((v) => v[0])
          }
          this.setData({
            columnsData: listData,
            backData: backData,
            value: defaultPickData
          })
          break
        case 'link':
          // let column_01 = this._getColumnData(newVal);
          // let column_02 = this._getColumnData(newVal[0].children);
          // let column_03 = this._getColumnData(newVal[0].children[0].children);
          // let columnsData = [column_01,column_02,column_03];
          var columnsData = []
          // 如果默認值
          if (Array.isArray(defaultPickData) && defaultPickData.length > 0 && defaultPickData.every((v, i) => isPlainObject(v))) {
            const key = this.data.onlyKey = Object.keys(defaultPickData[0])[0]
 
            const arr = []
 
            this._getIndexByIdOfObject(listData, defaultPickData, key, arr)
 
            defaultPickData = arr
            let tempI = 0
            do {
              lastValue.push(defaultPickData[tempI])
              columnsData.push(this._getColumnData(listData))
              listData = listData[defaultPickData[tempI]].children
              tempI++
            } while (listData)
            backData = columnsData.map((v, i) => v[defaultPickData[i]])
            // 如果沒有默認值
          } else {
            this.data.onlyKey = this.properties.keyWordsOfShow || 'name'
            do {
              lastValue.push(0)
              columnsData.push(this._getColumnData(listData))
              listData = listData[0].children
            } while (listData)
            backData = columnsData.map((v) => v[0])
          }
          this.data.tempValue = defaultPickData
          this.data.lastValue = defaultPickData
          this.setData({
            isUseKeywordOfShow: true,
            columnsData,
            backData
          })
          setTimeout(() => {
            this.setData({
              value: defaultPickData
            })
          }, 0)
          break
      }
    },
    _computedBackData(backData) {
      const { scrollType, listData } = this.properties
      const { onlyKey } = this.data
      if (scrollType === 'normal') {
        return backData.map((v, i) => listData[i].findIndex((vv, ii) => this._compareObj(v, vv)))
      } else {
        const t = backData.map((v, i) => {
          const o = {}
          o[onlyKey] = v[onlyKey]
          return o
        })
 
        return t
      }
    },
    _compareObj(o1, o2) {
      const { keyWordsOfShow } = this.properties
      if (typeof o1 !== 'object') {
        return o1 === o2
      } else {
        return o1[keyWordsOfShow] === o2[keyWordsOfShow]
      }
    },
    _getBackDataFromValue(val) {
      let tempArr = []
      if (val.length > 0) {
        tempArr = this.data.columnsData.reduce((t, v, i) => {
          return t.concat(v[val[i]])
        }, [])
      } else {
        tempArr = this.data.columnsData.map((v, i) => v[0])
      }
      return tempArr
    },
    _compareDate() { // 完全相等返回true
      const { defaultPickDataTemp, listDataTemp } = this.data
      const { defaultPickData, listData } = this.properties
 
      return defaultPickDataTemp === defaultPickData && listDataTemp === listData
    },
    _setTempData() {
      const { defaultPickData, listData } = this.properties
      this.data.defaultPickDataTemp = defaultPickData
      this.data.listDataTemp = listData
    }
  }
})

第五步:創建一個tool.js文件

function _typeof(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
function isString(obj) { //是否字符串
  return _typeof(obj) === 'string'
}
function isPlainObject(obj) {
  return _typeof(obj) === 'object';
}
module.exports = {
  isString,
  isPlainObject
}

第六步:在所需的頁面的json中進行引用

{
  "usingComponents": {
    "picker": "../../picker/picker"
  }
}

第七步:在所需的頁面的wxml中寫入佈局

 <button bindtap="showPicker_11">時間的五列聯動picker</button>
<view>選擇數據:{{picker_11_data}}</view>
<view>選擇索引:{{picker_11_index}}</view>
<picker isShowPicker="{{isShow_11}}" keyWordsOfShow="name" bindsure="sureCallBack_11" bindcancle="cancleCallBack_11" scrollType="link" listData="{{listData_11}}"></picker>

第八步:在所需的頁面的js中調用我們的自定義選擇器

// pages/index/index.js
import { times } from './time.js';
Page({
 
  /**
   * 頁面的初始數據
   */
  data: {
    isShow_11: false,
    listData_11:times,
    picker_11_data:[],
  },
  onLoad () {
    setTimeout(() => {
      this.setData({
        defaultPickData_08:[
          {code:'110000'},{code:'110100'},{code:'110101'}
        ]
      })
    },3000)
  },
 
  showPicker_11: function () {
    this.setData({
      isShow_11: true,
    })
 
  },
 
  sureCallBack_11 (e) {
    let data = e.detail
    console.log("data",data);
    this.setData({
      isShow_11: false,
      picker_11_data: JSON.stringify(e.detail.choosedData),
      picker_11_index:JSON.stringify(e.detail.choosedIndexArr)
 
    })
  },
  cancleCallBack_11 () {
    this.setData({
      isShow_11: false
    })
  },
 
})

第九步:創建一個time.js的文件

const times =[ {
  name:'2021年',
  id:1,
  children:[
    {
      name:'1月',
      id:11,
      children:[
        {
          name:'1日',
          id:111,
          children:[
            {
              name:'1時',
              id:1111,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
              ]
            },
          ]
        },
        {
          name:'2日',
          id:112,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'3日',
          id:113,
          children:[
            {
              name:'小',
              id:1131,
            },
            {
              name:'大',
              id:1132
            },
          ]
        }
      ]
    },
    {
      name:'2月',
      id:12,
      children:[
        {
          name:'1日',
          id:121,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'2日',
          id:122,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'3日',
          id:123,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        }
      ]
    }
  ]
},
{
  name:'2022年',
  id:1,
  children:[
    {
      name:'1月',
      id:11,
      children:[
        {
          name:'1日',
          id:111,
          children:[
            {
              name:'1時',
              id:1111,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
              ]
            },
          ]
        },
        {
          name:'2日',
          id:112,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'3日',
          id:113,
          children:[
            {
              name:'小',
              id:1131,
            },
            {
              name:'大',
              id:1132
            },
          ]
        }
      ]
    },
    {
      name:'2月',
      id:12,
      children:[
        {
          name:'1日',
          id:121,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'2日',
          id:122,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        },
        {
          name:'3日',
          id:123,
          children:[
            {
              name:'1時',
              id:1121,
              children:[
                {
                  name:'1分',
                  id:11111,
                },
                {
                  name:'2分',
                  id:11112,
                },
               
              ]
            },
            {
              name:'2時',
              id:1121,
              
            },
          ]
        }
      ]
    }
  ]
},]
module.exports = {
  times,
}

完成上述步驟後,一個自定義的日期選擇器就完成瞭

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: