基于vue3的Crontab组件
网上找的没有满意的,决定从若依前后端分离其前端vue2中的crontab进行转换,先上效果
若依v2版:
改v3后:
v2转v3没什么难度,其中有大量的将 this.*** 替换为 ***.value,笔者写了个正则替换,希望可以帮助大家
this.(\w+) $1.value
需要注意的有,在v2中【this.$refs[refName].cycle01 = indexArr[0]】这样写
在v3中要转换一下,在子组件中用【defineExpose】抛出一个setData方法,然后【proxy.$refs[refName].setData("cycle01", Number(indexArr[0]))】赋值
其中子组件CrontabSecond.vue的代码,其它分/时/日/月/周/年的类似,参照着改就可以了
<template> <el-form size="small"> <el-form-item> <el-radio v-model='radioValue' :label="1"> 每秒,允许的通配符[, - * /] </el-radio> </el-form-item> <el-form-item> <el-radio v-model='radioValue' :label="2"> 周期,从 <el-input-number v-model='cycle01' :min="0" :max="58" /> - <el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="59" /> 秒 </el-radio> </el-form-item> <el-form-item> <el-radio v-model='radioValue' :label="3"> 从 <el-input-number v-model='average01' :min="0" :max="58" /> 秒开始,每 <el-input-number v-model='average02' :min="1" :max="59 - average01 || 0" /> 秒执行一次 </el-radio> </el-form-item> <el-form-item class="crontab-select"> <el-radio v-model='radioValue' :label="4"> 指定 <el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:300px"> <el-option v-for="item in 60" :key="item" :value="String(item-1)" :label="'第' + String(item-1) + '秒'">第 {{item-1}} 秒</el-option> </el-select> </el-radio> </el-form-item> </el-form> </template> <script setup name="CrontabSecond"> import { ref } from 'vue'; const emits = defineEmits(["update"]); const props = defineProps({ check: { type: Function, require:false }, cron: { type: Object, default: function(){ return {} } }, }); const radioValue = ref(1); const cycle01 = ref(1); const cycle02 = ref(2); const average01 = ref(0); const average02 = ref(1); const checkboxList = ref([]); // 计算两个周期值 const cycleTotal = computed(() => { const c01 = props.check(cycle01.value, 0, 58) const c02 = props.check(cycle02.value, c01 ? c01 + 1 : 1, 59) return c01 + '-' + c02; }) // 计算平均用到的值 const averageTotal = computed(() => { const a01 = props.check(average01.value, 0, 58) const a02 = props.check(average02.value, 1, 59 - a01 || 0) return a01 + '/' + a02; }) // 计算勾选的checkbox值合集 const checkboxString = computed(() => { let str = checkboxList.value.join(); return str == '' ? '*' : str; }) watch(()=>radioValue.value, ()=>{ radioChange(); }) watch(()=>cycleTotal.value, ()=>{ cycleChange(); }) watch(()=>averageTotal.value, ()=>{ averageChange(); }) watch(()=>checkboxString.value, ()=>{ checkboxChange(); }) // 单选按钮值变化时 function radioChange() { switch (radioValue.value) { case 1: emits('update', 'second', '*', 'second'); break; case 2: emits('update', 'second', cycleTotal.value); break; case 3: emits('update', 'second', averageTotal.value); break; case 4: emits('update', 'second', checkboxString.value); break; } } // 周期两个值变化时 function cycleChange() { if (radioValue.value == '2') { emits('update', 'second', cycleTotal.value); } } // 平均两个值变化时 function averageChange() { if (radioValue.value == '3') { emits('update', 'second', averageTotal.value); } } // checkbox值变化时 function checkboxChange() { if (radioValue.value == '4') { emits('update', 'second', checkboxString.value); } } defineExpose({ setData(key, value){ eval(key).value = value } }) </script>
关于CrontabResult.vue的组件,转v3时,额外注意数组排序这里就好了,
# 如果改为compare.value会出现bug的 正确为 arr.sort(this.compare) 改为 arr.sort(compare)
贴出核心Crontab.vue的代码,其子组件就不一一贴了,需要的可以自己下若依代码进行转换
<template> <div class="crontab"> <el-tabs type="border-card"> <el-tab-pane label="秒"> <CrontabSecond @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronsecond" /> </el-tab-pane> <el-tab-pane label="分钟"> <CrontabMin @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronmin" /> </el-tab-pane> <el-tab-pane label="小时"> <CrontabHour @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronhour" /> </el-tab-pane> <el-tab-pane label="日"> <CrontabDay @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronday" /> </el-tab-pane> <el-tab-pane label="月"> <CrontabMonth @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronmonth" /> </el-tab-pane> <el-tab-pane label="周"> <CrontabWeek @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronweek" /> </el-tab-pane> <el-tab-pane label="年"> <CrontabYear @update="updateCrontabValue" v-model:check="checkNumber" v-model:cron="crontabValueObj" ref="cronyear" /> </el-tab-pane> </el-tabs> <div class="crontab-main"> <div class="crontab-main-table"> <table> <thead> <th v-for="item of tabTitles" width="40" :key="item">{{item}}</th> <!-- <th>Cron 表达式</th> --> </thead> <tbody> <td> <span>{{crontabValueObj.second}}</span> </td> <td> <span>{{crontabValueObj.min}}</span> </td> <td> <span>{{crontabValueObj.hour}}</span> </td> <td> <span>{{crontabValueObj.day}}</span> </td> <td> <span>{{crontabValueObj.month}}</span> </td> <td> <span>{{crontabValueObj.week}}</span> </td> <td> <span>{{crontabValueObj.year}}</span> </td> <!-- <td> <span>{{crontabValueString}}</span> </td> --> </tbody> </table> <table> <thead> <th>Cron 表达式</th> </thead> <tbody> <td> <span>{{crontabValueString}}</span> </td> </tbody> </table> </div> <div class="crontab-main-result"> <CrontabResult v-model:ex="crontabValueString"></CrontabResult> </div> </div> </div> </template> <script setup name="Crontab"> import CrontabSecond from "./crontab/CrontabSecond.vue"; import CrontabMin from "./crontab/CrontabMin.vue"; import CrontabHour from "./crontab/CrontabHour.vue"; import CrontabDay from "./crontab/CrontabDay.vue"; import CrontabMonth from "./crontab/CrontabMonth.vue"; import CrontabWeek from "./crontab/CrontabWeek.vue"; import CrontabYear from "./crontab/CrontabYear.vue"; import CrontabResult from "./crontab/CrontabResult.vue"; const { proxy } = getCurrentInstance(); const emits = defineEmits(["hide", "fill"]); const props = defineProps({ expression: {type: String, default: ""} }) const tabTitles = ref(["秒", "分钟", "小时", "日", "月", "周", "年"]) const tabActive = ref(0) const crontabValueObj = ref({ second: "*", min: "*", hour: "*", day: "*", month: "*", week: "?", year: "", }) const crontabValueString = computed(() => { let obj = crontabValueObj.value; let str = obj.second + " " + obj.min + " " + obj.hour + " " + obj.day + " " + obj.month + " " + obj.week + (obj.year == "" ? "" : " " + obj.year); return str; }) onMounted(() => { resolveExp(); }) watch(() => props.expression, (v) => { resolveExp(); }); function resolveExp() { // 反解析 表达式 if (props.expression) { let arr = props.expression.split(" "); if (arr.length >= 6) { //6 位以上是合法表达式 let obj = { second: arr[0], min: arr[1], hour: arr[2], day: arr[3], month: arr[4], week: arr[5], year: arr[6] ? arr[6] : "", }; crontabValueObj.value = { ...obj, }; for (let i in obj) { if (obj[i]) changeRadio(i, obj[i]); } } } else { // 没有传入的表达式 则还原 clearCron(); } } // tab切换值 function tabCheck(index) { tabActive.value = index; } // 由子组件触发,更改表达式组成的字段值 function updateCrontabValue(name, value, from) { // "updateCrontabValue", name, value, from; crontabValueObj.value[name] = value; if (from && from !== name) { console.log(`来自组件 ${from} 改变了 ${name} ${value}`); changeRadio(name, value); } } // 赋值到组件 function changeRadio(name, value) { let arr = ["second", "min", "hour", "month"] let refName = "cron" + name let insValue; if (!proxy.$refs[refName]) return; if (arr.includes(name)) { if (value === "*") { insValue = 1; } else if (value.indexOf("-") > -1) { let indexArr = value.split("-"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("cycle01", 0)) : (proxy.$refs[refName].setData("cycle01", Number(indexArr[0]))); proxy.$refs[refName].setData("cycle02", Number(indexArr[1])); insValue = 2; } else if (value.indexOf("/") > -1) { let indexArr = value.split("/"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("average01", 0)) : (proxy.$refs[refName].setData("average01", Number(indexArr[0]))); proxy.$refs[refName].setData("average02", Number(indexArr[1])); insValue = 3; } else { insValue = 4; let list = value.split(","); for(let item of list){ item = String(item) } proxy.$refs[refName].setData("checkboxList", list); } } else if (name == "day") { if (value === "*") { insValue = 1; } else if (value == "?") { insValue = 2; } else if (value.indexOf("-") > -1) { let indexArr = value.split("-"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("cycle01", 0)) : (proxy.$refs[refName].setData("cycle01", Number(indexArr[0]))); proxy.$refs[refName].setData("cycle02", Number(indexArr[1])); insValue = 3; } else if (value.indexOf("/") > -1) { let indexArr = value.split("/"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("average01", 0)) : (proxy.$refs[refName].setData("average01", Number(indexArr[0]))); proxy.$refs[refName].setData("average02", Number(indexArr[1])); insValue = 4; } else if (value.indexOf("W") > -1) { let indexArr = value.split("W"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("workday", 0)) : (proxy.$refs[refName].setData("workday", Number(indexArr[0]))); insValue = 5; } else if (value === "L") { insValue = 6; } else { let list = value.split(","); for(let item of list){ item = String(item) } proxy.$refs[refName].setData("checkboxList", list); insValue = 7; } } else if (name == "week") { if (value === "*") { insValue = 1; } else if (value == "?") { insValue = 2; } else if (value.indexOf("-") > -1) { let indexArr = value.split("-"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("cycle01", "0")) : (proxy.$refs[refName].setData("cycle01", String(indexArr[0]))); proxy.$refs[refName].setData("cycle02", String(indexArr[1])); insValue = 3; } else if (value.indexOf("#") > -1) { let indexArr = value.split("#"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("average01", 1)) : (proxy.$refs[refName].setData("average01", Number(indexArr[0]))); proxy.$refs[refName].setData("average02", String(indexArr[1])); insValue = 4; } else if (value.indexOf("L") > -1) { let indexArr = value.split("L"); isNaN(indexArr[0]) ? (proxy.$refs[refName].setData("weekday", "1")) : (proxy.$refs[refName].setData("weekday", String(indexArr[0]))); insValue = 5; } else { let list = value.split(","); for(let item of list){ item = String(item) } proxy.$refs[refName].setData("checkboxList", list); insValue = 6; } } else if (name == "year") { if (value == "") { insValue = 1; } else if (value == "*") { insValue = 2; } else if (value.indexOf("-") > -1) { insValue = 3; } else if (value.indexOf("/") > -1) { insValue = 4; } else { let list = value.split(","); for(let item of list){ item = String(item) } proxy.$refs[refName].setData("checkboxList", list); insValue = 5; } } proxy.$refs[refName].setData("radioValue", insValue); } // 表单选项的子组件校验数字格式(通过-props传递) function checkNumber(value, minLimit, maxLimit) { // 检查必须为整数 value = Math.floor(Number(value)); if (value < minLimit) { value = minLimit; } else if (value > maxLimit) { value = maxLimit; } return value; } // 隐藏弹窗 function hidePopup() { emits("hide"); } // 填充表达式 function submitFill() { emits("fill", crontabValueString); hidePopup(); } function clearCron() { // 还原选择项 ("准备还原"); crontabValueObj.value = { second: "*", min: "*", hour: "*", day: "*", month: "*", week: "?", year: "", }; for (let j in crontabValueObj.value) { changeRadio(j, crontabValueObj.value[j]); } } defineExpose({ submitFill, clearCron }) </script> <style scoped> .crontab{ flex: 1; height: 100%; display: flex; flex-direction: column; } .crontab-main { flex: 1; width: 100%; margin: 10px auto; background: #fff; border-radius: 5px; font-size: 12px; border: 1px solid #ccc; box-sizing: border-box; line-height: 24px; padding: 5px 10px 5px; display: flex; justify-content: space-between; overflow-y: auto; } .crontab-main-table { box-sizing: border-box; line-height: 24px; padding: 5px 10px 5px; width: 50%; display: flex; flex-direction: column; justify-content: space-around; table { text-align: center; width: 100%; margin: 0; span { display: block; width: 100%; font-family: arial; line-height: 30px; height: 30px; white-space: nowrap; overflow: hidden; border: 1px solid #e8e8e8; } } } .crontab-main-result { box-sizing: border-box; padding: 5px 10px 5px; background-color: #f1f1f1; background-size: cover; width: 48%; display: flex; flex-direction: column; .crontab-result-title{ padding: 5px; } :deep(.crontab-result-scroll) { font-size: 12px; line-height: 24px; margin: 0 !important; padding-left: 80px; } } .crontab-footer { text-align: right; height: 25px; padding: 5px 20px; } </style>
将原来组件的按钮移到引用层,引用样例
<el-dialog title="Cron表达式生成器" v-model="formCrontabOpen" append-to-body destroy-on-close class="nine-tanchuang-001"> <!-- <crontab @change="cronChange" v-model:value="formData.cronExpression" /> --> <Crontab ref="crontabRef" @hide="formCrontabOpen=false" @fill="crontabFill" v-model:expression="formData.cronExpression"></Crontab> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="formCrontSubmit">确 定</el-button> <el-button type="warning" @click="formCrontReset">重 置</el-button> <el-button @click="formCrontabOpen=false">取 消</el-button> </div> </template> </el-dialog>
对应引用位置的调用方法
/** 确定访问子组件方法 */ function formCrontSubmit(){ proxy.$refs.crontabRef.submitFill(); } /** 重置访问子组件方法 */ function formCrontReset(){ proxy.$refs.crontabRef.clearCron(); } /** 子组件确认回调方法 */ function crontabFill(value) { formData.value.cronExpression = value; }
作者名称:Vrapile
联系方式:发送邮件Vrapile@163.com,另博客账号也是本人微信号。
版权声明:此文是博主业余爱好所写,文章或有错误与不足之处,欢迎留言指正建议、共同探讨!另此文为博主原创,未经博主同意不得转载,否则保留追究法律责任的权利。