vue制作日历
Have you ever seen a calendar on a webpage and thought, how the heck did they did that? For something like that, it might be natural to reach for a plugin, or even an embedded Google Calendar, but it’s actually a lot more straightforward to make one than you might think.
您是否曾经在网页上看到日历并想过, 他们是怎么做到的 ? 对于这样的事情,可能会很自然地获得一个插件,甚至是一个嵌入式Google日历,但实际上制作一个插件要比您想象的要简单得多。
I’ve set up a demo over at CodeSandbox so you can see what we’re aiming for.
我已经在CodeSandbox上建立了一个演示,以便您可以看到我们的目标。
Let’s first identify some requirements for what the calendar should do. It should:
首先,让我们确定日历的功能要求。 这应该:
- Display a month grid for a given month 显示给定月份的月份网格
- Display dates from the previous and next months to so the grid is always full 显示从前几个月到下个月的日期,因此网格始终充满
- Indicate current date 指示当前日期
- Show the name of the currently selected month 显示当前所选月份的名称
- Navigate to the previous and next month 导航到上个月和下个月
- Allow the user to navigate back to current month with a single click 允许用户单击一下即可导航回当前月份
Oh, and we’ll build this as a single page application that fetches calendar dates from Day.js, a super light utility library.
哦,我们将其构建为一个单页面应用程序,该应用程序从超轻型实用程序库Day.js中获取日历日期。
步骤1:从基本标记开始 (Step 1: Start with the basic markup)
Let’s start with creating a basic template for our calendar.
让我们从为日历创建一个基本模板开始。
We can outline our markup as three layers where we have:
我们可以将标记概述为三层,其中:
A section for the calendar header. This will show components with the currently selected month and the elements responsible for paginating between months.
日历标题的一部分。 这将显示具有当前选定月份的组件以及负责在月份之间进行分页的元素。
A section for the calendar grid header. A table header that holds a list containing the days of the week, starting with Monday.
日历网格标题的一部分。 一个表头,其中包含一个列表,其中包含从星期一开始的星期几。
The calendar grid. You know, each day in the current month, represented as a square in the grid.
日历网格。 您知道,当月的每一天,都以网格中的正方形表示。
Let’s write this up in a file called CalendarMonth.vue
. This will be our main component.
让我们将其写在一个名为CalendarMonth.vue
的文件中。 这将是我们的主要组成部分。
Now that we have some markup to work with, let’s go one step further and create required components.
现在,我们有了一些标记可以使用,让我们进一步走一步,创建所需的组件。
步骤2:标头组件 (Step 2: Header components)
In our header we have two components:
在标题中,我们有两个部分:
CalendarDateIndicator — showing currently selected month
CalendarDateIndicator-显示当前选定的月份
CalendarDateSelector — responsible for paginating between months
CalendarDateSelector-负责在两个月之间进行分页
Let’s start with CalendarDateIndicator
. This component will accept selectedDate
property which will be a Day.js object, format it properly and show it to the user.
让我们从CalendarDateIndicator
开始。 该组件将接受selectedDate
属性,该属性将是Day.js对象,对其进行正确的格式化并将其显示给用户。
{
{ selectedMonth }}
That was easy. Let’s go and create the month pagination component now. In the template it will contain three elements responsible for selecting previous, current and next month. To each of those elements we add an event listener that will fire appropriate method when the element is clicked.
那很简单。 现在开始创建月份分页组件。 在模板中,它将包含三个元素,分别负责选择上个月,当前和下个月。 我们为每个元素添加了一个事件侦听器 ,该事件侦听器将在单击该元素时触发适当的方法。
<
Today
>
Then in the script section we set up two props that the component will accept:
然后,在脚本部分中,我们设置组件将接受的两个道具:
currentDate
that will allow us to come back to current month when clicked on the “Today” button当您点击“今天”按钮时,
currentDate
可使我们返回到当前月份selectedDate
that will tell us what month is currently selectedselectedDate
,它将告诉我们当前选择的月份
Also we will define methods responsible for calculating new selected date based on currently selected date using Day.js subtract and add methods. Each method will also $emit an event to the parent component with the newly selected month. This will allow us to keep the value of selected date in one place which will be our CalendarMonth.vue
component and pass it down to all child components (header, calendar grid).
另外,我们将使用Day.js 减去和添加方法定义负责根据当前选定日期计算新选定日期的方法。 每种方法还将使用新选择的月份将事件$ emit事件发送到父组件。 这将使我们能够将选定日期的值保留在一个位置,该值将成为CalendarMonth.vue
组件,并将其向下传递给所有子组件( 标头,日历网格 )。
Now, let’s go back to the CalendarMonth.vue
component and use our newly created components.
现在,让我们回到CalendarMonth.vue
组件并使用我们新创建的组件。
To use them we first need to import and register the components, also we need to create the values that will be passed as props to those components:
要使用它们,我们首先需要导入和注册组件,还需要创建将作为道具传递给这些组件的值:
today
— properly formatted today’s date, used as a value for “Today” pagination buttontoday
-正确格式化了今天的日期,用作“今天”分页按钮的值selectedDate
— currently selected date (set to today’s date initially)selectedDate
—当前选择的日期(最初设置为今天的日期)
The last thing we need to do before we can render the components is to create a method that will be responsible for changing the value of selectedDate
. This method will be fired when the event from the pagination component is received.
在渲染组件之前,我们需要做的最后一件事是创建一个负责更改selectedDate
值的方法。 收到来自分页组件的事件时将触发此方法。
Having that done we can finally render our calendar header.
完成之后,我们终于可以渲染我们的日历标题了。
:selected-date="selectedDate"
class="calendar-month-header-selected-month"
/> :current-date="today"
:selected-date="selectedDate"
@dateSelected="selectDate"
/>
This is a good spot to stop and see what we have so far.
这是一个停下来看看我们到目前为止所拥有的好地方 。
So we have our calendar header ready, let’s move forward and create components for our calendar grid.
现在我们准备好日历标题了,让我们继续前进,为日历网格创建组件。
步骤3:日历网格组件 (Step 3: Calendar grid components)
Here, again, we have two components:
同样,这里有两个部分:
CalendarWeekdays— shows the names of the weekdays
CalendarWeekdays-显示工作日的名称
CalendarMonthDayItem— represents a single day in the calendar
CalendarMonthDayItem —代表日历中的一天
CalendarWeekdays component contains a list that iterates through the weekday labels (using v-for directive) and renders that label for each weekday. In the script section we need to define our weekdays and create a computed property to make it available in the template and cache the result not to re-calculate it in the future.
CalendarWeekdays组件包含一个列表,该列表遍历工作日标签( 使用v-for指令 ),并在每个工作日呈现该标签。 在脚本部分,我们需要定义工作日并创建一个计算属性,以使其在模板中可用,并缓存结果,以免日后对其进行重新计算。
- {
{ weekday }}
CalendarMonthDayItem is a list item that receives a day
property that is an object and a boolean prop, isToday
, that allows us to style the list item properly for today’s date. We also have one computed property that formats the received day object to our desired format.
CalendarMonthDayItem是一个列表项,它接收一个day
属性(一个对象)和一个布尔属性isToday
,该属性允许我们为今天的日期正确设置列表项的样式。 我们还有一个计算属性,用于将收到的日对象格式化为所需格式。
class="calendar-day"
:class="{
'calendar-day--not-current': !isCurrentMonth,
'calendar-day--today': isToday
}"
>
{
{ label }}
Okay, now that we have these two let’s go back to our CalendarMonth
component and see how we can use them.
好的,现在我们有了这两个,让我们回到CalendarMonth
组件,看看如何使用它们。
Again, to use the new components we first need to import and register them. We also need to create a computed property that will return an array of objects representing our days. Each day contains date
property and isCurrentMonth
property.
同样,要使用新组件,我们首先需要导入和注册它们。 我们还需要创建一个计算属性,该属性将返回代表我们日子的对象数组。 每天包含date
属性和isCurrentMonth
属性。
Then in the template we can render our components. Again we use v-for directive to render required number of day elements.
然后,我们可以在模板中渲染组件。 同样,我们使用v-for指令来呈现所需数量的天元素。
...
v-for="day in days"
:key="day.date"
:day="day"
:is-today="day.date === today"
/>
Okay it starts to look good now, have a look where we are at this CodeSandbox.
好的,现在开始看起来不错, 看看我们在此CodeSandbox中的位置 。
It looks nice, but as you probably noticed, the template only contains static data at the moment. The month is hardcoded as July and the day numbers are hardcoded as well. In the next step we will change that by calculating what date should be shown on a specific month. Let’s dive into the code!
看起来不错,但是您可能已经注意到,模板目前仅包含静态数据。 月份硬编码为7月,日期编号也硬编码。 在下一步中,我们将通过计算应在特定月份显示的日期来更改该日期。 让我们深入研究代码!
步骤4:设定当月日历 (Step 4: Setting up current month calendar)
Let’s think how we can calculate what date should be shown on a specific month. That’s where Day.js comes into play. It provides all the data we need to properly place dates on the correct days of the week for a given month using real calendar data. It allows us to get and set anything from the start date of a month to all the date formatting options we need to display the data.
让我们考虑一下如何计算特定月份应显示的日期。 那就是Day.js发挥作用的地方。 它提供了我们需要的所有数据,以使用真实的日历数据将日期正确地放置在给定月份的一周的正确日期中。 它使我们能够获取和设置从一个月的开始日期到显示数据所需的所有日期格式选项的任何内容。
We will:
我们会:
- Get the current month 获取当前月份
- Calculate where the days should be placed (weekdays) 计算应该放置的日期(工作日)
- Calculate the days for displaying dates from the previous and next months 计算显示上个月和下个月的日期的天数
- Put all of the days together in a single array 将所有日子集中在一起
We already have Day.js imported in our CalendarMonth
component. We’re also going to lean on a couple of Day.js plugins for help. WeekDay helps us set the first day of the week. Some prefer Sunday as the first day of the week. Other prefer Monday. Heck, in some cases, it makes sense to start with Friday. We’re going to start with Monday.
我们已经在CalendarMonth
组件中导入了Day.js。 我们还将依靠一些Day.js插件寻求帮助。 WeekDay可帮助我们设置一周的第一天。 有些人将星期日作为一周的第一天。 其他人更喜欢星期一。 有时候,从星期五开始是有意义的。 我们将从星期一开始。
The weekOfYear plugin returns the numeric value for the current week out of all weeks in the year. There are 52 weeks in a year, so we’d say that the week starting January 1 is the the first week of the year, and so on.
weekOfYear插件返回一年中所有星期中当前星期的数值。 一年共有52周,因此我们可以说从1月1日开始的那一周是一年的第一周,依此类推。
So here what we put into CalendarMonth.vue
所以这是我们放入CalendarMonth.vue