網上找的沒有滿意的,決定從若依前後端分離其前端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; }