前言:
此單元較為複雜,若元件觀念較不熟悉的同學,請斟酌觀看
這裡會運用到props
、emit
、ES6
、day.js
讓我們先來觀看成品圖吧
首先我們將行事曆切分為三個區塊製作
分別為:一、header
二、body
三、footer
首先,定義一個父層名為:result的父元件
並在data內定義一個名稱,值為day.js
<script>
export default {
data () {
return {
selectedDate: dayjs()
}
}
}
</script>
接著在components創建一個名為pointer的子元件
並定義一個selectedMonth的function
,format的輸出格式可自訂規則
將父層傳進的的值「dayjs()」,透過props接收(props可自訂驗證規則,詳請可參考這篇)
並定義驗證規則:type必須為「物件型別」
、required必須為「true」
這個組件中就完成了表頭的年、月份
<template>
<div>
<div class="XXXXX">{{ selectedMonth }}</div>
</div>
</template>
<script>
export default {
props: {
selectedDate: {
type: Object,
required: true
}
},
computed: {
selectedMonth () {
return this.selectedDate.format('MMMM YYYY')
}
}
}
</script>
下個步驟就是要製作切換月份的按鈕!
先在父元件創立一個today
的function,並定義format的輸出格式
today () {
console.log(dayjs().format('YYYY-MM-DD')) //string型別
return dayjs().format('YYYY-MM-DD')
}
創立一個名為dateSelected的子元件將父元件的today及dayjs()
,分別透過currentDate、selectedDate傳入
接著開始定義上個月
、當月
、下個月
的按鈕,寫法請參照下方程式碼
定義完後使用$emit
將newDate
的值回傳給父元件
<template>
<div class="XXXXX">
<div class="YYYYY">
<span @click="selectedPre"><i class="fas fa-arrow-left"></i></span>
<span @click="selectedCurrent">Today</span>
<span @click="selectedNext"><i class="fas fa-arrow-right"></i></span>
</div>
</div>
</template>
<script>
import dayjs from 'dayjs'
export default {
props: {
currentDate: {
type: String,
required: true
},
selectedDate: {
type: Object,
required: true
}
},
methods: {
selectedPre () {
const newDate = dayjs(this.selectedDate).subtract(1, 'month')
this.$emit('dateSelected', newDate)
},
selectedCurrent () {
const newDate = dayjs(this.currentDate)
this.$emit('dateSelected', newDate)
},
selectedNext () {
const newDate = dayjs(this.selectedDate).add(1, 'month')
this.$emit('dateSelected', newDate)
}
}
}
</script>
父元件的code會呈現為:
<template>
<div class="XXXXX">
<Pointer :selected-date = "selectedDate"/>
<DateSelected
:current-date="today"
:selected-date="selectedDate"
@dateSelected="selDate"
/>
</div>
</template>
<script>
export default {
data () {
return {
selectedDate: dayjs()
}
},
methods: {
selDate (newDate) {
this.selectedDate = newDate
}
},
computed: {
today () {
return dayjs().format('YYYY-MM-DD')
},
getMonth () {
return dayjs(this.selectedDate).daysInMonth() //取得當月的總天數
},
year () {
return Number(this.selectedDate.format('YYYY')) //取得年份
},
month () {
return Number(this.selectedDate.format('M')) //取得月份,從 1 開始
},
}
}
</script>
接著第二步驟的body就單純了許多,因此就不多做解釋了,直接附上程式碼
<template>
<div>
<ol class="week">
<li v-for="weekday in weekDates" :key="weekday">{{ weekday }}</li>
</ol>
</div>
</template>
<script>
const week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
export default {
computed: {
weekDates () {
return week
}
}
}
</script>
終於完成了header和body的部分,先休息一下喝口水吧!因為複雜的要來了
第三步驟先引入day.js的Weekday
插件
Weekday的功能是計算上週,指的是計算出上個禮拜的周一到周日。
當然也可計算本週或是下週,詳細說明可參考這篇
import weekday from 'dayjs/plugin/weekday'
dayjs.extend(weekday)
在這邊我們要分別定義三個計算式
一個是當月份,一個是上個月份,另一個是下個月份
今日為2021/04/07,會以今日的日期分別console出結果給大家做寫法的比對
首先,定義一個function取得當月份的天數(展開函式寫法參考)
currentDays () {
return [...Array(this.getMonth)].map((day, index) => {
return {
date: dayjs(`${this.year}-${this.month}-${index + 1}`).format('YYYY-MM-DD'),
isMonth: true
}
})
}
接著,在定義一個function取得下個月份的天數程式碼解析:
const nextWeek:
主要是計算每個月的最後一天是星期幾。以本月為例,最後一天為星期五
const nextOfmonth:
主要為計算下個月份的1號。以本月為例,下個月的1號為週六,我們的月曆是由週一排序到週日,而本月的最後一天為週五,因此在本月最後一週的週六、週日,就是下個月的1、2號。
const nextMonth:
{ $D:月份裡的第一天, $M:月份, $W:下個月的第一天是星期幾, $y:年份 }
nextmonthDay () {
const nextWeek = this.getWeekday(`${this.year}-${this.month}-${this.currentDays.length}`)
console.log('每月的最後一天是星期 :', nextWeek) //每月的最後一天是星期 : 5
const nextMonth = dayjs(`${this.year}-${this.month}`).add(1, 'month')
console.log(nextMonth) //註一
const nextOfmonth = nextWeek ? 7 - nextWeek : nextWeek
console.log('下個月的可見天數:', nextOfmonth) //下個月的可見天數:2
return [...Array(nextOfmonth)].map((day, index) => {
return {
date: dayjs(`${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`).format('YYYY-MM-DD'),
isMonth: false
}
})
}
結束了下個月當然還得做上個月的天數,是不是開始不奈煩了阿~
在定義一個取得上個月份天數的function
這裡的做法與上一個差不多,因此就不多做解釋了
premonthDate () {
const firstWeek = this.getWeekday(this.currentDays[0].date)
console.log('每月第一個工作日是星期 :', firstWeek) //每月第一個工作日是星期 : 4
const prevMonth = dayjs(`${this.year}-${this.month}`).subtract(1, 'month')
const lastMonth = firstWeek ? firstWeek - 1 : 6
console.log('上個月的可見天數 :', lastMonth) //上個月的可見天數 : 3
const prefirstDay = dayjs(this.currentDays[0].date).subtract(lastMonth, 'day').date()
console.log('當月第一個星期一是 :', prefirstDay, '號') //當月第一個星期一是 : 29 號
return [...Array(lastMonth)].map((day, index) => {
return {
date: dayjs(`${prevMonth.year()}-${prevMonth.month() + 1}-${prefirstDay + index}`).format('YYYY-MM-DD'),
isMonth: false
}
})
}
附上父組件的最終程式碼給大家參考
<template>
<div>
<div class="xxxxxx">
<div class="yyyyyy">
<Pointer
:selected-date = "selectedDate"
/>
<DateSelected
:current-date="today"
:selected-date="selectedDate"
@dateSelected="selDate"
/>
</div>
<WeekDate/>
<ol class="zzzzzz">
<MonthDate
v-for="day in days"
:key="day.date"
:day="day"
:is-today="day.date === today"
/>
</ol>
</div>
</div>
</template>
<script>
import weekday from 'dayjs/plugin/weekday'
dayjs.extend(weekday)
export default {
data () {
return {
selectedDate: dayjs()
}
},
methods: {
getWeekday (date) {
return dayjs(date).weekday()
},
selDate (newDate) {
this.selectedDate = newDate
}
},
computed: {
today () {
return dayjs().format('YYYY-MM-DD')
},
getMonth () {
return dayjs(this.selectedDate).daysInMonth()
},
year () {
return Number(this.selectedDate.format('YYYY'))
},
month () {
return Number(this.selectedDate.format('M'))
},
currentDays () {
return [...Array(this.getMonth)].map((day, index) => {
return {
date: dayjs(`${this.year}-${this.month}-${index + 1}`).format('YYYY-MM-DD'),
isMonth: true
}
})
},
nextmonthDay () {
const nextWeek = this.getWeekday(`${this.year}-${this.month}-${this.currentDays.length}`)
const nextMonth = dayjs(`${this.year}-${this.month}`).add(1, 'month')
const nextOfmonth = nextWeek ? 7 - nextWeek : nextWeek
return [...Array(nextOfmonth)].map((day, index) => {
return {
date: dayjs(`${nextMonth.year()}-${nextMonth.month() + 1}-${index + 1}`).format('YYYY-MM-DD'),
isMonth: false
}
})
},
premonthDate () {
const firstWeek = this.getWeekday(this.currentDays[0].date)
const prevMonth = dayjs(`${this.year}-${this.month}`).subtract(1, 'month')
const lastMonth = firstWeek ? firstWeek - 1 : 6
const prefirstDay = dayjs(this.currentDays[0].date).subtract(lastMonth, 'day').date()
return [...Array(lastMonth)].map((day, index) => {
return {
date: dayjs(`${prevMonth.year()}-${prevMonth.month() + 1}-${prefirstDay + index}`).format('YYYY-MM-DD'),
isMonth: false
}
})
},
days () {
return [
...this.premonthDate,
...this.currentDays,
...this.nextmonthDay
]
}
},
最後我們要將非本月的日期
以及當天的日期
賦予顏色以便區隔
因此再新增一個子組件,名為monthDate
,透過props
將v-for的day值
傳入即可
<template>
<div>
<li class="XXXXXX" :class="{'current': !day.isMonth, 'today': isToday}">
<span>{{ isTag }}</span>
</li>
</div>
</template>
<script>
export default {
props: {
day: {
type: Object,
required: true
},
isMonth: {
type: Boolean,
default: false
},
isToday: {
type: Boolean,
default: false
}
},
computed: {
isTag () {
return dayjs(this.day.date).format('D')
}
}
}
</script>
若有錯誤的地方歡迎批評指教 謝謝