Showing
20 changed files
with
1312 additions
and
1 deletions
... | ... | @@ -11,5 +11,12 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v |
11 | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
12 | 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
13 | 13 | |
14 | - | |
14 | +//注册自定义组件 | |
15 | +export { | |
16 | + JEasyCron, | |
17 | + JEasyCronInner, | |
18 | + JEasyCronModal, | |
19 | +} from '/@/components/Form/src/jeecg/components/JEasyCron'; | |
20 | +// Jeecg自定义校验 | |
21 | +export { JCronValidator } from '/@/components/Form/src/jeecg/components/JEasyCron'; | |
15 | 22 | export { BasicForm }; | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import { CountdownInput } from '/@/components/CountDown'; |
30 | 30 | import ApiRadioGroup from './components/ApiRadioGroup.vue'; |
31 | 31 | //自定义组件 |
32 | 32 | import JAddInput from './jeecg/components/JAddInput.vue'; |
33 | +import { JEasyCron } from './jeecg/components/JEasyCron'; | |
33 | 34 | |
34 | 35 | const componentMap = new Map<ComponentType, Component>(); |
35 | 36 | |
... | ... | @@ -67,6 +68,7 @@ componentMap.set('InputCountDown', CountdownInput); |
67 | 68 | componentMap.set('Upload', BasicUpload); |
68 | 69 | //注册自定义组件 |
69 | 70 | componentMap.set('JAddInput', JAddInput); |
71 | +componentMap.set('JEasyCron', JEasyCron); | |
70 | 72 | |
71 | 73 | export function add(compName: ComponentType, component: Component) { |
72 | 74 | componentMap.set(compName, component); | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}`"> | |
3 | + <div class="content"> | |
4 | + <Tabs :size="`small`" v-model:activeKey="activeKey"> | |
5 | + <TabPane tab="秒" key="second" v-if="!hideSecond"> | |
6 | + <SecondUI v-model:value="second" :disabled="disabled" /> | |
7 | + </TabPane> | |
8 | + <TabPane tab="分" key="minute"> | |
9 | + <MinuteUI v-model:value="minute" :disabled="disabled" /> | |
10 | + </TabPane> | |
11 | + <TabPane tab="时" key="hour"> | |
12 | + <HourUI v-model:value="hour" :disabled="disabled" /> | |
13 | + </TabPane> | |
14 | + <TabPane tab="日" key="day"> | |
15 | + <DayUI v-model:value="day" :week="week" :disabled="disabled" /> | |
16 | + </TabPane> | |
17 | + <TabPane tab="月" key="month"> | |
18 | + <MonthUI v-model:value="month" :disabled="disabled" /> | |
19 | + </TabPane> | |
20 | + <TabPane tab="周" key="week"> | |
21 | + <WeekUI v-model:value="week" :day="day" :disabled="disabled" /> | |
22 | + </TabPane> | |
23 | + <TabPane tab="年" key="year" v-if="!hideYear && !hideSecond"> | |
24 | + <YearUI v-model:value="year" :disabled="disabled" /> | |
25 | + </TabPane> | |
26 | + </Tabs> | |
27 | + <Divider /> | |
28 | + <!-- 执行时间预览 --> | |
29 | + <Row :gutter="8"> | |
30 | + <Col :span="18" style="margin-top: 22px"> | |
31 | + <Row :gutter="8"> | |
32 | + <Col :span="8" style="margin-bottom: 12px"> | |
33 | + <Input v-model:value="inputValues.second" @blur="onInputBlur"> | |
34 | + <template #addonBefore> | |
35 | + <span class="allow-click" @click="activeKey = 'second'">秒</span> | |
36 | + </template> | |
37 | + </Input> | |
38 | + </Col> | |
39 | + <Col :span="8" style="margin-bottom: 12px"> | |
40 | + <Input v-model:value="inputValues.minute" @blur="onInputBlur"> | |
41 | + <template #addonBefore> | |
42 | + <span class="allow-click" @click="activeKey = 'minute'">分</span> | |
43 | + </template> | |
44 | + </Input> | |
45 | + </Col> | |
46 | + <Col :span="8" style="margin-bottom: 12px"> | |
47 | + <Input v-model:value="inputValues.hour" @blur="onInputBlur"> | |
48 | + <template #addonBefore> | |
49 | + <span class="allow-click" @click="activeKey = 'hour'">时</span> | |
50 | + </template> | |
51 | + </Input> | |
52 | + </Col> | |
53 | + <Col :span="8" style="margin-bottom: 12px"> | |
54 | + <Input v-model:value="inputValues.day" @blur="onInputBlur"> | |
55 | + <template #addonBefore> | |
56 | + <span class="allow-click" @click="activeKey = 'day'">日</span> | |
57 | + </template> | |
58 | + </Input> | |
59 | + </Col> | |
60 | + <Col :span="8" style="margin-bottom: 12px"> | |
61 | + <Input v-model:value="inputValues.month" @blur="onInputBlur"> | |
62 | + <template #addonBefore> | |
63 | + <span class="allow-click" @click="activeKey = 'month'">月</span> | |
64 | + </template> | |
65 | + </Input> | |
66 | + </Col> | |
67 | + <Col :span="8" style="margin-bottom: 12px"> | |
68 | + <Input v-model:value="inputValues.week" @blur="onInputBlur"> | |
69 | + <template #addonBefore> | |
70 | + <span class="allow-click" @click="activeKey = 'week'">周</span> | |
71 | + </template> | |
72 | + </Input> | |
73 | + </Col> | |
74 | + <Col :span="8"> | |
75 | + <Input v-model:value="inputValues.year" @blur="onInputBlur"> | |
76 | + <template #addonBefore> | |
77 | + <span class="allow-click" @click="activeKey = 'year'">年</span> | |
78 | + </template> | |
79 | + </Input> | |
80 | + </Col> | |
81 | + <Col :span="16"> | |
82 | + <Input v-model:value="inputValues.cron" @blur="onInputCronBlur"> | |
83 | + <template #addonBefore> | |
84 | + <Tooltip title="Cron表达式">式</Tooltip> | |
85 | + </template> | |
86 | + </Input> | |
87 | + </Col> | |
88 | + </Row> | |
89 | + </Col> | |
90 | + <Col :span="6"> | |
91 | + <div>近十次执行时间(不含年)</div> | |
92 | + <a-textarea type="textarea" :value="preTimeList" :rows="5" /> | |
93 | + </Col> | |
94 | + </Row> | |
95 | + </div> | |
96 | + </div> | |
97 | +</template> | |
98 | + | |
99 | +<script lang="ts" setup> | |
100 | + import { computed, reactive, ref, watch, provide } from 'vue'; | |
101 | + import { useDesign } from '/@/hooks/web/useDesign'; | |
102 | + import CronParser from 'cron-parser'; | |
103 | + import SecondUI from './tabs/SecondUI.vue'; | |
104 | + import MinuteUI from './tabs/MinuteUI.vue'; | |
105 | + import HourUI from './tabs/HourUI.vue'; | |
106 | + import DayUI from './tabs/DayUI.vue'; | |
107 | + import MonthUI from './tabs/MonthUI.vue'; | |
108 | + import WeekUI from './tabs/WeekUI.vue'; | |
109 | + import YearUI from './tabs/YearUI.vue'; | |
110 | + import { cronEmits, cronProps } from './easy.cron.data'; | |
111 | + import { dateFormat, simpleDebounce } from '/@/utils/common/compUtils'; | |
112 | + import { Row, Col, Input, Tabs, Divider, Tooltip } from 'ant-design-vue'; | |
113 | + const { TabPane } = Tabs; | |
114 | + const { prefixCls } = useDesign('easy-cron-inner'); | |
115 | + provide('prefixCls', prefixCls); | |
116 | + const emit = defineEmits([...cronEmits]); | |
117 | + const props = defineProps({ ...cronProps }); | |
118 | + const activeKey = ref(props.hideSecond ? 'minute' : 'second'); | |
119 | + const second = ref('*'); | |
120 | + const minute = ref('*'); | |
121 | + const hour = ref('*'); | |
122 | + const day = ref('*'); | |
123 | + const month = ref('*'); | |
124 | + const week = ref('?'); | |
125 | + const year = ref('*'); | |
126 | + const inputValues = reactive({ | |
127 | + second: '', | |
128 | + minute: '', | |
129 | + hour: '', | |
130 | + day: '', | |
131 | + month: '', | |
132 | + week: '', | |
133 | + year: '', | |
134 | + cron: '', | |
135 | + }); | |
136 | + const preTimeList = ref('执行预览,会忽略年份参数。'); | |
137 | + | |
138 | + // cron表达式 | |
139 | + const cronValueInner = computed(() => { | |
140 | + let result: string[] = []; | |
141 | + if (!props.hideSecond) { | |
142 | + result.push(second.value ? second.value : '*'); | |
143 | + } | |
144 | + result.push(minute.value ? minute.value : '*'); | |
145 | + result.push(hour.value ? hour.value : '*'); | |
146 | + result.push(day.value ? day.value : '*'); | |
147 | + result.push(month.value ? month.value : '*'); | |
148 | + result.push(week.value ? week.value : '?'); | |
149 | + if (!props.hideYear && !props.hideSecond) result.push(year.value ? year.value : '*'); | |
150 | + return result.join(' '); | |
151 | + }); | |
152 | + // 不含年 | |
153 | + const cronValueNoYear = computed(() => { | |
154 | + const v = cronValueInner.value; | |
155 | + if (props.hideYear || props.hideSecond) return v; | |
156 | + const vs = v.split(' '); | |
157 | + if (vs.length >= 6) { | |
158 | + // 转成 Quartz 的规则 | |
159 | + vs[5] = convertWeekToQuartz(vs[5]); | |
160 | + } | |
161 | + return vs.slice(0, vs.length - 1).join(' '); | |
162 | + }); | |
163 | + const calTriggerList = simpleDebounce(calTriggerListInner, 500); | |
164 | + | |
165 | + watch( | |
166 | + () => props.value, | |
167 | + (newVal) => { | |
168 | + if (newVal === cronValueInner.value) { | |
169 | + return; | |
170 | + } | |
171 | + formatValue(); | |
172 | + } | |
173 | + ); | |
174 | + | |
175 | + watch(cronValueInner, (newValue) => { | |
176 | + calTriggerList(); | |
177 | + emitValue(newValue); | |
178 | + assignInput(); | |
179 | + }); | |
180 | + | |
181 | + // watch(minute, () => { | |
182 | + // if (second.value === '*') { | |
183 | + // second.value = '0' | |
184 | + // } | |
185 | + // }) | |
186 | + // watch(hour, () => { | |
187 | + // if (minute.value === '*') { | |
188 | + // minute.value = '0' | |
189 | + // } | |
190 | + // }) | |
191 | + // watch(day, () => { | |
192 | + // if (day.value !== '?' && hour.value === '*') { | |
193 | + // hour.value = '0' | |
194 | + // } | |
195 | + // }) | |
196 | + // watch(week, () => { | |
197 | + // if (week.value !== '?' && hour.value === '*') { | |
198 | + // hour.value = '0' | |
199 | + // } | |
200 | + // }) | |
201 | + // watch(month, () => { | |
202 | + // if (day.value === '?' && week.value === '*') { | |
203 | + // week.value = '1' | |
204 | + // } else if (week.value === '?' && day.value === '*') { | |
205 | + // day.value = '1' | |
206 | + // } | |
207 | + // }) | |
208 | + // watch(year, () => { | |
209 | + // if (month.value === '*') { | |
210 | + // month.value = '1' | |
211 | + // } | |
212 | + // }) | |
213 | + | |
214 | + assignInput(); | |
215 | + formatValue(); | |
216 | + calTriggerListInner(); | |
217 | + | |
218 | + function assignInput() { | |
219 | + inputValues.second = second.value; | |
220 | + inputValues.minute = minute.value; | |
221 | + inputValues.hour = hour.value; | |
222 | + inputValues.day = day.value; | |
223 | + inputValues.month = month.value; | |
224 | + inputValues.week = week.value; | |
225 | + inputValues.year = year.value; | |
226 | + inputValues.cron = cronValueInner.value; | |
227 | + } | |
228 | + | |
229 | + function formatValue() { | |
230 | + if (!props.value) return; | |
231 | + const values = props.value.split(' ').filter((item) => !!item); | |
232 | + if (!values || values.length <= 0) return; | |
233 | + let i = 0; | |
234 | + if (!props.hideSecond) second.value = values[i++]; | |
235 | + if (values.length > i) minute.value = values[i++]; | |
236 | + if (values.length > i) hour.value = values[i++]; | |
237 | + if (values.length > i) day.value = values[i++]; | |
238 | + if (values.length > i) month.value = values[i++]; | |
239 | + if (values.length > i) week.value = values[i++]; | |
240 | + if (values.length > i) year.value = values[i]; | |
241 | + assignInput(); | |
242 | + } | |
243 | + | |
244 | + // Quartz 的规则: | |
245 | + // 1 = 周日,2 = 周一,3 = 周二,4 = 周三,5 = 周四,6 = 周五,7 = 周六 | |
246 | + function convertWeekToQuartz(week: string) { | |
247 | + let convert = (v: string) => { | |
248 | + if (v === '0') { | |
249 | + return '1'; | |
250 | + } | |
251 | + if (v === '1') { | |
252 | + return '0'; | |
253 | + } | |
254 | + return (Number.parseInt(v) - 1).toString(); | |
255 | + }; | |
256 | + // 匹配示例 1-7 or 1/7 | |
257 | + let patten1 = /^([0-7])([-/])([0-7])$/; | |
258 | + // 匹配示例 1,4,7 | |
259 | + let patten2 = /^([0-7])(,[0-7])+$/; | |
260 | + if (/^[0-7]$/.test(week)) { | |
261 | + return convert(week); | |
262 | + } else if (patten1.test(week)) { | |
263 | + return week.replace(patten1, ($0, before, separator, after) => { | |
264 | + if (separator === '/') { | |
265 | + return convert(before) + separator + after; | |
266 | + } else { | |
267 | + return convert(before) + separator + convert(after); | |
268 | + } | |
269 | + }); | |
270 | + } else if (patten2.test(week)) { | |
271 | + return week | |
272 | + .split(',') | |
273 | + .map((v) => convert(v)) | |
274 | + .join(','); | |
275 | + } | |
276 | + return week; | |
277 | + } | |
278 | + | |
279 | + function calTriggerListInner() { | |
280 | + // 设置了回调函数 | |
281 | + if (props.remote) { | |
282 | + props.remote(cronValueInner.value, +new Date(), (v) => { | |
283 | + preTimeList.value = v; | |
284 | + }); | |
285 | + return; | |
286 | + } | |
287 | + const format = 'yyyy-MM-dd hh:mm:ss'; | |
288 | + const options = { | |
289 | + currentDate: dateFormat(new Date(), format), | |
290 | + }; | |
291 | + const iter = CronParser.parseExpression(cronValueNoYear.value, options); | |
292 | + const result: string[] = []; | |
293 | + for (let i = 1; i <= 10; i++) { | |
294 | + result.push(dateFormat(new Date(iter.next() as any), format)); | |
295 | + } | |
296 | + preTimeList.value = result.length > 0 ? result.join('\n') : '无执行时间'; | |
297 | + } | |
298 | + | |
299 | + function onInputBlur() { | |
300 | + second.value = inputValues.second; | |
301 | + minute.value = inputValues.minute; | |
302 | + hour.value = inputValues.hour; | |
303 | + day.value = inputValues.day; | |
304 | + month.value = inputValues.month; | |
305 | + week.value = inputValues.week; | |
306 | + year.value = inputValues.year; | |
307 | + } | |
308 | + | |
309 | + function onInputCronBlur(event) { | |
310 | + emitValue(event.target.value); | |
311 | + } | |
312 | + | |
313 | + function emitValue(value) { | |
314 | + emit('change', value); | |
315 | + emit('update:value', value); | |
316 | + } | |
317 | +</script> | |
318 | +<style lang="less"> | |
319 | + @import './easy.cron.inner.less'; | |
320 | +</style> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}`"> | |
3 | + <a-input :placeholder="placeholder" v-model:value="editCronValue" :disabled="disabled"> | |
4 | + <template #addonAfter> | |
5 | + <a class="open-btn" :disabled="disabled ? 'disabled' : null" @click="showConfigModal"> | |
6 | + <Icon icon="ant-design:setting-outlined" /> | |
7 | + <span>选择</span> | |
8 | + </a> | |
9 | + </template> | |
10 | + </a-input> | |
11 | + <EasyCronModal | |
12 | + @register="registerModal" | |
13 | + v-model:value="editCronValue" | |
14 | + :exeStartTime="exeStartTime" | |
15 | + :hideYear="hideYear" | |
16 | + :remote="remote" | |
17 | + :hideSecond="hideSecond" | |
18 | + /> | |
19 | + </div> | |
20 | +</template> | |
21 | + | |
22 | +<script lang="ts" setup> | |
23 | + import { ref, watch } from 'vue'; | |
24 | + import { useDesign } from '/@/hooks/web/useDesign'; | |
25 | + import { useModal } from '/@/components/Modal'; | |
26 | + import { propTypes } from '/@/utils/propTypes'; | |
27 | + import Icon from '/@/components/Icon/src/Icon.vue'; | |
28 | + import EasyCronModal from './EasyCronModal.vue'; | |
29 | + import { cronEmits, cronProps } from './easy.cron.data'; | |
30 | + | |
31 | + const { prefixCls } = useDesign('easy-cron-input'); | |
32 | + const emit = defineEmits([...cronEmits]); | |
33 | + const props = defineProps({ | |
34 | + ...cronProps, | |
35 | + placeholder: propTypes.string.def('请输入cron表达式'), | |
36 | + exeStartTime: propTypes | |
37 | + .oneOfType([propTypes.number, propTypes.string, propTypes.object]) | |
38 | + .def(0), | |
39 | + }); | |
40 | + const [registerModal, { openModal }] = useModal(); | |
41 | + const editCronValue = ref(props.value); | |
42 | + | |
43 | + watch( | |
44 | + () => props.value, | |
45 | + (newVal) => { | |
46 | + if (newVal !== editCronValue.value) { | |
47 | + editCronValue.value = newVal; | |
48 | + } | |
49 | + } | |
50 | + ); | |
51 | + watch(editCronValue, (newVal) => { | |
52 | + emit('change', newVal); | |
53 | + emit('update:value', newVal); | |
54 | + }); | |
55 | + | |
56 | + function showConfigModal() { | |
57 | + if (!props.disabled) { | |
58 | + openModal(); | |
59 | + } | |
60 | + } | |
61 | +</script> | |
62 | + | |
63 | +<style lang="less"> | |
64 | + @import './easy.cron.input.less'; | |
65 | +</style> | ... | ... |
1 | +<template> | |
2 | + <BasicModal @register="registerModal" title="Cron表达式" width="800px" @ok="onOk"> | |
3 | + <EasyCron v-bind="attrs" /> | |
4 | + </BasicModal> | |
5 | +</template> | |
6 | + | |
7 | +<script lang="ts"> | |
8 | + import { defineComponent } from 'vue'; | |
9 | + import { useAttrs } from '/@/hooks/core/useAttrs'; | |
10 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
11 | + import EasyCron from './EasyCronInner.vue' | |
12 | + | |
13 | + export default defineComponent({ | |
14 | + name: 'EasyCronModal', | |
15 | + inheritAttrs: false, | |
16 | + components: { BasicModal, EasyCron }, | |
17 | + setup() { | |
18 | + const attrs = useAttrs(); | |
19 | + const [registerModal, { closeModal }] = useModalInner(); | |
20 | + | |
21 | + function onOk() { | |
22 | + closeModal(); | |
23 | + } | |
24 | + | |
25 | + return { attrs, registerModal, onOk }; | |
26 | + }, | |
27 | + }); | |
28 | +</script> | ... | ... |
1 | +MIT License | |
2 | + | |
3 | +Copyright (c) 2019 知行合一 | |
4 | + | |
5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | +of this software and associated documentation files (the "Software"), to deal | |
7 | +in the Software without restriction, including without limitation the rights | |
8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | +copies of the Software, and to permit persons to whom the Software is | |
10 | +furnished to do so, subject to the following conditions: | |
11 | + | |
12 | +The above copyright notice and this permission notice shall be included in all | |
13 | +copies or substantial portions of the Software. | |
14 | + | |
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
21 | +SOFTWARE. | ... | ... |
1 | +import { propTypes } from '/@/utils/propTypes'; | |
2 | + | |
3 | +export const cronEmits = ['change', 'update:value']; | |
4 | +export const cronProps = { | |
5 | + value: propTypes.string.def(''), | |
6 | + disabled: propTypes.bool.def(false), | |
7 | + hideSecond: propTypes.bool.def(false), | |
8 | + hideYear: propTypes.bool.def(false), | |
9 | + remote: propTypes.func, | |
10 | +}; | ... | ... |
1 | +//noinspection LessUnresolvedVariable | |
2 | +@prefix-cls: ~'@{namespace}-easy-cron-inner'; | |
3 | + | |
4 | +.@{prefix-cls} { | |
5 | + .content { | |
6 | + .ant-checkbox-wrapper + .ant-checkbox-wrapper { | |
7 | + margin-left: 0; | |
8 | + } | |
9 | + } | |
10 | + | |
11 | + &-config-list { | |
12 | + text-align: left; | |
13 | + margin: 0 10px 10px 10px; | |
14 | + | |
15 | + .item { | |
16 | + margin-top: 5px; | |
17 | + } | |
18 | + | |
19 | + .choice { | |
20 | + padding: 5px 8px; | |
21 | + } | |
22 | + | |
23 | + .w60 { | |
24 | + width: 60px; | |
25 | + min-width: 60px; | |
26 | + } | |
27 | + | |
28 | + .w80 { | |
29 | + width: 80px; | |
30 | + min-width: 80px; | |
31 | + } | |
32 | + | |
33 | + .list { | |
34 | + margin: 0 20px; | |
35 | + } | |
36 | + | |
37 | + .list-check-item { | |
38 | + padding: 1px 3px; | |
39 | + width: 4em; | |
40 | + } | |
41 | + | |
42 | + .list-cn .list-check-item { | |
43 | + width: 5em; | |
44 | + } | |
45 | + | |
46 | + .tip-info { | |
47 | + color: #999; | |
48 | + } | |
49 | + } | |
50 | + | |
51 | + .allow-click { | |
52 | + cursor: pointer; | |
53 | + } | |
54 | +} | ... | ... |
1 | +// 原开源项目地址:https://gitee.com/toktok/easy-cron | |
2 | + | |
3 | +export { default as JEasyCron } from './EasyCronInput.vue'; | |
4 | +export { default as JEasyCronInner } from './EasyCronInner.vue'; | |
5 | +export { default as JEasyCronModal } from './EasyCronModal.vue'; | |
6 | +export { default as JCronValidator } from './validator'; | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.unset" v-bind="beforeRadioAttrs">不设置</Radio> | |
6 | + <span class="tip-info">日和周只能设置其中之一</span> | |
7 | + </div> | |
8 | + <div class="item"> | |
9 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每日</Radio> | |
10 | + </div> | |
11 | + <div class="item"> | |
12 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
13 | + <span> 从 </span> | |
14 | + <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
15 | + <span> 日 至 </span> | |
16 | + <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
17 | + <span> 日 </span> | |
18 | + </div> | |
19 | + <div class="item"> | |
20 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
21 | + <span> 从 </span> | |
22 | + <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
23 | + <span> 日开始,间隔 </span> | |
24 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
25 | + <span> 日 </span> | |
26 | + </div> | |
27 | + <div class="item"> | |
28 | + <Radio :value="TypeEnum.work" v-bind="beforeRadioAttrs">工作日</Radio> | |
29 | + <span> 本月 </span> | |
30 | + <InputNumber v-model:value="valueWork" v-bind="typeWorkAttrs" /> | |
31 | + <span> 日,最近的工作日 </span> | |
32 | + </div> | |
33 | + <div class="item"> | |
34 | + <Radio :value="TypeEnum.last" v-bind="beforeRadioAttrs">最后一日</Radio> | |
35 | + </div> | |
36 | + <div class="item"> | |
37 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
38 | + <div class="list"> | |
39 | + <CheckboxGroup v-model:value="valueList"> | |
40 | + <template v-for="i in specifyRange" :key="i"> | |
41 | + <Checkbox :value="i" v-bind="typeSpecifyAttrs">{{ i }}</Checkbox> | |
42 | + </template> | |
43 | + </CheckboxGroup> | |
44 | + </div> | |
45 | + </div> | |
46 | + </RadioGroup> | |
47 | + </div> | |
48 | +</template> | |
49 | + | |
50 | +<script lang="ts"> | |
51 | + import { computed, defineComponent, watch } from 'vue'; | |
52 | + import { InputNumber } from 'ant-design-vue'; | |
53 | + import { TypeEnum, useTabEmits, useTabProps, useTabSetup } from './useTabMixin'; | |
54 | + import { Radio, Checkbox } from 'ant-design-vue'; | |
55 | + | |
56 | + const { CheckboxGroup } = Checkbox; | |
57 | + const { RadioGroup } = Radio; | |
58 | + | |
59 | + export default defineComponent({ | |
60 | + name: 'DayUI', | |
61 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
62 | + props: useTabProps({ | |
63 | + defaultValue: '*', | |
64 | + props: { | |
65 | + week: { type: String, default: '?' }, | |
66 | + }, | |
67 | + }), | |
68 | + emits: useTabEmits(), | |
69 | + setup(props, context) { | |
70 | + const disabledChoice = computed(() => { | |
71 | + return (props.week && props.week !== '?') || props.disabled; | |
72 | + }); | |
73 | + const setup = useTabSetup(props, context, { | |
74 | + defaultValue: '*', | |
75 | + valueWork: 1, | |
76 | + minValue: 1, | |
77 | + maxValue: 31, | |
78 | + valueRange: { start: 1, end: 31 }, | |
79 | + valueLoop: { start: 1, interval: 1 }, | |
80 | + disabled: disabledChoice, | |
81 | + }); | |
82 | + const typeWorkAttrs = computed(() => ({ | |
83 | + disabled: setup.type.value !== TypeEnum.work || props.disabled || disabledChoice.value, | |
84 | + ...setup.inputNumberAttrs.value, | |
85 | + })); | |
86 | + | |
87 | + watch( | |
88 | + () => props.week, | |
89 | + () => { | |
90 | + setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value); | |
91 | + } | |
92 | + ); | |
93 | + | |
94 | + return { ...setup, typeWorkAttrs }; | |
95 | + }, | |
96 | + }); | |
97 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每时</Radio> | |
6 | + </div> | |
7 | + <div class="item"> | |
8 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
9 | + <span> 从 </span> | |
10 | + <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
11 | + <span> 时 至 </span> | |
12 | + <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
13 | + <span> 时 </span> | |
14 | + </div> | |
15 | + <div class="item"> | |
16 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
17 | + <span> 从 </span> | |
18 | + <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
19 | + <span> 时开始,间隔 </span> | |
20 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
21 | + <span> 时 </span> | |
22 | + </div> | |
23 | + <div class="item"> | |
24 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
25 | + <div class="list"> | |
26 | + <CheckboxGroup v-model:value="valueList"> | |
27 | + <template v-for="i in specifyRange" :key="i"> | |
28 | + <Checkbox :value="i" v-bind="typeSpecifyAttrs">{{ i }}</Checkbox> | |
29 | + </template> | |
30 | + </CheckboxGroup> | |
31 | + </div> | |
32 | + </div> | |
33 | + </RadioGroup> | |
34 | + </div> | |
35 | +</template> | |
36 | + | |
37 | +<script lang="ts"> | |
38 | + import { defineComponent } from 'vue'; | |
39 | + import { InputNumber } from 'ant-design-vue'; | |
40 | + import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin'; | |
41 | + import { Radio, Checkbox } from 'ant-design-vue'; | |
42 | + const { CheckboxGroup } = Checkbox; | |
43 | + const { RadioGroup } = Radio; | |
44 | + | |
45 | + export default defineComponent({ | |
46 | + name: 'HourUI', | |
47 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
48 | + props: useTabProps({ | |
49 | + defaultValue: '*', | |
50 | + }), | |
51 | + emits: useTabEmits(), | |
52 | + setup(props, context) { | |
53 | + return useTabSetup(props, context, { | |
54 | + defaultValue: '*', | |
55 | + minValue: 0, | |
56 | + maxValue: 23, | |
57 | + valueRange: { start: 0, end: 23 }, | |
58 | + valueLoop: { start: 0, interval: 1 }, | |
59 | + }); | |
60 | + }, | |
61 | + }); | |
62 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每分</Radio> | |
6 | + </div> | |
7 | + <div class="item"> | |
8 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
9 | + <span> 从 </span> | |
10 | + <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
11 | + <span> 分 至 </span> | |
12 | + <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
13 | + <span> 分 </span> | |
14 | + </div> | |
15 | + <div class="item"> | |
16 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
17 | + <span> 从 </span> | |
18 | + <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
19 | + <span> 分开始,间隔 </span> | |
20 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
21 | + <span> 分 </span> | |
22 | + </div> | |
23 | + <div class="item"> | |
24 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
25 | + <div class="list"> | |
26 | + <CheckboxGroup v-model:value="valueList"> | |
27 | + <template v-for="i in specifyRange" :key="i"> | |
28 | + <Checkbox :value="i" v-bind="typeSpecifyAttrs">{{ i }}</Checkbox> | |
29 | + </template> | |
30 | + </CheckboxGroup> | |
31 | + </div> | |
32 | + </div> | |
33 | + </RadioGroup> | |
34 | + </div> | |
35 | +</template> | |
36 | + | |
37 | +<script lang="ts"> | |
38 | + import { defineComponent } from 'vue'; | |
39 | + import { InputNumber } from 'ant-design-vue'; | |
40 | + import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin'; | |
41 | + import { Checkbox, Radio } from 'ant-design-vue'; | |
42 | + const { CheckboxGroup } = Checkbox; | |
43 | + const { RadioGroup } = Radio; | |
44 | + | |
45 | + export default defineComponent({ | |
46 | + name: 'MinuteUI', | |
47 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
48 | + props: useTabProps({ | |
49 | + defaultValue: '*', | |
50 | + }), | |
51 | + emits: useTabEmits(), | |
52 | + setup(props, context) { | |
53 | + return useTabSetup(props, context, { | |
54 | + defaultValue: '*', | |
55 | + minValue: 0, | |
56 | + maxValue: 59, | |
57 | + valueRange: { start: 0, end: 59 }, | |
58 | + valueLoop: { start: 0, interval: 1 }, | |
59 | + }); | |
60 | + }, | |
61 | + }); | |
62 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每月</Radio> | |
6 | + </div> | |
7 | + <div class="item"> | |
8 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
9 | + <span> 从 </span> | |
10 | + <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
11 | + <span> 月 至 </span> | |
12 | + <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
13 | + <span> 月 </span> | |
14 | + </div> | |
15 | + <div class="item"> | |
16 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
17 | + <span> 从 </span> | |
18 | + <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
19 | + <span> 月开始,间隔 </span> | |
20 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
21 | + <span> 月 </span> | |
22 | + </div> | |
23 | + <div class="item"> | |
24 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
25 | + <div class="list"> | |
26 | + <CheckboxGroup v-model:value="valueList"> | |
27 | + <template v-for="i in specifyRange" :key="i"> | |
28 | + <Checkbox :value="i" v-bind="typeSpecifyAttrs">{{ i }}</Checkbox> | |
29 | + </template> | |
30 | + </CheckboxGroup> | |
31 | + </div> | |
32 | + </div> | |
33 | + </RadioGroup> | |
34 | + </div> | |
35 | +</template> | |
36 | + | |
37 | +<script lang="ts"> | |
38 | + import { defineComponent } from 'vue'; | |
39 | + import { InputNumber } from 'ant-design-vue'; | |
40 | + import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin'; | |
41 | + import { Checkbox, Radio } from 'ant-design-vue'; | |
42 | + const { CheckboxGroup } = Checkbox; | |
43 | + const { RadioGroup } = Radio; | |
44 | + | |
45 | + export default defineComponent({ | |
46 | + name: 'MonthUI', | |
47 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
48 | + props: useTabProps({ | |
49 | + defaultValue: '*', | |
50 | + }), | |
51 | + emits: useTabEmits(), | |
52 | + setup(props, context) { | |
53 | + return useTabSetup(props, context, { | |
54 | + defaultValue: '*', | |
55 | + minValue: 1, | |
56 | + maxValue: 12, | |
57 | + valueRange: { start: 1, end: 12 }, | |
58 | + valueLoop: { start: 1, interval: 1 }, | |
59 | + }); | |
60 | + }, | |
61 | + }); | |
62 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每秒</Radio> | |
6 | + </div> | |
7 | + <div class="item"> | |
8 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
9 | + <span> 从 </span> | |
10 | + <InputNumber v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
11 | + <span> 秒 至 </span> | |
12 | + <InputNumber v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
13 | + <span> 秒 </span> | |
14 | + </div> | |
15 | + <div class="item"> | |
16 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
17 | + <span> 从 </span> | |
18 | + <InputNumber v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
19 | + <span> 秒开始,间隔 </span> | |
20 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
21 | + <span> 秒 </span> | |
22 | + </div> | |
23 | + <div class="item"> | |
24 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
25 | + <div class="list"> | |
26 | + <CheckboxGroup v-model:value="valueList"> | |
27 | + <template v-for="i in specifyRange" :key="i"> | |
28 | + <Checkbox :value="i" v-bind="typeSpecifyAttrs">{{ i }}</Checkbox> | |
29 | + </template> | |
30 | + </CheckboxGroup> | |
31 | + </div> | |
32 | + </div> | |
33 | + </RadioGroup> | |
34 | + </div> | |
35 | +</template> | |
36 | + | |
37 | +<script lang="ts"> | |
38 | + import { defineComponent } from 'vue'; | |
39 | + import { InputNumber } from 'ant-design-vue'; | |
40 | + import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin'; | |
41 | + import { Radio, Checkbox } from 'ant-design-vue'; | |
42 | + const { CheckboxGroup } = Checkbox; | |
43 | + const { RadioGroup } = Radio; | |
44 | + | |
45 | + export default defineComponent({ | |
46 | + name: 'SecondUI', | |
47 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
48 | + props: useTabProps({ | |
49 | + defaultValue: '*', | |
50 | + }), | |
51 | + emits: useTabEmits(), | |
52 | + setup(props, context) { | |
53 | + return useTabSetup(props, context, { | |
54 | + defaultValue: '*', | |
55 | + minValue: 0, | |
56 | + maxValue: 59, | |
57 | + valueRange: { start: 0, end: 59 }, | |
58 | + valueLoop: { start: 0, interval: 1 }, | |
59 | + }); | |
60 | + }, | |
61 | + }); | |
62 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.unset" v-bind="beforeRadioAttrs">不设置</Radio> | |
6 | + <span class="tip-info">日和周只能设置其中之一</span> | |
7 | + </div> | |
8 | + <div class="item"> | |
9 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
10 | + <span> 从 </span> | |
11 | + <a-select | |
12 | + v-model:value="valueRange.start" | |
13 | + :options="weekOptions" | |
14 | + v-bind="typeRangeSelectAttrs" | |
15 | + /> | |
16 | + <span> 至 </span> | |
17 | + <a-select | |
18 | + v-model:value="valueRange.end" | |
19 | + :options="weekOptions" | |
20 | + v-bind="typeRangeSelectAttrs" | |
21 | + /> | |
22 | + </div> | |
23 | + <div class="item"> | |
24 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
25 | + <span> 从 </span> | |
26 | + <a-select | |
27 | + v-model:value="valueLoop.start" | |
28 | + :options="weekOptions" | |
29 | + v-bind="typeLoopSelectAttrs" | |
30 | + /> | |
31 | + <span> 开始,间隔 </span> | |
32 | + <InputNumber v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
33 | + <span> 天 </span> | |
34 | + </div> | |
35 | + <div class="item"> | |
36 | + <Radio :value="TypeEnum.specify" v-bind="beforeRadioAttrs">指定</Radio> | |
37 | + <div class="list list-cn"> | |
38 | + <CheckboxGroup v-model:value="valueList"> | |
39 | + <template v-for="opt in weekOptions" :key="i"> | |
40 | + <Checkbox :value="opt.value" v-bind="typeSpecifyAttrs">{{ opt.label }}</Checkbox> | |
41 | + </template> | |
42 | + </CheckboxGroup> | |
43 | + </div> | |
44 | + </div> | |
45 | + </RadioGroup> | |
46 | + </div> | |
47 | +</template> | |
48 | + | |
49 | +<script lang="ts"> | |
50 | + import { computed, watch, defineComponent } from 'vue'; | |
51 | + import { InputNumber } from 'ant-design-vue'; | |
52 | + import { useTabProps, useTabEmits, useTabSetup, TypeEnum } from './useTabMixin'; | |
53 | + import { Checkbox, Radio } from 'ant-design-vue'; | |
54 | + const { CheckboxGroup } = Checkbox; | |
55 | + const { RadioGroup } = Radio; | |
56 | + | |
57 | + const WEEK_MAP_EN = { | |
58 | + '1': 'SUN', | |
59 | + '2': 'MON', | |
60 | + '3': 'TUE', | |
61 | + '4': 'WED', | |
62 | + '5': 'THU', | |
63 | + '6': 'FRI', | |
64 | + '7': 'SAT', | |
65 | + }; | |
66 | + | |
67 | + const WEEK_MAP_CN = { | |
68 | + '1': '周日', | |
69 | + '2': '周一', | |
70 | + '3': '周二', | |
71 | + '4': '周三', | |
72 | + '5': '周四', | |
73 | + '6': '周五', | |
74 | + '7': '周六', | |
75 | + }; | |
76 | + | |
77 | + export default defineComponent({ | |
78 | + name: 'WeekUI', | |
79 | + components: { InputNumber, RadioGroup, Radio, CheckboxGroup, Checkbox }, | |
80 | + props: useTabProps({ | |
81 | + defaultValue: '?', | |
82 | + props: { | |
83 | + day: { type: String, default: '*' }, | |
84 | + }, | |
85 | + }), | |
86 | + emits: useTabEmits(), | |
87 | + setup(props, context) { | |
88 | + const disabledChoice = computed(() => { | |
89 | + return (props.day && props.day !== '?') || props.disabled; | |
90 | + }); | |
91 | + const setup = useTabSetup(props, context, { | |
92 | + defaultType: TypeEnum.unset, | |
93 | + defaultValue: '?', | |
94 | + minValue: 1, | |
95 | + maxValue: 7, | |
96 | + // 0,7表示周日 1表示周一 | |
97 | + valueRange: { start: 1, end: 7 }, | |
98 | + valueLoop: { start: 2, interval: 1 }, | |
99 | + disabled: disabledChoice, | |
100 | + }); | |
101 | + const weekOptions = computed(() => { | |
102 | + let options: { label: string; value: number }[] = []; | |
103 | + for (let weekKey of Object.keys(WEEK_MAP_CN)) { | |
104 | + let weekName: string = WEEK_MAP_CN[weekKey]; | |
105 | + options.push({ | |
106 | + value: Number.parseInt(weekKey), | |
107 | + label: weekName, | |
108 | + }); | |
109 | + } | |
110 | + return options; | |
111 | + }); | |
112 | + | |
113 | + const typeRangeSelectAttrs = computed(() => ({ | |
114 | + class: ['w80'], | |
115 | + disabled: setup.typeRangeAttrs.value.disabled, | |
116 | + })); | |
117 | + | |
118 | + const typeLoopSelectAttrs = computed(() => ({ | |
119 | + class: ['w80'], | |
120 | + disabled: setup.typeLoopAttrs.value.disabled, | |
121 | + })); | |
122 | + | |
123 | + watch( | |
124 | + () => props.day, | |
125 | + () => { | |
126 | + setup.updateValue(disabledChoice.value ? '?' : setup.computeValue.value); | |
127 | + } | |
128 | + ); | |
129 | + | |
130 | + return { | |
131 | + ...setup, | |
132 | + weekOptions, | |
133 | + typeLoopSelectAttrs, | |
134 | + typeRangeSelectAttrs, | |
135 | + WEEK_MAP_CN, | |
136 | + WEEK_MAP_EN, | |
137 | + }; | |
138 | + }, | |
139 | + }); | |
140 | +</script> | ... | ... |
1 | +<template> | |
2 | + <div :class="`${prefixCls}-config-list`"> | |
3 | + <RadioGroup v-model:value="type"> | |
4 | + <div class="item"> | |
5 | + <Radio :value="TypeEnum.every" v-bind="beforeRadioAttrs">每年</Radio> | |
6 | + </div> | |
7 | + <div class="item"> | |
8 | + <Radio :value="TypeEnum.range" v-bind="beforeRadioAttrs">区间</Radio> | |
9 | + <span> 从 </span> | |
10 | + <InputNumber class="w80" v-model:value="valueRange.start" v-bind="typeRangeAttrs" /> | |
11 | + <span> 年 至 </span> | |
12 | + <InputNumber class="w80" v-model:value="valueRange.end" v-bind="typeRangeAttrs" /> | |
13 | + <span> 年 </span> | |
14 | + </div> | |
15 | + <div class="item"> | |
16 | + <Radio :value="TypeEnum.loop" v-bind="beforeRadioAttrs">循环</Radio> | |
17 | + <span> 从 </span> | |
18 | + <InputNumber class="w80" v-model:value="valueLoop.start" v-bind="typeLoopAttrs" /> | |
19 | + <span> 年开始,间隔 </span> | |
20 | + <InputNumber class="w80" v-model:value="valueLoop.interval" v-bind="typeLoopAttrs" /> | |
21 | + <span> 年 </span> | |
22 | + </div> | |
23 | + </RadioGroup> | |
24 | + </div> | |
25 | +</template> | |
26 | + | |
27 | +<script lang="ts"> | |
28 | + import { defineComponent } from 'vue'; | |
29 | + import { InputNumber } from 'ant-design-vue'; | |
30 | + import { useTabProps, useTabEmits, useTabSetup } from './useTabMixin'; | |
31 | + import { Radio } from 'ant-design-vue'; | |
32 | + const { RadioGroup } = Radio; | |
33 | + | |
34 | + export default defineComponent({ | |
35 | + name: 'YearUI', | |
36 | + components: { InputNumber, RadioGroup, Radio }, | |
37 | + props: useTabProps({ | |
38 | + defaultValue: '*', | |
39 | + }), | |
40 | + emits: useTabEmits(), | |
41 | + setup(props, context) { | |
42 | + const nowYear = new Date().getFullYear(); | |
43 | + return useTabSetup(props, context, { | |
44 | + defaultValue: '*', | |
45 | + minValue: 0, | |
46 | + valueRange: { start: nowYear, end: nowYear + 100 }, | |
47 | + valueLoop: { start: nowYear, interval: 1 }, | |
48 | + }); | |
49 | + }, | |
50 | + }); | |
51 | +</script> | ... | ... |
1 | +// 主要用于日和星期的互斥使用 | |
2 | +import { computed, inject, reactive, ref, unref, watch } from 'vue'; | |
3 | +import { propTypes } from '/@/utils/propTypes'; | |
4 | + | |
5 | +export enum TypeEnum { | |
6 | + unset = 'UNSET', | |
7 | + every = 'EVERY', | |
8 | + range = 'RANGE', | |
9 | + loop = 'LOOP', | |
10 | + work = 'WORK', | |
11 | + last = 'LAST', | |
12 | + specify = 'SPECIFY', | |
13 | +} | |
14 | + | |
15 | +// use 公共 props | |
16 | +export function useTabProps(options) { | |
17 | + const defaultValue = options?.defaultValue ?? '?'; | |
18 | + return { | |
19 | + value: propTypes.string.def(defaultValue), | |
20 | + disabled: propTypes.bool.def(false), | |
21 | + ...options?.props, | |
22 | + }; | |
23 | +} | |
24 | + | |
25 | +// use 公共 emits | |
26 | +export function useTabEmits() { | |
27 | + return ['change', 'update:value']; | |
28 | +} | |
29 | + | |
30 | +// use 公共 setup | |
31 | +export function useTabSetup(props, context, options) { | |
32 | + const { emit } = context; | |
33 | + const prefixCls = inject('prefixCls'); | |
34 | + const defaultValue = ref(options?.defaultValue ?? '?'); | |
35 | + // 类型 | |
36 | + const type = ref(options.defaultType ?? TypeEnum.every); | |
37 | + const valueList = ref<any[]>([]); | |
38 | + // 对于不同的类型,所定义的值也有所不同 | |
39 | + const valueRange = reactive(options.valueRange); | |
40 | + const valueLoop = reactive(options.valueLoop); | |
41 | + const valueWeek = reactive(options.valueWeek); | |
42 | + const valueWork = ref(options.valueWork); | |
43 | + const maxValue = ref(options.maxValue); | |
44 | + const minValue = ref(options.minValue); | |
45 | + | |
46 | + // 根据不同的类型计算出的value | |
47 | + const computeValue = computed(() => { | |
48 | + let valueArray: any[] = []; | |
49 | + switch (type.value) { | |
50 | + case TypeEnum.unset: | |
51 | + valueArray.push('?'); | |
52 | + break; | |
53 | + case TypeEnum.every: | |
54 | + valueArray.push('*'); | |
55 | + break; | |
56 | + case TypeEnum.range: | |
57 | + valueArray.push(`${valueRange.start}-${valueRange.end}`); | |
58 | + break; | |
59 | + case TypeEnum.loop: | |
60 | + valueArray.push(`${valueLoop.start}/${valueLoop.interval}`); | |
61 | + break; | |
62 | + case TypeEnum.work: | |
63 | + valueArray.push(`${valueWork.value}W`); | |
64 | + break; | |
65 | + case TypeEnum.last: | |
66 | + valueArray.push('L'); | |
67 | + break; | |
68 | + case TypeEnum.specify: | |
69 | + if (valueList.value.length === 0) { | |
70 | + valueList.value.push(minValue.value); | |
71 | + } | |
72 | + valueArray.push(valueList.value.join(',')); | |
73 | + break; | |
74 | + default: | |
75 | + valueArray.push(defaultValue.value); | |
76 | + break; | |
77 | + } | |
78 | + return valueArray.length > 0 ? valueArray.join('') : defaultValue.value; | |
79 | + }); | |
80 | + // 指定值范围区间,介于最小值和最大值之间 | |
81 | + const specifyRange = computed(() => { | |
82 | + let range: number[] = []; | |
83 | + if (maxValue.value != null) { | |
84 | + for (let i = minValue.value; i <= maxValue.value; i++) { | |
85 | + range.push(i); | |
86 | + } | |
87 | + } | |
88 | + return range; | |
89 | + }); | |
90 | + | |
91 | + watch( | |
92 | + () => props.value, | |
93 | + (val) => { | |
94 | + if (val !== computeValue.value) { | |
95 | + parseValue(val); | |
96 | + } | |
97 | + }, | |
98 | + { immediate: true } | |
99 | + ); | |
100 | + | |
101 | + watch(computeValue, (v) => updateValue(v)); | |
102 | + | |
103 | + function updateValue(value) { | |
104 | + emit('change', value); | |
105 | + emit('update:value', value); | |
106 | + } | |
107 | + | |
108 | + /** | |
109 | + * parseValue | |
110 | + * @param value | |
111 | + */ | |
112 | + function parseValue(value) { | |
113 | + if (value === computeValue.value) { | |
114 | + return; | |
115 | + } | |
116 | + try { | |
117 | + if (!value || value === defaultValue.value) { | |
118 | + type.value = TypeEnum.every; | |
119 | + } else if (value.indexOf('?') >= 0) { | |
120 | + type.value = TypeEnum.unset; | |
121 | + } else if (value.indexOf('-') >= 0) { | |
122 | + type.value = TypeEnum.range; | |
123 | + const values = value.split('-'); | |
124 | + if (values.length >= 2) { | |
125 | + valueRange.start = parseInt(values[0]); | |
126 | + valueRange.end = parseInt(values[1]); | |
127 | + } | |
128 | + } else if (value.indexOf('/') >= 0) { | |
129 | + type.value = TypeEnum.loop; | |
130 | + const values = value.split('/'); | |
131 | + if (values.length >= 2) { | |
132 | + valueLoop.start = value[0] === '*' ? 0 : parseInt(values[0]); | |
133 | + valueLoop.interval = parseInt(values[1]); | |
134 | + } | |
135 | + } else if (value.indexOf('W') >= 0) { | |
136 | + type.value = TypeEnum.work; | |
137 | + const values = value.split('W'); | |
138 | + if (!values[0] && !isNaN(values[0])) { | |
139 | + valueWork.value = parseInt(values[0]); | |
140 | + } | |
141 | + } else if (value.indexOf('L') >= 0) { | |
142 | + type.value = TypeEnum.last; | |
143 | + } else if (value.indexOf(',') >= 0 || !isNaN(value)) { | |
144 | + type.value = TypeEnum.specify; | |
145 | + valueList.value = value.split(',').map((item) => parseInt(item)); | |
146 | + } else { | |
147 | + type.value = TypeEnum.every; | |
148 | + } | |
149 | + } catch (e) { | |
150 | + type.value = TypeEnum.every; | |
151 | + } | |
152 | + } | |
153 | + | |
154 | + const beforeRadioAttrs = computed(() => ({ | |
155 | + class: ['choice'], | |
156 | + disabled: props.disabled || unref(options.disabled), | |
157 | + })); | |
158 | + const inputNumberAttrs = computed(() => ({ | |
159 | + class: ['w60'], | |
160 | + max: maxValue.value, | |
161 | + min: minValue.value, | |
162 | + precision: 0, | |
163 | + })); | |
164 | + const typeRangeAttrs = computed(() => ({ | |
165 | + disabled: type.value !== TypeEnum.range || props.disabled || unref(options.disabled), | |
166 | + ...inputNumberAttrs.value, | |
167 | + })); | |
168 | + const typeLoopAttrs = computed(() => ({ | |
169 | + disabled: type.value !== TypeEnum.loop || props.disabled || unref(options.disabled), | |
170 | + ...inputNumberAttrs.value, | |
171 | + })); | |
172 | + const typeSpecifyAttrs = computed(() => ({ | |
173 | + disabled: type.value !== TypeEnum.specify || props.disabled || unref(options.disabled), | |
174 | + class: ['list-check-item'], | |
175 | + })); | |
176 | + | |
177 | + return { | |
178 | + type, | |
179 | + TypeEnum, | |
180 | + prefixCls, | |
181 | + defaultValue, | |
182 | + valueRange, | |
183 | + valueLoop, | |
184 | + valueWeek, | |
185 | + valueList, | |
186 | + valueWork, | |
187 | + maxValue, | |
188 | + minValue, | |
189 | + computeValue, | |
190 | + specifyRange, | |
191 | + updateValue, | |
192 | + parseValue, | |
193 | + beforeRadioAttrs, | |
194 | + inputNumberAttrs, | |
195 | + typeRangeAttrs, | |
196 | + typeLoopAttrs, | |
197 | + typeSpecifyAttrs, | |
198 | + }; | |
199 | +} | ... | ... |
1 | +import CronParser from 'cron-parser'; | |
2 | +import type { ValidatorRule } from 'ant-design-vue/lib/form/interface'; | |
3 | + | |
4 | +const cronRule: ValidatorRule = { | |
5 | + validator({}, value) { | |
6 | + // 没填写就不校验 | |
7 | + if (!value) { | |
8 | + return Promise.resolve(); | |
9 | + } | |
10 | + const values: string[] = value.split(' ').filter((item) => !!item); | |
11 | + if (values.length > 7) { | |
12 | + return Promise.reject('Cron表达式最多7项!'); | |
13 | + } | |
14 | + // 检查第7项 | |
15 | + let val: string = value; | |
16 | + if (values.length === 7) { | |
17 | + const year = values[6]; | |
18 | + if (year !== '*' && year !== '?') { | |
19 | + let yearValues: string[] = []; | |
20 | + if (year.indexOf('-') >= 0) { | |
21 | + yearValues = year.split('-'); | |
22 | + } else if (year.indexOf('/')) { | |
23 | + yearValues = year.split('/'); | |
24 | + } else { | |
25 | + yearValues = [year]; | |
26 | + } | |
27 | + // 判断是否都是数字 | |
28 | + const checkYear = yearValues.some((item) => isNaN(Number(item))); | |
29 | + if (checkYear) { | |
30 | + return Promise.reject('Cron表达式参数[年]错误:' + year); | |
31 | + } | |
32 | + } | |
33 | + // 取其中的前六项 | |
34 | + val = values.slice(0, 6).join(' '); | |
35 | + } | |
36 | + // 6位 没有年 | |
37 | + // 5位没有秒、年 | |
38 | + try { | |
39 | + const iter = CronParser.parseExpression(val); | |
40 | + iter.next(); | |
41 | + return Promise.resolve(); | |
42 | + } catch (e) { | |
43 | + return Promise.reject('Cron表达式错误:' + e); | |
44 | + } | |
45 | + }, | |
46 | +}; | |
47 | + | |
48 | +export default cronRule.validator; | ... | ... |