You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flatpickr选择日期后重绘丢失onDayCreate修改,如何重新触发处理?

问题

我用Vue.js写了一个自定义日历组件,基于Flatpickr实现,能给不同日期设置不同背景色。但选择任意日期后,所有自定义样式都会丢失——因为Flatpickr会重绘日期DOM元素。我试过Flatpickr文档里的多个事件钩子,但它们都没法像onDayCreate那样直接操作日期元素。请问怎么在日期网格更新时,重复执行onDayCreate回调里的样式设置逻辑?

组件代码

<script>
import { UI_DATE_FORMAT } from "../data/System";
import flatPickr from "vue-flatpickr-component";
import RequestService from "../services/RequestService";

const request = new RequestService();

export default {
  props: [],

  data() {
    return {
      selectedDate: "",
      currentMonth: null,
      busyDaysData: {},
      updateDataProcess: false,

      dateConfig: {
        inline: true,
        dateFormat: UI_DATE_FORMAT,
        monthSelectorType: "static",
        wrap: false,

        onDayCreate: function (dObj, dStr, fp, dayElem) {
          this.calendarDayCreate(dayElem, fp);
        }.bind(this),
      },
    };
  },

  components: {
    flatPickr,
  },

  mounted() {},

  methods: {
    calendarDayCreate(dayElem, fp) {
      const month = fp.currentMonth + 1;
      const dayDay = dayElem.dateObj.getDate();
      const dayMonth = dayElem.dateObj.getMonth() + 1;

      if (dayMonth !== month) return;

      if (this.updateDataProcess) {
        const timer = setTimeout(() => {
          if (!this.updateDataProcess) {
            clearTimeout(timer);
            this.handleDay(dayDay, dayElem);
          }
        }, 250);
      } else {
        if (this.currentMonth !== month) {
          this.updateDataProcess = true;

          new Promise((onSuccess, onError) => {
            request.requestData(
              {
                data: "Calendar\\BusyDaysForMonth",
                arguments: {
                  appointment_id: 0,
                  month,
                  extended: true,
                },
              },
              (response) => {
                onSuccess(response);
              },
              (error) => {
                onError(error);
              }
            );
          }).then(
            (response) => {
              this.updateDataProcess = false;

              this.currentMonth = month;
              this.busyDaysData = response.data;
              this.handleDay(dayDay, dayElem);
            },
            (error) => {
              this.updateDataProcess = false;
              console.log(error);
            }
          );
        }
      }
    },

    handleDay(day, dayElem) {
      if (!this.busyDaysData[day]) return;
      console.log(dayElem);
      dayElem.classList.add(
        "app-" + this.busyDaysData[day].appointment_status ?? ""
      );
    },
  },
};
</script>

<template>
  <b-row id="appointments-calendar">
    <b-col cols="12">
      <flat-pickr
        :config="dateConfig"
        v-model="selectedDate"
        class="form-control flatpickr active"
        :placeholder="$t('appointments_input_date_ph')"
      >
      </flat-pickr>
    </b-col>
  </b-row>
</template>

<style scoped lang="scss"></style>

问题图示

选择前
选择后


解决方案

Flatpickr在日期选择、月份切换等操作时会完全重绘日期网格,onDayCreate仅在初始创建日期元素时触发,所以需要在网格更新后重新执行样式设置逻辑。以下是两种可行方案:

方案一:监听网格更新事件,批量重设样式

在dateConfig中添加onMonthChange(月份切换时触发)和onValueChange(日期选择时触发)两个钩子,每次触发时遍历当前所有日期元素,重新应用样式:

  1. 修改dateConfig:
dateConfig: {
  inline: true,
  dateFormat: UI_DATE_FORMAT,
  monthSelectorType: "static",
  wrap: false,
  onDayCreate: function (dObj, dStr, fp, dayElem) {
    this.calendarDayCreate(dayElem, fp);
  }.bind(this),
  // 添加这两个事件钩子
  onMonthChange: function(selectedDates, dateStr, fp) {
    this.reapplyDayStyles(fp);
  }.bind(this),
  onValueChange: function(selectedDates, dateStr, fp) {
    this.reapplyDayStyles(fp);
  }.bind(this)
},
  1. 新增reapplyDayStyles方法:
methods: {
  // ... 原有方法
  reapplyDayStyles(fp) {
    // 获取当前日历容器内的所有日期元素
    const dayElements = fp.calendarContainer.querySelectorAll('.flatpickr-day');
    const currentMonth = fp.currentMonth + 1;
    
    dayElements.forEach(dayElem => {
      const dateObj = dayElem.dateObj;
      const dayMonth = dateObj.getMonth() + 1;
      
      // 只处理当前月份的日期
      if (dayMonth !== currentMonth) return;
      
      const day = dateObj.getDate();
      // 先清除之前的自定义类,避免重复叠加
      const existingAppClasses = Array.from(dayElem.classList).filter(cls => cls.startsWith('app-'));
      if (existingAppClasses.length) {
        dayElem.classList.remove(...existingAppClasses);
      }
      // 重新应用样式
      this.handleDay(day, dayElem);
    });
  }
}

方案二:优化数据加载后的样式覆盖

原代码中calendarDayCreate是逐个处理日期元素,切换月份时可能只处理了部分元素。可以在数据加载完成后,直接遍历当前月份的所有日期元素,统一应用样式:

修改calendarDayCreate中的Promise成功回调:

.then(
  (response) => {
    this.updateDataProcess = false;
    this.currentMonth = month;
    this.busyDaysData = response.data;
    
    // 数据加载完成后,遍历当前月份所有日期元素
    const dayElements = fp.calendarContainer.querySelectorAll('.flatpickr-day');
    dayElements.forEach(dayElem => {
      const dateObj = dayElem.dateObj;
      const dayMonth = dateObj.getMonth() + 1;
      if (dayMonth !== month) return;
      const day = dateObj.getDate();
      this.handleDay(day, dayElem);
    });
  },
  (error) => {
    this.updateDataProcess = false;
    console.log(error);
  }
)

注意事项

  • 两种方案可以结合使用:方案一处理日期选择、月份切换后的重绘,方案二确保月份切换时数据加载完成后所有日期都能应用样式。
  • 重设样式前清除原有自定义类,避免样式冲突或重复添加。

内容的提问来源于stack exchange,提问作者Михаил Хоменко

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 13:44:55