Browse Source

新增日历菜单和显示

pm 3 months ago
parent
commit
199ab99d6f
1 changed files with 270 additions and 0 deletions
  1. 270 0
      src/views/calendar/calendarView/index.vue

+ 270 - 0
src/views/calendar/calendarView/index.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="holiday-calendar">
+    <ContentWrap>
+      <div class="controls">
+        <el-select v-model="selectedYear" placeholder="选择年份" @change="handleYearChange">
+          <el-option
+            v-for="year in yearOptions"
+            :key="year"
+            :label="year"
+            :value="year"
+          />
+        </el-select>
+        <el-checkbox-group v-model="showOptions" class="display-options">
+          <el-checkbox label="weekend">显示周末</el-checkbox>
+          <el-checkbox label="legal">显示法定假日</el-checkbox>
+          <el-checkbox label="holiday">显示节假日</el-checkbox>
+          <el-checkbox label="workday">显示调休上班</el-checkbox>
+        </el-checkbox-group>
+      </div>
+    </ContentWrap>
+
+    <div class="calendar-wrapper">
+      <el-calendar v-model="selectedDate">
+        <template #date-cell="{ data }">
+          <div class="calendar-day" :class="getDayClasses(data.day)">
+            <div class="day-number">
+              {{ getDayNumber(data.day) }}
+              <!-- 如果是正常工作日显示圆圈“班” -->
+              <span v-if="isNormalWorkday(data.day)" class="work-circle">班</span>
+            </div>
+            <div class="day-info">
+              <div class="holiday-name" v-if="getHolidayName(data.day)">
+                {{ getHolidayName(data.day) }}
+              </div>
+              <template v-if="showOptions.includes('weekend') && isWeekend(data.day)">
+                <el-tag size="small" effect="plain" type="info">周末</el-tag>
+              </template>
+              <template v-if="showOptions.includes('legal') && isLegalHoliday(data.day)">
+                <el-tag size="small" effect="plain" type="danger">法定假日</el-tag>
+              </template>
+              <template v-if="showOptions.includes('holiday') && isHoliday(data.day)">
+                <el-tag size="small" effect="plain" type="success">节假日</el-tag>
+              </template>
+              <template v-if="showOptions.includes('workday') && isWorkday(data.day)">
+                <el-tag size="small" effect="plain" type="warning">调休上班</el-tag>
+              </template>
+            </div>
+          </div>
+        </template>
+      </el-calendar>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted, watch } from 'vue';
+import axios from 'axios';
+
+// 当前年
+const currentYear = new Date().getFullYear();
+const selectedYear = ref(currentYear);
+const selectedDate = ref(new Date());
+const holidayData = ref({});
+const showOptions = ref(['weekend', 'legal', 'holiday', 'workday']);
+
+// 年份选项
+const yearOptions = computed(() => {
+  const years = [];
+  for (let year = currentYear; year <= 2050; year++) {
+    years.push(year);
+  }
+  return years;
+});
+
+// 拉节假日接口,失败则用默认
+const fetchHolidayData = async (year) => {
+  try {
+    const res = await axios.get(`https://fastly.jsdelivr.net/gh/NateScarlet/holiday-cn@master/${year}.json`);
+    const days = res.data?.days || [];
+    if (Array.isArray(days) && days.length > 0) {
+      const dayMap = {};
+      days.forEach(item => {
+        dayMap[item.date] = {
+          name: item.name,
+          isOffDay: item.isOffDay,
+          isLegal: item.isOffDay // 简单视为法定
+        };
+      });
+      holidayData.value[year] = dayMap;
+    } else {
+      console.warn(`无 ${year} 节假日数据,用默认`);
+      holidayData.value[year] = generateDefaultHolidays(year);
+    }
+  } catch (err) {
+    console.error(`获取 ${year} 节假日失败,使用默认`, err);
+    holidayData.value[year] = generateDefaultHolidays(year);
+  }
+};
+
+// 默认假期
+const generateDefaultHolidays = (year) => {
+  const holidays = {};
+  holidays[`${year}-01-01`] = { name: '元旦', isOffDay: true, isLegal: true };
+  for (let i = 0; i < 7; i++) {
+    const date = new Date(year, 0, 22 + i);
+    holidays[formatDateKey(date)] = { name: '春节', isOffDay: true, isLegal: i < 3 };
+  }
+  holidays[`${year}-04-04`] = { name: '清明节', isOffDay: true, isLegal: true };
+  for (let i = 0; i < 3; i++) {
+    holidays[`${year}-05-0${1 + i}`] = { name: '劳动节', isOffDay: true, isLegal: i === 0 };
+  }
+  holidays[`${year}-09-29`] = { name: '中秋节', isOffDay: true, isLegal: true };
+  for (let i = 1; i <= 7; i++) {
+    holidays[`${year}-10-${i < 10 ? '0' + i : i}`] = {
+      name: '国庆节',
+      isOffDay: true,
+      isLegal: i <= 3
+    };
+  }
+  return holidays;
+};
+
+// 工具
+const formatDateKey = (date) => {
+  const y = date.getFullYear();
+  const m = date.getMonth() + 1;
+  const d = date.getDate();
+  return `${y}-${m < 10 ? '0' + m : m}-${d < 10 ? '0' + d : d}`;
+};
+
+const isWeekend = (dateStr) => {
+  const d = new Date(dateStr).getDay();
+  return d === 0 || d === 6;
+};
+const isLegalHoliday = (dateStr) => {
+  const k = formatDateKey(new Date(dateStr));
+  return holidayData.value[selectedYear.value]?.[k]?.isLegal || false;
+};
+const isHoliday = (dateStr) => {
+  const k = formatDateKey(new Date(dateStr));
+  return holidayData.value[selectedYear.value]?.[k]?.isOffDay || false;
+};
+const isWorkday = (dateStr) => {
+  const k = formatDateKey(new Date(dateStr));
+  const isWeekendDay = isWeekend(dateStr);
+  const isHolidayDay = holidayData.value[selectedYear.value]?.[k]?.isOffDay || false;
+  return isWeekendDay && !isHolidayDay;
+};
+// ✅ 新增:正常工作日
+const isNormalWorkday = (dateStr) => {
+  return (
+    !isWeekend(dateStr) &&
+    !isLegalHoliday(dateStr) &&
+    !isHoliday(dateStr)
+  );
+};
+
+const getDayClasses = (dateStr) => {
+  const c = [];
+  if (isWeekend(dateStr)) c.push('weekend');
+  if (isLegalHoliday(dateStr)) c.push('legal-holiday');
+  if (isHoliday(dateStr)) c.push('holiday');
+  if (isWorkday(dateStr)) c.push('workday');
+  return c;
+};
+const getDayNumber = (dateStr) => new Date(dateStr).getDate();
+const getHolidayName = (dateStr) => {
+  const k = formatDateKey(new Date(dateStr));
+  return holidayData.value[selectedYear.value]?.[k]?.name || '';
+};
+
+const handleYearChange = (year) => {
+  selectedDate.value = new Date(year, 0, 1);
+  if (!holidayData.value[year]) {
+    fetchHolidayData(year);
+  }
+};
+
+// 监听跨年
+watch(selectedDate, (newDate) => {
+  const newYear = newDate.getFullYear();
+  if (newYear !== selectedYear.value) {
+    selectedYear.value = newYear;
+    if (!holidayData.value[newYear]) {
+      fetchHolidayData(newYear);
+    }
+  }
+});
+
+onMounted(() => {
+  fetchHolidayData(currentYear);
+});
+</script>
+
+<style scoped>
+.holiday-calendar {
+  width: 100%;
+}
+
+.controls {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 20px;
+}
+
+.calendar-wrapper {
+  margin-top: 20px;
+}
+
+.el-calendar {
+  width: 100%;
+}
+
+.calendar-day {
+  height: 100%;
+  padding: 5px;
+  box-sizing: border-box;
+}
+
+.day-number {
+  font-weight: bold;
+  margin-bottom: 5px;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.work-circle {
+  display: inline-block;
+  background: #409EFF;
+  color: #fff;
+  border-radius: 50%;
+  font-size: 10px;
+  width: 18px;
+  height: 18px;
+  line-height: 18px;
+  text-align: center;
+}
+
+.day-info {
+  min-height: 20px;
+}
+
+.holiday-name {
+  font-size: 12px;
+  color: #f56c6c;
+  margin-bottom: 2px;
+}
+
+.el-tag {
+  margin: 2px;
+}
+
+.weekend {
+  background-color: #f0f9ff;
+}
+
+.legal-holiday {
+  background-color: #fff0f0;
+}
+
+.holiday {
+  background-color: #f0fff0;
+}
+
+.workday {
+  background-color: #fffaf0;
+}
+</style>