图1-外输入框
图2-内输入框
图3
问题描述:
这两天在迭代功能的时候,基本上碰到的问题都是出自这个“时间日期选择框”,昨天的bug38也是解决这个组件。如上图1和2所示,可以把图1中的输入框叫外输入框,图2中的输入框叫内输入框,el-date-picker组件在官网上其实是有一个属性叫“editable”,可以禁止用户在文本框输入,但是很遗憾这个只能在外输入框生效,内输入框还是能编辑。由于业务上对日期有一些限制,因此不能选择任意日期,如果在内输入框手动输入日期,就限制不了日期范围,因此还是考虑禁用。
以前其实碰到测试在外输入框输入日期的情况,然后使用editable属性可以规避用户输入,其实也不是不想让用户输入,只是有两个方面考虑:(1)效率不高,很少有人去手动输入(2)同时输入的格式不正确,导致不能转换成前后端约定的格式,然后查询报错,因此直接使用editable禁用外输入框。其实对于所有只有外框的el-date-picker组件已经可以说是规避解决了这个问题。但是对于type="datetimerange"的这种类型比较特殊的时间日期范围搜索框来说就不行,因为它有“内输入框”。其实还是很建议官方单独给datetimerange类型的搜索框加个属性,就是把内框也禁用掉,这样就能让我们这些牛马轻松点了。所以,这次的问题就是:我希望禁用掉内输入框输入。
方案:
还是跟上次一样有问题先请教cursor的意见。这次的cursor没有帮我“一步到位”的解决问题,尽管它“天花乱坠”的说了很多,而且引导了它几次都还是没有成功,我就知道那接下来只能靠我自己了。不过它还是为我提供了思路:那就是通过给内输入框添加“readonly”属性的方式实现禁用。
说到底,内输入框本质上也是个input,只要给它添加readonly或者把它disabled就可以达到我们的要求了。在没有相关的属性或者API能完成任务的时候,通过在合适的时机,使用原始的js插入属性的方式。
添加 @focus="timeEditable"方法,当用户点击外框然后显示了日期面板的时候,使用nextTick确保此时内输入框渲染完毕之后,再利用js定位到内输入框的元素位置,然后给所有的input追加一个readonly属性即可实现禁止输入的效果。
<el-date-picker
v-else-if="item.type === 'DATEPICKER'"
:key="item?.dateType + (item?.valueFormat || '')"
v-model="form[item.key]"
:type="item.dateType ?? 'daterange'"
:style="{ width: item.dateType ? '400px' : '300px' }"
start-placeholder="开始日期"
end-placeholder="结束日期"
range-separator="至"
:clearable="!!item.clearable"
:editable="false"
:disabled-date="(time: Date) => item.dynamicDisabled ? handleDymicDisabled(time, item) : false"
:value-format="item.valueFormat ?? 'YYYY-MM-DD'"
:format="item.valueFormat ?? 'YYYY-MM-DD'"
@focus="timeEditable"
@calendar-change="handleCalendarChange"
/>
//点击日期范围外面搜索框
function timeEditable(e: Event) {
console.log('timeEditable', e)
// 禁用时间日期范围组件里面的日期input输入框
nextTick(() => {
let els = document.querySelectorAll('.el-date-range-picker__time-picker-wrap input')
console.log('els', els)
for (var i = 0; i <= els.length - 1; i++) {
els[i].setAttribute('readonly', 'readonly')
}
})
}
然后发现第一个日期输入框是可以禁止编辑了,但是第二个日期输入框并没有被禁止编辑,就是见了鬼了。加了打印,找到控制台的元素,反复推敲发现了原因。当用户在面板上选择日期的时候,是需要点击两次来确定“开始日期”和“结束日期”的,当点击选择第一个日期之后,第二个日期输入框视图是会被触发重新挂载更新了,导致之前加的readonly丢失,需要再加一遍。因此:
function handleCalendarChange(date: Date[]) {
console.log('handleCalendarChange', date)
// 禁用时间日期范围组件里面的日期input输入框
nextTick(() => {
let els = document.querySelectorAll('.el-date-range-picker__time-picker-wrap input')
console.log('els', els)
for (var i = 0; i <= els.length - 1; i++) {
els[i].setAttribute('readonly', 'readonly')
}
})
}
在@calendar-change="handleCalendarChange"方法中是触发选择面板的日期的事件方法,在这里再找到所有input,再全部添加一次readonly即可,测试了一下确实验证了我的猜想。至此,终于实现了内输入框的禁用。补充一下,内输入框的时间选择框不受影响,它还是可以选择的。