计算任意时间内课时出现次数以及冲突情况判断

news2025/1/21 4:57:18

背景

 

整体由四部分组成,报名时间、报名周期、上课时间、上课周期 

通过选择报名时间、报名周期、以及上课时间,去计算在培训周期内总的培训课时,并当上课时间冲突时,给出提示。

需求:

 

  • 报名时间(日期+时分),不可选今日之前的日期
  • 培训周期(日期),不可选报名结束时间之前的日期,如果未选择报名时间,则不可选今日之前的日期
  • 上课时间:每周一到每周日,时间:24小时可选,可添加多条上课时间,一条上课时间为一次课
  • 报名开始时间必须大于当前时间
  • 培训周期开始时间必须大于报名时间
  • 上课时间不能冲突,任意两节课的开始时间或者结束时间相等也视为上课冲突。
  • 计算出培训周期范围内的总课时。

设计思想

当选择完培训周期后,计算出培训周期范围内,各个星期出现的次数,再遍历上课时间列表,通过列表的每周几作为key去进行匹配,当匹配成功时,将星期出现的次数加入总课时中。

时间冲突判断,通过将时间转换为数字,通过数字大小进行比较,通过双重循环判断是否存在数字相等或者数字重叠的情况。

界面搭建

自定义组件封装

上课时间是由一个选择框 和 一个 时间选择框共同组成,因此我们只需要对这两个组件进行封装形成一个新的selectTime组件即可。

SelectTime 

import { Button, Form, Input, Select, TimePicker } from 'antd';
import moment from 'moment';
import React, { useEffect, useState } from 'react';
const { Option } = Select;

const SelectTime = ({ value = {}, onChange, }) => {
  const options = [{ value: 1, label: "每周一" }, { value: 2, label: "每周二" }, { value: 3, label: "每周三" }, { value: 4, label: "每周四" }, { value: 5, label: "每周五" }, { value: 6, label: "每周六" }, { value: 7, label: "每周日" }]
  const [week, setWeek] = useState();
  const [timeRange, setTimeRange] = useState([]);

  const triggerChange = (changedValue) => {
    onChange?.({
      week,
      timeRange,
      ...value,
      ...changedValue,
    });
  };
  const onNumberChange = (value) => {
    setWeek(value);
    triggerChange({
      week: value,
    });
  };
  const onCurrencyChange = (newTimeRange) => {
    setTimeRange(newTimeRange);
    triggerChange({
      timeRange: newTimeRange,
    });
  };
  return (
    <>
      <Select
        style={{
          width: 100,
          margin: '0 8px',
        }}
        options={options}
        allowClear={true}
        placeholder={"每周几"}
        onChange={onNumberChange}
        value={value.week || week}
      >

      </Select>
      <TimePicker.RangePicker format={"HH:mm"} onChange={onCurrencyChange} value={value.timeRange || timeRange} />
    </>
  )
}
export default SelectTime;

通过将SelectTime 组件放在Form.List内部即可实现组件的多个添加。当添加到第二条数据的时候,由于没有label,所以需要对样式进行调整,所以引入formItemLayoutWithOutLabel。

formItemLayoutWithOutLabel 

  const formItemLayoutWithOutLabel = {
    wrapperCol: {
      xs: {
        span: 24,
        offset: 0,
      },
      sm: {
        span: 20,
        offset: 6,
      },
    },
  };

 formItemLayout 

  const formItemLayout = {
    labelCol: {
      xs: {
        span: 24,
      },
      sm: {
        span: 6,
      },
    },
    wrapperCol: {
      xs: {
        span: 24,
      },
      sm: {
        span: 20,
      },
    },
  };

报名时间 

 <FormItem name={"signRange"} label={"报名时间"} rules={[{ required: true }]}>
              <RangePicker
                disabledDate={disabledDate}
                // disabledTime={disabledRangeTime}
                showTime={{
                  format: 'HH:mm',
                }}
                format="YYYY-MM-DD HH:mm"
                onChange={getEndDate}
              />
            </FormItem>

培训周期 

<FormItem name={"trainRange"} label={"培训周期"} rules={[{ required: true }]}>
              <RangePicker disabledDate={disabledDate2} onChange={(value, dateString) => { isIncludeWeeks(dateString[0], dateString[1]); }} />
            </FormItem>

 上课时间 

 <Form.List
              name="trainingTime"
              initialValue={['1']}
              rules={[
                {
                  validator: async (_, trainingTime) => {
                    if (!trainingTime || trainingTime.length < 1) {
                      return Promise.reject(new Error('至少一条数据'));
                    }
                  },
                },
              ]}
            >
              {(fields, { add, remove }, { errors }) => (
                <>
                  {fields.map((field, index) => (
                    <Form.Item
                      label={index === 0 ? '上课时间' : ''}
                      {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
                      rules={[{ required: true, message: "请输入上课时间" }]}
                      key={field.key}
                      name={[field.name, 'timeRange']}
                      validateTrigger={['onChange', 'onBlur']}

                    >
                      <Form.Item
                        {...field}
                        validateTrigger={['onChange', 'onBlur']}
                        rules={[
                          {
                            required: true,
                            message: "请输入上课时间",
                          },
                        ]}
                        noStyle
                      >
                        <SelectTime onChange={() => { onChangetime(); }}></SelectTime>
                      </Form.Item>
                      <PlusCircleOutlined style={{ margin: "0 10px" }} onClick={() => add()} />
                      {fields.length > 1 ? (
                        <MinusCircleOutlined
                          className="dynamic-delete-button"
                          onClick={() => {
                            remove(field.name);
                            if (form.getFieldValue('trainRange') !== undefined) {
                              isIncludeWeeks(form.getFieldValue('trainRange')[0], form.getFieldValue('trainRange')[1]);
                            }
                          }}
                        />
                      ) : null}
                    </Form.Item>
                  ))}
                </>
              )}
            </Form.List>

培训总课时 

 <FormItem name={"totalTrainingHours"} label={"培训总课时"} rules={[{ required: true }]}>
              <Input readOnly style={{ border: 'none', boxShadow: "none" }}></Input>
            </FormItem>

功能实现

报名时间与培训时间的限制

添加报名时间限制——disabledDate

  const disabledDate = (current) => {
    var time = current.clone();
    return time.startOf('day') < moment().startOf('day');
  };

添加培训周期时间限制——disabledDate2

注意特殊情况:当报名时间未选择时,限制范围为前一日

  const disabledDate2 = (current) => {
    if (form.getFieldValue('signRange') !== undefined && endDate !== null) {
      return current.format('YYYY-MM-DD') < endDate.format('YYYY-MM-DD');
    } else {
      return current < moment().subtract(1, "days");
    }
  };

当报名时间选择后,培训周期的可选时间范围受限,因为我们需要对报名时间添加onchange事件——getEndDate

  const getEndDate = () => {
    if (form.getFieldValue('signRange') !== null) {
      endDate = form.getFieldValue('signRange')[1].clone();
      endDate = endDate.add(1, "days")
    }
  }

培训总课时与上课时间以及培训时间有关,所以当我们选择对应的培训周期后,就应该计算出这段时间内周一到周日出现的次数。添加onchange事件——isIncludeWeeks

将培训周期内的星期放入weeksArr中(1代表星期一,....,7代表星期日)

  const isIncludeWeeks = (startDate, endDate) => {
    let beginDateObj = new Date(startDate);
    let endDateObj = new Date(endDate);
    let weeksArr = [];
    while (beginDateObj <= endDateObj) {
      //直到结束日期,跳出循环
      let todayWeekNum = beginDateObj.getDay();
      if (todayWeekNum == 0) {
        //如果为0,则对应“周日”
        todayWeekNum = 7;
      }
      weeksArr.push(todayWeekNum);
      beginDateObj.setTime(beginDateObj.getTime() + 24 * 60 * 60 * 1000); //每次递增1天
    }
    console.log(getEleNums(weeksArr));
    weekNum = getEleNums(weeksArr);
    // setWeekNum(getEleNums(weeksArr));
    canClassTotalTime();
  }

计算周一到周日出现的次数

  const getEleNums = (data) => {
    var map = {}
    for (var i = 0; i < data.length; i++) {
      var key = data[i]
      if (map[key]) {
        map[key] += 1
      } else {
        map[key] = 1
      }
    }
    return map;
  }

当选择上课时间后,计算该课时在周期内出现次数,即星期出现次数。因此,对于课时的计算只需考虑对应的每周几即可,时间用于判断上课时间是否冲突。对于上课时间添加onchange事件——onChangetime

对于几种特殊情况通过if判断排除出去,避免报错。

  • 未选每周几,只选了上课时间
  • 未选上课时间,只选了每周几
  • 未选培训周期
  // 当选择上课时间时 计算总课时以及判断冲突
  const onChangetime = () => {
    // canClassTotalTime();
    if (form.getFieldValue('trainRange') !== undefined) {
      isIncludeWeeks(form.getFieldValue('trainRange')[0], form.getFieldValue('trainRange')[1]);
    }
    let rangetime = [];
    console.log(form.getFieldValue('trainingTime'));
    if (form.getFieldValue('trainingTime') !== undefined) {
      rangetime = form.getFieldValue('trainingTime').map((item) => {
        if (item.week !== undefined && item.timeRange[0] !== undefined) {
          return {
            week: item.week,
            timeRange: [item.timeRange[0].format('HH:mm'), item.timeRange[1].format('HH:mm')]
          }
        }
      }).filter(item => item !== undefined)
      judgmentTimeConflict(rangetime);
    }
  }

canClassTotalTime 

  const canClassTotalTime = () => {
    let total = 0;
    form.getFieldValue('trainingTime')
    console.log(form.getFieldValue('trainingTime'), weekNum);
    if (form.getFieldValue('trainRange') !== undefined) {
      form.getFieldValue('trainingTime').map((item) => {
        if (item.week !== undefined && weekNum[item.week] !== undefined)
          total += weekNum[item.week];
      })
    }
    console.log('total', total);
    form.setFieldsValue({ totalTrainingHours: total })
  }

报名时间冲突判断——judgmentTimeConflict

通过将时间转成字符串再转成数字类型,通过数字类型进行大小比较完成范围冲突的判断。

几种冲突情况分类讨论 (范围A[0]-A[1],范围B[0]-B[1])

  • A[0]<A[1]=B[0]<B[1]
  • A[0]<B[0]<A[1]<B[1]
  • A[0]<B[0]<A[1]=B[1]
  • A[0]<B[0]<B[1]<A[1]
  • A[0]=B[0]<B[1]<A[1]
  • B[0]<A[0]<B[1]<A[1]
  • A[0]<B[0]<A[1]=B[0]
  • B[0]<A[0]<A[1]<B[1]

judgmentTimeConflict 

  // 判断时间是否冲突
  const judgmentTimeConflict = (timeRangeList) => {
    let newrange = timeRangeList.map((item) => {
      return {
        week: item.week,
        timeRange: [parseInt(item.timeRange[0].replace(':', '')), parseInt(item.timeRange[1].replace(':', ''))]
      }
    })
    for (let i = 0; i < newrange.length; i++) {
      for (let j = i; j < newrange.length; j++) {
        if (i !== j && newrange[i].week === newrange[j].week) {
          if ((newrange[i].timeRange[1] === newrange[j].timeRange[0]) || (newrange[j].timeRange[1] === newrange[i].timeRange[0])) {
            message.error("上课时间冲突,请重新选择!")
            return false;
          } else if ((newrange[i].timeRange[0] < newrange[j].timeRange[0]) && (newrange[j].timeRange[0] < newrange[i].timeRange[1])) {
            message.error("上课时间冲突,请重新选择!")
            return false;
          } else if ((newrange[i].timeRange[0] < newrange[j].timeRange[1]) && (newrange[j].timeRange[1] < newrange[i].timeRange[1])) {
            message.error("上课时间冲突,请重新选择!")
            return false;
          } else if ((newrange[i].timeRange[0] === newrange[j].timeRange[0]) || (newrange[i].timeRange[1] === newrange[j].timeRange[1])) {
            message.error("上课时间冲突,请重新选择!")
            return false;
          }else if((newrange[i].timeRange[0] > newrange[j].timeRange[0]) && (newrange[i].timeRange[1] < newrange[j].timeRange[1])){
            message.error("上课时间冲突,请重新选择!")
            return false;
          }
        }
      }
    }
  }

效果展示

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/469058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分享10个非常好用的绘图工具

无论你是一个专业的插画师&#xff0c;还是一个有创造力的人&#xff0c;想要随时记录生活的灵感&#xff0c;现在你只需要拿起平板电脑或打开电脑浏览器来描述你脑海中的图片。在本文中&#xff0c;我们选择了10个强大、方便、易于使用的在线绘图软件&#xff0c;其中一个必须…

人工智能之配置环境教程一:安装VsCode和Anaconda

人工智能之配置环境教程一&#xff1a;安装VsCode和Anaconda 作者介绍一&#xff0e; 安装VScode编辑器二. 安装Anaconda 作者介绍 孟莉苹&#xff0c;女&#xff0c;西安工程大学电子信息学院&#xff0c;2021级硕士研究生&#xff0c;张宏伟人工智能课题组。 研究方向&#…

语音特征工程—时域分析

当下主流语音前端算法在特征工程方面&#xff0c;从vad&#xff0c;降噪、降混响到盲源分离&#xff0c;无论是传统做法还是NN做法&#xff0c;大多基于频域。但近年在语音分离领域也看到了利用时域的做法&#xff0c;也取得了不错的效果。 本文从特征工程的角度&#xff0c;对…

jQuery购物车案例模块

<div class"car-header"><div class"w"><div class"car-logo"><img src"img/logo.png" alt""> <b>购物车</b></div></div></div></div><div class"c-…

研究了一个多月ChatGPT我发现了这些!!AI时来了?

好久都没有更新过博客了&#xff0c;大一下是真的很忙碌&#xff0c;属实是真的要崩溃了&#xff0c;但是博主还是坚强的活了下来&#xff0c;好了&#xff0c;进入正题&#xff0c;博主使用了一段时间的chatGPT发现真的可能AI时代将要来临&#xff0c;原本以为gpt能做网页已经…

二十三种设计模式第一篇-设计模式的原则和分类

怎么说&#xff0c;其实我挺讨厌设计模式的&#xff0c;但是不得不说&#xff0c;这东西解决问题确实有一手&#xff0c;并且设计模式确实规范了我们在开发中的代码&#xff0c;并且我们可以使用这二十三种设计模式解决我们碰到的业务问题&#xff0c;并且我们学的框架里边多多…

利用Python操作Mysql数据库

我们在进行Python编程的时候&#xff0c;时常要将一些数据保存起来&#xff0c;其中最方便的莫过于保存在文本文件了。但是如果保存的文件太大&#xff0c;用文本文件就不太现实了&#xff0c;毕竟打开都是个问题&#xff0c;这个时候我们需要用到数据库。提到数据库&#xff0…

怎样从零开始编译一个魔兽世界开源服务端Windows

怎样从零开始编译一个魔兽世界开源服务端Windows 第二章&#xff1a;编译和安装 我是艾西&#xff0c;上期我们讲述到编译一个魔兽世界开源服务端环境准备&#xff0c;那么今天跟大家聊聊怎么编译和安装我们直接进入正题&#xff08;上一章没有看到的小伙伴可以点我主页查看&…

haproxy负载均衡+keepalived高可用

LVS和haproxy、nginx做负载均衡时的区别 调度算法 LVS:轮询(rr)、加权轮询(wrr)、最少连接(lc)、加权最少连接(wlc)、源地址哈希值(sh)、目的地址哈希&#xff08;dh&#xff09; haproxy:轮询&#xff08;roundrobin)、加权轮询&#xff08;static-rr)、最少连接&#xff08…

2023有潜力的新药都有哪些?新药筛选方法总结

2023有潜力的新药都有哪些&#xff1f;这是一道蕴含无限可能性和未知挑战的问题。新药研发是制药公司不断追求的目标&#xff0c;每一次成功都可以带来巨额利润&#xff0c;改善患者生命质量&#xff0c;成就公司声誉。但与此同时&#xff0c;新药研发风险也是极大的&#xff0…

电梯导航栏的实现

点击跳到指定位置类似于电梯导航 .w {width: 1200px;margin: 0 auto; } .fixedtool {position: fixed;top: 100px;left: 50%;margin-left: -676px;width: 66px;background-color: #fff;display: none; }.fixedtool li {height: 32px;line-height: 32px;text-align: center;fo…

基于dsp+fpga的半导体运动台高速数据采集FPGA endac设计(三)

EnDat 双向串行通信协议的实现 数据&#xff08;测量值或参数&#xff09;可以在位置编码器和 EnDat 协议内核之间进行双向传输&#xff0c; EnDat 协议内核的收发单元支持 RS-485 差分信号传输&#xff0c;数据传输与传感伺服控制系统 生成的时钟脉冲同步。传输的数据…

如何将Postman API测试转换为JMeter以进行扩展

许多测试工程师使用Postman进行API测试自动化。他们发现端点&#xff0c;发出请求&#xff0c;创建测试数据&#xff0c;运行回归测试&#xff0c;使用Newman等实现API测试的持续集成。但是&#xff0c;Postman有一定的测试限制。希望获得更多负载测试能力的开发人员&#xff0…

系统集成项目管理工程师 笔记(第10章:项目质量管理)

文章目录 10.1.2 质量管理及其 发展史 35610.1.3 项目质量管理 35810.2.2 规划质量管理的输入 35910.2.3 规划质量管理的工具与技术 3601、成本效益分析法2、质量成本法&#xff08;一致性成本【预防、评价】 和 非一致性成本【内部、外部】&#xff09;3、七种基本质量工具&am…

数字设计小思 - 谈谈时钟:数字系统的心脏

前言 本系列整理数字系统设计的相关知识体系架构&#xff0c;为了方便后续自己查阅与求职准备。时钟作为数字系统中的“心脏”&#xff0c;其设计的质量好坏直接关乎整个系统的稳定性&#xff0c;本文主要介绍了数字设计中的常见的时钟产生电路和时钟类型&#xff0c;并进行相…

音视频八股文(7)-- 音频aac adts

AAC介绍 AAC&#xff08;Advanced Audio Coding&#xff09;是一种现代的音频编码技术&#xff0c;用于数字音频的传输和存储领域。AAC是MPEG-2和MPEG-4标准中的一部分&#xff0c;可提供更高质量的音频数据&#xff0c;并且相比于MP3等旧有音频格式&#xff0c;AAC需要更少的…

【经典题】二叉搜索树与双向链表

二叉搜索树与双向链表链接 解题思路 思路1 &#xff1a; 中序遍历&#xff0c;将节点放进vector中&#xff0c;再改链接关系&#xff0c;这很容易想出并解决&#xff0c;但这样明显不符合题意。 思路2&#xff1a; 这道题目要求将一个二叉搜索树转换成一个排序的双向链表&a…

php连接sqlserver

1.使用工具 Wampserver--3.3 sqlserver2023 php7.4.33 2.连接流程 1.下载Microsoft Drivers for PHP for SQL Server 下载地址&#xff1a;下载 Microsoft Drivers for PHP for SQL Server - PHP drivers for SQL Server | Microsoft Learn 2.下载Microsoft ODBC Driver …

简记二分算法模板与代码案例:整数二分和浮点数二分

本文以 Java 语言实现&#xff0c;整理的代码模板适用于编程竞赛。对代码模板原理的讲解不多&#xff0c;主要记录一下如何使用。 目录 一、算法模板 &#xff08;1&#xff09;整数二分 &#xff08;2&#xff09;浮点数二分 二、例题 例题&#xff1a;acwing-789.数的范…

开源网安入选“2023年中国信通院开源供应商名录”

近日&#xff0c;为进一步推进开源供应链安全合规发展&#xff0c;中国信息通信研究院云大所发布了“2023年中国信通院开源供应商名录”&#xff08;简称名录&#xff09;&#xff0c;名录供应商均为通过可信开源供应链系列评估的企业。名录范围涵盖云计算、大数据、中间件、数…