实现 日期加具体时间的微信小程序组件

227 阅读3分钟

 项目:  Taro+react 微信小程序

1.需求:

  •  类似外卖提货的,左侧可选未来n天内的某一天提货, 右边可以选具体时+分
  •  限制某些时间段儿不能选

 ​编辑

2. 实现:        

  1. 首先: 检查Taro文档(基本与微信小程序一致),不支持这种 日期+时间的 ,so只能手撸一个

  2. 微信的picker组件有这种多列模式,基于这个写

 ​编辑

  1. 这里的基本实现很简单,把每一列的数据铺进去就ok

3.难点在于

 

  1. 不可选当前时间之前的

    1. 如果选到当前时间之前的时间则把时间改成第二天的,或者改到当前时间
  2. 店铺有营业时间,不能选到 营业时间之内的时间

    1. 如果不在营业时间内则播到当天的最早营业时间
    2. 如果选择的就是当天,则播到当前时间

遇到的bug:

回播: 自动改选中的每列的值

选择的时间不在营业时间范围内时回播到当前时间,这里用到了多列选择器的 bindcolumnchange

​编辑

第一次回拨的时候,把每列选中的值改成当前时间了,第二次选错的时候接着回播到当前时间,此时由于上一次的值也是当前时间, 每列选中的值未改变,则不生效,一开始一度以为是picker组件的问题,

所以这里列改变的时候,就直接先让列选中值改变了,再来判断需不需要回播

​编辑​编辑

代码: 


import Taro, { Component } from '@tarojs/taro'
import { View, Text, Picker } from '@tarojs/components'
import '../../app.less'
import './index.less'

/** 
 * 选择 日期 + 时 + 分 的组件
 * 1. 拿到 yyyy/mm/dd hh:mm 该格式的时间返回
 * 2. 传一个参数, 限制时间的范围 -> 当天只能选择现在时间之后
 */

export default class diyPicker extends Component {
    state = {
        multiArray: 
            [
                ['今天', '明天', '后天'],
                Array.from({length: 24}, (_, i) => (i).toString().padStart(2, '0')),
                Array.from({length: 60}, (_, i) => (i).toString().padStart(2, '0'))
            ],
        multiIndex: [0, 0, 0],
        timeSel: true
    }
    static defaultProps = {
        isDisablePastTime: false, // 是否限制不选旧时间,默认不限制
        businessHours: ['00:00','23:59'], // 可选的时间范围,....邪门的营业时间 12:00-03:00, 传值少个0都不行哦
        dayNum: 8,
    }

    componentDidMount() {
        const multiArray = this.countDateFn(this.props.dayNum) // 计算第一列
        const nowHours = new Date().getHours()
        const nowMinute = new Date().getMinutes()
        this.setState({
            multiIndex: [0, nowHours, nowMinute],
            multiArray
        })
    }

    countDateFn(dayNum) {
        // dayNum 需要的时间天数 当天为1, 2为今天和明天
        // dateArr 最终返回日期数组
        const timeStampArr = [] // 最终返回时间戳数组
        const dayTime = 1000 * 60 * 60 * 24
        var weeks = ["日", "一", "二", "三", "四", "五", "六"]
        for (let i = 0; i < dayNum; i++) {
            const str =  Date.now() + dayTime * i
            timeStampArr.push(str)
        }
        // const dateArr = timeStampArr.map(x => new Date(x).getMonth() + 1 + '-' + new Date(x).getDate()) // 不加星期几
        const dateArr = timeStampArr.map(x => new Date(x).getMonth() + 1 + '-' + new Date(x).getDate() + `(周${weeks[new Date(x).getDay()]})`)
        let cloneMultiArray = JSON.parse(JSON.stringify(this.state.multiArray))
        cloneMultiArray[0] = dateArr
        return cloneMultiArray
    }


    onDateChange(e) {
        // console.log(' e.detail.value-----65',  e.detail.value);
        const {multiArray} = this.state
        const { businessHours } = this.props
        let startTime = businessHours[0]
        let endTime = businessHours[1]
        const nowHours = new Date().getHours()
        const nowMinute = new Date().getMinutes()
        let arr = e.detail.value
        // if (this.props.isDisablePastTime) { // 不能选过去的时间,已在选择每列的时候限制
        //     if (arr[0] == 0 && arr[1] < nowHours) {
        //         arr = [arr[0], nowHours, nowMinute]
        //     }
        //     if (arr[0] == 0 && arr[1] == nowHours && arr[2] < nowMinute) {
        //         arr = [arr[0], arr[1], nowMinute]
        //     }
        // }
        // 选择的时间不在营业范围时间,则改为当前时间
        const selectHour =  arr[1].toString().padStart(2, '0')
        const selectTimeStr = selectHour + ':' + arr[2].toString().padStart(2, '0') // 选择的时间
        if (endTime < startTime) { // 跨天的 例: 12:00-3:00(营业时间) ; 3点 -12点 不在营业时间
            if (startTime > selectTimeStr && selectTimeStr > endTime) { 
                arr = [0, nowHours, nowMinute]
                Taro.showToast({
                    title: `选择时间不在营业时间内,营业时间为${startTime}-次日${endTime}`,
                    icon: 'none',
                    duration: 2000
                })
            }
        }else {
            if (!(startTime <= selectTimeStr && selectTimeStr <= endTime)) { //  例: 9:00-20:00(营业时间)
                arr = [0, nowHours, nowMinute]
                Taro.showToast({
                    title: `选择时间不在营业时间内,营业时间为${startTime}-${endTime}`,
                    icon: 'none',
                    duration: 2000
                })
            }
        }
        const yearStr = new Date().getFullYear()
        const dateStr = multiArray[0][arr[0]].replace(/-/g, '/').replace(/((.*))/, "")
        const hourStr = multiArray[1][arr[1]]
        const minuteStr = multiArray[2][arr[2]]
        const finallyTime = yearStr + '/' + dateStr + ' ' + hourStr + ':' + minuteStr + ':' + '00'
        // console.log('finallyTime', finallyTime);
        this.props.onCallBackFn(finallyTime)  // 返回给调用组件的方法- 时间示例(2024-1-23 01:07)
        let timeSel = !(arr[0] != 0 || ( arr[1] != nowHours || arr[2] != nowMinute))
        this.setState({
            multiIndex: arr,
            timeSel: timeSel
        })
    }

    onDateColumnChange(e) {
        const { multiArray , multiIndex} = this.state
        const { businessHours, isDisablePastTime } = this.props
        let startTime = businessHours[0]
        let endTime = businessHours[1]
        // console.log('修改的列为', e.detail.column, ',值为', e.detail.value, businessHours);
        let cloneMultiIndex = JSON.parse(JSON.stringify(multiIndex))
        let cloneMultiArray = JSON.parse(JSON.stringify(multiArray))
        cloneMultiIndex[e.detail.column] = e.detail.value;
        this.setState({
            multiIndex: cloneMultiIndex,
            multiArray: cloneMultiArray
        },() => {
            // 重新赋值,不然值未改变-选中的值不更新
            const nowHours = new Date().getHours()
            const nowMinute = new Date().getMinutes()
            if (this.props.isDisablePastTime) {
                switch (e.detail.column) {
                    case 0:
                        if (isDisablePastTime && (cloneMultiIndex[1] < nowHours || (cloneMultiIndex[1] == nowHours && e.detail.value < nowMinute))) { // 天数变成当天注意判断时间
                            cloneMultiIndex[1] = nowHours
                            cloneMultiIndex[2] = nowMinute
                        }
                        break
                    case 1:
                        if (isDisablePastTime && cloneMultiIndex[0] == 0  && e.detail.value < nowHours) { // 当天选过去时间则变成第二天
                            if (cloneMultiArray[0].length == 1) {
                                cloneMultiIndex[1] = nowHours
                                cloneMultiIndex[2] = nowMinute
                            }else {
                                cloneMultiIndex[0] = 1
                            }
                        }else {
                            // 限制营业时间内
                            const selectHour =  e.detail.value.toString().padStart(2, '0')
                            const selectTimeStr = selectHour + ':' + cloneMultiIndex[2].toString().padStart(2, '0') // 选择的时间
                            if (endTime < startTime) { // 跨天的 12:00-3:00(营业时间) ; 3点 -12点 不在营业时间
                                if (startTime > selectTimeStr && selectTimeStr > endTime) { // 前一段
                                    if (cloneMultiIndex[0] == 0) {
                                        cloneMultiIndex = [0, nowHours, nowMinute]
                                    }else {
                                        cloneMultiIndex = [cloneMultiIndex[0], Number(startTime.split(':')[0]), Number(startTime.split(':')[1])]
                                    }
                                    Taro.showToast({
                                        title: `选择时间不在营业时间内,营业时间为${startTime}-次日${endTime}`,
                                        icon: 'none',
                                        duration: 2000
                                    })
                                }
                            }else {
                                if (!(startTime <= selectTimeStr && selectTimeStr <= endTime)) { //  例: 9:00-20:00(营业时间)
                                    if (cloneMultiIndex[0] == 0) {
                                        cloneMultiIndex = [0, nowHours, nowMinute]
                                    }else {
                                        cloneMultiIndex = [cloneMultiIndex[0], Number(startTime.split(':')[0]), Number(startTime.split(':')[1])]
                                    }
                                    Taro.showToast({
                                        title: `选择时间不在营业时间内,营业时间为${startTime}-${endTime}`,
                                        icon: 'none',
                                        duration: 2000
                                    })
                                }
                            }
                        }
                        break;
                    case 2:
                        // 限制不能选当前时间之前
                        if (isDisablePastTime && cloneMultiIndex[0] == 0  && cloneMultiIndex[1] == nowHours && e.detail.value < nowMinute) {
                            if (cloneMultiArray[0].length == 1) {
                                cloneMultiIndex[2] = nowMinute
                            }else {
                                cloneMultiIndex[0] = 1
                            }
                        }else {
                            // 限制营业时间内
                            const selectMinute =  e.detail.value.toString().padStart(2, '0')
                            const selectMinuteStr = cloneMultiIndex[1].toString().padStart(2, '0') + ':' + selectMinute // 选择的时间
                            if (endTime < startTime) { // 跨天的 12:00-3:00(营业时间) ; 3点 -12点 不在营业时间
                                if (startTime > selectMinuteStr && selectMinuteStr > endTime) { // 前一段
                                    if (cloneMultiIndex[0] == 0) {
                                        cloneMultiIndex = [0, nowHours, nowMinute]
                                    }else {
                                        cloneMultiIndex = [cloneMultiIndex[0], Number(startTime.split(':')[0]), Number(startTime.split(':')[1])]
                                    }
                                    Taro.showToast({
                                        title: `选择时间不在营业时间内,营业时间为${startTime}-次日${endTime}`,
                                        icon: 'none',
                                        duration: 2000
                                        })
                                }
                            }else {
                                if (!(startTime <= selectMinuteStr && selectMinuteStr <= endTime)) { //  例: 9:00-20:00(营业时间)
                                    if (cloneMultiIndex[0] == 0) {
                                        cloneMultiIndex = [0, nowHours, nowMinute]
                                    }else {
                                        cloneMultiIndex = [cloneMultiIndex[0], Number(startTime.split(':')[0]), Number(startTime.split(':')[1])]
                                    }
                                    Taro.showToast({
                                        title: `选择时间不在营业时间内,营业时间为${startTime}-${endTime}`,
                                        icon: 'none',
                                        duration: 2000
                                    })
                                }
                            }
                        }
                        break;
                }
            }
              
            this.setState({
                multiIndex: cloneMultiIndex,
                multiArray: cloneMultiArray
            })
        })
    }

  render() {
    const { multiArray, multiIndex, timeSel} = this.state
    return <View>
        <Picker mode='multiSelector' onChange={this.onDateChange} onColumnChange={this.onDateColumnChange} range={multiArray} value={multiIndex}>
            <View className='picker c222 fs28 bold fsbc f1'>
                {timeSel ? '尽快提货 ' : ''}
                {multiArray[0][multiIndex[0]]} {multiArray[1][multiIndex[1]]}:{multiArray[2][multiIndex[2]]}
                <Text className='fs24 c999 iconfont'>&#xe632;</Text>
            </View>
        </Picker>
    </View>
  }
}