日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

怎么開發(fā)一個(gè)自定義日歷的vue組件,下面本篇文章就手把手教你如何封裝一個(gè)自定義日歷組件,希望對(duì)大家有所幫助!


詳解怎么使用vue封裝一個(gè)自定義日歷組件


眾所周知啊,一般來說,如果項(xiàng)目中有需要用到日歷組件,往往是找第三方UI庫(kù)中的組件來使用,或者是找現(xiàn)成的其他第三方插件。對(duì)于很多小伙伴來說,第一眼看到日歷組件,下意識(shí)的就會(huì)覺得很復(fù)雜,無從下手。但是當(dāng)我閱讀了這個(gè)日歷插件的源碼之后,發(fā)現(xiàn)并沒有我想象中的復(fù)雜。我以前傻傻得認(rèn)為,想要做一個(gè)日歷組件,得需要把距離現(xiàn)在年份前后至少十年的日歷數(shù)據(jù)都獲取到,然后才能進(jìn)行下一步的開發(fā)。

然而,在我嘗試著閱讀了dycalendar.js這個(gè)庫(kù)的源碼之后,一方面感覺自己太笨了,把問題想得太復(fù)雜了。另外也感慨作者思路之清晰。看完之后感覺受益匪淺。

在將作者的思路邏輯梳理完畢后,我依據(jù)這個(gè)思路開發(fā)了一個(gè)vue組件。如下圖所示:


詳解怎么使用vue封裝一個(gè)自定義日歷組件


接下來,就隨著我一起看看如何開發(fā)一個(gè)自己的日歷組件吧。


核心代碼實(shí)現(xiàn)

1、梳理思路

獲取到目標(biāo)日期數(shù)據(jù)

獲取到當(dāng)前日期的各項(xiàng)重要屬性,諸如當(dāng)前年當(dāng)前月當(dāng)前日期當(dāng)前星期幾當(dāng)前月一共有幾天當(dāng)前月的第一天對(duì)應(yīng)的是星期幾上個(gè)月總共有多少天等。

根據(jù)這些屬性,來生成具體的日歷日期數(shù)據(jù)列表,然后將其循環(huán)渲染到模板中。

當(dāng)切換月份的時(shí)候,獲取到新的目標(biāo)日期對(duì)應(yīng)的各項(xiàng)關(guān)鍵數(shù)據(jù)。vue檢測(cè)到日歷屬性變化之后,通知頁(yè)面進(jìn)行更新。

2、初始化所需要的數(shù)據(jù)

一般來說,成熟的日歷組件,日期都是一個(gè)雙向綁定的變量。為了方便使用,我們也采用雙向綁定的方式。

<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;
/**
 * 目標(biāo)日期
 */
const targetDate = ref(props.modelValue);

接下來,我們還需要初始化一些常量用來表示月份和日期:

/**
 * 有關(guān)月度的名稱列表
 */
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",
  ],
};
/**
 * 有關(guān)周幾的名稱列表
 */
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,
  },
];

接下來,準(zhǔn)備幾個(gè)vue的響應(yīng)式數(shù)據(jù):

/**
 * 今日
 */
const today = new Date();
/**
 * 日歷的各項(xiàng)屬性
 */
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,
  },
});
/**
 * 用于展現(xiàn)的日歷數(shù)據(jù)
 */
const calendarData = ref([]);

3、初始化日歷的各項(xiàng)屬性

接下來,通過setCalendarProps方法獲取日歷的各個(gè)屬性,逐個(gè)填充calendarProps中的數(shù)據(jù):

function setCalendarProps() {
  if (!targetDate.value) {
    targetDate.value = today;
  }
  // 獲取目標(biāo)日期的年月日星期幾數(shù)據(jù)
  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("無效的年份,請(qǐng)檢查傳入的數(shù)據(jù)是否是正常");
    return;
  }
  // 獲取到目標(biāo)日期的月份【中文】名稱
  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];
  // 獲取目標(biāo)月份的第一天是星期幾,和在星期幾中的索引值
  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
  );
  // 獲取目標(biāo)月份總共多少天
  const targetMonthLastDay = new Date(
    calendarProps.target.year,
    calendarProps.target.month + 1,
    0
  );
  calendarProps.target.totalDays = targetMonthLastDay.getDate();
  // 獲取目標(biāo)月份的上個(gè)月總共多少天
  const previousMonth = new Date(
    calendarProps.target.year,
    calendarProps.target.month,
    0
  );
  calendarProps.previous.totalDays = previousMonth.getDate();
}

需要注意的一個(gè)知識(shí)點(diǎn)是,在獲取本月多少天和上個(gè)月多少天的時(shí)候,都將date值設(shè)置為了0。這是因?yàn)楫?dāng)date值為0的時(shí)候,返回的Date對(duì)象是上個(gè)月的最后一天。所以說,為了獲取本月多少天,需要將本月的month值加1

執(zhí)行這個(gè)方法之后,此時(shí)calendarProps的值為:


詳解怎么使用vue封裝一個(gè)自定義日歷組件


4、根據(jù)日歷屬性生成日歷日期的數(shù)據(jù)

當(dāng)我們已經(jīng)知道本月第一天對(duì)應(yīng)的周幾索引值本月一共有多少天上個(gè)月一共有多少天這三個(gè)核心數(shù)據(jù)之后,就可以開始生成對(duì)應(yīng)的日歷數(shù)據(jù)了。

思路如下

由于大部分情況下,本月的第一天不是從頭開始的,之前的部分是上個(gè)月的日期。所以第一行要單獨(dú)進(jìn)行處理。

設(shè)置一個(gè)公用的date數(shù)值,初始值設(shè)置為1。然后從本月第一天對(duì)應(yīng)的周幾索引值開始進(jìn)行遞增。本月之前的日期和之后的日期設(shè)置一個(gè)算法進(jìn)行計(jì)算。

為了方便之后進(jìn)行日期切換、樣式區(qū)分,將生成的數(shù)據(jù)加工成一個(gè)對(duì)象,其中包含日期類型——dateType,表示是本月還是上月還是下月;

/**
 * 生成日歷的數(shù)據(jù)
 */
function setCalendarData() {
  let i;
  let date = 1;
  const originData = [];
  const firstRow = [];
  // 設(shè)置第一行數(shù)據(jù)
  for (i = 0; i <= 6; i++) {
    // 設(shè)置目標(biāo)月份之前月份的日期數(shù)據(jù)
    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 {
      // 設(shè)置目標(biāo)月份當(dāng)月的日期數(shù)據(jù)
      firstRow.push({
        dateObj: new Date(
          calendarProps.target.year,
          calendarProps.target.month,
          date
        ),
        dateNumber: date,
        dateType: "current"
      });
      date++;
    }
  }
  originData.push(firstRow);
  // 設(shè)置后面五行的數(shù)據(jù)
  for (let j = 0; j <= 4; j++) {
    const rowData = [];
    for (let k = 0; k <= 6; k++) {
      // 設(shè)置目標(biāo)月份剩下的日期數(shù)據(jù)
      if (date <= calendarProps.target.totalDays) {
        rowData.push({
          dateObj: new Date(
            calendarProps.target.year,
            calendarProps.target.month,
            date
          ),
          dateNumber: date,
          dateType: "current"
        });
      } else {
        // 設(shè)置目標(biāo)月份下個(gè)月的日期數(shù)據(jù)
        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;
}

至此,這個(gè)日歷組件的核心部分的邏輯就已經(jīng)實(shí)現(xiàn)了。你看,是不是很簡(jiǎn)單?

接下來,我們只需要根據(jù)calendarData中的數(shù)據(jù)渲染出相應(yīng)的html模板和添加上樣式就可以了。

5、添加模板和樣式部分

一般來說,日歷組件都是網(wǎng)格狀的結(jié)構(gòu),所以我選擇table的方式進(jìn)行渲染。不過你要是問我還有沒有別的方式,那還是有的,比如使用flex布局或者grid布局,但是如果采用這種方式的話,calendarData的數(shù)據(jù)結(jié)構(gòu)就不是現(xiàn)在這個(gè)樣子了。

dom結(jié)構(gòu)如下圖:


詳解怎么使用vue封裝一個(gè)自定義日歷組件


至于按鈕邊框的流動(dòng)效果,是我參照蘇蘇的文章做的,詳情請(qǐng)見:

Clip-path實(shí)現(xiàn)按鈕流動(dòng)邊框動(dòng)畫 juejin.cn/post/719877…

然后剩下的樣式部分,即興發(fā)揮或者根據(jù)UI設(shè)計(jì)圖繪制即可。想必各位都領(lǐng)教過UI姐姐們精美的設(shè)計(jì)圖吧(嘻嘻

具體的代碼部分就不貼在文章中了,如有需要可以直接查看下方的完整源碼

gitee.com/wushengyuan…


結(jié)語(yǔ)

有些感覺很麻煩的組件,可能核心邏輯往往不是那么復(fù)雜。有些時(shí)候,可能僅僅是需要一些耐心,將代碼一行一行的拆解出來閱讀,理清楚其中的思路。


分享到:
標(biāo)簽:vue封裝組件 自定義日歷組件
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定