需求:咱们常常会在一些旅游、订票、酒店的页面中看到一些日历,这些日历有公历、农历、节假日、非节假日(调休)的标注,同时还有产品业务上的信息,好比 票价、余票等等。那如今开始造造轮子了,首先这个组件应该能够生成一个基本的日历,而且挂入一些扩展的信息在每一天的对象上。它可能看上去应该像这样⤵,或者更复杂。javascript
注:这是一个无关UI的组件,咱们要作的只是如何去构建一个这样的日历对象。java
日历的展示通常是一个表格,咱们须要将每一天的信息放入表格,生成一个月或几个月的完整日历。git
这个函数很是简单,它接收一个参数 date
,可变天数为 2 月,这里进行闰年判断便可。github
export function getMonthDays(date) {
const year = date.getFullYear()
const month = date.getMonth()
const leapYear = !(year % 4) && ((year % 100) || !(year % 400))
const days = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
return days[month]
}
复制代码
四年一闰,百年不闰,四百年再闰。npm
请注意这里参数的 date
, 颇有多是 new Date()
,好比当天为 2018-08-31
, 当用户得到正常的本月数据后,再直接设置月份,如向上翻到 6 月时就会出现问题,由于 6 月没有 31 天,此时的 date
会获得 7 月 1 号的日期,与预期不符。 因此在操做完 date
后必定要注意将日期设置为 1 号。数组
const days = utils.getMonthDays(date)
date.setDate(1)
复制代码
天天的信息应该包含一些公共的属性,这些属性来自于 date
对象中,还有一些源于用户的扩展字段。这里我用一个类来构造这个对象。函数
class Day {
constructor(dateObj, extension = {}) {
this.year = dateObj.getFullYear()
this.month = dateObj.getMonth()
this.date = dateObj.getDate()
this.day = dateObj.getDay()
this.dateText = utils.getChinaStandard(dateObj)
this.past = this.toDay.getTime() > dateObj.getTime()
this.today = utils.getChinaStandard(new Date()) === this.dateText
this.timestamp = dateObj.getTime()
const _self = this
Object.keys(extension).forEach(key => {
_self[key] = extension[key]
})
}
get toDay() {
const date = new Date()
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date
}
}
复制代码
注释:ui
这个对象是二维数组的形式,一个月中有若干周,每周的数组中存放天天的对象this
将 monthly
方法设计为静态方法,方便调用者经过 Kalendar.monthly()
快速构建某一个月的数据对象,
参数
date
对象static monthly({date, mount = {}, weekStart = 0, unifiedMount = {}}) {
const monthTable = []
const days = utils.getMonthDays(date)
date.setDate(1)
const day = date.getDay()
let skip = 0
if (day !== weekStart) skip = day - weekStart
for (let i = 0; i < days + skip; i += 7) {
const week = []
let num = 7
if (!i && skip) {
for (let k = 0; k < skip; k++) week.push(null)
num -= skip
}
for (let j = 0; j < num; j++) {
const dateText = utils.getChinaStandard(date)
week.push(new Day(date, Object.assign({}, unifiedMount, mount[dateText])))
if (date.getDate() >= days) break
date.setDate(date.getDate() + 1)
}
monthTable.push(week)
}
return monthTable
}
复制代码
unifiedMount
全部日期都要添加的多是票价,或者一些默认数据
var unifiedMount = {
total: 1000,
price: 550,
bg: 'info'
}
复制代码
mount
是一些具体日期的数据,好比某一天票已售罄,应该禁用这一天的选择
var mount = {
'2018-08-30': {
total: 0,
price: 550
},
'2018-08-31': {
total: 10,
price: 750
},
}
复制代码
在一些移动端的场景,每每是一次性获取三个月或更长的时间,滑动显示的效果。
我在 Kalendar
类中的构造函数中 返回了 _create()
函数,_create()
函数用户生成多个月的信息。
class Kalendar {
constructor({start, end, unifiedMount = {}, mount = {}, weekStart = 0} = {}) {
this.startTime = start
this.endTime = end
this.unifiedMount = unifiedMount
this.mount = mount
this.weekStart = weekStart
return this._create()
}
......
_create() {
const {mount, weekStart, unifiedMount} = this
const table = {}
let count = (this.endDate.getFullYear() * 12 + this.endDate.getMonth() + 1)
- (this.startDate.getFullYear() * 12 + this.startDate.getMonth() + 1)
if (count < 0) return null
let idx = 0
do {
const date = this.startDate
date.setMonth(date.getMonth() + idx)
const monthTable = Kalendar.monthly({date, mount, weekStart, unifiedMount})
table[utils.getChinaStandard(date, true)] = monthTable
count--
idx++
} while (count > 0)
return table
}
static monthly({date, mount = {}, weekStart = 0, unifiedMount = {}}) {
......
}
}
复制代码
相比生成一个月的数据,构造函数须要设置 日期起止值,在计算相差月份后,循环调用 monthly
函数,避免 startTime
和 endTime
是同一个月,没有月份差,这里使用 do ... while
至少产生一个月的数据。
为了得到更小的体积,我使用 rollup
构建组件代码,并使用 MIT
协议 npm publish
。