vue中日期與日歷聯動組件(自定義日歷組件)

因為項目需要用到日歷組件捂贿,沒有找到適用的組件捞附,只能無奈自己寫一個了巾乳,達到項目使用所需的功能,同時保留了較高的擴展性鸟召。

先貼上效果圖

screenshots.gif

具體day的點擊事件邏輯就不貼出來了胆绊,有需要的根據自己的項目需求加上click即可

代碼分享

-父組件代碼

有使用到 elementui 的 el-button el-date-picker,照抄的同學記得下載依賴
<template>
  <div>
    <div class="top clearfix">
      <div class="top-left fl">
        <div class="tlt clearfix">
          <span class="title1 fl">我的課表</span>
          <el-button class="fr ml10" type="primary">下載排課表</el-button>
          <div class="block fr">
            <el-date-picker
              v-model="value"
              type="month"
              :clearable=false
              format="yyyy年 MM月"
              @change="changeDate()"
              placeholder="查看月份">
            </el-date-picker>
          </div>
        </div>
        <Calendar :defaultMonth.sync='defaultMonth'></Calendar>
      </div>
    </div>
  </div>
</template>

<script>
import Calendar from '@/components/Calendar'

export default {
  components:{Calendar},
  data() {
    return {
      value:'',
      defaultMonth:''
    }
  },
  methods: {
    changeDate(){
      this.defaultMonth = this.value
    }
  },
  created(){
    let time = new Date()
    this.value = time
    this.defaultMonth = time
  }
}
</script>

<style lang="stylus" scoped>
.clearfix::before,
.clearfix::after {
  content: '';
  display: table;
  clear: both;
}
.fl{
  float:left;
}
.fr{
  float:right;
}
.title1{
  color: #333
  font-size: 20px;
  font-weight 900
}
.top-left
  height 750px
  width 70%
  background-color #fff
  padding:20px;
  border-radius: 10px;
  &>.tlt
    line-height 40px;
    padding-bottom:10px;
</style>

-子組件代碼

<template>
  <!-- 日歷組件 -->
  <div class="calendar-box">
    <table class="calendar-table">
      <thead>
        <th class="fc-day-header">日</th>
        <th class="fc-day-header">一</th>
        <th class="fc-day-header">二</th>
        <th class="fc-day-header">三</th>
        <th class="fc-day-header">四</th>
        <th class="fc-day-header">五</th>
        <th class="fc-day-header">六</th>
      </thead>
      <tbody>
        <tr>
          <td 
            v-for="(item, index) in calendarData"
            :key="index"
            :style="{'height': height}"
            @click="clickDay(item)">
            <div class="includeContent"
              :class="{
                'include': item.include,
                'active': defaultDate.indexOf(item.date)>-1
              }"
            >
              <slot name="day" :row="item">
                <div class="day">
                  <span class="d_one">{{ item.day2 }}</span>
                  <span class="d_two" v-if="defaultDate.indexOf(item.date)>-1">課</span>
                  <span class="d_three" v-if="defaultDate.indexOf(item.date)>-1" v-text="courseList[defaultDate.indexOf(item.date)]"></span>
                </div>
              </slot>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
export default {
  name: 'calendarBox',
  props: {
    type: {  // all 可選中顯示的所有日期 portion 只能選中當前月份的數據
      type: String,
      default: 'portion'
    },
    defaultMonth: {  // 默認日期
      type: Date,
      default () {
        let year = new Date().getFullYear() // 當月年份
        let month = new Date().getMonth() + 1 // 當月月份
        return new Date(year, month - 1, 1)
      }
    },
    height: { //設置td高度
      type: String,
      default: '94px'
    }
  },
  data () {
    return {
      calendarData: [],
      activeData: null,
      defaultDate:['2022-4-1','2022-4-7','2022-4-13','2022-4-18','2022-4-26','2022-4-28','2022-5-5','2022-5-15','2022-5-18','2022-5-20','2022-5-21','2022-5-30'],
      courseList:['美術課','武器課','瑜伽課','音樂課','吃飯','睡覺','看電影','逛街','考試','答辯','畢業(yè)','結婚'],
      // allDate:[  //這里是模擬數據欧募,一般是從后臺接口獲取而來压状,需要自己獲取的時候拆分成 defaultDate 和 courseList
      //   {date:'2022-4-1',course:'美術課'},
      //   {date:'2022-4-7',course:'武器課'},
      //   {date:'2022-4-13',course:'瑜伽課'},
      //   {date:'2022-4-18',course:'音樂課'},
      //   {date:'2022-4-26',course:'吃飯'},
      //   {date:'2022-4-28',course:'睡覺'},
      //   {date:'2022-5-5',course:'看電影'},
      //   {date:'2022-5-13',course:'逛街'},
      //   {date:'2022-5-14',course:'畢業(yè)'},
      //   {date:'2022-5-20',course:'答辯'},
      //   {date:'2022-5-25',course:'結婚'},
      // ] 
    }
  },
  created () {
    this.getCalendarDay()
  },
  methods: {
    // 獲取日歷天數
    getCalendarDay () {
      let year = new Date(this.defaultMonth).getFullYear() // 當月年份
      let month = new Date(this.defaultMonth).getMonth() + 1 // 當月月份
      let days = new Date(year, month, 0).getDate() // 當月天數

      let prevYear = month === 1 ? (year - 1) : year // 上一年年份
      let prevMonth = month === 1 ? 12 : (month - 1) // 上月月份
      let prevDays = new Date(prevYear, prevMonth, 0).getDate() // 上一月天數

      let lastYear = month === 12 ? (year + 1) : year // 下一年年份
      let lastMonth = month === 12 ? 1 : (month + 1) // 下月月份
      // let lastDays = new Date(lastYear, lastMonth, 0).getDate() // 下一月天數

      let prevMonthArr = [] // 上月數據
      let nextMonthArr = [] // 下月數據
      let currentMonthArr = [] // 當月數據
      // 判斷當月1號是星期幾
      let firstDay = new Date(year, month - 1, 1).getDay()
      // 獲取顯示上月的數據
      for (let i = 0; i < firstDay; i++) {
        let day = prevDays - firstDay + (i + 1)
        let day2 = day >= 10 ? day : ('0' + day)
        let month2 = prevMonth >= 10 ? prevMonth : ('0' + prevMonth)
        prevMonthArr.push({
          year: prevYear,
          month: prevMonth,
          month2: month2,
          day: day,
          day2: day2,
          date: `${prevYear}-${prevMonth}-${day}`,
          date2: `${prevYear}-${month2}-${day2}`,
          include: false
        })
      }
      // 獲取顯示的下一月的數據
      for (let i = 0; i < (42 - firstDay - days); i++) {
        let day = i + 1
        let day2 = day >= 10 ? day : ('0' + day)
        let month2 = lastMonth >= 10 ? lastMonth : ('0' + lastMonth)
        nextMonthArr.push({
          year: lastYear,
          month: lastMonth,
          month2: month2,
          day: day,
          day2: day2,
          date: `${lastYear}-${lastMonth}-${day}`,
          date2: `${lastYear}-${month2}-${day2}`,
          include: false
        })
      }
      // 獲取當月顯示數據
      for (let i = 1; i <= days; i++) {
        let day2 = i >= 10 ? i : ('0' + i)
        let month2 = month >= 10 ? month : ('0' + month)
        currentMonthArr.push({
          year: year,
          month: month,
          month2: month2,
          day: i,
          day2: day2,
          date: `${year}-${month}-${i}`,
          date2: `${year}-${month2}-${day2}`,
          include: true
        })
      }

      this.calendarData = [...prevMonthArr, ...currentMonthArr, ...nextMonthArr]
    },
    // 設置默認值
    setDefaultValue (date) {
      if (!date) {
        this.activeData = null
        return
      }
      let year = new Date(date).getFullYear() // 當月年份
      let month = new Date(date).getMonth() + 1 // 當月月份
      let month2 = month >= 10 ? month : ('0' + month)
      let day = new Date(date).getDate()
      let day2 = day >= 10 ? day : '0' + day
      let row = {
        year: year,
        month: month,
        month2: month2,
        day: day,
        day2: day2,
        date: `${year}-${month}-${day}`,
        date2: `${year}-${month2}-${day2}`,
        include: true
      }
      this.clickDay(row)
    },
    clickDay (row) {
      if (this.type === 'portion' && !row.include) {
        this.$message.closeAll()
        this.$message({
          type: 'info',
          message: '只能選擇當前月份的日期'
        })
        return
      }
      this.activeData = { ...row }
      this.$emit('click', { ...row })
      // console.log(this.activeData);
    }
  },
  watch: {
    defaultMonth(){
      this.setDefaultValue()
      this.getCalendarDay()
    }
  }
}
</script>
<style lang="stylus" scoped>
$blue = #0176F6
.calendar-box {
  background-color #f6f6f6
  padding 10px;
  .calendar-table {
    width: 100%;
    border-radius: 4px;
    display: block;
    overflow: hidden;
    thead,
    tbody {
      display: flex;
      width: 100%;
    }
    tr {
      width: 100%;
    }
    th {
      height: 50px;
      line-height: 50px;
      text-align: center;
      color: #3E597B;
      box-sizing: border-box;
      display: inline-block;
      width: 14.28%;
    }
    td {
      width: 14.28%;
      display: inline-block;
      position: relative;
      font-size: 16px;
      font-weight: 500;
      box-sizing: border-box;
      text-align: center;
      cursor: pointer;
      border: solid 2px transparent;
      color: rgba(62, 89, 123, 0.3);
      background-color: #f5f5f5;
      padding: 5px;
        &>.includeContent{
          border: 1px solid #DDD;
          height 80px;
          &.include {
            color: #3E597B;
            background-color: #fff;
          }
          &.active {
            border: 2px solid $blue;
            background: #BCDCFF;
            color: $blue;
          }
          &:hover{
            border: 2px solid $blue;
          }
      }
      .day {
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        &>.d_one{
          position: absolute
          top 5px
          left 5px
        }
        &>.d_two{
          position: absolute
          top 5px
          right 5px
        }
        &>.d_three{
          width 100%
          position: absolute
          top 40px
          left 50%;
          transform: translateX(-50%)
        }
      }
      &:nth-child(1),
      &:nth-child(7),
      &:nth-child(8),
      &:nth-child(14),
      &:nth-child(15),
      &:nth-child(21),
      &:nth-child(22),
      &:nth-child(28),
      &:nth-child(29),
      &:nth-child(35),
      &:nth-child(36),
      &:nth-child(42) {
        background: #f6f6f6;  //根據自己需要,設置td周末的背景顏色
      }
    }
    tbody {
      .clickDay {
        border: solid 2px #00BBBB;
      }
    }
  }
}

</style>

使用注意事項

1.<style lang="stylus" scoped>這里我用的是stylu
2.子組件props中的 type 和 height 我在父組件中沒有傳槽片,使用的是子組件的默認值
3.data(){} 中 defaultDate何缓、courseList、allDate參數請根據項目實際獲取參數使用

結束 over

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末还栓,一起剝皮案震驚了整個濱河市碌廓,隨后出現的幾起案子,更是在濱河造成了極大的恐慌剩盒,老刑警劉巖谷婆,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡纪挎,警方通過查閱死者的電腦和手機期贫,發(fā)現死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來异袄,“玉大人通砍,你說我怎么就攤上這事】就桑” “怎么了封孙?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長讽营。 經常有香客問我虎忌,道長,這世上最難降的妖魔是什么橱鹏? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任膜蠢,我火速辦了婚禮,結果婚禮上莉兰,老公的妹妹穿的比我還像新娘挑围。我一直安慰自己,他們只是感情好贮勃,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布贪惹。 她就那樣靜靜地躺著苏章,像睡著了一般寂嘉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枫绅,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天泉孩,我揣著相機與錄音,去河邊找鬼并淋。 笑死寓搬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的县耽。 我是一名探鬼主播句喷,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兔毙!你這毒婦竟也來了唾琼?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤澎剥,失蹤者是張志新(化名)和其女友劉穎锡溯,沒想到半個月后卦尊,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡卧土,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年盹舞,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倡蝙。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡九串,死狀恐怖,靈堂內的尸體忽然破棺而出寺鸥,到底是詐尸還是另有隱情蒸辆,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布析既,位于F島的核電站躬贡,受9級特大地震影響,放射性物質發(fā)生泄漏眼坏。R本人自食惡果不足惜拂玻,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宰译。 院中可真熱鬧檐蚜,春花似錦、人聲如沸沿侈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缀拭。三九已至咳短,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛛淋,已是汗流浹背咙好。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留褐荷,地道東北人勾效。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像叛甫,于是被迫代替她去往敵國和親层宫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348