小程序日期(日历)时间 选择器组件

Posted 少年的范儿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序日期(日历)时间 选择器组件相关的知识,希望对你有一定的参考价值。

封装一个小程序日期(日历)时间 选择器组件

简要说明:
一共两个版本 ,date-time-picker 和 date-time-picker-plus.
date-time-picker 弹窗层是 基于 vant-weappvan-popup 组件实现的
date-time-picker-plus 弹窗层是 基于 小程序 自带的 page-container 组件来实现的
*注意:date-time-picker 需要下载 vant-weapp 组件库配合一起使用,date-time-picker-plus 可以直接在小程序里面使用

首先看一下效果图

组件代码

两个版本代码几乎一样,只是弹窗层用的不一样,这里我就用 基于 vant-weapp UI一起使用的版本来写

  • wxml 中
<view class="date-time-picker-box">
  <van-popup show=" show " zIndex="9999" round position="bottom" custom-style="border-radius:16px 16px 0 0;min-height:355px;max-height:400px;" bind:close="handleCancel">
    <view class="date-time-picker-title">
      <view class="date-time-picker-nav">
        <view bindtap="handleChangeTag" data-index="0" class="tag date-tag currentIndex == 0 ? 'active' : ''">
          yearmonthday</view>
        <view bindtap="handleChangeTag" data-index="1" class="tag time-tag currentIndex == 1 ? 'active' : ''">
          hour:minute
        </view>
        <view class="date-time-line" style="left:leftpx;width: widthpx;"></view>
      </view>
      <button bindtap="handleConfirm" class="date-picker-oper-btn">确定</button>
    </view>
    <view class="date-time-picker-content">
      <!-- 日历主体 -->
      <swiper current="currentIndex" style="height:100%;">
        <swiper-item catchtouchmove="catchtouchmove">
          <view class="swiper-item ">
            <!-- 日历组件 -->
            <calendar bindselectDay="selectDay" bindgetDateList="getDateList" vertical="vertical"></calendar>
          </view>
        </swiper-item>
        <swiper-item catchtouchmove="catchtouchmove">
          <view class="swiper-item ">
            <div class="date-time-item">
              <picker-view indicator-style="height: 50px;" style="width: 100%; height: 200px;text-align:center;" value="value" bindpickstart="bindpickstart" bindpickend="bindpickend" bindchange="bindChange">
                <picker-view-column>
                  <view wx:for="hours" wx:key="*this" style="line-height: 50px">item</view>
                </picker-view-column>
                <picker-view-column>
                  <view wx:for="minutes" wx:key="*this" style="line-height: 50px">
                    item</view>
                </picker-view-column>
              </picker-view>
            </div>
          </view>
        </swiper-item>
      </swiper>
    </view>
  </van-popup>
</view>
  • wxss 中
.date-time-picker-box .date-time-picker-title 
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0rpx 48rpx;
  height: 112rpx;
  box-sizing: border-box;
  border-bottom: 2rpx solid #E5E3E3;
  max-height: 400px;
  /* overflow: hidden; */


.date-time-picker-box .date-time-picker-title .title-txt 
  font-size: 17px;
  font-weight: 500;
  color: #1F1E1E;


.date-time-picker-box .date-time-picker-title .date-picker-oper-btn 
  width: 104rpx;
  height: 64rpx;
  line-height: 64rpx;
  text-align: center;
  font-size: 28rpx;
  border-radius: 8rpx;
  font-weight: 500;
  margin: 0;
  padding: 0;
  color: var(--themeTxtColor);
  background-color: var(--themeColor);


.date-time-picker-box .date-time-picker-title .cancel-btn 
  color: #5C5959;
  background-color: #fff;
  text-align: left;


.date-time-picker-box .date-time-picker-title .date-time-picker-nav 
  position: relative;
  display: flex;
  align-items: center;
  font-weight: 500;
  color: #8F8D8D;
  height: 100%;


.date-time-picker-box .date-time-picker-nav .date-tag 
  margin-right: 32rpx;


.date-time-picker-box .date-time-picker-nav .tag 
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;


.date-time-picker-box .date-time-picker-nav .tag.active 
  color: #1F1E1E;


.date-time-picker-box .date-time-picker-nav .date-time-line 
  position: absolute;
  bottom: 0;
  width: 40rpx;
  height: 4rpx;
  background-color: #5C5959;
  transition: all 0.3s linear;


.date-picker-btn-box 
  display: flex;
  font-weight: 600;
  margin-top: 40rpx;
  color: var(--themeColor);
  justify-content: space-around;


.date-time-picker-content 
  height: 600rpx;
  box-sizing: border-box;

  • json 中

  "component": true,
  "usingComponents": 
    "van-popup": "@vant/weapp/popup/index",
    "calendar":"./calendar/index"
  

  • js 中
const date = new Date(); // 获取系统日期
var getYear = date.getFullYear(),
  getMonth =
    date.getMonth() + 1 < 10
      ? "0" + (date.getMonth() + 1)
      : date.getMonth() + 1,
  getDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(),
  isScroll = false;
Component(
  properties: 
    show: 
      // 控制弹窗显示和隐藏
      type: Boolean,
      value: false,
    ,
    vertical: 
      // 是否垂直滑动
      type: Boolean,
      value: false,
    ,
    /**
     * type 时间类型 分钟, 支持两种类型 time 和 part , 默认是part
     * time : 2023-02-03 09:00  正常的日期时间 (例如: 09:30 、11:10 、12:36 ...)
     * part :  2023-02-03 09:00  分钟只包含 00 和 30 (例如: 09:30 、11:00 、12:30 ...)
     */
    type: 
      type: String,
      value: "part", // time 和 part
    ,
  ,
  data: 
    year: getYear, // 当前已选年份,默认是当年
    month: getMonth, // 当前已选月份,默认是当月
    day: getDate, // 当前已选天,默认是当天
    hour: "09", // 当前已选小时, 默认 09 点
    minute: "00", // 当前已选分钟,默认00
    hours: [], // 时间选择器,存放的小时数组 0 - 23
    minutes: [], // 时间选择器,存放的分钟数组 00 和 30 或者 00 - 59
    value: [0, 0], // 时间选择器,当前选择时间 索引
    currentIndex: 0, // tab导航当前索引
    left: 0, // tab导航 底部线条 当前离左侧位置 距离
    width: 123, // tab导航 底部线条 当前 宽度
    firstEnter: true, // 是否第一次打开, 默认true
  ,
  lifetimes: 
    attached: function () 
      this.initDateTimePicker();
    ,
    moved: function () ,
    detached: function () ,
  ,
  methods: 
    // 日期改变的回调
    selectDay( detail ) 
      console.log(detail, "selectDay detail");
      let  year, month, day  = detail;
      month = month > 9 ? month : "0" + month;
      day = day > 9 ? day : "0" + day;
      if (!this.data.firstEnter) 
        setTimeout(() => 
          this.setData( currentIndex: 1 );
          this.changeline();
        , 300);
      
      this.setData( year, month, day, firstEnter: false );
    ,
    // 切换导航
    handleChangeTag(e) 
      this.setData(
        currentIndex: e.currentTarget.dataset.index,
        firstEnter: false,
      );
      this.changeline();
    ,
    // 渲染横线位置的方法
    changeline() 
      let _this = this;
      // SelectorQuery.in(Component component): 将选择器的选取范围更改为自定义组件 component 内
      let query = wx.createSelectorQuery().in(this);
      // select() 在当前页面下选择第一个匹配选择器 selector 的节点
      // boundingClientRect() 添加节点的布局位置的查询请求。相对于显示区域,以像素为单位
      query.select(".active").boundingClientRect();
      // SelectorQuery.exec(function callback) 执行所有的请求
      // 请求结果按请求次序构成数组,在callback的第一个参数中返回
      query.exec(function (res) 
        // console.log(res);
        _this.setData(
          left: res && res[0].left - 24,
          width: parseInt(res[0].width),
        );
      );
    ,
    /***
     * 滚动选择时触发change事件,
     * 可以查看小程序官方文档了解
     */
    bindChange(e) 
      const val = e.detail.value;
      this.setData(
        hour: this.data.hours[val[0]],
        minute: this.data.minutes[val[1]],
      );
    ,
    // 滚动开始
    bindpickstart() 
      isScroll = true;
    ,
    //滚动结束
    bindpickend() 
      isScroll = false;
    ,
    // 点击取消按钮,关闭日期选择器
    handleCancel() 
      this.setData( show: false );
    ,
    // 点击确定按钮
    handleConfirm() 
      let  year, month, day, hour, minute  = this.data;
      // console.log(year, month, day, hour,  minute)
      // 判断用户选择时间滚动是否结束,解决 picker-view bindChange 延迟问题
      if (isScroll) return;
      const timeStr =
        year + "-" + month + "-" + day + " " + hour + ":" + minute;
      this.triggerEvent("onSelectDate", timeStr);
      this.setData( show: false, currentIndex: 0 );
      this.changeline();
    ,
    // 初始化 picker 时间数据
    initDateTimePicker() 
      let hours = [],
        minutes = [];
      // 存放小时的数组
      for (let i = 0; i <= 23; i++) 
        if (i < 10) 
          i = "0" + i;
        
        hours.push(i);
      
      // 存放分钟的数组
      if (this.data.type === 'time') 
        for (let i = 0; i <= 59; i++) 
          if (i < 10)  i = '0' + i 
          minutes.push(i)
        
       else 
        minutes = ['00', '30']
      
      this.setData( hours, minutes );
      this.setData( value: [9, 0] );
    ,
    // 加载日历数据
    getDateList() 
      // 防止滑动时 tab导航切换到时间模块
      this.setData( firstEnter: true );
    ,
    // 禁止 日期时间 swiper 滑动
    catchtouchmove() 
      return false;
    ,
  ,
);

简要说明:
在 index.wxml 文件 中 ,会有一个 日历组件
<calendar bindselectDay="selectDay" bindgetDateList="getDateList" vertical="vertical"></calendar>
这个组件会放在 date-time-picker 组件目录下,这里我就不把代码贴出来了,可以直接去我的远程仓库去拉取。
组件代码仓库地址:Gitee | Github 或者直接复制下面的链接 直接下载代码

  • Gitee 远程仓库下载链接
git clone https://gitee.com/junfeng535/wx-date-time-picker.git
  • Github 远程仓库下载链接
git clone https://github.com/junfeng-git/wx-date-time-picker.git

反应日期选择器日期范围

【中文标题】反应日期选择器日期范围【英文标题】:React-datepicker date range 【发布时间】:2020-10-12 00:06:15 【问题描述】:

我正在为我的应用程序使用 react-typescript。我正在使用React-datepicker 包。我想创建一个全球日期范围组件。所以我可以将它重用于不同的组件。我使用 React-datepicker 的 customInput 进行样式设置并创建了显示时间的按钮。我关注this link 的日期范围逻辑。在日历日期范围内似乎有效,这是image。我想在customInput's 按钮中显示选择开始日期和结束日期。但是一旦我选择了结束日期,两个按钮都会显示结束日期的值。我不知道如何解决它。

这是日期范围组件

import React,  useState  from "react";
import DatePicker,  ReactDatePickerProps  from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import moment from "moment";
import  Button  from "components/buttons";

export interface IDatepicker 
  value: 
    start: Date;
    end: Date;
  ;
  onBlur?: (i: React.FocusEvent<HTMLInputElement>) => void;
  onFocus?: (i: React.FocusEvent<HTMLInputElement>) => void;
  onChange: (i: Date) => void;
  buttonComponent?: JSX.Element;
  withPortal?: boolean;
  showTimeSelect?: boolean;
  dateFormat?: string;
  timeFormat?: string;


export default (
  value,
  onChange,
  onBlur,
  onFocus,
  buttonComponent,
  withPortal = false,
  showTimeSelect = false,
  dateFormat = "MMMM d, yyyy h:mm aa",
  timeFormat = "HH:mm"
: IDatepicker) => 

  const handleChange = (date: Date | null) => 
    date && onChange(date);
  ;

  return (
    <div style= display: "flex" >
      <DatePicker
        selected=value.start
        onChange=handleChange
        showTimeSelect=showTimeSelect
        selectsStart
        startDate=value.start
        endDate=value.end
        customInput=
          <Button>
            moment(value.start)
              .format("MMMM Do YYYY")
              .toString()
          </Button>
        
      />
      <div style= color: "black" >-</div>
      <DatePicker
        selected=value.end
        onChange=handleChange
        showTimeSelect=showTimeSelect
        selectsEnd
        startDate=value.start
        endDate=value.end
        minDate=value.start
        customInput=
          <Button>
            moment(value.end)
              .format("MMMM Do YYYY")
              .toString()
          </Button>
        
      />
    </div>

  );
;

这是我正在使用的日期范围组件

import React from "react";

import TimeLine from "components/datepicker";


export default () => 
const [state, setState] = useState(new Date());
  return (
    <div>
      <TimeLine
          value= start: state, end: state 
          onChange=setState
        />
    </div>
  );
;

【问题讨论】:

每次设置结束日期时,开始日期都会被覆盖,因为您在开始和结束日期DatePicker 实例中使用了相同的handleChange 回调。您需要在您所在的州保留两个日期,而不是一个日期,请参阅下面@Mahesh 的答案。 【参考方案1】:

您对开始和结束都使用单一状态值。我认为这是你问题的根本原因。 而不是使用

const [state, setState] = useState(new Date());

我认为您应该将其声明为

const [startDate, setStartDate] = useState(new Date());
const [endDate, setEndDate] = useState(new Date());

【讨论】:

以上是关于小程序日期(日历)时间 选择器组件的主要内容,如果未能解决你的问题,请参考以下文章

iview日期组件不可选日期怎么用

日期选择器的 Android 日历视图

微信小程序实现地址选择组件

微信小程序uniapp封装多列选择器组件

微信小程序-自定义picker选择器

微信小程序滑动选择器怎么实现