Day 28:「今天几月几号啊?」- 简易日历

Day28-Banner

「今天是几月几号啊?」

今天是 ... 等等!
不准看电脑上的!

你先等我造出一个,我们要看日期 ... 就看我们自己做的日历!

(这兔是有什麽毛病,连这个时候都还要造元件)

carrotPoint 建立元件

首先,在专案里的 ./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>

做了三次应该都熟悉了吧?
下一步罗!
 

carrotPoint 简易日历

今天碰巧看到了一张 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>

已经开始有模有样了!

不过上个月的末几天、下个月的头几天和今天日期都还没有出现,那麽我们接着就来弄这些。
 

carrotPoint 细节工程

其实只要把本月 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
}

这样就真的全部完成啦~~
 

老实说,今天的好像也不难嘛 (?)

不过我留了一个挑战给你们,
在作业中。

有兴趣的人玩玩看嘿!

carrotPoint 给你们的回家作业:

  • 作业实施要点:
    • 实作一个自己的简易日历
    • 进阶挑战 - 让日历可以按按钮切换月份
  • 范例成品:

关於兔兔们:


 


( # 兔兔小声说 )

其实,我做的是不是叫做月历,而不是日历啊 ...?

刚刚才发现日历是 ...


<<:  Day14:SwiftUI—ForEach、ScrollView

>>:  Swift 新手-使用者介面(UX/UI/Core)

Day 25: Macie 简介及操作

What is Amazon Macie? Macie是一项全代管的服务,它透过机器学习来辨别你的S...

我要成为时间管理大师!

本系列文记录了我近三年的转变,系列文的内容基本上都会与资讯科技扯上边,希望本文也可以对与我有相似背景...

Day 02 - 行前说明 — 网页微切版架构 和 UI 元件

作为正式开始的第一篇要来讲的是很基础的网页切版和怎麽去看网页中有哪些元件,会分两部分: 网页微切版...

Day 22.5 | Livewire 实作 购物网站: 建立资料表

本来预计都写在 Day22 的,但是加上本篇内容後会让一天的篇幅太长,且考虑到有些夥伴可能没有建立资...

Day 29 QuickSight 连接 Redshift - Part 2

在设定完相关的环境参数後,可以开始设定 Redshift 的连接 一样先到 QuickSight 的...