Commit 2a9ea02096ca441dde6699ab75c0c02870f42b4d

Authored by fengtao
1 parent a853904c

feat:新增引入EasyCron组件

... ... @@ -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 +//noinspection LessUnresolvedVariable
  2 +@prefix-cls: ~'@{namespace}-easy-cron-input';
  3 +
  4 +.@{prefix-cls} {
  5 + a.open-btn {
  6 + cursor: pointer;
  7 +
  8 + .app-iconify {
  9 + position: relative;
  10 + top: 1px;
  11 + right: 2px;
  12 + }
  13 + }
  14 +}
... ...
  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;
... ...
... ... @@ -82,6 +82,7 @@ export interface ColEx {
82 82
83 83 export type ComponentType =
84 84 | 'Input'
  85 + | 'JEasyCron'
85 86 | 'ApiRadioGroup'
86 87 | 'InputGroup'
87 88 | 'InputPassword'
... ...