需求逻辑
- 今天到明天预约时间范围是下一分钟开始往后的24小时
- 切换每列数据时,其他列对应的数据要自动切换到合适的范围
- 每次打开预约开关时,预约的数据列表及时更新
- antd-mobile的PickerView组件有个bug:在onChange事件中切换数据源后,有时可以自动再次触发onChange以获取正确的值,但有时不会。所以这里在每次切换数据源后,根据情况手动获取picker的值以进行修正
完成效果如下:
组件代码
import { useState, useEffect } from 'react'
import { PickerView, Switch } from 'antd-mobile'
let day = [{
label: '今天',
value: '今天',
}, {
label: '明天',
value: '明天',
}]
export default function AppointmentPickerView({ setTime, time, setOpen, open, getInitTime }) {
const [dataTodayH, setDataTodayH] = useState([])
const [dataTomorrowH, setDataTomorrowH] = useState([])
const [dataTodayFirstM, setDataTodayFirstM] = useState([])
const [dataTomorrowLastM, setDataTomorrowLastM] = useState([])
const [dataM60, setDataM60] = useState([])
const [sourceData, setSourceData] = useState([])
const [isNow23H59M, setIsNow23H59M] = useState(false)
useEffect(() => {
// const now = new Date(2010,1,1,23,1,1,1)
const now = new Date()
let nowH = now.getHours()
let nowM = now.getMinutes()
getInitTime(now)
let isNow59M = nowM === 59
setIsNow23H59M(isNow59M && nowH === 23)
let min, max
// 生成picker每列数据的通用方法
const getPickerColData = (min, max, step = 1) => {
let [arr, i] = [[], min]
while (i <= max) {
arr.push({
label: i,
value: i,
})
i += step
}
return arr
}
// 今天小时
min = isNow59M ? nowH + 1 : nowH
setDataTodayH(getPickerColData(min, 23))
// 明天小时
// max = isNow59M ? nowH - 1 : nowH
max = isNow23H59M ? 23 : nowH
setDataTomorrowH(getPickerColData(0, max))
// 今天第一个小时的分钟
min = isNow59M ? 0 : nowM + 1 // 判断进位
setDataTodayFirstM(getPickerColData(min, 59))
// 明天最后一个小时的分钟
max = isNow59M ? 59 : nowM
setDataTomorrowLastM(getPickerColData(0, max))
// 60分钟
setDataM60(getPickerColData(0, 59))
}, [open])
// 设置数据源
useEffect(() => {
if (isNow23H59M) {
setSourceData([[day[1]], dataTomorrowH, dataM60])
return
}
setSourceData([day, dataTodayH, dataTodayFirstM])
}, [day, dataTodayH, dataTodayFirstM])
// 滑动选择事件
const onPickerChange = (v, index) => {
// antd-mobile的PickerView组件有个bug:
// 在onChange事件中切换数据源后,有时可以自动再次触发onChange以获取正确的值,但有时不会。
// 所以这里在每次切换数据源后,根据情况手动获取picker的值以进行修正
const tomorrowLastH = dataTomorrowH[dataTomorrowH.length - 1].value
if (index === 0) { // 选择第一列今天明天
let dataH = dataTodayH
let dataM = dataM60
if (v[0] === '今天') {
dataM = dataTodayFirstM
v[1] = dataTodayH[0].value
v[2] = dataTodayFirstM[0].value
} else { // 选择明天
dataH = dataTomorrowH
if (dataTodayH[0].value !== tomorrowLastH) { // 整点时今天的小时数明天没有
v[1] = 0
}
if (v[1] === tomorrowLastH) { // 明天仅剩最后一个小时
dataM = dataTomorrowLastM
v[2] = 0
}
}
setSourceData(data => {
data[1] = dataH
data[2] = dataM
return data
})
} else if (index === 1) { // 选择第二列小时
let dataM = dataM60
if (v[0] === '今天' && v[1] === dataTodayH[0].value) { // 今天第一个小时
dataM = dataTodayFirstM
}
if (v[0] === '明天' && v[1] === tomorrowLastH) { // 明天最后一个小时
dataM = dataTomorrowLastM
}
setSourceData(data => {
data[2] = dataM
return data
})
}
console.log('~~v', v)
setTime(v)
}
// 预约开关
const onSwitchChange = v => {
setOpen(v)
if (!v) return
if (isNow23H59M) {
setTime(['明天', 0, 0])
return
}
setTime(['今天', dataTodayH[0].value, dataTodayFirstM[0].value])
}
return <>
<div className='itemForPicker' >
<span className='content'>预约 (完成时间)</span>
<Switch checked={open} onChange={onSwitchChange} />
</div>
{open &&
<div className="openAppointmentInner">
<PickerView cascade={false} data={sourceData} value={time} onChange={onPickerChange} />
</div>
}
</>
}
组件引用
<AppointmentPickerView
setTime={setAppointmentTime} // 设置时间的回调
time={appointmentTime} // 时间的值
setOpen={setAppointmentOpenChangeBg} // 设置预约开关的回调
open={appointmentOpen} // 预约开关的值
getInitTime={setAppointmentInitTime} // 预约组件参考的时间,在编辑页停留时间过长判断是否跨过0点时用
/>
样式代码
.openAppointmentInner {
.am-picker {
position: relative;
&:before {
content: ':';
position: absolute;
left: 67%;
transform: translate(-50%, -50%);
font-size: 1rem;
}
}
}