「今天是幾月幾號啊?」
今天是 ... 等等!
不准看電腦上的!
你先等我造出一個,我們要看日期 ... 就看我們自己做的日曆!
(這兔是有什麼毛病,連這個時候都還要造元件)
首先,在專案裡的 ./src/components
資料夾中新增一個 SimpleCalendar.vue
的元件:
好了嗎?
那一樣,增添以下內容:
<template>
</template>
<script>
export default {
name: "SimpleCalendar",
}
</script>
接著把元件新增到畫面中,然後因為這次只有一個元件,所以我們 App.vue
要記得讓內容置中:
<template>
<div :class="[
'w-screen h-screen',
'flex',
'justify-center items-center'
]"
>
<SimpleCalendar />
</div>
</template>
<script>
import SimpleCalendar from './components/SimpleCalendar.vue'
export default {
data() {
return {
}
},
components: {
SimpleCalendar,
}
}
</script>
做了三次應該都熟悉了吧?
下一步囉!
今天碰巧看到了一張 Windows App,
那我們就做的像這個吧!
我左看右看、上看下看,原來每個女孩,都不簡單
不是啦,是看出端倪了!
不難發現日曆分成兩大塊,分別是日曆標題區塊
以及 日曆內容區塊
。
但這樣切分我覺得不夠好,
還可以依照內容物屬性分類的方式:
像這樣,即使 週期區塊
與 日期區塊
非常的類似,但分開來做的話資料處理起來也會方便許多。
我們就先來完成基本的樣式,週期的部分用 v-for 跑迴圈來產生,資料兔兔已經先準備給你們了,在這裡:
weekDays: ['Su','Mo','Tu','We','Th','Fr','Sa']
而日期的部分也是用 v-for 先產生假資料就好,那麼大概就會像是這個樣子:
<template>
<div class="border flex flex-col rounded-md overflow-hidden">
<div class="flex justify-center items-center p-3 border-b bg-blue-600 text-white font-bold">
2021 9 月
</div>
<div class="grid grid-cols-7 place-items-center">
<div class="w-10 h-10 text-xs font-bold flex justify-center items-center" v-for="wd in weekDays" :key="wd">
{{ wd }}
</div>
</div>
<div class="grid grid-cols-7 place-items-center">
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'rounded-full transition-all'
]"
v-for="d in 30" :key="d"
>
{{ d }}
</div>
</div>
</div>
</template>
<script>
export default {
name: "SimpleCalendar",
data() {
return {
weekDays: ['Su','Mo','Tu','We','Th','Fr','Sa']
}
}
}
</script>
已經開始有模有樣了!
不過上個月的末幾天、下個月的頭幾天和今天日期都還沒有出現,那麼我們接著就來弄這些。
其實只要把本月 1 號跟星期幾對上,就可以了,但這樣還是不夠完整,所以我們就要想辦法來補全其他的細節。
先再來看一次原圖:
透過原圖,我們可以知道開始日若是星期二
,則前面會有兩天是上個月的
;以此類推,開始日若是星期四,則前面會有四天是上個月的。
再來是下個月的部分。很直觀的來看,日曆上總共規劃了 42 天
,除了前個月和本月的天數之外,剩下
都會拿來顯示下個月的日期
,那麼規則就很清晰了。
我們來試著完成這個部分,先做出前個月和本月的。
js 中的 Date()
是可以允許倒數的,這麼一來就好辦了,我們可以利用迴圈來達成。
在開始撰寫函數之前,我們先在 data 中擺設我們所需要的資料,先給個預設值:
<script>
export default {
name: "SimpleCalendar",
data() {
return {
weekDays: ['Su','Mo','Tu','We','Th','Fr','Sa'],
year: 2021,
month: 9,
}
}
}
</script>
然後在 vue 中寫一個 computed 的 lastMonth()
函數:
lastMonth() {
const days = []
const current = new Date(this.year,this.month-1,1)
const wd = current.getDay()
for(let i=wd;i>0;i--) {
const temp = new Date(current)
temp.setDate(current.getDate()-i)
days.push(temp.getDate())
}
return days
},
我們接著把函數返回的結果利用 v-for
顯示在日曆上當作前個月的日期,文字顏色記得用 text-gray-400
改淺一點:
<div class="border flex flex-col rounded-md overflow-hidden">
<div class="flex justify-center items-center p-3 border-b bg-blue-600 text-white font-bold">
2021 9 月
</div>
<div class="grid grid-cols-7 place-items-center">
<div class="w-10 h-10 text-xs font-bold flex justify-center items-center" v-for="wd in weekDays" :key="wd">
{{ wd }}
</div>
</div>
<div class="grid grid-cols-7 place-items-center">
<!-- 前個月的 -->
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'text-gray-400',
'rounded-full transition-all'
]"
v-for="d in lastMonth" :key="d"
>
{{ d }}
</div>
<!-- 本月的 -->
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'rounded-full transition-all'
]"
v-for="d in 30" :key="d"
>
{{ d }}
</div>
</div>
</div>
又離完成更近了一步~ 那接著,馬上來完成本月吧!
本月的部分跟前個月的其實算是類似,我們只要知道這個月有幾天,然後就一樣可以用迴圈來達成。 在 js 中取得當月份天數的方法是:
let count = new Date(2021,9,0).getDate()
對,你沒看錯。
就是把日期設為零,取得到的日期就會是這個月份的總天數。
那麼有了這行,我們就可以快速做完 thisMonth()
函數了:
thisMonth() {
const days = []
const current = new Date(this.year,this.month,0)
for(let i=1;i<=current.getDate();i++) {
days.push(i)
}
return days
}
函數寫完歸寫完,別忘了應用到 template 上啊! 修改一下,把 30
改成 thisMonth
:
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'rounded-full transition-all'
]"
v-for="d in thisMonth" :key="d"
>
{{ d }}
</div>
OK,看起來沒什麼變化很正常,因為這個月本來就是 30 天。
那再來,就輪到下個月的啦!
下個月的更簡單了,我們只要計算前個月的末幾天
和本月天數
扣掉之後,42 天還剩多少天,剩下的全部都是下個月的啦~
所以,就會像是這樣:
nextMonth() {
const count = 42 - this.lastMonth.length - this.thisMonth.length
const days = []
for(let i=1;i<=count;i++) {
days.push(i)
}
return days
}
還是一樣,函數寫完要記得應用到 template 中:
<div class="border flex flex-col rounded-md overflow-hidden">
<div class="flex justify-center items-center p-3 border-b bg-blue-600 text-white font-bold">
2021 9 月
</div>
<div class="grid grid-cols-7 place-items-center">
<div class="w-10 h-10 text-xs font-bold flex justify-center items-center" v-for="wd in weekDays" :key="wd">
{{ wd }}
</div>
</div>
<div class="grid grid-cols-7 place-items-center">
<!-- 前個月的 -->
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'text-gray-400',
'rounded-full transition-all'
]"
v-for="d in lastMonth" :key="d"
>
{{ d }}
</div>
<!-- 本月的 -->
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'rounded-full transition-all'
]"
v-for="d in thisMonth" :key="d"
>
{{ d }}
</div>
<!-- 下個月的 -->
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
'text-gray-400',
'rounded-full transition-all'
]"
v-for="d in nextMonth" :key="d"
>
{{ d }}
</div>
</div>
</div>
完全大成 ... ㄍ ...
還沒,雖然看著很順眼但別忘了,日曆標題的年份和月份要跟隨實際時間的改變
啊!
<!-- 修改前 -->
<div class="flex justify-center items-center p-3 border-b bg-blue-600 text-white font-bold">
2021 9 月
</div>
<!-- 修改後 -->
<div class="flex justify-center items-center p-3 border-b bg-blue-600 text-white font-bold">
{{year}} {{month}} 月
</div>
那麼最後終於就來到我們的本日日期顯示啦!
先在 data 中多加上一個變數來儲存今天日期
:
export default {
name: "SimpleCalendar",
data() {
return {
weekDays: ['Su','Mo','Tu','We','Th','Fr','Sa'],
year: 2021,
month: 9,
+ today: new Date(),
}
},
...
}
好了之後,我們就可以在 template 上動手腳啦!
把我們顯示當月日期的部分,在 tailwind 的樣式上用三元運算子加一行判斷
,如果確定是本日日期
,就將底色設定成藍色
:
<div
:class="[
'w-10 h-10',
'text-sm',
'flex justify-center items-center',
(
(year===today.getFullYear() && month-1===today.getMonth() && d===today.getDate())
?
'bg-blue-600 text-white font-bold'
:
'hover:bg-gray-200'
),
'rounded-full transition-all'
]"
v-for="d in thisMonth" :key="d"
>
{{ d }}
</div>
改好後就會發現! 完成了~
不過 ... 還有一個小問題存在著哦!
(你又來了...)
就是再過幾天這個日曆就會出 bug
了~
為什麼呢? 因為我們 data 中的 year 和 month 還是寫死的呀!
所以我們要在 mounted 的時候,依照今日時間,覆寫掉那兩個變數的內容:
mounted() {
this.today = new Date()
this.year = this.today.getFullYear()
this.month = this.today.getMonth() + 1
}
這樣就真的全部完成啦~~
老實說,今天的好像也不難嘛 (?)
不過我留了一個挑戰給你們,
在作業中。
有興趣的人玩玩看嘿!
關於兔兔們:
( # 兔兔小聲說 )
其實,我做的是不是叫做月曆,而不是日曆啊 ...?
剛剛才發現日曆是 ...