背景
最近项目中需要使用到日历组件,并且需要在日视图上显示当天的日报数据,最终实现的效果图如下所示:
看起来还是有些丑的,不过在此还是把实现的过程记录一下。
1、需求拆分
从上图来看,我们的界面主要分为三部分,日历周视图、时间侧边栏、右侧日程内容,下面就对这三部分内容分别进行实现。
2、顶部日历周视图
顶部日历周视图采用了自定义组件去实现,以便于后面需要用到它的地方可以进行复用。
2.1 日历周试图的实现
1)在微信开发者工具中创建一个工程,并且在工程中与 pages文件夹同级下,新建一个 component文件夹。
2)在component文件夹下新建 calendarweek文件夹,并且在微信开发者工具右键 点击 calendarweek文件夹, 选择 “新建Component” 输入 "calendarweek"文件名,如下图所示结构:
2.2 calendarweek.js 的实现
// component/calendarweek.js
var utils = require('../../utils/util')
Component({
/**
* 组件的属性列表
*/
properties: {
setdate: {
type: String,
value: ''
},
},
/**
* 组件的初始数据
*/
data: {
dateList: [], // 日历数据数组
swiperCurrent: 0, // 日历轮播正处在哪个索引位置
dateCurrent: new Date(), // 正选择的当前日期
dateCurrentStr: '', // 正选择日期的 id
dateMonth: '1月', // 正显示的月份
dateListArray: ['日', '一', '二', '三', '四', '五', '六'],
},
ready: function () {
//获取高度
const query = this.createSelectorQuery()
query.select('#calendarweekheight').boundingClientRect()
query.selectViewport().scrollOffset()
query.exec((res)=>{
// console.log(res[0].height)
let a = res[0].height
this.triggerEvent('getHeader',a)
})
var today = utils.formatTime2(new Date());
this.setData({
today,
});
//2021-03-19
if(this.properties.setdate != ''){
let year = this.properties.setdate.substring(0,4);
let month = this.properties.setdate.substring(5,7);
let day = this.properties.setdate.substring(8);
var d = new Date(year,month - 1,day);
this.initDate(-5, 2, d); // 日历组件程序 -4左表示过去4周 右1表示过去一周
} else {
var d = new Date();
this.initDate(-5, 2, d); // 日历组件程序 -4左表示过去4周 右1表示过去一周
}
},
/**
* 组件的方法列表
*/
methods: {
tiaotime(e) {
let data = e.detail.value.split("-")
var d = new Date(Number(data[0]), Number(data[1]) - 1, Number(data[2]));
this.setData({
dateList: []
})
this.initDate(-5, 2, d); // 日历组件程序 -4左表示过去4周 右1表示过去一周
},
// 日历组件部分
// ----------------------------
initDate(left, right, d) {
var month = utils.addZero(d.getMonth() + 1),
year = utils.addZero(d.getFullYear()),
day = utils.addZero(d.getDate());
for (var i = left; i <= right; i++) {
this.updateDate(utils.DateAddDay(d, i * 7)); //多少天之后的
}
this.setData({
swiperCurrent: 5,
dateCurrent: d,
dateCurrentStr: d.getFullYear() + '-' + month + '-' + day,
dateMonth: month + '月',
dateYear: year + '年',
dateCurrentStr: year + "-" + month + "-" + day,
});
},
// 获取这周从周日到周六的日期
calculateDate(_date) {
var first = utils.FirstDayInThisWeek(_date);
var d = {
'month': first.getMonth() + 1,
'days': [],
};
for (var i = 0; i < 7; i++) {
var dd = utils.DateAddDay(first, i);
var day = utils.addZero(dd.getDate()),
year = utils.addZero(dd.getFullYear()),
month = utils.addZero(dd.getMonth() + 1);
d.days.push({
'day': day,
'id': dd.getFullYear() + '-' + month + '-' + day,
'ids': dd.getFullYear() + ',' + month + ',' + day,
});
}
return d;
},
// 更新日期数组数据
updateDate(_date) {
var week = this.calculateDate(_date);
this.setData({
dateList: this.data.dateList.concat(week),
});
},
// 日历组件轮播切换
dateSwiperChange(e) {
const lastIndex = this.data.swiperCurrent,
currentIndex = e.detail.current,
dateList = this.data.dateList
let flag = false,
key = 'lastMonth' //判断是左划还是右
if (lastIndex > currentIndex) {
lastIndex === 7 && currentIndex === 0 ?
flag = true :
null
} else {
lastIndex === 0 && currentIndex === 7 ?
null :
flag = true
}
if (flag) {
key = 'nextMonth'
}
if (key == 'lastMonth') {
let nowindex = currentIndex - 3;
let uptime = currentIndex - 4;
let a = 0;
if (nowindex < 0) {
nowindex = nowindex + 8;
a = 0
}
if (uptime < 0) {
uptime = uptime + 8
}
let seltime = dateList[nowindex].days[a].ids.split(',')
var week = this.calculateDate(utils.formatTime(utils.DateAddDay(new Date(Number(seltime[0]), Number(seltime[1]) - 1, Number(seltime[2])), -1)));
dateList[uptime] = week
this.setData({
dateList: dateList
})
}
if (key == 'nextMonth') {
let indexne = 0
let aa = 0
if (currentIndex == 7) { //要更新的下标
indexne = 0
aa = 1
} else {
indexne = currentIndex + 1
aa = currentIndex + 2
}
if (aa == 8) {
aa = 0
}
//aa 要更新的数组下标 datanex要往后查询一周的日期
let datanex = dateList[indexne].days[6].ids.split(',')
//获取下一周的
var week = this.calculateDate(utils.formatTime(utils.DateAddDay(new Date(Number(datanex[0]), Number(datanex[1]) - 1, Number(datanex[2])), 1)));
dateList[aa] = week
this.setData({
dateList: dateList
})
}
var dDateFormat = this.data.dateList[currentIndex].days[3].ids.split(',');
this.setData({
swiperCurrent: currentIndex,
dateMonth: dDateFormat[1] + '月',
dateYear: dDateFormat[0] + "年"
})
},
// 获得日期字符串
getDateStr: function (arg) {
if (utils.type(arg) == 'array') {
return arr[0] + '-' + arr[1] + '-' + arr[2] + ' 00:00:00';
} else if (utils.type(arg) == 'date') {
return arg.getFullYear() + '-' + (arg.getMonth() + 1) + '-' + arg.getDate() + ' 00:00:00';
} else if (utils.type(arg) == 'object') {
return arg.year + '-' + arg.month + '-' + arg.day + ' 00:00:00';
}
},
// 点击日历某日
chooseDate(e) {
var str = e.currentTarget.id;
console.log("当前选择的日期:",str);
this.setData({
dateCurrentStr: str
});
this.triggerEvent('myevent', {
data: str
})
},
}
})
2.3 calendarweek.wxml 的实现
<!--component/calendarweek.wxml-->
<view id="calendarweekheight" class="date-choose shrink border-bottom10">
<view class="weekday">
<block wx:for-item="weekday" wx:for="{{dateListArray}}" wx:key="{{index}}">
<text class="week">{{weekday}}</text>
</block>
</view>
<swiper class="date-choose-swiper" circular="true" indicator-dots="{{false}}" current="{{swiperCurrent}}"
bindchange="dateSwiperChange">
<block wx:for="{{dateList}}" wx:for-item="date" wx:key="date.id">
<swiper-item class="swiper-item">
<view class="dateday">
<block wx:for="{{date.days}}" wx:for-item="day" wx:key="{{day.id}}">
<view class="day" id="{{day.id}}" bindtap="chooseDate">
<text class="{{dateCurrentStr==day.id?'active':'nomal'}}{{today==day.id?' reds':''}}">{{day.day}}</text>
</view>
</block>
</view>
</swiper-item>
</block>
</swiper>
</view>
2.4 、calendarweek.wxss的实现
/* component/calendarweek.wxss */
.date-choose {
background: #fff;
overflow: hidden;
height: auto;
}
.data-month {
width: 100%;
align-items: center;
padding: .5rem .35rem;
text-align: left;
color: #333;
font-size: 24rpx;
}
.date-choose-swiper {
flex-grow: 1;
height: 120rpx;
}
.swiper-item {
display: flex;
flex-direction: column;
}
.weekday,
.dateday {
display: flex;
justify-content: space-between;
align-items: center;
text-align: center;
flex-wrap: wrap;
flex-grow: 1;
}
.week,
.day {
width: 14.286%;
flex-basis: 14.286%;
}
.week {
font-size: 20rpx;
font-family: PingFang SC;
/* font-weight: bold; */
color: #000000;
}
.day text {
position: relative;
color: #333333;
}
.day .active:before {
/* 圈圈 */
content: "";
position: absolute;
width: 55rpx;
height: 55rpx;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
border: 1px solid #286AFE;
border-radius: 100%;
background-color: #286AFE;
/* opacity: 0.8; */
z-index: -1;
}
.day text.active {
color: #ffffff;
font-size: 24rpx;
font-family: PingFang SC;
/* font-weight: bold; */
}
.nomal{
font-size: 24rpx;
font-family: PingFang SC;
/* font-weight: bold; */
}
.day text.reds {
color: #ff0000;
font-size: 24rpx;
}
/*开始*/
.headerone {
width: 100%;
height: auto;
font-size: 24rpx;
}
.headerone .ra {
margin-right: 20rpx;
}
.headerone .radio-group {
margin: 20rpx 0 20rpx 30rpx;
}
.headertwo {
width: 100%;
height: auto;
font-size: 24rpx;
margin-top: 10rpx;
margin-bottom: 26rpx;
}
.headertwo .le image {
width: 70rpx;
height: 70rpx;
border-radius: 10px;
margin-left: 30rpx;
margin-right: 20rpx
}
.headertwo .ri {
flex: 1;
margin-right: 30rpx;
border-radius: 6px;
box-shadow: 0px 1px 6px 0px rgba(198, 198, 198, 0.5);
}
.headertwo .ri .one {
width: 100%;
padding-top: 24rpx;
padding-bottom: 24rpx
}
.headertwo .ri .one view .jiao {
margin: 0 16rpx;
border: 15rpx solid;
border-color: #ffffff #ffffff #b3b3b3 #ffffff;
}
.xi {
background: red;
color: #ffffff;
padding: 3px 10px;
border-radius: 6px 0px 0 6px;
}
.headertwo .ri .one view view.jiaos {
margin: 0 16rpx;
border: 15rpx solid;
margin-top: 14rpx;
border-color: #b3b3b3 #ffffff #ffffff #ffffff;
}
.headertwo .ri .two {
width: 100%;
overflow: hidden;
transition: all 0.5s
}
.headertwo .ri .two .body {
width: 100%;
padding-top: 24rpx;
padding-bottom: 24rpx;
}
2.5 、util.js 的实现
// 时间格式转换 yyyy-mm-dd
function formatTime2(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
var hour = date.getHours()
var minute = date.getMinutes()
var second = date.getSeconds()
return [year, month, day].map(formatNumber).join('-')
}
// 时间格式转换 yyyy/mm/dd
function formatTime(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
var hour = date.getHours()
var minute = date.getMinutes()
var second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
// 两位数自动补零
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
// 计算变化多少天后的日期
function DateAddDay(d, days) {
var d = new Date(d);
return new Date(d.setDate(d.getDate() + days));
}
// 获得本周周日的日期
function FirstDayInThisWeek(d) {
var d = new Date(d);
return DateAddDay(d, 0 - d.getDay());
}
module.exports = {
formatTime,
formatTime2,
DateAddDay,
addZero: formatNumber,
FirstDayInThisWeek
}
至此,canlendar 组件的实现已经完成,这个时候就可以进行使用了。
3、canlendar 组件的使用
在pages 文件夹下新建 如下图所示的文件:
然后在calendarday.json 文件中添加如下代码:
{
"usingComponents": {
"calendarweek":"../../component/calendarweek/calendarweek"
}
}
其中 "../../component/calendarweek/calendarweek" 就是我们自定义组件的路径;
"calendarweek" 是我们给组定义组件取得别名,当然你可以可以替换为其它名字,但在使用得时候,你这个地方定义的是什么名字,使用的时候就要用什么名字。
具体在 calendarday.wxml 文件的实现中使用到了我们自定义的组件。
下面看下calendarday 其它文件的具体实现代码。
3.1、calendarday.js 的实现
// pages/calendarday/calendarday.js
Page({
/**
* 页面的初始数据
*/
data: {
// 页面总高度将会放在这里
windowHeight: 0,
calendarWeekHeight: 0,
// scroll-view的高度
scrollViewHeight: 0,
title:'',
hour_titles: Array.from({ length: 24 }).map(function (value, index) {
var hour = (index + 1) % 24;
return ((hour < 10) ? "0" : "") + hour + ":00";
}),
day_hour_items: Array.from({length:24}).map((value,index)=>index+1),
todo_item_sizes: Array.from({ length: 24 }).map(function(){
return {
width:1,
height :1
}
}),
bar_item_sizes: Array.from({ length: 24 }).map(function () {
return {
width: 1,
height: 1
}
}),
calendarList:[],
curDate:'',
testData:[{"key":"测试1","top":1,"height":1},
{"key":"测试sssss","top":1,"height":1},
{"key":"测试sssss","top":1,"height":1},
{"key":"测试sssss","top":1,"height":1},
{"key":"测试3","top":2,"height":2},
{"key":"测试3","top":2,"height":3},
{"key":"测试5","top":10,"height":4},
{"key":"测试6","top":1,"height":4},
{"key":"测试7","top":30,"height":5},
{"key":"测试8","top":5,"height":10},
{"key":"测试8","top":60,"height":10}],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
//获取选择的日历数据
onMyEvent: function(e){
console.log('选择的日期:',e.detail);
},
//获取组件高度
getHeader: function(e){
console.log('获取日历组件的高度:',e.detail);
let calendarWeekHeight = e.detail;
wx.getSystemInfo({
success: res => {
this.data.windowHeight = res.windowHeight;
}
});
let scrollViewHeight = this.data.windowHeight - calendarWeekHeight;
console.log("scrollViewHeight = ",scrollViewHeight);
// 算出来之后存到data对象里面
this.setData({
scrollViewHeight: scrollViewHeight
});
},
})
3.1、calendarday.wxml 的实现
<!--pages/calendarday/calendarday.wxml-->
<view class="page">
<calendarweek id="calendarweekmenu" bindmyevent="onMyEvent" bindgetHeader="getHeader"></calendarweek>
<scroll-view scroll-y="true" style="height: {{scrollViewHeight}}px" >
<view class='day-content'>
<view class='day-hour-bar'>
<view class='day-hour-bar-item' wx:for="{{hour_titles}}" wx:key="{{index}}">
<view id="hour-bar-{{index}}" class='item' bindtap='handleTap' style='height:calc(50rpx + {{bar_item_sizes[index].height}} * 50rpx);line-height:calc(50rpx + {{bar_item_sizes[index].height}} * 50rpx)'>
<block wx:if="{{item === '00:00'}}">
<text> </text>
</block>
<block wx:else>
<text>{{item}}</text>
</block>
</view>
<view wx:if="{{index != 0}}" class="day-hour-context-item-div"></view>
</view>
</view>
<view class="day-content-item" >
<view class="day-content-item-sub" wx:for="{{testData}}">
<view class="day-content-show" style="margin-top:calc({{item.top}}*10rpx); height:calc({{item.height == 0 ? 1 : item.height}} * 60rpx)" >
<text class="day-content-show-tex">{{item.key}}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
3.3、calendarday.wxss 的实现
/* pages/calendarday/calendarday.wxss */
.page {
overflow: scroll;
width: 100vw;
height: 100vh;
}
.day-hour-bar {
position: absolute;
z-index: 2;
width: 100%;
}
.day-content {
position: relative;
z-index: 1;
width: 100%;
}
.day-hour-bar-item {
height: 60rpx;
display: flex;
flex-direction: row;
background-color: #F6F8FA;
/* background-color: #B8F1F1;
border: 3rpx solid #FCBDAD; */
/* line-height: 100rpx; */
text-align: center;
}
.item {
display: inline-block;
width: 70rpx;
margin-left: 30rpx;
margin-top: 10rpx;
font-size: 20rpx;
font-family: PingFang SC;
color: #999999;
}
.day-hour-context-item-div{
background-color: #DBDCDC;
width: 100%;
margin-left: 30rpx;
height: 2rpx;
margin-right: 20rpx;
}
.day-content-item{
position: relative;
z-index: 3;
display: flex;
margin-left: 110rpx;
width: 100%;
}
.day-content-item-sub{
width: fit-content;
}
.day-content-show{
border: 2rpx solid #DBDCDC;
border-left: 10rpx solid #00B853;
border-radius: 5px;
background-color: #ffffff;
margin-left: 10rpx;
overflow:hidden;
text-overflow:ellipsis;
width: auto;
}
至此,我们的日历/日视图的代码实现已经完成。
如果上面的内容对你有所帮助,不要忘记点个赞哟。
4、运行动态效果图