Skip to content
0

首页侧边栏日历及节日倒计时

首页侧边栏日历及节日倒计时

image-20251021143003155

目录

[toc]

背景

Hyde大佬自动和女友爬山回来,灵感陡增,产出干活满满。这不,我就可以马上抄作业了。🤣

版权

次功能来自《Hyde》大佬,感谢大佬。❤️❤️❤️

环境

Teek@1.5.1-2025.10.19版本

Teek-one开箱即用版仓库:https://cnb.cool/onedayxyy/vitepress-theme-teek-one-public

配置

(1)创建2个文件

docs\.vitepress\theme\components\CalendarCard.vue

docs\.vitepress\theme\components\ScheduleCard.vue

vue
<template>
    <TkPageCard>
      <div class="card-widget" id="card-widget-calendar">
        <div class="item-headline">
          <i class="icon-calendar"></i>
        </div>
        <div class="item-content">
          <div id="calendar-area-left">
            <div id="calendar-week">
              第{{ weekNumber }}周&nbsp;{{ weekDays[today.getDay()] }}
            </div>
            <div id="calendar-date">{{ today.getDate() }}</div>
            <div id="calendar-solar">
              {{ today.getFullYear() }}年{{ today.getMonth() + 1 }}月第{{
                dayOfYear
              }}天
            </div>
            <div id="calendar-lunar">
              {{ lunarYear }}&nbsp;{{ lunarMonth }}&nbsp;{{ lunarDay }}
            </div>
          </div>
          <div id="calendar-area-right">
            <div id="calendar-main">
              <!-- 星期标题行 -->
              <div class="calendar-r0">
                <div class="calendar-d0"><a>日</a></div>
                <div class="calendar-d1"><a>一</a></div>
                <div class="calendar-d2"><a>二</a></div>
                <div class="calendar-d3"><a>三</a></div>
                <div class="calendar-d4"><a>四</a></div>
                <div class="calendar-d5"><a>五</a></div>
                <div class="calendar-d6"><a>六</a></div>
              </div>
  
              <!-- 日期行 -->
              <div
                v-for="(week, weekIndex) in calendarWeeks"
                :key="weekIndex"
                :class="`calendar-r${weekIndex + 1}`"
              >
                <div
                  v-for="(day, dayIndex) in week"
                  :key="dayIndex"
                  :class="`calendar-d${dayIndex}`"
                >
                  <a
                    :class="{ now: day.isToday, 'other-month': day.isOtherMonth }"
                    v-if="day.date"
                  >
                    {{ day.date }}
                  </a>
                  <a v-else></a>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </TkPageCard>
  </template>
  
  <script setup>
  import { TkPageCard } from "vitepress-theme-teek";
  import { ref, onMounted, computed } from "vue";
  
  // 星期几中文映射
  const weekDays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
  
  // 当前日期
  const today = ref(new Date());
  
  // 生成当前月份的日历数据
  const calendarWeeks = computed(() => {
    const year = today.value.getFullYear();
    const month = today.value.getMonth();
  
    // 当月第一天
    const firstDay = new Date(year, month, 1);
    // 当月最后一天
    const lastDay = new Date(year, month + 1, 0);
  
    // 日历需要显示的第一天(可能是上月的日期)
    const startDay = new Date(firstDay);
    startDay.setDate(firstDay.getDate() - firstDay.getDay());
  
    // 日历需要显示的最后一天(可能是下月的日期)
    const endDay = new Date(lastDay);
    if (endDay.getDay() < 6) {
      endDay.setDate(lastDay.getDate() + (6 - endDay.getDay()));
    }
  
    // 生成日历数据
    const weeks = [];
    let currentDay = new Date(startDay);
  
    while (currentDay <= endDay) {
      const week = [];
      for (let i = 0; i < 7; i++) {
        const date = currentDay.getDate();
        const isToday = currentDay.toDateString() === today.value.toDateString();
        const isOtherMonth = currentDay.getMonth() !== month;
  
        week.push({ date, isToday, isOtherMonth });
  
        currentDay.setDate(currentDay.getDate() + 1);
      }
      weeks.push(week);
    }
  
    return weeks;
  });
  
  // 计算当前是今年的第几天
  const dayOfYear = computed(() => {
    const start = new Date(today.value.getFullYear(), 0, 0);
    const diff = today.value - start;
    const oneDay = 1000 * 60 * 60 * 24;
    return Math.floor(diff / oneDay);
  });
  
  // 计算当前是第几周
  const weekNumber = computed(() => {
    const firstDay = new Date(today.value.getFullYear(), 0, 1);
    const pastDaysOfYear = (today.value - firstDay) / 86400000;
    return Math.ceil((pastDaysOfYear + firstDay.getDay() + 1) / 7);
  });
  
  // 农历转换相关
  const lunarInfo = [
    0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0,
    0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,
    0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,
    0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566, 0x0d4a0,
    0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2,
    0x0a950, 0x0b557, 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573,
    0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4,
    0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5,
    0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46,
    0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58,
    0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960, 0x0d954, 0x0d4a0, 0x0da50,
    0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0,
    0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260,
    0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,
    0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2, 0x049b0,
    0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0,
  ];
  
  const gan = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
  const zhi = [
    "子",
    "丑",
    "寅",
    "卯",
    "辰",
    "巳",
    "午",
    "未",
    "申",
    "酉",
    "戌",
    "亥",
  ];
  const animals = [
    "鼠",
    "牛",
    "虎",
    "兔",
    "龙",
    "蛇",
    "马",
    "羊",
    "猴",
    "鸡",
    "狗",
    "猪",
  ];
  const lunarMonths = [
    "正月",
    "二月",
    "三月",
    "四月",
    "五月",
    "六月",
    "七月",
    "八月",
    "九月",
    "十月",
    "十一月",
    "十二月",
  ];
  const lunarDays = [
    "初一",
    "初二",
    "初三",
    "初四",
    "初五",
    "初六",
    "初七",
    "初八",
    "初九",
    "初十",
    "十一",
    "十二",
    "十三",
    "十四",
    "十五",
    "十六",
    "十七",
    "十八",
    "十九",
    "二十",
    "廿一",
    "廿二",
    "廿三",
    "廿四",
    "廿五",
    "廿六",
    "廿七",
    "廿八",
    "廿九",
    "三十",
  ];
  
  // 转换为农历
  const getLunarDate = (date) => {
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    const day = date.getDate();
  
    let springStart = new Date(2000, 1, 4); // 2000年春节是2月4日
    if (year > 2000) {
      // 简单计算春节日期,实际应用中可能需要更精确的算法
      springStart = new Date(year, 1, 4 + Math.floor((year - 2000) * 0.2422));
    }
  
    let lunarYear = year;
    let isLeap = false;
    let lunarMonthIdx = 0;
    let lunarDayIdx = 0;
  
    // 简化的农历计算,实际应用可能需要更复杂的算法
    const offset = Math.floor((date - new Date(year, 0, 0)) / 86400000);
    let days = 0;
    let i = 0;
  
    for (; i < 12; i++) {
      const monthDays = (lunarInfo[year - 1900] >> (12 - i)) & 0x1 ? 30 : 29;
      if (days + monthDays >= offset) {
        lunarDayIdx = offset - days - 1;
        break;
      }
      days += monthDays;
    }
    lunarMonthIdx = i;
  
    // 计算农历年的干支和生肖
    const ganIndex = (year - 3) % 10;
    const zhiIndex = (year - 3) % 12;
    const lunarYearStr = `${gan[ganIndex]}${zhi[zhiIndex]}${animals[zhiIndex]}年`;
  
    return {
      lunarYear: lunarYearStr,
      lunarMonth: lunarMonths[lunarMonthIdx],
      lunarDay: lunarDays[lunarDayIdx],
    };
  };
  
  // 响应式农历数据
  const lunarDate = computed(() => getLunarDate(today.value));
  const lunarYear = computed(() => lunarDate.value.lunarYear);
  const lunarMonth = computed(() => lunarDate.value.lunarMonth);
  const lunarDay = computed(() => lunarDate.value.lunarDay);
  
  // 每天更新一次日历
  onMounted(() => {
    // 检查是否需要更新(跨天)
    const checkUpdate = () => {
      const now = new Date();
      if (now.toDateString() !== today.value.toDateString()) {
        today.value = now;
      }
    };
  
    // 每分钟检查一次
    setInterval(checkUpdate, 60000);
  });
  </script>
  
  <style scoped>
  /* 基础样式重置 */
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Microsoft YaHei", sans-serif;
  }
  
  body {
    background-color: #f5f7fa;
    padding: 20px;
  }
  
  :root {
    --other-month-bg: #fa0000;
  }
  
  .dark {
    --other-month-bg: #0080ff;
  }
  
  .tk-page-card {
    margin-top: 10px;
  }
  
  .card-widget {
    max-height: calc(100vh - 100px);
    position: relative;
  }
  
  #card-widget-calendar .item-headline {
    padding-bottom: 0;
    margin-left: 8px;
    font-size: 1em;
    font-weight: 700;
    display: flex;
    align-items: center;
    gap: 5px;
    margin-bottom: 10px;
  }
  
  #card-widget-calendar .item-headline i {
    font-size: 18px;
  }
  
  #card-widget-calendar .item-content {
    display: flex;
  }
  
  #calendar-area-left,
  #calendar-area-right {
    height: 100%;
    padding: 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  
  #calendar-area-left {
    width: 45%;
  }
  
  #calendar-week {
    height: 1.2rem;
    font-size: 14px;
    letter-spacing: 1px;
    font-weight: 700;
    align-items: center;
    display: flex;
  }
  
  #calendar-date {
    height: 3rem;
    line-height: 1.3;
    font-size: 36px;
    letter-spacing: 3px;
    color: var(--vp-c-brand-1);
    font-weight: 700;
    align-items: center;
    display: flex;
    position: absolute;
    top: calc(50% - 2.1rem);
  }
  
  #calendar-solar {
    bottom: 2.1rem;
  }
  
  #calendar-lunar,
  #calendar-solar {
    height: 1rem;
    font-size: 11px;
    align-items: center;
    display: flex;
    position: absolute;
  }
  
  #calendar-lunar {
    bottom: 1rem;
  }
  
  #calendar-area-right {
    width: 55%;
  }
  
  #calendar-main {
    width: 100%;
  }
  
  .calendar-r0,
  .calendar-r1,
  .calendar-r2,
  .calendar-r3,
  .calendar-r4,
  .calendar-r5,
  .calendar-rh {
    height: 1.2rem;
    display: flex;
  }
  
  .calendar-d0,
  .calendar-d1,
  .calendar-d2,
  .calendar-d3,
  .calendar-d4,
  .calendar-d5,
  .calendar-d6 {
    width: calc(100% / 7);
    display: flex;
    justify-content: center;
    align-items: center;
  }
  
  #calendar-main a {
    height: 1.2rem;
    width: 1.5rem;
    border-radius: 50%;
    font-size: 12px;
    line-height: 12px;
    display: flex;
    justify-content: center;
    align-items: center;
    text-decoration: none;
    color: var(--calendar-main-a-clolr);
  }
  
  #calendar-main a.now {
    background: var(--vp-c-brand-1);
    color: #fff;
  }
  
  #calendar-main a.other-month {
    color: var(--other-month-clolr);
  }
  </style>
vue
<template>
  <TkPageCard>
    <div class="card-widget" id="card-widget-schedule">
      <div class="item-headline">
        <i></i>
      </div>
      <div class="item-content">
        <div id="schedule-area-right">
          <div class="schedule-r0">
            <!-- <div class="schedule-d0">本年</div> -->
            <div class="schedule-d1">
              <span id="p_span_year" class="aside-span1"
                >{{ yearProgress }}%</span
              >
              <div class="progress-container">
                <span class="aside-span2"
                  >本年还剩<a id="year_days_left">{{ yearDaysLeft }}</a
                  >天</span
                >
                <progress
                  max="365"
                  id="pBar_year"
                  :value="yearPassedDays"
                ></progress>
              </div>
            </div>
          </div>
          <div class="schedule-r1">
            <!-- <div class="schedule-d0">本月</div> -->
            <div class="schedule-d1">
              <span id="p_span_month" class="aside-span1"
                >{{ monthProgress }}%</span
              >
              <div class="progress-container">
                <span class="aside-span2"
                  >本月还剩<a id="month_days_left">{{ monthDaysLeft }}</a
                  >天</span
                >
                <progress
                  max="31"
                  id="pBar_month"
                  :value="monthPassedDays"
                ></progress>
              </div>
            </div>
          </div>
          <div class="schedule-r2">
            <!-- <div class="schedule-d0">本周</div> -->
            <div class="schedule-d1">
              <span id="p_span_week" class="aside-span1"
                >{{ weekProgress }}%</span
              >
              <div class="progress-container">
                <span class="aside-span2"
                  >本周还剩<a id="week_days_left">{{ weekDaysLeft }}</a
                  >天</span
                >
                <progress
                  max="7"
                  id="pBar_week"
                  :value="weekDisplayValue"
                ></progress>
              </div>
            </div>
          </div>
        </div>
        <div id="schedule-area-left">
          <div id="schedule-title">距离春节</div>
          <div id="schedule-days">{{ springFestivalDays }}</div>
          <div id="schedule-date">{{ springFestivalDateText }}</div>
        </div>
      </div>
    </div>
  </TkPageCard>
</template>

<script setup>
import { TkPageCard } from "vitepress-theme-teek";
import { ref, onMounted } from "vue";

// 响应式变量
const springFestivalDays = ref("--");
const springFestivalDateText = ref("--"); // 动态春节日期文本
const yearProgress = ref("--");
const yearDaysLeft = ref("--");
const yearPassedDays = ref(0);
const monthProgress = ref("--");
const monthDaysLeft = ref("--");
const monthPassedDays = ref(0);
const weekProgress = ref("--");
const weekDaysLeft = ref("--");
const weekDisplayValue = ref(0);

/**
 * 计算指定年份春节(农历正月初一)的公历日期(1900-2100年适用)
 * @param {number} lunarYear - 农历年份
 * @returns {Date} 春节的公历日期
 */
const getSpringFestivalDate = (lunarYear) => {
  // 1900-2100年春节公历日期表([月, 日]),已精准修正2026年(索引126)为[2,17]
  const springFestivalData = [
    [2, 19], [2, 8], [1, 28], [2, 16], [2, 5], [1, 25], [2, 13], [2, 2], [1, 22], [2, 10], // 1900-1909 (0-9)
    [1, 30], [2, 18], [2, 7], [1, 26], [2, 14], [2, 3], [1, 23], [2, 11], [1, 31], [2, 19], // 1910-1919 (10-19)
    [2, 8], [1, 28], [2, 16], [2, 5], [1, 24], [2, 12], [2, 1], [1, 21], [2, 9], [1, 28],  // 1920-1929 (20-29)
    [2, 16], [2, 5], [1, 24], [2, 12], [2, 1], [1, 21], [2, 9], [1, 29], [2, 17], [2, 6],  // 1930-1939 (30-39)
    [1, 26], [2, 14], [2, 2], [1, 22], [2, 10], [1, 29], [2, 17], [2, 6], [1, 26], [2, 13], // 1940-1949 (40-49)
    [2, 2], [1, 22], [2, 10], [1, 30], [2, 17], [2, 6], [1, 25], [2, 13], [2, 1], [1, 21], // 1950-1959 (50-59)
    [2, 8], [1, 28], [2, 15], [2, 5], [1, 24], [2, 12], [1, 31], [2, 18], [2, 7], [1, 27], // 1960-1969 (60-69)
    [2, 15], [2, 3], [1, 23], [2, 11], [1, 31], [2, 18], [2, 6], [1, 26], [2, 14], [2, 3], // 1970-1979 (70-79)
    [1, 23], [2, 10], [1, 29], [2, 16], [2, 5], [1, 24], [2, 12], [2, 1], [1, 22], [2, 9], // 1980-1989 (80-89)
    [1, 28], [2, 15], [2, 4], [1, 23], [2, 10], [1, 30], [2, 17], [2, 6], [1, 26], [2, 14], // 1990-1999 (90-99)
    [2, 2], [1, 22], [2, 10], [1, 29], [2, 17], [2, 5], [1, 24], [2, 12], [1, 31], [2, 18], // 2000-2009 (100-109)
    [2, 7], [1, 26], [2, 14], [2, 3], [1, 23], [2, 10], [1, 31], [2, 18], [2, 7], [1, 26], // 2010-2019 (110-119)
    [2, 12], [2, 1], [1, 22], [2, 10], [1, 29], [2, 17], [2, 17], [1, 24], [2, 12], [2, 1], // 2020-2029 (120-129) 
    // 索引126对应2026年,已明确设置为[2,17]
    [1, 22], [2, 10], [1, 29], [2, 17], [2, 6], [1, 26], [2, 14], [2, 3], [1, 23], [2, 10], // 2030-2039 (130-139)
    [1, 30], [2, 17], [2, 6], [1, 26], [2, 13], [2, 2], [1, 22], [2, 10], [1, 29], [2, 17], // 2040-2049 (140-149)
    [2, 5], [1, 25], [2, 13], [2, 1], [1, 21], [2, 9], [1, 28], [2, 16], [2, 5], [1, 24], // 2050-2059 (150-159)
    [2, 12], [2, 1], [1, 21], [2, 9], [1, 28], [2, 15], [2, 4], [1, 24], [2, 11], [1, 31], // 2060-2069 (160-169)
    [2, 18], [2, 7], [1, 27], [2, 15], [2, 3], [1, 23], [2, 11], [1, 31], [2, 18], [2, 6], // 2070-2079 (170-179)
    [1, 26], [2, 14], [2, 3], [1, 23], [2, 10], [1, 29], [2, 16], [2, 5], [1, 24], [2, 12], // 2080-2089 (180-189)
    [2, 1], [1, 22], [2, 9], [1, 28], [2, 15], [2, 4], [1, 23], [2, 10], [1, 30], [2, 17], // 2090-2099 (190-199)
    [2, 5] // 2100年 (200)
  ];

  const index = lunarYear - 1900;
  if (index >= 0 && index < springFestivalData.length) {
    const [month, day] = springFestivalData[index];
    return new Date(lunarYear, month - 1, day); // 月份在Date中为0基,需减1
  }
  return new Date(lunarYear, 1, 1); // 超出范围默认2月1日
};

/**
 * 动态获取目标春节日期(今年或明年)
 * @returns {Date} 目标春节日期
 */
const getTargetSpringFestival = () => {
  const today = new Date();
  const currentYear = today.getFullYear();
  const currentYearSpring = getSpringFestivalDate(currentYear);
  // 若今天在今年春节前,目标为今年春节;否则为明年春节
  return today < currentYearSpring ? currentYearSpring : getSpringFestivalDate(currentYear + 1);
};

/**
 * 计算两个日期的天数差
 * @param {Date} date1 - 起始日期
 * @param {Date} date2 - 目标日期
 * @returns {number} 天数差(向上取整)
 */
const getDaysDifference = (date1, date2) => {
  const oneDay = 24 * 60 * 60 * 1000;
  const diffTime = date2 - date1;
  return Math.ceil(diffTime / oneDay);
};

/**
 * 更新春节倒计时及日期显示
 */
const updateSpringFestivalCountdown = () => {
  const today = new Date();
  today.setHours(0, 0, 0, 0); // 忽略时分秒,按整天计算
  const targetSpring = getTargetSpringFestival();
  // 计算剩余天数
  springFestivalDays.value = getDaysDifference(today, targetSpring);
  // 格式化日期为YYYY-MM-DD
  const year = targetSpring.getFullYear();
  const month = String(targetSpring.getMonth() + 1).padStart(2, '0');
  const day = String(targetSpring.getDate()).padStart(2, '0');
  springFestivalDateText.value = `${year}-${month}-${day}`;
};

/**
 * 更新年度进度
 */
const updateYearProgress = () => {
  const today = new Date();
  const year = today.getFullYear();
  const startOfYear = new Date(year, 0, 1);
  const endOfYear = new Date(year, 11, 31);
  const totalDays = getDaysDifference(startOfYear, endOfYear) + 1;
  const daysPassed = getDaysDifference(startOfYear, today);
  const daysLeft = totalDays - daysPassed;
  const progress = (daysPassed / totalDays) * 100;

  yearDaysLeft.value = daysLeft;
  yearProgress.value = progress.toFixed(1);
  yearPassedDays.value = daysPassed;
};

/**
 * 更新月度进度
 */
const updateMonthProgress = () => {
  const today = new Date();
  const year = today.getFullYear();
  const month = today.getMonth();
  const startOfMonth = new Date(year, month, 1);
  const endOfMonth = new Date(year, month + 1, 0);
  const totalDays = getDaysDifference(startOfMonth, endOfMonth) + 1;
  const daysPassed = today.getDate() - 1; // 当月已过天数(不含今天)
  const daysLeft = totalDays - daysPassed - 1; // 剩余天数(含今天)
  const progress = (daysPassed / totalDays) * 100;

  monthDaysLeft.value = daysLeft;
  monthProgress.value = progress.toFixed(1);
  monthPassedDays.value = daysPassed;
};

/**
 * 更新本周进度(已修复0%问题)
 */
const updateWeekProgress = () => {
  const today = new Date();
  let dayOfWeek = today.getDay() || 7; // 周日转为7,周一到周六为1-6
  const daysPassed = dayOfWeek; // 已过天数(含今天:周一=1,周日=7)
  const daysLeft = 7 - dayOfWeek; // 剩余天数

  // 避免进度条完全消失(最小0.1)
  const displayValue = daysPassed === 0 ? 0.1 : daysPassed;

  weekDaysLeft.value = daysLeft;
  weekProgress.value = ((daysPassed / 7) * 100).toFixed(1);
  weekDisplayValue.value = displayValue;
};

/**
 * 统一更新所有数据
 */
const updateAllData = () => {
  updateSpringFestivalCountdown();
  updateYearProgress();
  updateMonthProgress();
  updateWeekProgress();
};

// 页面加载时初始化,并设置每天凌晨更新
onMounted(() => {
  updateAllData();

  // 计算距离次日凌晨0:00:01的毫秒数,设置定时器每天更新
  const now = new Date();
  const tomorrow = new Date(now);
  tomorrow.setDate(now.getDate() + 1);
  tomorrow.setHours(0, 0, 1, 0);
  const timeToUpdate = tomorrow - now;

  setInterval(updateAllData, timeToUpdate);
});
</script>

<style scoped>
.tk-page-card {
  margin-top: 10px;
}

/* 进度条样式 */
progress {
  width: 100%;
  height: 10px;
  border-radius: 5px;
  background-color: #f0f0f0;
  border: none;
}
progress::-webkit-progress-bar {
  background-color: #f0f0f0;
  border-radius: 5px;
}
progress::-webkit-progress-value {
  background-color: var(--vp-c-brand-1);
  border-radius: 5px;
  transition: width 0.3s ease;
}

/* 基础样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Microsoft YaHei", sans-serif;
}

.item-headline span {
  font-size: 18px;
  font-weight: 600;
  color: #333333;
}

/* 内容区域样式 */
.item-content {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

/* 左侧春节倒计时样式 */
#schedule-area-left {
  flex: 1;
  min-width: 200px;
  border-radius: 8px;
  padding: 20px;
  text-align: center;
  border: 1px solid var(--vp-c-brand-1);
}
#schedule-title {
  font-size: 16px;
  color: var(--vp-c-brand-1);
  margin-bottom: 10px;
}
#schedule-days {
  font-size: 48px;
  font-weight: 700;
  color: var(--vp-c-brand-1);
  margin: 10px 0;
}
#schedule-date {
  font-size: 14px;
  color: #666666;
}

/* 右侧进度区域样式 */
#schedule-area-right {
  flex: 2;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.schedule-r0,
.schedule-r1,
.schedule-r2 {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.schedule-d0 {
  font-size: 14px;
  color: #333333;
  font-weight: 500;
}
.schedule-d1 {
  display: flex;
  align-items: center;
  gap: 12px;
}
.progress-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 2;
}
.aside-span1 {
  font-size: 14px;
  color: var(--vp-c-brand-1);
  font-weight: 600;
  width: 60px;
  display: inline-block;
}
.aside-span2 {
  font-size: 13px;
  color: #666666;
  flex: 1;
}
.aside-span2 a {
  color: var(--vp-c-brand-1);
  text-decoration: none;
  font-weight: 600;
}

/* 响应式样式 */
@media (max-width: 480px) {
  .item-content {
    flex-direction: column;
  }
  #schedule-days {
    font-size: 36px;
  }
}
</style>

(2)引入

编辑docs\.vitepress\theme\components\TeekLayoutProvider.vue文件:

vue
import CalendarCard from "./CalendarCard.vue"; //日历卡片
import ScheduleCard from "./ScheduleCard.vue"; //节日倒计时卡片

    <!-- 自定义公告卡片 公告-->
    <template #teek-home-card-top-article-before>
      <!-- <WelcomeCard /> -->
      <NoticeCard />
      <CalendarCard />
      <ScheduleCard />
    </template>

具体代码可见如下源码:https://cnb.cool/onedayxyy/vitepress-theme-teek-one-public/-/blob/main/docs/.vitepress/theme/components/TeekLayoutProvider.vue

(3)验证

image-20251021142900347

结束。

最近更新