尽管Antd4.x已经有了对应的控件,但由于公司项目是基于3.x开发,前两天刚好用到相关控件,便封装并分享出来,目的是希望能得到优化建议,遗憾的是不是ts环境不然会好用一些。
下面代码+注释=文档
,所以直接贴代码
TimeRangePicker
/**
* 引入該有一定的順序規則
* 库--組件/本地組件--本地utils--前置变量let/const--当前组件/页面
* 如果有别名,别名的引入要在相对路径之前,如:'@/utils'要在'./utils'之前
*/
/** 库 */
import React, { memo, Fragment, useState } from "react";
import moment from "moment";
/** 组件 */
import { message, TimePicker } from "antd";
/** 前置变量--let/const */
// const nowTime()[0] = moment().hours();
// const nowTime()[1] = moment().minutes();
// const nowTime()[2] = moment().seconds();
const nowTime = () => {
// 必须实时调用,否则时间不准确
return [
moment().hours(), // 当前小时数
moment().minutes() + 1, // 当前分钟数(如果不+1,当前分钟也可以选,但+1后面的秒数禁用也就没意义了,按需取用)
moment().seconds() // 当前秒数
];
};
const today = moment().format("YYYY-MM-DD");
const initTimeRange = {
/** 时间段列表--默认值,有id是为了增删时所用 */
id: 0,
startTime: "00:00:00",
endTime: "00:00:00"
};
export function Picker({
showTip /** 时间不符合的时候是否给提示,可以为Boolean或者string或者{text,duration,end()}三种格式 */,
format = "HH:mm" /** formart格式,默认HH:mm,可以设为HH:mm:ss等时分秒格式 */,
disabledNow /** 是否禁用小于当前时间的其他时间--控件在选择小时之后仍然会默认选中分钟00 */,
disabledHours /** 禁用的小时数,格式为0-23的number数组,优先级比disabledNow高 */,
disabledMinutes /** 禁用的分钟数,格式为0-59的number数组,优先级比disabledNow高 */,
disabledSeconds /** 禁用的秒数,格式为0-59的number数组,优先级比disabledNow高 */,
timeRange = initTimeRange /** 时间段--json,id/startTime/endTime都必传 */,
permission /** 返回当前组件的区间值是否合法--false是开始时间大于结束时间,可用于禁止页面其他关联的操作 */,
getRangeTime /** 获取当前时间段,以及开始/结束时间差的permission */,
...props /** 继承官方组件(不止)其他属性 */
}) {
const [thisTimeRange, setTimeRange] = useState(timeRange);
/** 获取前后时间段对照 */
const getTimeRangeState = (status, str) => {
const timeObj = { ...thisTimeRange };
if (!timeObj.startTime || !timeObj.endTime) {
return false;
}
let isOk = true;
let startHour = moment(`${today} ${timeObj.startTime}`).hours();
let startMinute = moment(`${today} ${timeObj.startTime}`).minutes();
let startSeconds = moment(`${today} ${timeObj.startTime}`).seconds();
let endHour = moment(`${today} ${timeObj.endTime}`).hours();
let endMinute = moment(`${today} ${timeObj.endTime}`).minutes();
let endSeconds = moment(`${today} ${timeObj.endTime}`).seconds();
if (status === "startTime") {
/** 开始时间的 小时数 和 分钟数 */
startHour = str ? moment(`${today} ${str}`).hours() : 0;
startMinute = str ? moment(`${today} ${str}`).minutes() : 0;
startSeconds = str ? moment(`${today} ${str}`).seconds() : 0;
}
if (status === "endTime") {
/** 结束时间的 小时数 和 分钟数 */
endHour = str ? moment(`${today} ${str}`).hours() : 0;
endMinute = str ? moment(`${today} ${str}`).minutes() : 0;
endSeconds = str ? moment(`${today} ${str}`).seconds() : 0;
}
const numHour = startHour - endHour;
const numMin = startMinute - endMinute;
const numSeconds = startSeconds - endSeconds;
if (
numHour > 0 ||
(numHour === 0 && numMin > 0) ||
(numHour === 0 && numMin === 0 && numSeconds >= 0)
) {
// 开始hour·大于·结束hour
// 开始hour·等于·结束hour 并且 开始minute·大于·结束minute
isOk = false;
}
/** 避免页面上出现多个message先销毁 */
if (!isOk) {
permission && permission(false);
if ((status === "startTime" && timeObj.endTime) || status === "endTime") {
const duration = 1.5;
let text = "结束时间必须大于开始时间";
if (showTip && typeof showTip === "string") {
text = showTip;
}
if (showTip && typeof showTip === "object") {
showTip.end && showTip.end();
}
showTip &&
message.error(
showTip.text || text,
(showTip && showTip.duration) || duration
);
}
} else {
message.destroy();
permission && permission(true);
}
return isOk;
};
/** 时间改变 */
const onTimeChange = (time, str, status) => {
/** time值不取用,因为当前"antd": "^3.13.0"的此控件有bug */
const timeArr = str.split(":") || [];
const maxHour = timeArr.length > 0 ? timeArr[0] : 0;
const maxMinute = timeArr.length > 1 ? timeArr[1] : 0;
const obj = { ...thisTimeRange };
obj[status] = str;
setTimeRange(obj);
if (str) {
const can = getTimeRangeState(status, str);
getRangeTime && getRangeTime(obj, can);
} else {
getRangeTime && getRangeTime(obj);
}
};
/** 时间面板开启/关闭事件 */
const onOpenChange = flag => {
if (!flag /** 关闭 */) {
getTimeRangeState();
}
};
/** 禁用starthours */
const getStartDisabledHours = () => {
let noHours = []; // allHours
for (let i = 0; i < +nowTime()[0]; i += 1) {
noHours.push(i);
}
if (disabledNow) {
noHours = disabledHours || noHours;
} else {
noHours = disabledHours || [];
}
return noHours;
};
/** 禁用startminutes */
const getStartDisabledMinutes = (selectedHour) /** 被选中的hour */ => {
let minutes = [];
if (selectedHour === +nowTime()[0]) {
for (let i = 0; i < +nowTime()[1]; i += 1) {
minutes.push(i);
}
}
if (disabledNow) {
minutes = disabledMinutes || minutes;
} else {
minutes = disabledMinutes || [];
}
return minutes;
};
const getStartDisabledSeconds = (selectedHour, selectedMinute) => {
let seconds = [];
if (selectedHour === +nowTime()[0] && selectedMinute === +nowTime()[1]) {
for (let i = 0; i < +nowTime()[2]; i += 1) {
seconds.push(i);
}
}
if (disabledNow) {
seconds = disabledSeconds || seconds;
} else {
seconds = disabledSeconds || [];
}
return seconds;
};
return (
<Fragment>
<TimePicker
{...props}
format={format}
defaultValue={
thisTimeRange.startTime
? moment(thisTimeRange.startTime, format)
: null
}
disabledHours={() => getStartDisabledHours()}
disabledMinutes={e => getStartDisabledMinutes(e)}
onOpenChange={e => onOpenChange(e, "startTime")}
onChange={(e, str) => onTimeChange(e, str, "startTime")}
/>
<TimePicker
{...props}
format={format}
defaultValue={
thisTimeRange.endTime ? moment(thisTimeRange.endTime, format) : null
}
disabledHours={() => getStartDisabledHours()}
disabledMinutes={e => getStartDisabledMinutes(e)}
disabledSeconds={(h, m) => getStartDisabledSeconds(h, m)}
onOpenChange={e => onOpenChange(e, "endTime")}
onChange={(e, str) => onTimeChange(e, str, "endTime")}
style={{ margin: "0 10px" }}
/>
</Fragment>
);
}
export const TimeRangePicker = memo(Picker);
TimeRangePickerList
/**
* 引入該有一定的順序規則
* 库--組件/本地組件--本地utils--前置变量let/const--当前组件/页面
* 如果有别名,别名的引入要在相对路径之前,如:'@/utils'要在'./utils'之前
*/
/** 库 */
import React, { memo, Fragment, useState, useEffect } from "react";
import moment from "moment";
/** 组件 */
import { Icon, Button, message, TimePicker } from "antd";
import { TimeRangePicker } from "./TimeRangePicker";
/** 时间段列表--默认值,有id是为了增删时所用 */
const initTimeRangeList = [
{
id: 0,
startTime: "00:00:00",
endTime: "00:00:00"
}
];
export function PickerList({
rangeTimeList = initTimeRangeList /** 时间段,id/startTime/endTime都必传 */,
getRangeTime /** 子组件的getRangeTime */,
getTimeList /** 获取当前的时间列表,以及permission--false是开始时间大于结束时间 */,
...props /** 继承官方组件(不止)其他属性 */
}) {
const [permission, setPermission] = useState(false);
const [thisTimeRangeList, setTimeRange] = useState(rangeTimeList);
useEffect(() => {
getTimeList && getTimeList(thisTimeRangeList, permission);
}, [thisTimeRangeList]);
/** 时间改变 */
const getItemTime = (time, index, flag) => {
const arr = [...thisTimeRangeList];
arr[index].startTime = time.startTime;
arr[index].endTime = time.endTime;
setPermission(flag);
setTimeRange(arr);
};
/** 删除 */
const onMinus = time => {
let arr = [...thisTimeRangeList];
arr = arr.filter(item => item.id !== time.id);
setTimeRange(arr);
};
/** 增加 */
const onPlus = time => {
/** 因为react是用key值去做diff的,所以如果用index作为key,会出现数据正确视图不正确等问题 */
let arr = [...thisTimeRangeList];
const lastId = Number(time.id) + 1;
arr = arr.concat({
id: lastId,
startTime: "00:00:00",
endTime: "00:00:00"
});
setTimeRange(arr);
};
const len = thisTimeRangeList.length;
return thisTimeRangeList.map((time, index) => {
return (
<Fragment>
{/**
* 注意,
* 这里如果仅仅用index作为key值会导致增删出现问题,
* 这就为什么官方不建议使用index作为key值的原因,
* 所以id必须有且呈递增趋势
*
* 细节:{...props} 放在最后可覆盖当前的属性
*/}
<TimeRangePicker
key={time.id}
timeRange={time}
getRangeTime={(range, per) => {
getRangeTime && getRangeTime(range, per);
getItemTime(range, index, per);
}}
{...props}
/>
{len > 1 && index < len - 1 && (
<Button
ghost
size="small"
shape="circle"
type="primary"
icon="minus"
{...props}
onClick={() => onMinus(time)}
/>
)}
{index === len - 1 && (
<Button
ghost
size="small"
shape="circle"
type="primary"
icon="plus"
{...props}
onClick={() => onPlus(time)}
/>
)}
<br />
</Fragment>
);
});
}
export const TimeRangePickerList = memo(PickerList);
demo
/* eslint-disable no-irregular-whitespace */
import React, { Fragment } from "react";
import { TimeRangePicker, TimeRangePickerList } from "./index";
export function TimeRangeDemo() {
return (
<Fragment>
<TimeRangePicker
// ...props
getRangeTime={(tRange, per) => console.log(tRange, per)}
/>
<TimeRangePickerList
// ...props
size="large"
format="HH:mm"
disabledNow
rangeTimeList={[
{
id: 0,
startTime: "00:00:00",
endTime: "00:00:00"
},
{
id: 1,
startTime: "00:10:00",
endTime: "00:10:00"
}
]}
disabledMinutes={[1, 2, 3]}
disabledSeconds={[11, 12, 13]}
getTimeList={(tList, permission) => console.log(tList, permission)}
/>
</Fragment>
);
}
网友评论