返回

PrimeVue Calendar 组件时间默认值设置详解

vue.js

PrimeVue Calendar 时间默认值处理

在应用开发过程中,PrimeVueCalendar 组件是一个常用的日期时间选择工具。常见场景下,当组件从后端接收到预设的日期和时间值时,需要在日历组件中进行初始的选中。组件的 v-model 可以显示预设值,但日历面板通常不会直接定位到该时间点。本篇文章讨论如何有效解决 PrimeVue Calendar 组件时间默认值处理的问题,实现初始化时的精准显示。

问题分析

Calendar 组件通过 v-model 绑定一个日期时间字符串,如 "15.10.2024 11:48",该日期时间会显示在输入框中。但日历面板并没有将焦点定位到该日期,尤其是当日期范围比较大的时候,用户需要手动滚动月份,然后寻找并选择相应的时间,增加了用户操作步骤。根本原因在于,v-model 主要负责同步值到输入框,但日历面板内部并没有读取 v-model 值作为其内部选中时间的初始参考。它可能使用了当前时间或 minDate/maxDate 规则来初始化,因此需要特定的策略来同步两者的初始状态。

解决方案一:使用 defaultDatemodelValue 的组合

一种解决方式是组合使用 defaultDate 属性和 v-modeldefaultDate 可用于在首次加载时设置日历的初始日期,配合 modelValue 来同步初始日期与时间:

  1. modelValue 转换为 Date 对象
    通常 Calendar 组件绑定的数据可能是一个字符串。将 v-model 的字符串值先转换为 Date 对象。
  2. 使用转换后的 Date 对象赋值给 defaultDate :将此 Date 对象赋值给 CalendardefaultDate 属性,作为日历面板初始显示的月份。这样能保证面板默认展示到初始日期对应的月份。
<template>
    <Calendar
        showIcon
        iconDisplay="input"
        @update:modelValue="(value) => submitCalendar(value, field.id)"
        v-model="formattedDate"
        :defaultDate="defaultDateObject"
        showTime
        hourFormat="24"
    >
    <template #inputicon="{ clickCallback }">
        <svg class="cursor-pointer" @click="clickCallback" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#8F95B2" d="M7 11h2v2H7v-2Zm0 4h2v2H7v-2Zm4-4h2v2h-2v-2Zm0 4h2v2h-2v-2Zm4-4h2v2h-2v-2Zm0 4h2v2h-2v-2Z"/><path fill="#8F95B2" d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2ZM19 8l.001 12H5V8h14Z"/></svg>
      </template>
    </Calendar>
</template>

<script setup>
import { ref, watch } from 'vue';

const field = ref({
    value: ["15.10.2024 11:48"],
    id: 'calendarId'
});

const formattedDate = ref(null);
const defaultDateObject = ref(null);


watch(() => field.value[0], (newValue) => {
    formattedDate.value = newValue
    defaultDateObject.value =  convertStringToDate(newValue);
}, {immediate:true});



function convertStringToDate(dateString){
        const [datePart, timePart] = dateString.split(" ");
        const [day, month, year] = datePart.split(".");
        const [hour, minute] = timePart.split(":");
        return new Date(year, month - 1, day, hour, minute);

}

function submitCalendar(value, fieldId){
    console.log('submitted date:', value, ' fieldId:' ,fieldId);
}


</script>

这个方案主要通过程序辅助实现了 defaultDate 的初始化,保持了日历面板与初始值同步。 defaultDate 只会在初始化的时候生效,因此不影响用户之后的操作。

解决方案二:自定义 Calendar 的显示和隐藏逻辑

另一种方式是控制日历的显示和隐藏。当从后端接收到初始值时,可以先将 v-model 的值更新到输入框,然后再手动打开日历面板,此时组件会自动选择 v-model 中的日期。这需要控制组件的 visible 属性来实现:

  1. 使用一个 ref 来控制 Calendar 的显示和隐藏。

  2. 在接收到后端数据后,更新 v-model 的值。

  3. 设置日历面板的显示状态为 true,触发其渲染,这时候 PrimeVue 组件会自动识别并选中 v-model 中的时间值。
    如果想要隐藏,只需要将visible设置为 false

<template>
  <Calendar
        showIcon
        iconDisplay="input"
        @update:modelValue="(value) => submitCalendar(value, field.id)"
        v-model="formattedDate"
        :visible="calendarVisible"
        @show="onCalendarShow"
        @hide="calendarVisible=false"
        showTime
        hourFormat="24"
    >
   <template #inputicon="{ clickCallback }">
        <svg class="cursor-pointer" @click="clickCallback" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#8F95B2" d="M7 11h2v2H7v-2Zm0 4h2v2H7v-2Zm4-4h2v2h-2v-2Zm0 4h2v2h-2v-2Zm4-4h2v2h-2v-2Zm0 4h2v2h-2v-2Z"/><path fill="#8F95B2" d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2ZM19 8l.001 12H5V8h14Z"/></svg>
    </template>
    </Calendar>
</template>

<script setup>
import { ref, watch } from 'vue';

const field = ref({
    value: ["15.10.2024 11:48"],
    id: 'calendarId'
});

const formattedDate = ref(null);
const calendarVisible = ref(false)



watch(() => field.value[0], (newValue) => {
    formattedDate.value = newValue
    calendarVisible.value = true // 接收到后端值后,打开日历面板
}, {immediate:true});



function submitCalendar(value, fieldId){
    console.log('submitted date:', value, ' fieldId:' ,fieldId);
}

function onCalendarShow() {
  // calendar组件初始化完成后将其关闭
     setTimeout(()=>{
      calendarVisible.value = false
      }, 10);

}
</script>

此方法利用了组件的内部逻辑,即在日历面板首次显示时会自动选取 v-model 中的日期,实现了初始化时选择。但要注意,需要控制显示的逻辑,避免页面加载的时候自动展开。

安全提示

在使用这些解决方案时,建议始终对从后端接收到的日期时间字符串进行校验,确保格式正确且值有效。这不仅可以防止错误发生,还可以增强应用的安全性。如果存在不同日期时间格式转换需求,务必使用专门的工具库来避免解析上的不一致。

这两种方案都能有效地解决 PrimeVue Calendar 组件在初始显示时不能正确选中默认时间的问题。根据具体需求和场景选择最合适的解决方案。选择 defaultDate 配合 modelValue 可以让组件更像是一个纯组件,而控制组件 visible 可以更灵活控制。