这篇文章主要介绍“怎么封装一个vue自定义日历组件”,在日常操作中,相信很多人在怎么封装一个vue自定义日历组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么封装一个vue自定义日历组件”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
获取到目标日期数据
获取到当前日期的各项重要属性,诸如当前年
,当前月
,当前日期
,当前星期几
,当前月一共有几天
,当前月的第一天对应的是星期几
,上个月总共有多少天
等。
根据这些属性,来生成具体的日历日期数据列表
,然后将其循环渲染到模板中。
当切换月份的时候,获取到新的目标日期对应的各项关键数据。vue检测到日历属性变化之后,通知页面进行更新。
一般来说,成熟的日历组件,日期都是一个双向绑定的变量。为了方便使用,我们也采用双向绑定的方式。
<script setup>
import { reactive, ref, computed, watch } from "vue";
const props = defineProps({
modelValue: Date,
});
const emits = defineEmits(["update:modelValue"]);
/**
* 最小年份
*/
const MIN_YEAR = 1900;
/**
* 最大年份
*/
const MAX_YEAR = 9999;
/**
* 目标日期
*/
const targetDate = ref(props.modelValue);
接下来,我们还需要初始化一些常量用来表示月份和日期:
/**
* 有关月度的名称列表
*/
const monthNameList = {
chineseFullName: [
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月",
],
fullName: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
mmm: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
};
/**
* 有关周几的名称列表
*/
const dayNameList = [
{
chineseFullName: "周日",
chineseShortName: "日",
fullName: "Sunday",
shortName: "Sun",
dayNumber: 0,
},
{
chineseFullName: "周一",
chineseShortName: "一",
fullName: "Monday",
shortName: "Mon",
dayNumber: 1,
},
{
chineseFullName: "周二",
chineseShortName: "二",
fullName: "Tuesday",
shortName: "Tue",
dayNumber: 2,
},
{
chineseFullName: "周三",
chineseShortName: "三",
fullName: "Wednesday",
shortName: "Wed",
dayNumber: 3,
},
{
chineseFullName: "周四",
chineseShortName: "四",
fullName: "Thursday",
shortName: "Thu",
dayNumber: 4,
},
{
chineseFullName: "周五",
chineseShortName: "五",
fullName: "Friday",
shortName: "Fri",
dayNumber: 5,
},
{
chineseFullName: "周六",
chineseShortName: "六",
fullName: "Saturday",
shortName: "Sat",
dayNumber: 6,
},
];
接下来,准备几个vue的响应式数据:
/**
* 今日
*/
const today = new Date();
/**
* 日历的各项属性
*/
const calendarProps = reactive({
target: {
year: null,
month: null,
date: null,
day: null,
monthShortName: null,
monthFullName: null,
monthChineseFullName: null,
firstDay: null,
firstDayIndex: null,
totalDays: null,
},
previous: {
totalDays: null,
},
});
/**
* 用于展现的日历数据
*/
const calendarData = ref([]);
接下来,通过setCalendarProps
方法获取日历的各个属性,逐个填充calendarProps
中的数据:
function setCalendarProps() {
if (!targetDate.value) {
targetDate.value = today;
}
// 获取目标日期的年月日星期几数据
calendarProps.target.year = targetDate.value.getFullYear();
calendarProps.target.month = targetDate.value.getMonth();
calendarProps.target.date = targetDate.value.getDate();
calendarProps.target.day = targetDate.value.getDay();
if (
calendarProps.target.year < MIN_YEAR ||
calendarProps.target.year > MAX_YEAR
) {
console.error("无效的年份,请检查传入的数据是否是正常");
return;
}
// 获取到目标日期的月份【中文】名称
let dateString;
dateString = targetDate.value.toString().split(" ");
calendarProps.target.monthShortName = dateString[1];
calendarProps.target.monthFullName =
monthNameList.fullName[calendarProps.target.month];
calendarProps.target.monthChineseFullName =
monthNameList.chineseFullName[calendarProps.target.month];
// 获取目标月份的第一天是星期几,和在星期几中的索引值
const targetMonthFirstDay = new Date(
calendarProps.target.year,
calendarProps.target.month,
1
);
calendarProps.target.firstDay = targetMonthFirstDay.getDay();
calendarProps.target.firstDayIndex = dayNameList.findIndex(
(day) => day.dayNumber === calendarProps.target.firstDay
);
// 获取目标月份总共多少天
const targetMonthLastDay = new Date(
calendarProps.target.year,
calendarProps.target.month + 1,
0
);
calendarProps.target.totalDays = targetMonthLastDay.getDate();
// 获取目标月份的上个月总共多少天
const previousMonth = new Date(
calendarProps.target.year,
calendarProps.target.month,
0
);
calendarProps.previous.totalDays = previousMonth.getDate();
}
需要注意的一个知识点是,在获取本月多少天和上个月多少天的时候,都将date值设置为了
0
。这是因为当date值为0
的时候,返回的Date对象是上个月的最后一天。所以说,为了获取本月多少天,需要将本月的month值加1
。
执行这个方法之后,此时calendarProps
的值为:
当我们已经知道本月第一天对应的周几索引值、本月一共有多少天和上个月一共有多少天这三个核心数据之后,就可以开始生成对应的日历数据了。
思路如下:
由于大部分情况下,本月的第一天不是从头开始的,之前的部分是上个月的日期。所以第一行要单独进行处理。
设置一个公用的date数值,初始值设置为1
。然后从本月第一天对应的周几索引值开始进行递增。本月之前的日期和之后的日期设置一个算法进行计算。
为了方便之后进行日期切换、样式区分,将生成的数据加工成一个对象,其中包含日期类型——dateType
,表示是本月还是上月还是下月;
/**
* 生成日历的数据
*/
function setCalendarData() {
let i;
let date = 1;
const originData = [];
const firstRow = [];
// 设置第一行数据
for (i = 0; i <= 6; i++) {
// 设置目标月份之前月份的日期数据
if (i < calendarProps.target.firstDayIndex) {
const previousDate =
calendarProps.previous.totalDays -
calendarProps.target.firstDayIndex +
(i + 1);
firstRow.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month - 1,
previousDate
),
dateNumber: previousDate,
dateType: "previous"
});
} else {
// 设置目标月份当月的日期数据
firstRow.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month,
date
),
dateNumber: date,
dateType: "current"
});
date++;
}
}
originData.push(firstRow);
// 设置后面五行的数据
for (let j = 0; j <= 4; j++) {
const rowData = [];
for (let k = 0; k <= 6; k++) {
// 设置目标月份剩下的日期数据
if (date <= calendarProps.target.totalDays) {
rowData.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month,
date
),
dateNumber: date,
dateType: "current"
});
} else {
// 设置目标月份下个月的日期数据
const nextDate = date - calendarProps.target.totalDays;
rowData.push({
dateObj: new Date(
calendarProps.target.year,
calendarProps.target.month + 1,
nextDate
),
dateNumber: nextDate,
dateType: "next"
});
}
date++;
}
originData.push(rowData);
}
calendarData.value = originData;
}
至此,这个日历组件的核心部分的逻辑就已经实现了。你看,是不是很简单?
接下来,我们只需要根据calendarData
中的数据渲染出相应的html模板和添加上样式就可以了。
一般来说,日历组件都是网格状的结构,所以我选择table的方式进行渲染。不过你要是问我还有没有别的方式,那还是有的,比如使用flex布局或者grid布局,但是如果采用这种方式的话,calendarData
的数据结构就不是现在这个样子了。
dom结构如下图:
到此,关于“怎么封装一个vue自定义日历组件”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。