让前端飞TypeScript 极简教程

基于Antd3.x版本封装TimeRangePicker和Tim

2020-04-18  本文已影响0人  郁南

尽管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>
  );
}
上一篇下一篇

猜你喜欢

热点阅读