项目: Taro+react 微信小程序
1.需求:
- 类似外卖提货的,左侧可选未来n天内的某一天提货, 右边可以选具体时+分
- 限制某些时间段儿不能选
2. 实现:
1. 首先: 检查Taro文档(基本与微信小程序一致),不支持这种 日期+时间的 ,so只能手撸一个
2. 微信的picker组件有这种多列模式,基于这个写
3. 这里的基本实现很简单,把每一列的数据铺进去就ok
3.难点在于
- 不可选当前时间之前的
- 如果选到当前时间之前的时间则把时间改成第二天的,或者改到当前时间
- 店铺有营业时间,不能选到 营业时间之内的时间
- 如果不在营业时间内则播到当天的最早营业时间
- 如果选择的就是当天,则播到当前时间
遇到的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'></Text>
</View>
</Picker>
</View>
}
}