草庐IT

微信小程序之日期时间筛选器实现(支持年月日时分)

就是有点怕怕 2023-04-03 原文

开发微信小程序过程中,有个需求需要用到日期时间筛选器,查看微信官方文档后,发现官方文档的picker筛选器只能单独支持日期或者是时间,所以为了实现需求自己参考企业微信封装了个日期时间筛选器组件。

写在前面!!! 由于较多朋友有源码需求,我抽了个时间整理了一下代码,并对一年前的代码进行了升级和优化,并放在我github上,有需要的自取:微信小程序日期时间选择器

原理

筛选器的实现,我参考的是小程序官方方式,通过半屏弹窗(mp-half-screen-dialog)结合picker_view进行日期、时间的选择,最终在选择时间后,通过事件触发返回一个change事件,其中参数值为毫秒级时间戳。需要注意的是,由于组件样式需要,对mp-half-screen-dialog做了一定的样式改造,并且为了不影响全局的mp-half-screen-dialog样式,使用指定的extClass进行样式控制,样式具体实现代码在app.wxss中。

实现

1.弹窗的显隐:
在组件的 properties 中传入一个 show 字段,用于控制弹窗的显隐;默认值为 false
2.开始时间,时间戳:
毫秒级时间戳,非必填,默认为,距现在一年前的时间。
3.结束时间,时间戳:
毫秒级时间戳,非必填,默认为,距现在一年后的时间。

具体思路:整体思路是监听弹窗的显隐(show),当弹窗显示时,获取对应配置项(类型、年、月)计算对应筛选器的范围,并初始化默认日期时间为当前年月日时分

以下是效果图以及具体代码实现:

wxml代码

<!--components/date-time-picker.wxml-->
<mp-half-screen-dialog show="{{ show }}" maskClosable="{{ false }}" closabled="{{ false }}" extClass="date-time-dialog" catchtouchmove="preventTouchMove" bindclose="bindclose">
  <view slot="desc" class="date-time-container">
    <view class="date-time-body">
      <!-- 日期 -->
      <view class="selector-panel selector-panel-date">
        <picker-view indicator-style="height: 55px;" mode="selector" value="{{ [dateIndex] }}" class="selector-picker" bindchange="dateChange" bindpickstart="bindpickstart" bindpickend="bindpickend">
          <picker-view-column>
            <view wx:for="{{ dateArr }}" wx:key="index" class="selector-item selector-item-date">{{ item.name }}</view>
          </picker-view-column>
        </picker-view>
      </view>
      <!-- 小时 -->
      <view class="selector-panel">
        <picker-view indicator-style="height: 55px;" mode="selector" value="{{ [hourValue] }}" class="selector-picker" bindchange="hourChange" bindpickstart="bindpickstart" bindpickend="bindpickend">
          <picker-view-column>
            <view wx:for="{{ hourArr }}" wx:key="index" class="selector-item">{{ item }}</view>
          </picker-view-column>
        </picker-view>
      </view>
      <!-- 分钟 -->
      <view class="selector-panel">
        <picker-view indicator-style="height: 55px;" mode="selector" value="{{ [minValue] }}" class="selector-picker" bindchange="minChange" bindpickstart="bindpickstart" bindpickend="bindpickend">
          <picker-view-column>
            <view wx:for="{{ minArr }}" wx:key="index" class="selector-item">{{ item }}</view>
          </picker-view-column>
        </picker-view>
      </view>
    </view>

    <view class="date-time-oper">
      <button type="default" class="weui-btn oper-btn" bindtap="bindclose">取消</button>
      <button type="primary" class="weui-btn oper-btn" bindtap="handleSubmit" disabled="{{ disabled }}">确定</button>
    </view>
  </view>
</mp-half-screen-dialog>


js代码

const utils = require('../utils/util')
const yearTimestamp = 365 * 24 * 60 * 60 * 1000 // 一年

Component({
  /**
   * 组件的属性列表
   */
  properties: {
    show: {
      type: Boolean,
      value: false,
      observer: '_showChange'
    },
    // 开始时间,时间戳
    startTime: {
      type: Number,
      value: +new Date() - yearTimestamp  // 默认,一年前
    },
    // 结束时间,时间戳
    endTime: {
      type: Number,
      value: +new Date() + yearTimestamp  // 默认,一年后
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    disabled: false, // button disable态

    dateIndex: null,
    hourValue: '',
    minValue: '',
    dateArr: [],
    hourArr: [],
    minArr: [],

    activeTime: null // 抛出的时间戳
  },

  /**
   * 组件的方法列表
   */
  methods: {
    _showChange (e) {
      if (e) {
        this.getDateTimeData()
        this.initData()
      }
    },

    handleSubmit () {
      this.bindclose()
      this.triggerEvent('change', { value: this.data.activeTime })
    },

    /**
     * 处理日期时间初始值
     */
    initData () {
      if (!this.data.activeTime) {
        const now = +new Date()
        if (now >= this.data.startTime && now <= this.data.endTime) {
          const index = this.data.dateArr.map(v => v.name).indexOf(utils.formatTime(now, '{m}月{d}日周{a}'))
          let tmp = this.data.dateArr
          tmp[index].name = '今天'

          this.setData({
            dateIndex: index,
            hourValue: utils.formatTime(now, '{h}'),
            minValue: utils.formatTime(now, '{i}'),
            dateArr: tmp,
            activeTime: now
          })
        }
      }
    },

    /**
     * 获取日期时间数据,思路:获取固定的小时、分钟数组,将开始时间、结束时间进行年月天对比,取其差值,从而形成对应日期数组
     */
    getDateTimeData () {
      const mins = []
      const hours = []
      let dates = []

      // 获取小时、分钟数组
      for (let i = 0; i < 60; i++) {
        mins.push(i.toString().length < 2 ? '0' + i : i.toString())
      }
      for (let j = 0; j < 24; j++) {
        hours.push(j.toString().length < 2 ? '0' + j : j.toString())
      }

      // 开始时间需小于结束时间
      if (this.data.startTime >= this.data.endTime) {
        console.log('error_params_not_valid')
        return
      }

      // 获取日期数组
      dates = this.getDateData()

      this.setData({
        hourArr: hours,
        minArr: mins,
        dateArr: dates
      })
    },

    getDateData () {
      const startDate = new Date(this.data.startTime)
      const endDate = new Date(this.data.endTime)
      const nowYear = new Date().getFullYear()
      const startYear = startDate.getFullYear()
      const endYear = endDate.getFullYear()
      const startMonth = startDate.getMonth()
      const endMonth = endDate.getMonth()
      const startDay = startDate.getDate()
      const endDay = endDate.getDate()

      const dateArr = []

      // 处理逻辑:判断年限是否相同;若同年则判断是否同月,同月则直接添加日期;不同月则从开始时间月份处理,同时处理月日边界问题;以此类推...
      let time = 0
      // 同年
      if (startYear === endYear) {
        // 同月
        if (startMonth === endMonth) {
          for (let day = startDay; day <= endDay; day++) {
            time = new Date(startYear, startMonth, day)
            dateArr.push({ name: startYear === nowYear ? utils.formatTime(time, '{m}月{d}日周{a}') : utils.formatTime(time, '{y}年{m}月{d}日周{a}'), value: time })
          }
        } else {
          // 不同月
          let tmpDay = startDay
          let lastMonth = endMonth
          for (let month = startMonth; month <= lastMonth; month++) {
            // 默认获取的月份方法得到月份值是0-11,该方法获取天数的月份值是1-12
            const days = month === lastMonth ? endDay : new Date(startYear, month + 1, 0).getDate()

            for (let day = tmpDay; day <= days; day++) {
              time = new Date(startYear, month, day)
              dateArr.push({ name: startYear === nowYear ? utils.formatTime(time, '{m}月{d}日周{a}') : utils.formatTime(time, '{y}年{m}月{d}日周{a}'), value: time })

              // 处理边界(日)
              day === days && (tmpDay = 1)
            }
          }
        }
      } else {
        // 不同年
        let tmpDay = startDay
        let tmpMonth = startMonth
        let lastMonth = 11

        for (let year = startYear; year <= endYear; year++) {
          for (let month = tmpMonth; month <= lastMonth; month++) {
            // 默认获取的月份方法得到月份值是0-11,该方法获取天数的月份值是1-12
            const days = (month === lastMonth && year === endYear) ? endDay : new Date(startYear, month + 1, 0).getDate()

            for (let day = tmpDay; day <= days; day++) {
              time = +new Date(year, month, day)
              dateArr.push({ name: year === nowYear ? utils.formatTime(time, '{m}月{d}日周{a}') : utils.formatTime(time, '{y}年{m}月{d}日周{a}'), value: time })

              // 处理边界(日)
              day === days && (tmpDay = 1)
            }

            // 处理边界(月)
            month === lastMonth && (tmpMonth = 0)
            month === lastMonth && year === endYear - 1 && (lastMonth = endMonth)
          }
        }
      }

      return dateArr
    },

    dateChange (e) {
      const day = this.data.dateArr[e.detail.value[0]]
      let time = day.value + (Number(this.data.hourValue) * 3600 + Number(this.data.minValue) * 60) * 1000
      this.setData({
        dateIndex: e.detail.value[0],
        activeTime: time
      })
    },

    hourChange (e) {
      const hour = Number(this.data.hourArr[e.detail.value[0]])
      let time = this.data.dateArr[this.data.dateIndex].value + (hour * 3600 + Number(this.data.minValue) * 60) * 1000
      this.setData({
        hourValue: this.data.hourArr[e.detail.value[0]],
        activeTime: time
      })
    },

    minChange (e) {
      const min = Number(this.data.minArr[e.detail.value[0]])
      let time = this.data.dateArr[this.data.dateIndex].value + (Number(this.data.hourValue) * 3600 + min * 60) * 1000
      this.setData({
        minValue: this.data.minArr[e.detail.value[0]],
        activeTime: time
      })
    },

    bindclose () {
      this.setData({
        show: false
      })
    },

    bindpickstart () {
      this.setData({
        disabled: true
      })
    },

    bindpickend () {
      this.setData({
        disabled: false
      })
    },

    preventTouchMove () {
      // 阻止半屏状态下 页面滑动
    }

  }
})

wxss代码

.date-time-container {
  min-height: 350px !important;
  display: flex;
  flex-direction: column;
}

.date-time-body {
  width: 100% !important;
  display: flex;
  flex: auto;
}

.date-time-oper {
  flex: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.selector-panel {
  width: 80px;
  display: flex;
  align-items: center;
}
.selector-panel-date {
  flex: auto;
}

.selector-picker {
  width: 100%;
  height: 80%;
  font-size: 24px;
}

.selector-item {
  line-height: 50px !important;
  text-align: center;
}

.selector-item-date{
  font-size: 16px;
}

.oper-btn {
  width: 45% !important;
  height: 40px !important;
}

有关微信小程序之日期时间筛选器实现(支持年月日时分)的更多相关文章

  1. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  4. ruby - 检查日期是否在过去 7 天内 - 2

    我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

  5. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  6. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  7. ruby-on-rails - ruby 日期方程不返回预期的真值 - 2

    为什么以下不同?Time.now.end_of_day==Time.now.end_of_day-0.days#falseTime.now.end_of_day.to_s==Time.now.end_of_day-0.days.to_s#true 最佳答案 因为纳秒数不同:ruby-1.9.2-p180:014>(Time.now.end_of_day-0.days).nsec=>999999000ruby-1.9.2-p180:015>Time.now.end_of_day.nsec=>999999998

  8. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

随机推荐