react+ts手写cron表达式转换组件

前言

最近在写的一个分布式调度系统,后端同学需要让我传入cron表达式,给调度接口传参。我去了学习了解了cron表达式的用法,发现有3个通用的表达式刚好符合我们的需求:

需求

  1. 每天 xx 的时间:

0 11 20 * * ?

上面是每天20:11的cron表达式

  1. 每周的 xxx 星期 的 xxx 时间

0 14 20 * * WED,THU

上面是 每周星期三,星期四20:14的cron表达式

  1. 每周的 xxx号 的 xxx时间

0 15 20 3,7 * ?

上面是 每月的3,7号20:15的cron表达式

这三个表达式刚好符合我们的需求,并且每个符号表达的意思也很直观。那么,话不多说,直接开写!

环境

  • react

    • 我的版本:“react”: “18.2.0”,用的函数组件+react hooks
  • moment

    • npm install moment
  • semi-design(组件库)

    • npm install semi-design
  • typescript

实现

数据

utils下创建cron.ts,对组件所用到的数据进行统一的管理:

export const dayOfTheWeekData = [{ key: 'MON', label: '星期一' },{ key: 'TUE', label: '星期二' },{ key: 'WED', label: '星期三' },{ key: 'THU', label: '星期四' },{ key: 'FRI', label: '星期五' },{ key: 'SAT', label: '星期六' },{ key: 'SUN', label: '星期天' }
];export const dayOfTheWeekOption = [{ key: '1', label: '星期一' },{ key: '2', label: '星期二' },{ key: '3', label: '星期三' },{ key: '4', label: '星期四' },{ key: '5', label: '星期五' },{ key: '6', label: '星期六' },{ key: '7', label: '星期天' }
];export const monthOption = [{ key: '1', label: '一月' },{ key: '2', label: '二月' },{ key: '3', label: '三月' },{ key: '4', label: '四月' },{ key: '5', label: '五月' },{ key: '6', label: '六月' },{ key: '7', label: '七月' },{ key: '8', label: '八月' },{ key: '9', label: '九月' },{ key: '10', label: '十月' },{ key: '11', label: '十一月' },{ key: '12', label: '十二月' }
];//获取dayOfTheMonthOption的每月对象
function getDayOfTheMonthOption() {const days = [];for (let i = 1; i < 32; i += 1) {days.push({ key: i.toString(), label: i.toString().concat('号') });}return days;
}export const dayOfTheMonthOption = getDayOfTheMonthOption();

组件

到了组件的具体实现,个人感觉我写的注释挺全的,就单挑几个核心重点讲下:

时间转换函数(handleTimeChange)

  //时间选择函数const handleTimeChange = (time: moment.Moment | null) => {setSelectTime(time);if (!time) return;const currentCron = expression ? expression.split(' ') : [];const [seconds, , , dayOfMonth, month1, dayOfWeek] = currentCron;const minutes = moment(time).minutes().toString(); //获取分钟const hours = moment(time).hours().toString(); //获取小时let result = null;if (!Number.isNaN(Number(hours)) && !Number.isNaN(Number(minutes))) {const minutesAndHour = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space);if (defaultTimeType === 'everyDay') result = minutesAndHour.concat('* * ?');if (defaultTimeType !== 'everyDay')result = minutesAndHour.concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(dayOfWeek);}if (result) onChange?.(result);setExpression(result);};
  1. 使用moment函数将time转成数字类型
    1. minutes = moment(time).minutes().toString(); //获取分钟
    2. hours = moment(time).hours().toString(); //获取小时
  2. 获取时间cron字符串minutesAndHour:
const minutesAndHour = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space);
  1. 拼接得到完整的cron表达式:
    1. defaultTimeType === ‘everyDay’

result = minutesAndHour.concat(‘* * ?’);

  1. defaultTimeType !== ‘everyDay’
result = minutesAndHour.concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(dayOfWeek);

日期转换函数(handleSelectChange)

setSelectedValue(data);
const selectValues = data.join(',');
const currentCron = expression ? expression.split(' ') : [];
const [seconds, minutes, hours, dayOfMonth, month1, dayOfWeek] = currentCron;
let result = '';
if (defaultTimeType === 'everyWeek') {result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(selectValues);
}
if (defaultTimeType === 'everyMonth') {result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(data.length ? selectValues : '*').concat(space).concat(month1).concat(space).concat(dayOfWeek);
}
if (selectTime) onChange?.(result);
setExpression(result);
  1. defaultTimeType === ‘everyWeek’
result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(selectValues);
  1. defaultTimeType === ‘everyMonth’
result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(data.length ? selectValues : '*').concat(space).concat(month1).concat(space).concat(dayOfWeek);

组件全部代码(CronInput)

import { ConfigProvider, TimePicker } from '@douyinfe/semi-ui';
import { Fragment, useState } from 'react';
import { Select } from '@douyinfe/semi-ui';
import moment from 'moment';
//引入数据
import { dayOfTheMonthOption, dayOfTheWeekData } from '@/utils/cron';const { Option } = Select;
const format = 'HH:mm';
const defaultCron = '0 * * * * ?';
const space = ' '; //空格
//类型选择
const timeTypes = [{ key: 'everyDay', label: '每天' },{ key: 'everyWeek', label: '每周' },{ key: 'everyMonth', label: '每月' }
];interface Props {onChange?: (cron?: string) => void;
}
const CronInput: React.FC<Props> = ({ onChange }) => {const [defaultTimeType, setDefaultTimeType] = useState(timeTypes[0].key); //选择类型const [selectedValue, setSelectedValue] = useState<[]>([]); //日期,多选数组const [selectTime, setSelectTime] = useState<any>(null); //时间const [expression, setExpression] = useState<string | null>(defaultCron); //bzd//类型选择函数const handleTimeTypeChange = (selectValue: string) => {setDefaultTimeType(selectValue);setSelectTime(null);setSelectedValue([]);setExpression(defaultCron);};//时间选择函数const handleTimeChange = (time: moment.Moment | null) => {setSelectTime(time);if (!time) return;const currentCron = expression ? expression.split(' ') : [];const [seconds, , , dayOfMonth, month1, dayOfWeek] = currentCron;const minutes = moment(time).minutes().toString(); //获取分钟const hours = moment(time).hours().toString(); //获取小时let result = null;if (!Number.isNaN(Number(hours)) && !Number.isNaN(Number(minutes))) {const minutesAndHour = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space);if (defaultTimeType === 'everyDay') result = minutesAndHour.concat('* * ?');if (defaultTimeType !== 'everyDay')result = minutesAndHour.concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(dayOfWeek);}if (result) onChange?.(result);setExpression(result);};const handleSelectChange = (data: []) => {setSelectedValue(data);const selectValues = data.join(',');const currentCron = expression ? expression.split(' ') : [];const [seconds, minutes, hours, dayOfMonth, month1, dayOfWeek] = currentCron;let result = '';if (defaultTimeType === 'everyWeek') {result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(selectValues);}if (defaultTimeType === 'everyMonth') {result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(data.length ? selectValues : '*').concat(space).concat(month1).concat(space).concat(dayOfWeek);}if (selectTime) onChange?.(result);setExpression(result);};const RenderSelect = ({placeholder,data = []}: {placeholder: string;data: { key: string; label: string }[];}) => {return (<Fragment><Selectmultipleplaceholder={placeholder}onChange={(val: any) => handleSelectChange(val)}style={{ marginRight: '16px', width: 'auto' }}value={selectedValue}>{data.map((item: { key: string; label: string }) => (<Option key={item.key} value={item.key}>{item.label}</Option>))}</Select><ConfigProvider><TimePickervalue={selectTime && moment(selectTime, format).toDate()}format={format}placeholder="请选择时间"onChange={(val: any) => handleTimeChange(val)}/></ConfigProvider></Fragment>);};return (<><div className={'cron'}><Select// role="cron-type"style={{ marginRight: '16px', width: 'auto' }}placeholder="请选择类型"onChange={(val: any) => handleTimeTypeChange(val)}value={defaultTimeType}>{timeTypes.map((item) => (<Option key={item.key} value={item.key}>{' '}{item.label}</Option>))}</Select>{defaultTimeType === 'everyDay' && (<ConfigProvider><TimePickervalue={selectTime && moment(selectTime, format).toDate()}format={format}placeholder="请选择时间"onChange={(val: any) => handleTimeChange(val)}/></ConfigProvider>)}{defaultTimeType === 'everyWeek' && (<RenderSelect data={dayOfTheWeekData} placeholder="请选择星期" />)}{defaultTimeType === 'everyMonth' && (<RenderSelect data={dayOfTheMonthOption} placeholder="请选择日期" />)}</div></>);
};export default CronInput;

使用与效果

使用

使用方法很简单,接收onChange传来的cron表达式即可:

const App: FC<IProps> = (props) => {const { datas = [] } = props;let [value, setValue] = useState<string>();return (<div><CronInput onChange={(cron) => setValue(cron)} /><div>{value}</div></div>);
};

效果

  1. 每天

image.png

  1. 每周

image.png

  1. 每月

image.png

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

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

相关文章

Java开发树结构数据封装!

目录 源数据如下controller接口&#xff1a;service层封装:Dao接口&#xff1a;Dao层Mapper:映射实体类&#xff1a; 源数据如下 controller接口&#xff1a; RequestMapping("/UserTreeInfo")public RespBody getUserTreeInfo(Long userId) {List<MenuTreeVo>…

一文学会使用WebRTC API

WebRTC&#xff08;Web Real-Time Communication&#xff09;是一项开放标准和技术集合&#xff0c;由 W3C 和 IETF 等组织共同推动和维护&#xff0c;旨在通过Web浏览器实现实时通信和媒体流传输。WebRTC于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的…

JVS-rules中的基础与复合变量:规则引擎的心脏

JVS-rules中的“变量”概念与编程语言中的变量类似&#xff0c;但它们通常在规则系统中处理条件判断、业务结果复制场景&#xff0c;如下所示&#xff1a; 条件判断&#xff1a;在规则引擎中&#xff0c;规则通常由两个部分组成&#xff1a;条件和分支。变量用于描述条件部分中…

Arduino驱动ICG-20660L传感器(惯性测量传感器篇)

目录 1、传感器特性 2、硬件原理图 3、控制器和传感器连线图 4、驱动程序

职场题:有一件特别紧急的事,群众要办理,且联系不上领导,你怎么办?(2)

接1所写 如果无法联系上领导且有一项特别紧急的事情需要办理&#xff0c;以下是进一步的建议&#xff1a; 11. 尝试其他沟通渠道&#xff1a;除了直接联系领导外&#xff0c;尝试通过其他沟通渠道与领导取得联系。这可能包括电子邮件、即时通讯工具或其他内部通信平台。确保详…

逐字稿 | 2 MoCo 论文逐段精读【论文精读】

bryanyzhu的个人空间-bryanyzhu个人主页-哔哩哔哩视频 评价 今天我们一起来读一下 MOCO 这篇论文。 MOCO 是 CVPR 2020 的最佳论文提名&#xff0c;算是视觉领域里使用对比学习的一个里程碑式的工作。而对比学习作为从 19 年开始一直到现在视觉领域乃至整个机器学习领域里最炙…

深度学习零基础教程

代码运行软件安装&#xff1a; anaconda:一个管理环境的软件–>https://blog.csdn.net/scorn_/article/details/106591160&#xff08;可选装&#xff09; pycharm&#xff1a;一个深度学习运行环境–>https://blog.csdn.net/scorn_/article/details/106591160&#xf…

PAM从入门到精通(七)

接前一篇文章&#xff1a;PAM从入门到精通&#xff08;六&#xff09; 本文参考&#xff1a; 《The Linux-PAM Application Developers Guide》 先再来重温一下PAM系统架构&#xff1a; 更加形象的形式&#xff1a; 五、主要函数详解 5. pam_strerror 概述&#xff1a; 描述…

在vue使用class选择器和下标更改点击列表样式

如果您正在使用Vue 3的<script setup>语法&#xff0c;可以按照以下步骤在Vue中使用class和下标来更改点击项的样式&#xff1a; 首先&#xff0c;在<script setup>部分导入所需的响应式API和定义需要使用的变量。 <script setup> import { ref } from vue…

千兆光模块和万兆光模块的区别?

在网络通信领域&#xff0c;千兆光模块和万兆光模块是最为常见且广泛应用的两种光模块。不同之处在于传输速率、封装、传输距离、功耗、发射光功率、接收光功率和应用场景等。 千兆光模块的传输速率为1 Gbps&#xff0c;万兆光模块的传输速率为10 Gbps&#xff0c;这意味着万…

vue-cli脚手架创建项目时报错Error: command failed: npm install --loglevel error

项目背景 环境&#xff1a;vue-cli 5.x 在工程文件中&#xff0c;后端模块wms已经创建完成&#xff0c;现在想新建一个名为vue-web的前端模块 执行命令vue create vue-web时&#xff0c; 报错Error: command failed: npm install --loglevel error 问题分析及解决 排查过程…

ScyllaDB获4300万美元融资,NoSQL数据库市场再掀热潮!

ScyllaDB是一家成立于2012年12月的美国公司&#xff0c;总部位于加利福尼亚州桑尼维尔。作为一家数据密集型应用程序数据库供应商&#xff0c;ScyllaDB生产的NoSQL数据库兼容Apache Cassandra和Amazon DynamoDB&#xff0c;具有可靠的低延迟和10倍的吞吐量。在2023年10月17日&a…

代码随想录算法训练营第五十八天| LeetCode 583 两个字符串的删除操作、LeetCode 72 编辑距离、编辑距离总结

1 LeetCode 583 两个字符串的删除操作 题目链接&#xff1a;LeetCode 583 两个字符串的删除操作 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;LeetCode&#xff1a;583.两个字符串的删除操作 2 LeetCode 72 编辑距离 题目链接&#xff1a;LeetCod…

JVM第十三讲:调试排错 - JVM 调优参数

调试排错 - JVM 调优参数 本文是JVM第十三讲&#xff0c;调试排错 - JVM 调优参数。对JVM涉及的常见的调优参数和垃圾回收参数进行阐述。 文章目录 调试排错 - JVM 调优参数1、Jvm参数2、垃圾回收 问题1&#xff1a;线上ECS治理问题2&#xff1a;白龙马线上服务机JVM参数配置&a…

Qt move和setGeometry的区别

move 和 setGeometry 都是用于管理窗口或小部件的位置的方法&#xff0c;通常在使用 Qt 编程时会用到。它们之间的主要区别在于&#xff1a; move 方法&#xff1a;这个方法用于设置小部件的左上角的坐标位置&#xff0c;它需要两个参数&#xff0c;即横坐标和纵坐标。使用 mov…

idea dubge 详细

目录 一、概述 二、debug操作分析 1、打断点 2、运行debug模式 3、重新执行debug 4、让程序执行到下一次断点后暂停 5、让断点处的代码再加一行代码 6、停止debug程序 7、显示所有断点 8、添加断点运行的条件 9、屏蔽所有断点 10、把光标移到当前程序运行位置 11、单步跳过 12、…

访问 github 问题解决方法

一、macOS版 PS. Windows 版的还没试&#xff0c;不过应该也差不多 1.基本信息 硬件&#xff1a;MacBook Pro 2017 (A1707) 系统&#xff1a;macOS 13.6 (Ventura) 应用&#xff1a;SwitchHosts 4.1.2 (Releases oldj/SwitchHosts GitHub) hosts内容网站&#xff1a;ht…

迅为RK3588开发板Android12单摄方案设备树修改

打开 3588-android12/kernel-5.10/arch/arm64/boot/dts/rockchip/topeet_camera_config.dtsi 设备树&#xff0c;此设备树中对底板上的摄像头接口进行了配置&#xff0c;如下图所示&#xff1a; 如果想要使用 J1 接口打开摄像头 OV5695 或者 摄像头 OV13850&#xff0c;只需要在…

pytorch代码实现之动态蛇形卷积模块DySnakeConv

动态蛇形卷积模块DySnakeConv 血管、道路等拓扑管状结构的精确分割在各个领域都至关重要&#xff0c;确保下游任务的准确性和效率。 然而&#xff0c;许多因素使任务变得复杂&#xff0c;包括薄的局部结构和可变的全局形态。在这项工作中&#xff0c;我们注意到管状结构的特殊…

vue中动态设置source标签

项目中有个视频播放,路径通过接口返回,而且不带后缀,并不确定是什么类型的视频文件,所以要通过source标签去进行设置. 问题:当video中存在source标签的时候&#xff0c;浏览器渲染之后会自动去获取地址&#xff0c;即便地址改变&#xff0c;浏览器也不会再去获取地址。 解决方…