/jest.setup.js'],
9 | }
10 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | import { configure } from 'enzyme'
3 | import Adapter from 'enzyme-adapter-react-16'
4 |
5 | configure({ adapter: new Adapter() })
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tulaoda/cron-editor",
3 | "version": "1.0.1-beta.0",
4 | "description": "基于antd、react的crontab表达式生成工具",
5 | "main": "./dist/index",
6 | "scripts": {
7 | "start": "webpack-dev-server",
8 | "build": "webpack -p --config ./webpack.config.build.js",
9 | "release": "standard-version",
10 | "postrelease": "git push --follow-tags origin master",
11 | "test": "jest"
12 | },
13 | "keywords": [
14 | "cron",
15 | "react",
16 | "js"
17 | ],
18 | "author": {
19 | "name": "tulaoda",
20 | "email": "coderaxin@163.com"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git@github.com:tulaoda/cron-editor-react.git"
25 | },
26 | "license": "MIT",
27 | "dependencies": {
28 | "antd": "^3.9.3",
29 | "classnames": "^2.2.6",
30 | "cron-parser": "^2.13.0",
31 | "moment": "^2.19.1",
32 | "prop-types": "^15.7.2",
33 | "react": "^16.0.0",
34 | "react-dom": "^16.0.0"
35 | },
36 | "devDependencies": {
37 | "@babel/core": "^7.4.5",
38 | "@babel/plugin-proposal-class-properties": "^7.4.4",
39 | "@babel/plugin-proposal-decorators": "^7.4.4",
40 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
41 | "@babel/plugin-transform-runtime": "^7.4.4",
42 | "@babel/preset-env": "^7.11.0",
43 | "@babel/preset-react": "^7.10.4",
44 | "@babel/types": "^7.4.4",
45 | "@testing-library/react": "^10.4.7",
46 | "babel-jest": "^26.2.2",
47 | "babel-loader": "^8.0.6",
48 | "babel-plugin-import": "^1.12.0",
49 | "css-loader": "^3.0.0",
50 | "enzyme": "^3.11.0",
51 | "enzyme-adapter-react-16": "^1.15.2",
52 | "identity-obj-proxy": "^3.0.0",
53 | "jest": "^26.2.2",
54 | "less": "^3.9.0",
55 | "less-loader": "^5.0.0",
56 | "react-test-renderer": "^16.13.1",
57 | "style-loader": "^0.23.1",
58 | "webpack": "^4.35.2",
59 | "webpack-cli": "^3.3.5",
60 | "webpack-dev-server": "^3.7.2"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tulaoda/cron-editor-react/b72e1f33b3225addfcf929a8fec2b3408488f358/screenshot.png
--------------------------------------------------------------------------------
/src/.npmignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | npm-debug.log.*
3 | node_modules/
4 | .DS_Store
5 | package-lock.json
6 | yarn.lock
7 | yarn-error.log
8 | src/
9 | stories/
10 | test/
11 | storybook-static/
12 | App.jsx
13 |
--------------------------------------------------------------------------------
/src/components/Day.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-天
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from "react";
7 | import { Radio, InputNumber, Row, Col, Select, List, Checkbox, message } from "antd";
8 | const { Group } = Radio;
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Day extends PureComponent {
12 | constructor(props) {
13 | super(props);
14 | this.formatDayOptions();
15 | }
16 |
17 | // formatDayOptions() {
18 | // this.dayOptions = [];
19 | // for (let x = 1; x < 32; x++) {
20 | // this.dayOptions.push({
21 | // label: x,
22 | // value: `${x}`
23 | // });
24 | // }
25 | // }
26 |
27 | formatDayOptions() {
28 | this.dayOptions = [];
29 | for (let x = 1; x < 32; x++) {
30 | const label = x < 10 ? `0${x}` : x;
31 | const value = `${x}`;
32 | const ele = (
33 |
34 | {label}
35 |
36 | );
37 | this.dayOptions.push(ele);
38 | }
39 | }
40 |
41 | changeParams(type, value) {
42 | const state = { ...this.props.day };
43 | state[type] = value;
44 | if (type === 'start') {
45 | if (state.end - state.start <= 1) {
46 | state.end = value + 1;
47 | }
48 | }
49 | if (type === 'end') {
50 | if (state.end - state.start <= 1) {
51 | state.start = value - 1;
52 | }
53 | }
54 | this.props.onChange(state);
55 | }
56 |
57 | changeType = e => {
58 | const state = { ...this.props.day };
59 | // if (e.target.value === "some") {
60 | // state.some = ["1"];
61 | // }
62 | state.type = e.target.value;
63 | this.props.onChange(state);
64 | };
65 |
66 | render() {
67 | const {
68 | day: { type, start, end, some, begin, beginEvery, last, closeWorkDay }
69 | } = this.props;
70 | return (
71 |
72 |
73 |
74 |
75 | 每日
76 |
77 |
78 | 不指定
79 |
80 |
81 | 周期从{" "}
82 | value.toString().replace(/[^\d\.]/g, '')}
91 | onChange={(value) => {
92 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 30) {
93 | this.changeParams("start", value);
94 | } else {
95 | message.info('输入不合法')
96 | }
97 | }}
98 | disabled={type !== "period"}
99 | />{" "}
100 | 到{" "}
101 | value.toString().replace(/[^\d\.]/g, '')}
110 | onChange={(value) => {
111 | if (isNumber(value) && Number(value) >= 2 && Number(value) <= 31) {
112 | this.changeParams("end", value);
113 | } else {
114 | message.info('输入不合法')
115 | }
116 | }}
117 | disabled={type !== "period"}
118 | />
119 | 日
120 |
121 |
122 |
123 | 从{" "}
124 | value.toString().replace(/[^\d\.]/g, '')}
131 | onChange={(value) => {
132 | if (isNumber(value) && Number(value) >= 1) {
133 | this.changeParams("begin", value);
134 | } else {
135 | message.info('输入不合法')
136 | }
137 | }}
138 | disabled={type !== "beginInterval"}
139 | />{" "}
140 | 日开始, 每{" "}
141 | value.toString().replace(/[^\d\.]/g, '')}
148 | onChange={(value) => {
149 | if (isNumber(value) && Number(value) >= 0) {
150 | this.changeParams("beginEvery", value);
151 | } else {
152 | message.info('输入不合法')
153 | }
154 | }}
155 | disabled={type !== "beginInterval"}
156 | />
157 | 天执行一次
158 |
159 |
160 |
161 | 每月{" "}
162 | value.toString().replace(/[^\d\.]/g, '')}
169 | onChange={(value) => {
170 | if (isNumber(value) && Number(value) >= 1) {
171 | this.changeParams("closeWorkDay", value);
172 | } else {
173 | message.info('输入不合法')
174 | }
175 | }}
176 | disabled={type !== "closeWorkDay"}
177 | />
178 | 日最近的那个工作日
179 |
180 |
181 |
182 | 本月最后{" "}
183 | value.toString().replace(/[^\d\.]/g, '')}
189 | onChange={(value) => {
190 | if (isNumber(value) && Number(value) >= 0) {
191 | this.changeParams("last", value);
192 | } else {
193 | message.info('输入不合法')
194 | }
195 | }}
196 | disabled
197 | // disabled={type !== "last"}
198 | />{" "}
199 | 天
200 |
201 |
202 |
203 | 具体天数(可多选)
204 |
222 | {/* {
225 | this.changeParams("some", value);
226 | }}
227 | options={this.dayOptions}
228 | disabled={type !== "some"}
229 | /> */}
230 |
231 |
232 |
233 |
234 | );
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/components/Hour.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-小时
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from "react";
7 | import { Radio, InputNumber, message, List, Checkbox, Select } from "antd";
8 | const { Group } = Radio;
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Hour extends PureComponent {
12 | constructor(props) {
13 | super(props);
14 | this.formatHourOptions();
15 | }
16 |
17 | // formatHourOptions() {
18 | // this.hourOptions = [];
19 | // for (let x = 0; x < 24; x++) {
20 | // this.hourOptions.push({
21 | // label: x < 10 ? `0${x}` : x,
22 | // value: `${x}`
23 | // });
24 | // }
25 | // }
26 |
27 | formatHourOptions() {
28 | this.hourOptions = [];
29 | for (let x = 0; x < 24; x++) {
30 | const label = x < 10 ? `0${x}` : x;
31 | const value = `${x}`;
32 | const ele = (
33 |
34 | {label}
35 |
36 | );
37 | this.hourOptions.push(ele);
38 | }
39 | }
40 |
41 | changeParams(type, value) {
42 | const state = { ...this.props.hour };
43 | state[type] = value;
44 | if (type === 'start') {
45 | if (state.end - state.start <= 1) {
46 | state.end = value + 1;
47 | }
48 | }
49 | if (type === 'end') {
50 | if (state.end - state.start <= 1) {
51 | state.start = value - 1;
52 | }
53 | }
54 | this.props.onChange(state);
55 | }
56 |
57 | changeType = e => {
58 | const state = { ...this.props.hour };
59 | // if (e.target.value === "some") {
60 | // state.some = ["1"];
61 | // }
62 | state.type = e.target.value;
63 | this.props.onChange(state);
64 | };
65 |
66 | render() {
67 | const {
68 | hour: { type, start, end, begin, some, beginEvery }
69 | } = this.props;
70 | return (
71 |
72 |
73 |
74 |
75 | 每小时
76 |
77 |
78 | 周期从{" "}
79 | value.toString().replace(/[^\d\.]/g, '')}
88 | onChange={(value) => {
89 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 22) {
90 | this.changeParams("start", value);
91 | } else {
92 | message.info('输入不合法')
93 | }
94 | }}
95 | disabled={type !== "period"}
96 | />
97 | 到
98 | value.toString().replace(/[^\d\.]/g, '')}
107 | onChange={(value) => {
108 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 23) {
109 | this.changeParams("end", value);
110 | } else {
111 | message.info('输入不合法')
112 | }
113 | }}
114 | disabled={type !== "period"}
115 | />
116 | 小时
117 |
118 |
119 |
120 | 从
121 | value.toString().replace(/[^\d\.]/g, '')}
129 | onChange={(value) => {
130 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 23) {
131 | this.changeParams("begin", value);
132 | } else {
133 | message.info('输入不合法')
134 | }
135 | }}
136 | disabled={type !== "beginInterval"}
137 | />
138 | 时开始, 每
139 | value.toString().replace(/[^\d\.]/g, '')}
147 | onChange={(value) => {
148 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 23) {
149 | this.changeParams("beginEvery", value);
150 | } else {
151 | message.info('输入不合法')
152 | }
153 | }}
154 | disabled={type !== "beginInterval"}
155 | />
156 | 时执行一次
157 |
158 |
159 | 具体小时数(可多选)
160 |
178 | {/* {
181 | if (value.length < 1) {
182 | return message.warn("至少选择一项");
183 | }
184 | this.changeParams("some", value);
185 | }}
186 | options={this.hourOptions}
187 | disabled={type !== "some"}
188 | /> */}
189 |
190 |
191 |
192 |
193 | );
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/components/Minute.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-分钟
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import { Radio, InputNumber, message, List, Checkbox, Select } from 'antd'
8 | const { Group } = Radio
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Minute extends PureComponent {
12 | constructor(props) {
13 | super(props)
14 | this.formatMinuteOptions()
15 | }
16 |
17 | // formatMinuteOptions() {
18 | // this.minuteOptions = [];
19 | // for (let x = 0; x < 60; x++) {
20 | // this.minuteOptions.push({
21 | // label: x < 10 ? `0${x}` : x,
22 | // value: `${x}`
23 | // });
24 | // }
25 | // }
26 |
27 | formatMinuteOptions() {
28 | this.minuteOptions = []
29 | for (let x = 0; x < 60; x++) {
30 | const label = x < 10 ? `0${x}` : x
31 | const value = `${x}`
32 | const ele = (
33 |
34 | {label}
35 |
36 | )
37 | this.minuteOptions.push(ele)
38 | }
39 | }
40 |
41 | changeParams(type, value) {
42 | const state = { ...this.props.minute }
43 | state[type] = value
44 | if (type === 'start') {
45 | if (state.end - state.start <= 1) {
46 | state.end = value + 1
47 | }
48 | }
49 | if (type === 'end') {
50 | if (state.end - state.start <= 1) {
51 | state.start = value - 1
52 | }
53 | }
54 | this.props.onChange(state)
55 | }
56 |
57 | changeType = (e) => {
58 | const state = { ...this.props.minute }
59 | // if (e.target.value === "some") {
60 | // state.some = ["1"];
61 | // }
62 | state.type = e.target.value
63 | this.props.onChange(state)
64 | }
65 |
66 | render() {
67 | const {
68 | minute: { type, start, end, some, begin, beginEvery },
69 | } = this.props
70 | return (
71 |
72 |
73 |
74 |
75 | 每分钟
76 |
77 |
78 | 周期
79 | 从
80 | value.toString().replace(/[^\d\.]/g, '')}
89 | onChange={(value) => {
90 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 58) {
91 | this.changeParams('start', value)
92 | } else {
93 | message.info('输入不合法')
94 | }
95 | }}
96 | disabled={type !== 'period'}
97 | />
98 | 到
99 | value.toString().replace(/[^\d\.]/g, '')}
108 | onChange={(value) => {
109 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 59) {
110 | this.changeParams('end', value)
111 | } else {
112 | message.info('输入不合法')
113 | }
114 | }}
115 | disabled={type !== 'period'}
116 | />
117 | 分钟
118 |
119 |
120 |
121 | 从第
122 | value.toString().replace(/[^\d\.]/g, '')}
130 | onChange={(value) => {
131 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 59) {
132 | this.changeParams('begin', value)
133 | } else {
134 | message.info('输入不合法')
135 | }
136 | }}
137 | disabled={type !== 'beginInterval'}
138 | />
139 | 分开始, 每
140 | value.toString().replace(/[^\d\.]/g, '')}
148 | onChange={(value) => {
149 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 59) {
150 | this.changeParams('beginEvery', value)
151 | } else {
152 | message.info('输入不合法')
153 | }
154 | }}
155 | disabled={type !== 'beginInterval'}
156 | />
157 | 分执行一次
158 |
159 |
160 | 具体分钟数(可多选)
161 |
179 | {/* {
182 | if (value.length < 1) {
183 | return message.warn("至少选择一项");
184 | }
185 | this.changeParams("some", value);
186 | }}
187 | options={this.minuteOptions}
188 | disabled={type !== "some"}
189 | /> */}
190 |
191 |
192 |
193 |
194 | )
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/components/Month.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-月
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import { Radio, InputNumber, Row, Col, List, message, Select } from 'antd'
8 | const { Group } = Radio
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Month extends PureComponent {
12 | constructor(props) {
13 | super(props)
14 | this.formatMonthOptions()
15 | }
16 |
17 | changeParams(type, value) {
18 | const state = { ...this.props.month }
19 | state[type] = value
20 | if (type === 'start') {
21 | if (state.end - state.start <= 1) {
22 | state.end = value + 1
23 | }
24 | }
25 | if (type === 'end') {
26 | if (state.end - state.start <= 1) {
27 | state.start = value - 1
28 | }
29 | }
30 | this.props.onChange(state)
31 | }
32 |
33 | // eachMonthOptions() {
34 | // const options = [];
35 | // for (let i = 1; i < 13; i++) {
36 | // options.push({ label: `${i}月`, value: `${i}` });
37 | // }
38 | // return options;
39 | // }
40 |
41 | formatMonthOptions() {
42 | this.monthOptions = []
43 | for (let x = 1; x < 13; x++) {
44 | const label = `${x}月`
45 | const value = `${x}`
46 | const ele = (
47 |
48 | {label}
49 |
50 | )
51 | this.monthOptions.push(ele)
52 | }
53 | }
54 |
55 | changeType = (e) => {
56 | const state = { ...this.props.month }
57 | // if (e.target.value === "some") {
58 | // state.some = ["1"];
59 | // }
60 | state.type = e.target.value
61 | this.props.onChange(state)
62 | }
63 |
64 | render() {
65 | const {
66 | month: { type, start, end, beginEvery, begin, some },
67 | } = this.props
68 | return (
69 |
70 |
71 |
72 |
73 | 每月
74 |
75 | {/*
76 | 不指定
77 | */}
78 |
79 | 周期从{' '}
80 | value.toString().replace(/[^\d\.]/g, '')}
88 | onChange={(value) => {
89 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 11) {
90 | this.changeParams('start', value)
91 | } else {
92 | message.info('输入不合法')
93 | }
94 | }}
95 | disabled={type !== 'period'}
96 | />{' '}
97 | 到{' '}
98 | value.toString().replace(/[^\d\.]/g, '')}
106 | onChange={(value) => {
107 | if (isNumber(value) && Number(value) >= 2 && Number(value) <= 12) {
108 | this.changeParams('end', value)
109 | } else {
110 | message.info('输入不合法')
111 | }
112 | }}
113 | disabled={type !== 'period'}
114 | />
115 | 月
116 |
117 |
118 |
119 | 从
120 | value.toString().replace(/[^\d\.]/g, '')}
128 | onChange={(value) => {
129 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 12) {
130 | this.changeParams('begin', value)
131 | } else {
132 | message.info('输入不合法')
133 | }
134 | }}
135 | disabled={type !== 'beginInterval'}
136 | />{' '}
137 | 月开始, 每{' '}
138 | value.toString().replace(/[^\d\.]/g, '')}
146 | onChange={(value) => {
147 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 12) {
148 | this.changeParams('beginEvery', value)
149 | } else {
150 | message.info('输入不合法')
151 | }
152 | }}
153 | disabled={type !== 'beginInterval'}
154 | />{' '}
155 | 月执行一次
156 |
157 |
158 | 具体月数(可多选)
159 |
177 | {/* {
180 | this.changeParams("some", value);
181 | }}
182 | options={this.eachMonthOptions()}
183 | disabled={type !== "some"}
184 | /> */}
185 |
186 |
187 |
188 |
189 | )
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/components/Second.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-秒
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import { Radio, InputNumber, message, List, Checkbox, Select } from 'antd'
8 | const { Group } = Radio
9 | import { isNumber } from '../utils/index'
10 | export default class Second extends PureComponent {
11 | constructor(props) {
12 | super(props)
13 | this.formatSecondOptions()
14 | }
15 |
16 | // formatSecondOptions() {
17 | // this.secondOptions = [];
18 | // for (let x = 0; x < 60; x++) {
19 | // this.secondOptions.push({
20 | // label: x < 10 ? `0${x}` : x,
21 | // value: `${x}`
22 | // });
23 | // }
24 | // }
25 |
26 | changeParams(type, value) {
27 | const state = { ...this.props.second }
28 | state[type] = value
29 | if (type === 'start') {
30 | if (state.end - state.start <= 1) {
31 | state.end = value + 1
32 | }
33 | }
34 | if (type === 'end') {
35 | if (state.end - state.start <= 1) {
36 | state.start = value - 1
37 | }
38 | }
39 | this.props.onChange(state)
40 | }
41 |
42 | formatSecondOptions() {
43 | this.secondOptions = []
44 | for (let x = 0; x < 60; x++) {
45 | const label = x < 10 ? `0${x}` : x
46 | const value = `${x}`
47 | const ele = (
48 |
49 | {label}
50 |
51 | )
52 | this.secondOptions.push(ele)
53 | }
54 | }
55 |
56 | render() {
57 | const {
58 | second: { type, start, end, begin, beginEvery, some },
59 | } = this.props
60 | return (
61 |
62 | {
65 | const state = { ...this.props.second }
66 | // if (e.target.value !== "some") {
67 | // state.some = ["0"];
68 | // }
69 | state.type = e.target.value
70 | this.props.onChange(state)
71 | }}
72 | >
73 |
74 |
75 | 每秒
76 |
77 |
78 | 周期
79 | 从
80 | value.toString().replace(/[^\d\.]/g, '')}
89 | onChange={(value) => {
90 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 58) {
91 | this.changeParams('start', value)
92 | } else {
93 | message.info('输入不合法')
94 | }
95 | }}
96 | disabled={type !== 'period'}
97 | />
98 | 到
99 | value.toString().replace(/[^\d\.]/g, '')}
108 | onChange={(value) => {
109 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 59) {
110 | this.changeParams('end', value)
111 | } else {
112 | message.info('输入不合法')
113 | }
114 | }}
115 | disabled={type !== 'period'}
116 | />
117 | 秒
118 |
119 |
120 |
121 | 从第
122 | value.toString().replace(/[^\d\.]/g, '')}
130 | onChange={(value) => {
131 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 59) {
132 | this.changeParams('begin', value)
133 | } else {
134 | message.info('输入不合法')
135 | }
136 | }}
137 | disabled={type !== 'beginInterval'}
138 | />{' '}
139 | 秒开始, 每
140 | value.toString().replace(/[^\d\.]/g, '')}
148 | onChange={(value) => {
149 | if (isNumber(value) && Number(value) >= 0 && Number(value) <= 59) {
150 | this.changeParams('beginEvery', value)
151 | } else {
152 | message.info('输入不合法')
153 | }
154 | }}
155 | disabled={type !== 'beginInterval'}
156 | />{' '}
157 | 秒执行一次
158 |
159 |
160 | 具体秒数(可多选)
161 |
179 | {/* {
182 | if (value.length < 1) {
183 | return message.warn("至少选择一项");
184 | }
185 | this.changeParams("some", value);
186 | }}
187 | options={this.secondOptions}
188 | disabled={type !== "some"}
189 | /> */}
190 |
191 |
192 |
193 |
194 | )
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/components/Week.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-周
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import { Radio, InputNumber, Row, Col, Select, List, Checkbox, message } from 'antd'
8 | const { Group } = Radio
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Week extends PureComponent {
12 | weekOptions = [
13 | {
14 | label: '星期日',
15 | value: '1',
16 | },
17 | {
18 | label: '星期一',
19 | value: '2',
20 | },
21 | {
22 | label: '星期二',
23 | value: '3',
24 | },
25 | {
26 | label: '星期三',
27 | value: '4',
28 | },
29 | {
30 | label: '星期四',
31 | value: '5',
32 | },
33 | {
34 | label: '星期五',
35 | value: '6',
36 | },
37 | {
38 | label: '星期六',
39 | value: '7',
40 | },
41 | ]
42 |
43 | getWeekOptions() {
44 | return this.weekOptions.map((item, index) => {
45 | return (
46 |
47 | {item.label}
48 |
49 | )
50 | })
51 | }
52 |
53 | changeParams(type, value) {
54 | const state = { ...this.props.week }
55 | state[type] = value
56 | // if (type === 'start') {
57 | // if (state.end - state.start <= 1) {
58 | // state.end = value + 1;
59 | // }
60 | // }
61 | // if (type === 'end') {
62 | // if (state.end - state.start <= 1) {
63 | // state.start = value - 1;
64 | // }
65 | // }
66 | this.props.onChange(state)
67 | }
68 |
69 | render() {
70 | const {
71 | week: { type, start, end, some, begin, beginEvery, last },
72 | } = this.props
73 | return (
74 |
75 | {
78 | const state = { ...this.props.week }
79 | // if (e.target.value === "some") {
80 | // state.some = ["1"];
81 | // }
82 | state.type = e.target.value
83 | this.props.onChange(state)
84 | }}
85 | >
86 |
87 |
88 | 每周
89 |
90 |
91 | 不指定
92 |
93 |
94 | 周期从{' '}
95 | {' '}
107 | 到{' '}
108 |
120 |
121 |
122 | 第{' '}
123 | value.toString().replace(/[^\d\.]/g, '')}
130 | onChange={(value) => {
131 | if (isNumber(value) && Number(value) >= 1 && Number(value) <= 4) {
132 | this.changeParams('begin', value)
133 | } else {
134 | message.info('输入不合法')
135 | }
136 | }}
137 | disabled={type !== 'beginInterval'}
138 | />{' '}
139 | 周的{' '}
140 |
153 |
154 |
155 |
156 | 本月最后一个
157 |
169 |
170 |
171 | 具体星期数(可多选)
172 |
189 | {/* {
193 | this.changeParams("some", value);
194 | }}
195 | options={this.weekOptions}
196 | disabled={type !== "some"}
197 | /> */}
198 |
199 |
200 |
201 |
202 | )
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/components/Year.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:周期-年
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import { Radio, InputNumber, message, Col, List } from 'antd'
8 | const { Group } = Radio
9 | import { isNumber } from '../utils/index'
10 |
11 | export default class Year extends PureComponent {
12 | changeParams(type, value) {
13 | const state = { ...this.props.year }
14 | state[type] = value
15 | if (type === 'start') {
16 | if (state.end - state.start <= 1) {
17 | state.end = value + 1
18 | }
19 | }
20 | if (type === 'end') {
21 | if (state.end - state.start <= 1) {
22 | state.start = value - 1
23 | }
24 | }
25 | this.props.onChange(state)
26 | }
27 |
28 | render() {
29 | const {
30 | year: { type, start, end },
31 | } = this.props
32 | return (
33 |
34 | {
37 | this.changeParams('type', e.target.value)
38 | }}
39 | defaultValue=""
40 | >
41 |
42 |
43 | 不指定
44 |
45 |
46 | 每年
47 |
48 |
49 | 周期
50 | value.toString().replace(/[^\d\.]/g, '')}
55 | onChange={(value) => {
56 | if (isNumber(value) && Number(value) >= new Date().getFullYear()) {
57 | this.changeParams('start', value)
58 | } else {
59 | message.info('输入不合法')
60 | }
61 | }}
62 | disabled={type !== 'period'}
63 | />
64 | {' - '}
65 | value.toString().replace(/[^\d\.]/g, '')}
70 | onChange={(value) => {
71 | if (isNumber(value) && Number(value) >= new Date().getFullYear() + 1) {
72 | this.changeParams('end', value)
73 | } else {
74 | message.info('输入不合法')
75 | }
76 | }}
77 | disabled={type !== 'period'}
78 | />
79 |
80 |
81 |
82 |
83 | )
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/css/index.less:
--------------------------------------------------------------------------------
1 | .cron-editor-mlamp-react {
2 | min-width: 200px;
3 | overflow: auto;
4 | .ant-radio-group {
5 | width: 100%;
6 | }
7 | .ant-select {
8 | min-width: 90px;
9 | }
10 | .highlight {
11 | border: 1px solid #40a9ff;
12 | color: #000;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:主界面
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 | import React, { PureComponent } from 'react'
7 | import classNames from 'classnames'
8 | import PropTypes from 'prop-types'
9 | import { Tabs, Dropdown, Row, Col, Input, List, Collapse } from 'antd'
10 | import Year from './components/Year'
11 | import Month from './components/Month'
12 | import Week from './components/Week'
13 | import Day from './components/Day'
14 | import Hour from './components/Hour'
15 | import Minute from './components/Minute'
16 | import Second from './components/Second'
17 | import CronParse from './utils/parse-lib'
18 | import CronParser from 'cron-parser'
19 | import moment from 'moment'
20 | const { TabPane } = Tabs
21 | const { Panel } = Collapse
22 | import './css/index.less'
23 |
24 | const noop = function () {}
25 |
26 | const dateMinute = 'YYYY-MM-DD HH:mm'
27 |
28 | class Cron extends PureComponent {
29 | constructor(props) {
30 | super(props)
31 | const date = new Date()
32 | this.state = {
33 | activeKey: 'second',
34 | year: {
35 | type: '',
36 | start: date.getFullYear(),
37 | end: date.getFullYear() + 1,
38 | },
39 | month: {
40 | start: 1,
41 | end: 2,
42 | begin: 1,
43 | beginEvery: 1,
44 | type: '*',
45 | some: ['1'],
46 | },
47 | week: {
48 | start: '1',
49 | end: '2',
50 | last: '1',
51 | begin: 1,
52 | beginEvery: '1',
53 | type: '?',
54 | some: ['1'],
55 | },
56 | day: {
57 | last: 1,
58 | closeWorkDay: 1,
59 | start: 1,
60 | end: 2,
61 | begin: 1,
62 | beginEvery: 1,
63 | type: '*',
64 | some: ['1'],
65 | },
66 | hour: {
67 | start: 0,
68 | end: 1,
69 | begin: 0,
70 | beginEvery: 1,
71 | type: '*',
72 | some: ['0'],
73 | },
74 | minute: {
75 | start: 0,
76 | end: 1,
77 | begin: 0,
78 | beginEvery: 1,
79 | type: '*',
80 | some: ['0'],
81 | },
82 | second: {
83 | start: 0,
84 | end: 1,
85 | begin: 0,
86 | beginEvery: 1,
87 | type: '*',
88 | some: ['0'],
89 | },
90 | runTime: [],
91 | }
92 | }
93 |
94 | initValue() {
95 | let { value } = this.props
96 | value = value.toUpperCase()
97 | const valuesArray = value.split(' ')
98 | let newState = { ...this.state }
99 | newState.second.value = valuesArray[0] || ''
100 | newState.minute.value = valuesArray[1] || ''
101 | newState.hour.value = valuesArray[2] || ''
102 | newState.day.value = valuesArray[3] || ''
103 | newState.month.value = valuesArray[4] || ''
104 | newState.week.value = valuesArray[5] || ''
105 | newState.year.value = valuesArray[6] || ''
106 | this.setState(newState, () => {
107 | this.parse()
108 | })
109 | }
110 |
111 | componentDidMount(props) {
112 | this.initValue(props)
113 | }
114 |
115 | componentDidUpdate(props) {
116 | const { value } = this.props
117 | if (props.value !== value && value) {
118 | this.initValue()
119 | }
120 | }
121 |
122 | parse() {
123 | let { year, month, week, day, hour, minute, second } = this.state
124 | if (year.value.indexOf('-') > -1) {
125 | year.type = 'period'
126 | year.start = year.value.split('-')[0]
127 | year.end = year.value.split('-')[1]
128 | } else {
129 | year.type = year.value
130 | }
131 | if (week.value.indexOf('-') > -1) {
132 | week.type = 'period'
133 | week.start = week.value.split('-')[0]
134 | week.end = week.value.split('-')[1]
135 | } else if (week.value.indexOf('L') > -1) {
136 | week.type = 'last'
137 | week.last = week.value.split('L')[0] || 1
138 | } else if (week.value.indexOf('#') > -1) {
139 | week.type = 'beginInterval'
140 | week.begin = week.value.split('#')[1]
141 | week.beginEvery = week.value.split('#')[0]
142 | } else if (week.value.indexOf(',') > -1 || /^[0-9]+$/.test(week.value)) {
143 | week.type = 'some'
144 | week.some = week.value.split(',')
145 | } else {
146 | week.type = week.value || '?'
147 | }
148 |
149 | if (month.value.indexOf('-') > -1) {
150 | month.type = 'period'
151 | month.start = month.value.split('-')[0]
152 | month.end = month.value.split('-')[1]
153 | } else if (month.value.indexOf('/') > -1) {
154 | month.type = 'beginInterval'
155 | month.begin = month.value.split('/')[0]
156 | month.beginEvery = month.value.split('/')[1]
157 | } else if (month.value.indexOf(',') > -1 || /^[0-9]+$/.test(month.value)) {
158 | month.type = 'some'
159 | month.some = month.value.split(',')
160 | } else {
161 | month.type = month.value || '?'
162 | }
163 |
164 | if (day.value.indexOf('-') > -1) {
165 | day.type = 'period'
166 | day.start = day.value.split('-')[0]
167 | day.end = day.value.split('-')[1]
168 | } else if (day.value.indexOf('W') > -1) {
169 | day.type = 'closeWorkDay'
170 | day.closeWorkDay = day.value.split('W')[0] || 1
171 | } else if (day.value.indexOf('L') > -1) {
172 | day.type = 'last'
173 | day.last = day.value.split('L')[0] || 1
174 | } else if (day.value.indexOf('/') > -1) {
175 | day.type = 'beginInterval'
176 | day.begin = day.value.split('/')[0]
177 | day.beginEvery = day.value.split('/')[1]
178 | } else if (day.value.indexOf(',') > -1 || /^[0-9]+$/.test(day.value)) {
179 | day.type = 'some'
180 | day.some = day.value.split(',')
181 | } else {
182 | day.type = day.value || '?'
183 | }
184 |
185 | if (hour.value.indexOf('-') > -1) {
186 | hour.type = 'period'
187 | hour.start = hour.value.split('-')[0]
188 | hour.end = hour.value.split('-')[1]
189 | } else if (hour.value.indexOf('/') > -1) {
190 | hour.type = 'beginInterval'
191 | hour.begin = hour.value.split('/')[0]
192 | hour.beginEvery = hour.value.split('/')[1]
193 | } else if (hour.value.indexOf(',') > -1 || /^[0-9]+$/.test(hour.value)) {
194 | hour.type = 'some'
195 | hour.some = hour.value.split(',')
196 | } else {
197 | hour.type = hour.value || '?'
198 | }
199 |
200 | if (minute.value.indexOf('-') > -1) {
201 | minute.type = 'period'
202 | minute.start = minute.value.split('-')[0]
203 | minute.end = minute.value.split('-')[1]
204 | } else if (minute.value.indexOf('/') > -1) {
205 | minute.type = 'beginInterval'
206 | minute.begin = minute.value.split('/')[0]
207 | minute.beginEvery = minute.value.split('/')[1]
208 | } else if (minute.value.indexOf(',') > -1 || /^[0-9]+$/.test(minute.value)) {
209 | minute.type = 'some'
210 | minute.some = minute.value.split(',')
211 | } else {
212 | minute.type = minute.value || '?'
213 | }
214 |
215 | if (second.value.indexOf('-') > -1) {
216 | second.type = 'period'
217 | second.start = second.value.split('-')[0]
218 | second.end = second.value.split('-')[1]
219 | } else if (second.value.indexOf('/') > -1) {
220 | second.type = 'beginInterval'
221 | second.begin = second.value.split('/')[0]
222 | second.beginEvery = second.value.split('/')[1]
223 | } else if (second.value.indexOf(',') > -1 || /^[0-9]+$/.test(second.value)) {
224 | second.type = 'some'
225 | second.some = second.value.split(',')
226 | } else {
227 | second.type = second.value || '?'
228 | }
229 | this.setState({
230 | year: { ...year },
231 | month: { ...month },
232 | week: { ...week },
233 | day: { ...day },
234 | hour: { ...hour },
235 | minute: { ...minute },
236 | second: { ...second },
237 | })
238 | console.log('this.state :', this.state)
239 | }
240 |
241 | format() {
242 | const { year, month, week, day, hour, minute, second } = this.state
243 | return `${second.value} ${minute.value} ${hour.value} ${day.value} ${month.value} ${week.value} ${year.value}`
244 | }
245 |
246 | changeState(state) {
247 | this.setState(state, () => {
248 | this.culcCron()
249 | })
250 | }
251 |
252 | // 计算用户的cron
253 | culcCron() {
254 | const { n2s } = this
255 | let { year, month, week, day, hour, minute, second } = this.state
256 | if (year.type === 'period') {
257 | year.value = `${n2s(year.start)}-${n2s(year.end)}`
258 | } else {
259 | year.value = year.type
260 | }
261 | if (month.type === 'period') {
262 | month.value = `${n2s(month.start)}-${n2s(month.end)}`
263 | } else if (month.type === 'beginInterval') {
264 | month.value = `${n2s(month.begin)}/${n2s(month.beginEvery)}`
265 | } else if (month.type === 'some') {
266 | month.value = month.some.join(',')
267 | } else {
268 | month.value = month.type
269 | }
270 | if (week.type === 'period') {
271 | week.value = `${n2s(week.start)}-${n2s(week.end)}`
272 | } else if (week.type === 'beginInterval') {
273 | week.value = `${n2s(week.beginEvery)}#${n2s(week.begin)}`
274 | } else if (week.type === 'last') {
275 | week.value = n2s(week.last) + 'L'
276 | } else if (week.type === 'some') {
277 | week.value = week.some.join(',')
278 | } else {
279 | week.value = week.type
280 | }
281 | if (day.type === 'period') {
282 | day.value = `${n2s(day.start)}-${n2s(day.end)}`
283 | } else if (day.type === 'beginInterval') {
284 | day.value = `${n2s(day.begin)}/${n2s(day.beginEvery)}`
285 | } else if (day.type === 'closeWorkDay') {
286 | day.value = n2s(day.closeWorkDay || 1) + 'W'
287 | } else if (day.type === 'last') {
288 | // day.value = n2s(day.last || 1) + "L";
289 | day.value = 'L'
290 | } else if (day.type === 'some') {
291 | day.value = day.some.join(',')
292 | } else {
293 | day.value = day.type
294 | }
295 | if (hour.type === 'period') {
296 | hour.value = `${n2s(hour.start)}-${n2s(hour.end)}`
297 | } else if (hour.type === 'beginInterval') {
298 | hour.value = `${n2s(hour.begin)}/${n2s(hour.beginEvery)}`
299 | } else if (hour.type === 'some') {
300 | hour.value = hour.some.join(',')
301 | } else {
302 | hour.value = hour.type
303 | }
304 | if (minute.type === 'period') {
305 | minute.value = `${n2s(minute.start)}-${n2s(minute.end)}`
306 | } else if (minute.type === 'beginInterval') {
307 | minute.value = `${n2s(minute.begin)}/${n2s(minute.beginEvery)}`
308 | } else if (minute.type === 'some') {
309 | minute.value = minute.some.join(',')
310 | } else {
311 | minute.value = minute.type
312 | }
313 | if (second.type === 'period') {
314 | second.value = `${n2s(second.start)}-${n2s(second.end)}`
315 | } else if (second.type === 'beginInterval') {
316 | second.value = `${n2s(second.begin)}/${n2s(second.beginEvery)}`
317 | } else if (second.type === 'some') {
318 | second.value = second.some.join(',')
319 | } else {
320 | second.value = second.type
321 | }
322 | this.setState(
323 | {
324 | year: { ...year },
325 | month: { ...month },
326 | week: { ...week },
327 | day: { ...day },
328 | hour: { ...hour },
329 | minute: { ...minute },
330 | second: { ...second },
331 | },
332 | () => {
333 | this.triggerChange()
334 | }
335 | )
336 | }
337 |
338 | n2s(number) {
339 | if (typeof number === 'number' && number !== NaN) {
340 | return `${number}`
341 | }
342 | return number
343 | }
344 |
345 | triggerChange() {
346 | const { onChange, showRunTime } = this.props
347 | const crontab = this.format()
348 | console.log('crontab', crontab)
349 | onChange && onChange(crontab)
350 | if (!showRunTime) return // 既然不需要,那就不算了
351 | let tempArr = []
352 | const weekCron = crontab.split(' ')[5]
353 | try {
354 | if (weekCron !== '?') {
355 | const interval = CronParser.parseExpression(String(crontab).trim())
356 | for (let i = 0; i < 5; i++) {
357 | const temp = moment(interval.next().toString()).format(dateMinute)
358 | tempArr.push(temp)
359 | }
360 | } else {
361 | const cron = new CronParse()
362 | tempArr = cron.expressionChange(String(crontab).trim())
363 | }
364 | } catch (error) {
365 | // console.log("error :", error);
366 | tempArr.push('暂无最新执行周期')
367 | }
368 | if (tempArr.length > 0) {
369 | this.setState({
370 | runTime: tempArr,
371 | })
372 | }
373 | }
374 |
375 | // 发生表单值改变,重新计算
376 | onChange = (type, value) => {
377 | this.state[type].value = value
378 |
379 | this.setState({ ...this.state }, () => {
380 | this.parse()
381 | })
382 | }
383 |
384 | renderOverLay() {
385 | const { activeKey, week, day } = this.state
386 | const { tabType } = this.props
387 | return (
388 | {
391 | this.setState({ activeKey: key })
392 | }}
393 | type={tabType}
394 | >
395 |
396 | {
399 | this.changeState({ second: state })
400 | }}
401 | />
402 |
403 |
404 | {
407 | this.changeState({ minute: state })
408 | }}
409 | />
410 |
411 |
412 | {
415 | this.changeState({ hour: state })
416 | }}
417 | />
418 |
419 |
420 | {
423 | if (week.type === '?' && state.type === '?') {
424 | const obj = { ...week, type: '*' }
425 | console.log('obj', obj)
426 | this.setState({
427 | week: obj,
428 | })
429 | } else {
430 | const obj = { ...week, type: '?' }
431 | console.log('obj', obj)
432 | this.setState({
433 | week: obj,
434 | })
435 | }
436 | this.changeState({ day: state })
437 | }}
438 | />
439 |
440 |
441 | {
444 | if (day.type === '?' && state.type === '?') {
445 | const obj = { ...week, type: '*' }
446 | console.log('obj', obj)
447 | this.setState({
448 | day: obj,
449 | })
450 | } else {
451 | const obj = { ...week, type: '?' }
452 | console.log('obj', obj)
453 | this.setState({
454 | day: obj,
455 | })
456 | }
457 |
458 | this.changeState({ week: state })
459 | }}
460 | />
461 |
462 |
463 | {
466 | this.changeState({ month: state })
467 | }}
468 | />
469 |
470 |
471 |
472 | {
475 | this.changeState({ year: state })
476 | }}
477 | />
478 |
479 |
480 | )
481 | }
482 |
483 | render() {
484 | const state = JSON.parse(JSON.stringify(this.state))
485 | const { year, month, week, day, hour, minute, second, runTime, activeKey } = state
486 | const { showRunTime, showCrontab } = this.props
487 | return (
488 |
595 | )
596 | }
597 | }
598 |
599 | Cron.propTypes = {
600 | onChange: PropTypes.func,
601 | showRunTime: PropTypes.bool,
602 | value: PropTypes.string,
603 | tabType: PropTypes.string,
604 | showCrontab: PropTypes.bool,
605 | }
606 |
607 | Cron.defaultProps = {
608 | onChange: noop,
609 | showRunTime: false,
610 | value: '0 0 0 * * ? *',
611 | tabType: 'line',
612 | showCrontab: true,
613 | }
614 |
615 | export default Cron
616 |
--------------------------------------------------------------------------------
/src/test/__mocks__/fileMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'test-file-stub'
2 |
--------------------------------------------------------------------------------
/src/test/__mocks__/styleMock.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | process: function () {
3 | return ''
4 | },
5 | }
6 |
--------------------------------------------------------------------------------
/src/test/__test__/app.spec.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Todo from '../../index'
3 | import { shallow } from 'enzyme'
4 |
5 | const setup = () => {
6 | const props = {
7 | text: 'todo-1',
8 | completed: false,
9 | onClick: jest.fn(),
10 | onRemoveTodoClick: jest.fn(),
11 | }
12 | const wrapper = shallow()
13 | return {
14 | props,
15 | wrapper,
16 | }
17 | }
18 |
19 | describe('Todo', () => {
20 | const { props, wrapper } = setup()
21 |
22 | // 通过 input 是否存在来判断 Todo组件是否被渲染
23 | it('Todo item should render', () => {
24 | expect(wrapper.find('input').length).toBe(1)
25 | })
26 |
27 | // 当点击 单选按钮,onClick 方法应该被调用
28 | it('click checkbox input, onClick called', () => {
29 | const mockEvent = {
30 | key: 'Click',
31 | }
32 | wrapper.find('input').simulate('click', mockEvent)
33 | expect(props.onClick).toBeCalled()
34 | })
35 |
36 | it('the item should remove when click remove button', () => {
37 | const mockEvent = {
38 | key: 'Click',
39 | }
40 | wrapper.find('button').simulate('click', mockEvent)
41 | expect(props.onRemoveTodoClick).toBeCalled()
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export const isNumber = (obj) => {
2 | return typeof obj === 'number' && !isNaN(obj)
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/parse-lib.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 功能:解析cron类
3 | * 作者:宋鑫鑫
4 | * 日期:2019.11.04
5 | */
6 |
7 | class CronParse {
8 | constructor() {
9 | // constructor是一个构造方法,用来接收参数
10 | this.dayRule = "";
11 | this.dayRuleSup = "";
12 | this.dateArr = [];
13 | this.resultList = [];
14 | this.isShow = false;
15 | }
16 |
17 | // 表达式值变化时,开始去计算结果
18 | expressionChange(cron) {
19 | // 计算开始-隐藏结果
20 | this.isShow = false;
21 | // 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
22 | const ruleArr = cron.split(" ");
23 | // 用于记录进入循环的次数
24 | let nums = 0;
25 | // 用于暂时存符号时间规则结果的数组
26 | const resultArr = [];
27 | // 获取当前时间精确至[年、月、日、时、分、秒]
28 | const nTime = new Date();
29 | const nYear = nTime.getFullYear();
30 | let nMouth = nTime.getMonth() + 1;
31 | let nDay = nTime.getDate();
32 | let nHour = nTime.getHours();
33 | let nMin = nTime.getMinutes();
34 | let nSecond = nTime.getSeconds();
35 | // 根据规则获取到近100年可能年数组、月数组等等
36 | this.getSecondArr(ruleArr[0]);
37 | this.getMinArr(ruleArr[1]);
38 | this.getHourArr(ruleArr[2]);
39 | this.getDayArr(ruleArr[3]);
40 | this.getMouthArr(ruleArr[4]);
41 | this.getWeekArr(ruleArr[5]);
42 | this.getYearArr(ruleArr[6], nYear);
43 | // 将获取到的数组赋值-方便使用
44 | const sDate = this.dateArr[0];
45 | const mDate = this.dateArr[1];
46 | const hDate = this.dateArr[2];
47 | const DDate = this.dateArr[3];
48 | const MDate = this.dateArr[4];
49 | const YDate = this.dateArr[5];
50 | // 获取当前时间在数组中的索引
51 | let sIdx = this.getIndex(sDate, nSecond);
52 | let mIdx = this.getIndex(mDate, nMin);
53 | let hIdx = this.getIndex(hDate, nHour);
54 | let DIdx = this.getIndex(DDate, nDay);
55 | let MIdx = this.getIndex(MDate, nMouth);
56 | const YIdx = this.getIndex(YDate, nYear);
57 | // 重置月日时分秒的函数(后面用的比较多)
58 | const resetSecond = function() {
59 | sIdx = 0;
60 | nSecond = sDate[sIdx];
61 | };
62 | const resetMin = function() {
63 | mIdx = 0;
64 | nMin = mDate[mIdx];
65 | resetSecond();
66 | };
67 | const resetHour = function() {
68 | hIdx = 0;
69 | nHour = hDate[hIdx];
70 | resetMin();
71 | };
72 | const resetDay = function() {
73 | DIdx = 0;
74 | nDay = DDate[DIdx];
75 | resetHour();
76 | };
77 | const resetMouth = function() {
78 | MIdx = 0;
79 | nMouth = MDate[MIdx];
80 | resetDay();
81 | };
82 | // 如果当前年份不为数组中当前值
83 | if (nYear !== YDate[YIdx]) {
84 | resetMouth();
85 | }
86 | // 如果当前月份不为数组中当前值
87 | if (nMouth !== MDate[MIdx]) {
88 | resetDay();
89 | }
90 | // 如果当前“日”不为数组中当前值
91 | if (nDay !== DDate[DIdx]) {
92 | resetHour();
93 | }
94 | // 如果当前“时”不为数组中当前值
95 | if (nHour !== hDate[hIdx]) {
96 | resetMin();
97 | }
98 | // 如果当前“分”不为数组中当前值
99 | if (nMin !== mDate[mIdx]) {
100 | resetSecond();
101 | }
102 |
103 | // 循环年份数组
104 | goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
105 | const YY = YDate[Yi];
106 | // 如果到达最大值时
107 | if (nMouth > MDate[MDate.length - 1]) {
108 | resetMouth();
109 | continue;
110 | }
111 | // 循环月份数组
112 | goMouth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
113 | // 赋值、方便后面运算
114 | let MM = MDate[Mi];
115 | MM = MM < 10 ? `0${MM}` : MM;
116 | // 如果到达最大值时
117 | if (nDay > DDate[DDate.length - 1]) {
118 | resetDay();
119 | if (Mi === MDate.length - 1) {
120 | resetMouth();
121 | continue goYear;
122 | }
123 | continue;
124 | }
125 | // 循环日期数组
126 | goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
127 | // 赋值、方便后面运算
128 | let DD = DDate[Di];
129 | let thisDD = DD < 10 ? `0${DD}` : DD;
130 | // 如果到达最大值时
131 | if (nHour > hDate[hDate.length - 1]) {
132 | resetHour();
133 | if (Di === DDate.length - 1) {
134 | resetDay();
135 | if (Mi === MDate.length - 1) {
136 | resetMouth();
137 | continue goYear;
138 | }
139 | continue goMouth;
140 | }
141 | continue;
142 | }
143 | // 判断日期的合法性,不合法的话也是跳出当前循环
144 | if (
145 | this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true &&
146 | this.dayRule !== "workDay" &&
147 | this.dayRule !== "lastWeek" &&
148 | this.dayRule !== "lastDay"
149 | ) {
150 | resetDay();
151 | continue goMouth;
152 | }
153 | // 如果日期规则中有值时
154 | if (this.dayRule === "lastDay") {
155 | // 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
156 | if (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
157 | while (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
158 | DD--;
159 | thisDD = DD < 10 ? `0${DD}` : DD;
160 | }
161 | }
162 | } else if (this.dayRule === "workDay") {
163 | // 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
164 | if (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
165 | while (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
166 | DD--;
167 | thisDD = DD < 10 ? `0${DD}` : DD;
168 | }
169 | }
170 | // 获取达到条件的日期是星期X
171 | const thisWeek = this.formatDate(new Date(`${YY}-${MM}-${thisDD} 00:00:00`), "week");
172 | // 当星期日时
173 | if (thisWeek === 0) {
174 | // 先找下一个日,并判断是否为月底
175 | DD++;
176 | thisDD = DD < 10 ? `0${DD}` : DD;
177 | // 判断下一日已经不是合法日期
178 | if (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
179 | DD -= 3;
180 | }
181 | } else if (thisWeek === 6) {
182 | // 当星期6时只需判断不是1号就可进行操作
183 | if (this.dayRuleSup !== 1) {
184 | DD--;
185 | } else {
186 | DD += 2;
187 | }
188 | }
189 | } else if (this.dayRule === "weekDay") {
190 | // 如果指定了是星期几
191 | // 获取当前日期是属于星期几
192 | const thisWeek = this.formatDate(new Date(`${YY}-${MM}-${DD} 00:00:00`), "week");
193 | // 校验当前星期是否在星期池(dayRuleSup)中
194 | if (Array.indexOf(this.dayRuleSup, thisWeek) < 0) {
195 | // 如果到达最大值时
196 | if (Di === DDate.length - 1) {
197 | resetDay();
198 | if (Mi === MDate.length - 1) {
199 | resetMouth();
200 | continue goYear;
201 | }
202 | continue goMouth;
203 | }
204 | continue;
205 | }
206 | } else if (this.dayRule === "assWeek") {
207 | // 如果指定了是第几周的星期几
208 | // 获取每月1号是属于星期几
209 | const thisWeek = this.formatDate(new Date(`${YY}-${MM}-${DD} 00:00:00`), "week");
210 | if (this.dayRuleSup[1] >= thisWeek) {
211 | DD = (this.dayRuleSup[0] - 1) * 7 + this.dayRuleSup[1] - thisWeek + 1;
212 | } else {
213 | DD = this.dayRuleSup[0] * 7 + this.dayRuleSup[1] - thisWeek + 1;
214 | }
215 | } else if (this.dayRule === "lastWeek") {
216 | // 如果指定了每月最后一个星期几
217 | // 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
218 | if (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
219 | while (this.checkDate(`${YY}-${MM}-${thisDD} 00:00:00`) !== true) {
220 | DD--;
221 | thisDD = DD < 10 ? `0${DD}` : DD;
222 | }
223 | }
224 | // 获取月末最后一天是星期几
225 | const thisWeek = this.formatDate(new Date(`${YY}-${MM}-${thisDD} 00:00:00`), "week");
226 | // 找到要求中最近的那个星期几
227 | if (this.dayRuleSup < thisWeek) {
228 | DD -= thisWeek - this.dayRuleSup;
229 | } else if (this.dayRuleSup > thisWeek) {
230 | DD -= 7 - (this.dayRuleSup - thisWeek);
231 | }
232 | }
233 | // 判断时间值是否小于10置换成“05”这种格式
234 | DD = DD < 10 ? `0${DD}` : DD;
235 | // 循环“时”数组
236 | goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
237 | const hh = hDate[hi] < 10 ? `0${hDate[hi]}` : hDate[hi];
238 | // 如果到达最大值时
239 | if (nMin > mDate[mDate.length - 1]) {
240 | resetMin();
241 | if (hi === hDate.length - 1) {
242 | resetHour();
243 | if (Di === DDate.length - 1) {
244 | resetDay();
245 | if (Mi === MDate.length - 1) {
246 | resetMouth();
247 | continue goYear;
248 | }
249 | continue goMouth;
250 | }
251 | continue goDay;
252 | }
253 | continue;
254 | }
255 | // 循环"分"数组
256 | goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
257 | const mm = mDate[mi] < 10 ? `0${mDate[mi]}` : mDate[mi];
258 | // 如果到达最大值时
259 | if (nSecond > sDate[sDate.length - 1]) {
260 | resetSecond();
261 | if (mi === mDate.length - 1) {
262 | resetMin();
263 | if (hi === hDate.length - 1) {
264 | resetHour();
265 | if (Di === DDate.length - 1) {
266 | resetDay();
267 | if (Mi === MDate.length - 1) {
268 | resetMouth();
269 | continue goYear;
270 | }
271 | continue goMouth;
272 | }
273 | continue goDay;
274 | }
275 | continue goHour;
276 | }
277 | continue;
278 | }
279 | // 循环"秒"数组
280 | for (let si = sIdx; si <= sDate.length - 1; si++) {
281 | const ss = sDate[si] < 10 ? `0${sDate[si]}` : sDate[si];
282 | // 添加当前时间(时间合法性在日期循环时已经判断)
283 | resultArr.push(`${YY}-${MM}-${DD} ${hh}:${mm}:${ss}`);
284 | nums++;
285 | // 如果条数满了就退出循环
286 | if (nums === 5) break goYear;
287 | // 如果到达最大值时
288 | if (si === sDate.length - 1) {
289 | resetSecond();
290 | if (mi === mDate.length - 1) {
291 | resetMin();
292 | if (hi === hDate.length - 1) {
293 | resetHour();
294 | if (Di === DDate.length - 1) {
295 | resetDay();
296 | if (Mi === MDate.length - 1) {
297 | resetMouth();
298 | continue goYear;
299 | }
300 | continue goMouth;
301 | }
302 | continue goDay;
303 | }
304 | continue goHour;
305 | }
306 | continue goMin;
307 | }
308 | } // goSecond
309 | } // goMin
310 | } // goHour
311 | } // goDay
312 | } // goMouth
313 | }
314 | // 判断100年内的结果条数
315 | if (resultArr.length === 0) {
316 | this.resultList = ["没有达到条件的结果!"];
317 | } else {
318 | this.resultList = resultArr;
319 | if (resultArr.length !== 5) {
320 | this.resultList.push(`最近100年内只有上面${resultArr.length}条结果!`);
321 | }
322 | }
323 | // 计算完成-显示结果
324 | this.isShow = true;
325 | return this.resultList;
326 | }
327 |
328 | // 用于计算某位数字在数组中的索引
329 | getIndex(arr, value) {
330 | if (value <= arr[0] || value > arr[arr.length - 1]) {
331 | return 0;
332 | }
333 | for (let i = 0; i < arr.length - 1; i++) {
334 | if (value > arr[i] && value <= arr[i + 1]) {
335 | return i + 1;
336 | }
337 | }
338 | }
339 |
340 | // 获取"年"数组
341 | getYearArr(rule, year) {
342 | this.dateArr[5] = this.getOrderArr(year, year + 100);
343 | if (rule !== undefined) {
344 | if (rule.indexOf("-") >= 0) {
345 | this.dateArr[5] = this.getCycleArr(rule, year + 100, false);
346 | } else if (rule.indexOf("/") >= 0) {
347 | this.dateArr[5] = this.getAverageArr(rule, year + 100);
348 | } else if (rule !== "*") {
349 | this.dateArr[5] = this.getAssignArr(rule);
350 | }
351 | }
352 | }
353 |
354 | // 获取"月"数组
355 | getMouthArr(rule) {
356 | this.dateArr[4] = this.getOrderArr(1, 12);
357 | if (rule.indexOf("-") >= 0) {
358 | this.dateArr[4] = this.getCycleArr(rule, 12, false);
359 | } else if (rule.indexOf("/") >= 0) {
360 | this.dateArr[4] = this.getAverageArr(rule, 12);
361 | } else if (rule !== "*") {
362 | this.dateArr[4] = this.getAssignArr(rule);
363 | }
364 | }
365 |
366 | // 获取"日"数组-主要为日期规则
367 | getWeekArr(rule) {
368 | // 只有当日期规则的两个值均为“”时则表达日期是有选项的
369 | if (this.dayRule === "" && this.dayRuleSup === "") {
370 | if (rule.indexOf("-") >= 0) {
371 | this.dayRule = "weekDay";
372 | this.dayRuleSup = this.getCycleArr(rule, 7, false);
373 | } else if (rule.indexOf("#") >= 0) {
374 | this.dayRule = "assWeek";
375 | const matchRule = rule.match(/[0-9]{1}/g);
376 | this.dayRuleSup = [Number(matchRule[0]), Number(matchRule[1])];
377 | this.dateArr[3] = [1];
378 | if (this.dayRuleSup[1] === 7) {
379 | this.dayRuleSup[1] = 0;
380 | }
381 | } else if (rule.indexOf("L") >= 0) {
382 | this.dayRule = "lastWeek";
383 | this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
384 | this.dateArr[3] = [31];
385 | if (this.dayRuleSup === 7) {
386 | this.dayRuleSup = 0;
387 | }
388 | } else if (rule !== "*" && rule !== "?") {
389 | this.dayRule = "weekDay";
390 | this.dayRuleSup = this.getAssignArr(rule);
391 | }
392 | // 如果weekDay时将7调整为0【week值0即是星期日】
393 | if (this.dayRule === "weekDay") {
394 | for (let i = 0; i < this.dayRuleSup.length; i++) {
395 | if (this.dayRuleSup[i] === 7) {
396 | this.dayRuleSup[i] = 0;
397 | }
398 | }
399 | }
400 | }
401 | }
402 |
403 | // 获取"日"数组-少量为日期规则
404 | getDayArr(rule) {
405 | this.dateArr[3] = this.getOrderArr(1, 31);
406 | this.dayRule = "";
407 | this.dayRuleSup = "";
408 | if (rule.indexOf("-") >= 0) {
409 | this.dateArr[3] = this.getCycleArr(rule, 31, false);
410 | this.dayRuleSup = "null";
411 | } else if (rule.indexOf("/") >= 0) {
412 | this.dateArr[3] = this.getAverageArr(rule, 31);
413 | this.dayRuleSup = "null";
414 | } else if (rule.indexOf("W") >= 0) {
415 | this.dayRule = "workDay";
416 | this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
417 | this.dateArr[3] = [this.dayRuleSup];
418 | } else if (rule.indexOf("L") >= 0) {
419 | this.dayRule = "lastDay";
420 | this.dayRuleSup = "null";
421 | this.dateArr[3] = [31];
422 | } else if (rule !== "*" && rule !== "?") {
423 | this.dateArr[3] = this.getAssignArr(rule);
424 | this.dayRuleSup = "null";
425 | } else if (rule === "*") {
426 | this.dayRuleSup = "null";
427 | }
428 | }
429 |
430 | // 获取"时"数组
431 | getHourArr(rule) {
432 | this.dateArr[2] = this.getOrderArr(0, 23);
433 | if (rule.indexOf("-") >= 0) {
434 | this.dateArr[2] = this.getCycleArr(rule, 24, true);
435 | } else if (rule.indexOf("/") >= 0) {
436 | this.dateArr[2] = this.getAverageArr(rule, 23);
437 | } else if (rule !== "*") {
438 | this.dateArr[2] = this.getAssignArr(rule);
439 | }
440 | }
441 |
442 | // 获取"分"数组
443 | getMinArr(rule) {
444 | this.dateArr[1] = this.getOrderArr(0, 59);
445 | if (rule.indexOf("-") >= 0) {
446 | this.dateArr[1] = this.getCycleArr(rule, 60, true);
447 | } else if (rule.indexOf("/") >= 0) {
448 | this.dateArr[1] = this.getAverageArr(rule, 59);
449 | } else if (rule !== "*") {
450 | this.dateArr[1] = this.getAssignArr(rule);
451 | }
452 | }
453 |
454 | // 获取"秒"数组
455 | getSecondArr(rule) {
456 | this.dateArr[0] = this.getOrderArr(0, 59);
457 | if (rule.indexOf("-") >= 0) {
458 | this.dateArr[0] = this.getCycleArr(rule, 60, true);
459 | } else if (rule.indexOf("/") >= 0) {
460 | this.dateArr[0] = this.getAverageArr(rule, 59);
461 | } else if (rule !== "*") {
462 | this.dateArr[0] = this.getAssignArr(rule);
463 | }
464 | }
465 |
466 | // 根据传进来的min-max返回一个顺序的数组
467 | getOrderArr(min, max) {
468 | const arr = [];
469 | for (let i = min; i <= max; i++) {
470 | arr.push(i);
471 | }
472 | return arr;
473 | }
474 |
475 | // 根据规则中指定的零散值返回一个数组
476 | getAssignArr(rule) {
477 | const arr = [];
478 | const assiginArr = rule.split(",");
479 | for (let i = 0; i < assiginArr.length; i++) {
480 | arr[i] = Number(assiginArr[i]);
481 | }
482 | arr.sort(this.compare);
483 | return arr;
484 | }
485 |
486 | // 根据一定算术规则计算返回一个数组
487 | getAverageArr(rule, limit) {
488 | const arr = [];
489 | const agArr = rule.split("/");
490 |
491 | let min = Number(agArr[0]);
492 | const step = Number(agArr[1]);
493 | while (min <= limit) {
494 | arr.push(min);
495 | min += step;
496 | }
497 | return arr;
498 | }
499 |
500 | // 根据规则返回一个具有周期性的数组
501 | getCycleArr(rule, limit, status) {
502 | // status--表示是否从0开始(则从1开始)
503 | const arr = [];
504 | const cycleArr = rule.split("-");
505 | const min = Number(cycleArr[0]);
506 | let max = Number(cycleArr[1]);
507 | if (min > max) {
508 | max += limit;
509 | }
510 | for (let i = min; i <= max; i++) {
511 | let add = 0;
512 | if (status === false && i % limit === 0) {
513 | add = limit;
514 | }
515 | arr.push(Math.round((i % limit) + add));
516 | }
517 | arr.sort(this.compare);
518 | return arr;
519 | }
520 |
521 | // 比较数字大小(用于Array.sort)
522 | compare(value1, value2) {
523 | if (value2 - value1 > 0) {
524 | return -1;
525 | }
526 | return 1;
527 | }
528 |
529 | // 格式化日期格式如:2017-9-19 18:04:33
530 | formatDate(value, type) {
531 | // 计算日期相关值
532 | const time = typeof value === "number" ? new Date(value) : value;
533 | const Y = time.getFullYear();
534 | const M = time.getMonth() + 1;
535 | const D = time.getDate();
536 | const h = time.getHours();
537 | const m = time.getMinutes();
538 | const s = time.getSeconds();
539 | const week = time.getDay();
540 | // 如果传递了type的话
541 | if (type === undefined) {
542 | return `${Y}-${M < 10 ? `0${M}` : M}-${D < 10 ? `0${D}` : D} ${h < 10 ? `0${h}` : h}:${m < 10 ? `0${m}` : m}:${s < 10 ? `0${s}` : s}`;
543 | }
544 | if (type === "week") {
545 | return week;
546 | }
547 | }
548 |
549 | // 检查日期是否存在
550 | checkDate(value) {
551 | const time = new Date(value);
552 | const format = this.formatDate(time);
553 | return value === format;
554 | }
555 | }
556 |
557 | // export default {
558 | // CronParse
559 | // }
560 | export default CronParse;
561 | // module.exports = CronParse
562 |
--------------------------------------------------------------------------------
/webpack.config.build.js:
--------------------------------------------------------------------------------
1 | const webpackConfig = {
2 | entry: './src/index.jsx',
3 | output: {
4 | filename: 'index.js',
5 | library: 'antd-cron',
6 | libraryTarget: 'umd',
7 | libraryExport: 'default', // 默认导出
8 | },
9 | externals: {
10 | react: 'react', //打包时候排除react
11 | antd: 'antd',
12 | reactDom: 'react-dom',
13 | moment: 'moment',
14 | },
15 | mode: 'production',
16 | module: {
17 | rules: [
18 | {
19 | test: /\.(js|jsx)$/,
20 | exclude: /(node_modules|bower_components)/,
21 | use: ['babel-loader'],
22 | },
23 | {
24 | test: /\.less$/,
25 | use: [
26 | { loader: 'style-loader' },
27 | { loader: 'css-loader' },
28 | {
29 | loader: 'less-loader',
30 | options: {
31 | javascriptEnabled: true,
32 | },
33 | },
34 | ],
35 | },
36 | ],
37 | },
38 | resolve: {
39 | extensions: ['.js', '.jsx'],
40 | },
41 | }
42 | module.exports = webpackConfig
43 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const webpackConfig = {
3 | entry: './index.js',
4 | output: {
5 | filename: 'index.js',
6 | publicPath: '/',
7 | },
8 | mode: 'development',
9 | devtool: 'cheap-module-eval-source-map',
10 | devServer: {
11 | port: 3000,
12 | open: true,
13 | host: '0.0.0.0',
14 | openPage: '/dist/index.html',
15 | hot: true,
16 | publicPath: '/',
17 | },
18 |
19 | module: {
20 | rules: [
21 | {
22 | test: /\.(js|jsx)$/,
23 | exclude: /(node_modules|bower_components)/,
24 | use: [
25 | {
26 | loader: 'babel-loader',
27 | options: {
28 | plugins: [
29 | [
30 | 'import',
31 | {
32 | libraryName: 'antd',
33 | libraryDirectory: 'lib',
34 | style: true,
35 | },
36 | ],
37 | ],
38 | },
39 | },
40 | ],
41 | },
42 | {
43 | test: /\.less$/,
44 | use: [
45 | { loader: 'style-loader' },
46 | { loader: 'css-loader' },
47 | {
48 | loader: 'less-loader',
49 | options: {
50 | javascriptEnabled: true,
51 | },
52 | },
53 | ],
54 | },
55 | ],
56 | },
57 | resolve: {
58 | extensions: ['.js', '.jsx'],
59 | },
60 | plugins: [new webpack.HotModuleReplacementPlugin()],
61 | }
62 |
63 | module.exports = webpackConfig
64 |
--------------------------------------------------------------------------------