一、前言

最近公司要做一个酒店入住的小程序,不可避免的一定会使用到日历,而小程序没有内置的日历组件。在网上看了一下也没有非常适合需求的日历,于是自己写了一个。

二、代码

1. 原理分析

写一个日历只需要知道两件事情:

  1. 一个月有多少天;
  2. 每个月的第一天是星期几。

2. 功能分析

由于是酒店入住的日历,所以需要实现如下功能:

  1. 渲染日历,一般是从本月开始,到半年之后的日历
  2. 过去的日期不可选
  3. 实现点击获取入住日期、退房日期,以及改变相应日期的颜色和整个时间段的颜色

3. 数据分析

根据最后的功能实现,我生成的每个月的数据结构如下:

{
year: 2018,
month: 3,
fullMonth: '03',
allDays:[
{
day: 1,
fullDay: '01',
fullDate: '2018-03-01'
},
{
day: 2,
fullDay: '02',
fullDate: '2018-03-02'
},
//......
//(后面的数据同上)
]
}

year就是年份,month是月份,day是日期,fullDate是完整日期。
fullMonth和fullDay原本是两个不需要的数据,但是在点击日期改变颜色的时候用到了,因为小程序没有提供很好的处理数据的filter。当然这个问题也和我的个人水平有关,如果有哪位大神有更好的方法,请留言告诉我。我非常想去掉这两个数据。

4.代码分析

// calendar.js文件

Page({
data: {
week_list: ['日','一','二','三','四','五','六'],
startDate: '',
endDate: '',
date_click: 0
},
// 获取每月总天数
getAllDaysOfMonth(year,month) {
return new Date(year,month,0).getDate();
},
// 获取每月第一天是星期几
getFirstDayOfMonth(year,month) {
return new Date(year, month - 1, 1).getDay();
},
// 计算本月前空了几格
getEmptyGrids(year,month) {
// FirstDayOfMonth代表本月的第一天是星期几
const FirstDayOfMonth = this.getFirstDayOfMonth(year, month);
let emptyGrids = []; // 有空格的情况
if (FirstDayOfMonth > 0) {
for (let i = 0; i < FirstDayOfMonth; i++) {
emptyGrids.push({
'num': '',
'fullDate': 'x' //x是我自己定义的一个值,代表没有日期
});
}
// 将空格放入数组
return emptyGrids;
}else{
// 否则返回一个新数组
return [];
}
},
// 计算本月日历
getDaysOfThisMonth(year,month) {
let days = [];
const AllDaysOfMonth = this.getAllDaysOfMonth(year, month); let fullMonth = month.toString().length === 1 ? `0${month}`:month;
for (let i = 0; i < AllDaysOfMonth; i++) {
let day = i+1,
fullDay = day; fullDay = fullDay.toString().length === 1 ? `0${day}` : fullDay;
days.push({
day,
fullDay,
'fullDate': `${year}-${fullMonth}-${fullDay}`
});
}
// 返回每个月的具体日期
return days;
},
// 循环渲染日历
// 从本月开始渲染,n代表包括本月开始连续渲染几个月
fillCalendar(n) {
let year = this.data.cur_year,
month = this.data.cur_month,
fullMonth,
canlendar_data = []; // 计算年月以及具体日历
for (let i = this.data.cur_month; i < this.data.cur_month + n; i++) {
let EmptyGrids = this.getEmptyGrids(year, month);
let DaysOfThisMonth = this.getDaysOfThisMonth(year, month); // 把空格和具体日历合为一个数组
let allDays = [...EmptyGrids, ...DaysOfThisMonth]; // 对年份和月份的计算做一些判断
if (month > 12) {
year++;
month = 1;
fullMonth = '01'
canlendar_data.push({
year,
month,
fullMonth,
allDays });
month++;
}else{
fullMonth = month.toString().length === 1 ? `0${month}` : month;
canlendar_data.push({
year,
month,
fullMonth,
allDays });
month++; } } this.setData({
canlendar_data
})
},
onLoad() {
const date = new Date();
const cur_year = date.getFullYear();
const cur_month = date.getMonth() + 1;
const cur_day = date.getDate();
this.setData({
date,
cur_year,
cur_month,
cur_day
}) let month = this.data.cur_month.toString().length === 1 ? `0${this.data.cur_month}` : this.data.cur_month;
let day = this.data.cur_day.toString().length === 1 ? `0${this.data.cur_day}` : this.data.cur_day;
let nowDate = `${cur_year}-${month}-${day}`; this.setData({
nowDate
}) this.fillCalendar(6);
},
// 点击日期
chooseDate(e) {
const year_click = e.currentTarget.dataset.year;
const month_click = e.currentTarget.dataset.month;
const day_click = e.currentTarget.dataset.day;
console.log(year_click,month_click,day_click);
// 如果是空格或者以前的日期就直接返回
if(day_click === ''||`${year_click}-${month_click}-${day_click}` < this.data.nowDate) {
return;
} // 获取点击对象的id
let id = e.currentTarget.dataset.id; // data_click为0代表选择的是入住日期,否则就是离店日期
if (this.data.date_click == 0){
// 选择入住日期
this.setData({
startDate: `${year_click}-${month_click}-${day_click}`,
date_click: 1
})
}else {
let newDay = new Date(Date.parse(id));
let oldDay = new Date(Date.parse(this.data.startDate)); // 判断第二次点击的日期在第一次点击的日期前面还是后面
if (newDay > oldDay) {
this.setData({
endDate: `${year_click}-${month_click}-${day_click}`,
date_click: 2
})
}else{
this.setData({
startDate: `${year_click}-${month_click}-${day_click}`,
endDate: '',
date_click: 1
})
}
}
}
})
<!-- calendar.wxml文件 -->

<view class="container">
<view id="week">
<view class="week-item {{idx===0||idx===6?'relax':''}}" wx:for="{{week_list}}" wx:for-index="idx">{{item}}</view>
</view>
<scroll-view scoll-y="true">
<view class="month-block" wx:for="{{canlendar_data}}" wx:for-item="canlendar_item">
<view class="month-title">{{canlendar_item.year}}年{{canlendar_item.month}}月</view>
<view class="month-content">
<view class="month-day {{item.fullDate<nowDate?'gray':''}} {{startDate===item.fullDate?'startActive':''}} {{endDate===item.fullDate?'endActive':''}} {{item.fullDate>startDate&&item.fullDate<endDate&&startDate!==''&&endDate!==''?'midActive':''}}" bindtap="chooseDate" data-year="{{canlendar_item.year}}" data-month="{{canlendar_item.fullMonth}}" data-day="{{item.fullDay}}" data-id="{{item.fullDate}}" wx:for="{{canlendar_item.allDays}}">{{item.day}}</view>
</view>
</view>
</scroll-view>
</view>

{{idx===0||idx===6?'relax':''}} 是改变周六周日的颜色,
{{item.fullDate<nowDate?'gray':''}} 是改变过去日期的颜色,
{{startDate===item.fullDate?'startActive':''}} 判断点击的是入住日期,
{{endDate===item.fullDate?'endActive':''}} 判断点击的是离店日期,
{{item.fullDate>startDate&&item.fullDate<endDate&&startDate!==''&&endDate!==''?'midActive':''}} 改变入住日期和离店日期之间的日期颜色

四、结语

到此一个简单的日历就完成了,当然这个日历无法满足所有业务需求,但是基本的日历渲染功能以及点击选择功能都有。所以在业务需求之上对其进行小部分改变就可以了,希望大家可以留言指出我的问题,我也会进一步的改善这个日历代码。

微信小程序:手写日历组件的更多相关文章

  1. 微信小程序横版日历,tab栏

    代码地址如下:http://www.demodashi.com/demo/14243.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  2. [小程序开发] 微信小程序audio音频播放组件+api_wx.createAudioContext

    引言: audio是微信小程序中的音频组件,可以轻松实现小程序中播放/停止音频等自定义动作. 附上微信小程序audio组件的相关属性说明:https://mp.weixin.qq.com/debug/ ...

  3. 微信小程序页面调用自定义组件内的事件

    微信小程序页面调用自定义组件内的事件 page page.json { "usingComponents": { "my-component": ". ...

  4. 微信小程序(二十)-UI组件(Vant Weapp)-01按装配置

    1.官网 https://vant-contrib.gitee.io/vant-weapp/#/intro https://gitee.com/vant-contrib/vant-weapp 2.按装 ...

  5. 微信小程序开发05-日历组件的实现

    接上文:微信小程序开发04-打造自己的UI库 github地址:https://github.com/yexiaochai/wxdemo 我们这里继续实现我们的日历组件,这个日历组件稍微有点特殊,算是 ...

  6. 微信小程序——页面中调用组件方法

    我现在有一个弹层的组件(popup),组件里面定义了显示组件(showPopup)和隐藏组件(hidePopup)的方法. 我们如何在调用组件的页面中调用组件里面的方法呢? 在调用组件的页面写如下代码 ...

  7. 微信小程序 基本介绍及组件

    创建项目 微信开发工具深入介绍 https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html 基本项目目录 1. 配置 ...

  8. 微信小程序初探--写个扫雷分享给你玩

    闲暇里,想学一下微信小程序, 于是,用微信小程序原生做了个扫雷玩. 以下略作总结,分享给大家. 微信里下拉,输入[mini计算器], 看到这个图标的就是了: 说好的扫雷,怎么变成计算器了?原因后面解释 ...

  9. 微信小程序中的自定义组件

    微信小程序中的组件 前言 之前做小程序开发的时候,对于开发来说比较头疼的莫过于自定义组件了,当时官方对这方面的文档也只是寥寥几句,一笔带过而已,所以写起来真的是非常非常痛苦!! 好在微信小程序的库从 ...

  10. 微信小程序开发—快速掌握组件及API的方法

    微信小程序框架为开发者提供了一系列的组件和API接口. 组件主要完成小程序的视图部分,例如文字.图片显示.API主要完成逻辑功能,例如网络请求.数据存储.音视频播放控制,以及微信开放的微信登录.微信支 ...

随机推荐

  1. qt(一)

    一.Qt安装 qt离线安装地址:http://download.qt.io/archive/qt/ 参考教程:https://blog.csdn.net/u013934107/article/deta ...

  2. 如何建立自己的代理IP池,减少爬虫被封的几率

    如何建立自己的代理IP池,减少爬虫被封的几率 在爬虫过程中,难免会遇到各种各样的反爬虫,运气不好,还会被对方网站给封了自己的IP,就访问不了对方的网站,爬虫也就凉凉. 代理参数-proxies 首先我 ...

  3. LeetCode-045-跳跃游戏 II

    跳跃游戏 II 题目描述:给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 假设你总是可以到 ...

  4. awk讲义-1-快速入门

    awk讲义-1-快速入门 一.目标问题: 1.统计各个省份中城市的数量(一维数组) 2.统计城市中区县数量,要求输出格式:省份 城市 区县数量(二维数组) 3.求两个文件的交集 4.省市和市区两个文件 ...

  5. 【一】工程配置与电机控制part1

    前言 学校发的无刷电机: 我们准备的有刷电机: 带霍尔编码器! 电机参数: 名称:驰名电机(直流减速电机) 型号:JGA25-370 电压:12V 转数:1360r/min 做云台,核心是使用PID控 ...

  6. MYSQL如何在创建表时添加判断条件

    大家好,我是小皓. 一.背景 今天在博主练习MYS创建表操作时遇到一个语法报错,就想着来和大家分享一下MYSQL如何在创建表时添加判断条件: ERROR 1064 (42000): You have ...

  7. rancher更新集群证书

    进入rancher 等待更新完成 查看证书到期时间 >>>在rancher容器内部执行查看集群证书信息 for i in ls /var/lib/rancher/k3s/server ...

  8. ArcGIS Server创建站点失败Failed to register the activation group

    ArcGIS Server:10.6 解决方法:右键我的电脑-->管理-->服务,重启arcgis server 服务

  9. JSON.parse()和JSON.stringfy()区别

    JSON.parse() 用于从一个json格式字符串解析出json类型的数据,如: 注意事项:json格式字符串必须是写在一排的,且括号外面用单引号,里面的每一个字符串用双引号 JSON.strin ...

  10. java高级用法之:无所不能的java,本地方法调用实况

    目录 简介 JDK的本地方法 自定义native方法 总结 简介 相信每个程序员都有一个成为C++大师的梦想,毕竟C++程序员处于程序员鄙视链的顶端,他可以俯视任何其他语言的程序员. 但事实情况是,无 ...