Este guia demonstra como construir um componente de calendário interativo utilizando Vue.js e a biblioteca de compnoentes Element UI. Abordaremos a configuração, a integração de funcionalidades de calendário lunar e a exibição de feriados.
Instalação de Dependências
Para começar, instale as bibliotecas necessárias para manipulação de datas e calendário lunar.
npm install moment@2.29.4 --save
npm install lunar-javascript@1.6.7 --save
Definição de Feriados Mundiais
Crie um arquivo, por exemplo, utils/holidays.js, para armazenar uma lista de feriados internacionais. Esta lista servirá como base para a exibição de datas comemorativas.
export const worldHolidays = [
{ month: 1, day: 1, name: 'Ano Novo' },
{ month: 2, day: 14, name: 'Dia dos Namorados' },
{ month: 3, day: 8, name: 'Dia Internacional da Mulher' },
{ month: 4, day: 1, name: 'Dia da Mentira' },
{ month: 4, day: 22, name: 'Dia da Terra' },
{ month: 5, day: 1, name: 'Dia do Trabalhador' },
{ month: 12, day: 25, name: 'Natal' },
];
Utilitários de Formatação de Data
Adicione funções auxiliares ao seu arquivo de utilitários (ex: utils/dateUtils.js) para facilitar a formatação de datas e a conversão de timestamps.
// Adiciona um método de formatação ao protótipo Date
Date.prototype.formatDate = function (formatString) {
const dateParts = {
'M+': this.getMonth() + 1,
'd+': this.getDate(),
'h+': this.getHours(),
'm+': this.getMinutes(),
's+': this.getSeconds(),
'q+': Math.floor((this.getMonth() + 3) / 3),
'S': this.getMilliseconds(),
};
if (/(y+)/.test(formatString)) {
formatString = formatString.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (let key in dateParts) {
if (new RegExp('(' + key + ')').test(formatString)) {
const replacement = RegExp.$1.length === 1
? dateParts[key]
: ('00' + dateParts[key]).substr(('' + dateParts[key]).length);
formatString = formatString.replace(RegExp.$1, replacement);
}
}
return formatString;
};
// Função para converter timestamp em objeto Date
Date.transferTimestamp = function (timestamp) {
if (!timestamp) {
// Retornar um valor padrão ou lançar um erro se o timestamp for inválido
return null;
}
return new Date(timestamp);
};
Componente de Calendário
Implemente o componente Vue, utilizando o el-calendar do Element UI e integrando as funções de calendário lunar e feriaods.
<template>
<div class="calendar-container">
<section>
<div class="navigation-buttons">
<el-button @click="previousYear" size="mini">Ano Anterior</el-button>
<el-button @click="nextYear" size="mini">Próximo Ano</el-button>
</div>
<el-calendar v-model="currentDate" ref="calendar">
<template #dateCell="{ date, data }">
<div class="cell-content">
<div class="month-day">
{{ Date.transferTimestamp(data.day).formatDate("MM-dd") }}
</div>
<div class="lunar-date">{{ formatLunarDate(date) }}</div>
<div class="solar-term" v-html="getSolarTerm(getLunarYearMonthDay('y', date), getLunarYearMonthDay('m', date), getLunarYearMonthDay('d', date))"></div>
<div class="traditional-holiday">{{ getTraditionalHoliday(parseInt(data.day.split("-")[1]), parseInt(data.day.split("-")[2])) }}</div>
</div>
<div>{{ getOtherFestivals(getLunarYearMonthDay("y", date), getLunarYearMonthDay("m", date), getLunarYearMonthDay("d", date)) }}</div>
</template>
</el-calendar>
</section>
</div>
</template>
<script>
import moment from "moment"
import lunar from "lunar-javascript"
import { worldHolidays } from "@/utils/holidays" // Ajuste o caminho conforme necessário
export default {
name: "CustomCalendar",
data() {
return {
currentDate: null,
displayDate: moment(new Date()),
}
},
methods: {
previousYear() {
this.navigateYear("-");
},
nextYear() {
this.navigateYear("+");
},
navigateYear(direction) {
let targetYear = this.displayDate.year() + (direction === "+" ? 1 : -1);
let newDate = moment(`${targetYear}-${moment().month() + 1}-${moment().date()}`);
this.currentDate = newDate.toDate();
this.displayDate = newDate;
},
convertMomentToDate(momentObj) {
return momentObj.toDate();
},
synchronizeDisplayDate(dateObj) {
this.displayDate = moment(dateObj);
},
formatLunarDate(solarDate) {
// Retorna apenas a parte do mês e dia do calendário lunar
return lunar.Solar.fromDate(solarDate).getLunar().toString().split("年")[1];
},
getTraditionalFestival(month, day) {
const festival = worldHolidays.find(f => f.month === month && f.day === day);
return festival ? festival.name : "";
},
getSolarTerm(year, month, day) {
const lunarDate = lunar.Lunar.fromYmd(year, month, day);
const solarTerm = lunarDate.getJieQi();
if (solarTerm) {
const termTime = lunarDate.getJieQiTable()[solarTerm].toYmdHms().match(/\d\d:\d\d:\d\d/)[0];
return `<span style="font-family: 'zkxw'">${solarTerm}</span> ${termTime}`;
}
return "";
},
getLunarYearMonthDay(type, date) {
const lunarDate = lunar.Lunar.fromDate(date);
switch (type) {
case "y": return lunarDate.getYear();
case "m": return lunarDate.getMonth();
case "d": return lunarDate.getDay();
default: return null;
}
},
getOtherFestivals(year, month, day) {
const lunarDate = lunar.Lunar.fromYmd(year, month, day);
const festivals = lunarDate.getFestivals();
return festivals.length > 0 ? festivals[0] : "";
},
getTraditionalHolidaysFromLunar(year, month, day) {
const lunarDate = lunar.Lunar.fromYmd(year, month, day);
const holiday = lunarDate.getOtherFestivals();
return holiday.length > 0 ? holiday[0] : "";
}
},
mounted() {
this.currentDate = this.convertMomentToDate(this.displayDate);
},
}
</script>
<style lang="less" scoped>
.calendar-container {
width: 100%;
height: 100%;
font-family: "lgq";
position: relative;
font-size: 0.8rem; // Ajuste o tamanho base conforme necessário
.navigation-buttons {
position: absolute;
top: 1rem;
right: 5rem;
z-index: 10;
.el-button + .el-button {
margin-left: 0;
}
}
::v-deep .el-button {
border: none;
&:hover {
color: #409eff;
background-color: #dae6f5;
}
}
::v-deep .el-calendar-table .el-calendar-day {
display: flex;
align-items: center;
justify-content: center;
min-height: 80px; // Ajustar altura mínima para acomodar conteúdo
}
::v-deep .el-calendar__body {
padding: 0;
}
::v-deep .el-calendar-table td.is-today {
color: #409eff;
}
.cell-content {
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
.month-day {
font-size: 0.7rem;
font-weight: bold;
margin-bottom: 0.2rem;
border-radius: 0.2rem;
padding: 0.1rem 0.3rem;
}
.lunar-date {
font-size: 0.6rem;
margin-bottom: 0.1rem;
}
.solar-term {
color: blue;
font-size: 0.5rem;
}
.traditional-holiday {
color: red;
font-family: "dyh"; // Certifique-se que esta fonte está carregada
font-size: 0.7rem;
}
}
}
/* Adaptação para telas menores (Mobile) */
@media screen and (max-width: 768px) {
.calendar-container {
font-size: 0.7rem;
.navigation-buttons {
top: 0.5rem;
right: 1rem;
}
::v-deep .el-calendar-table .el-calendar-day {
min-height: 60px;
}
.cell-content {
.month-day {
font-size: 0.6rem;
}
.lunar-date {
font-size: 0.5rem;
}
.solar-term {
font-size: 0.4rem;
}
.traditional-holiday {
font-size: 0.6rem;
}
}
}
}
</style>