效果图:
功能:
- 可限制开始、结束日期
- 可在日期上附加信息
- 可指定某些日期不可用
<style lang="less">
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.clearfix:after {
content: '';
height: 0;
line-height: 0;
display: block;
visibility: hidden;
clear: both;
}
.calendar-wrapper {
width: 100%;
overflow: hidden;
box-sizing: border-box;
background-color: white;
min-height: 772rpx;
}
.pick-view {
margin: auto;
width: 400rpx;
height: 100rpx;
position: relative;
.pick-img {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
view{
width: 60rpx; height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
}
picker {
width: 130rpx;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.calendar-content {
width: 100%;
padding: 0rpx;
background-color: white;
box-sizing: border-box;
font-size: 24rpx;
.week {
margin-bottom: 40rpx;
> view {
flex: 1;
color: gray;
}
}
.day {
width: 100%;
> view {
position: relative;
float: left;
width: calc(100% / 7);
height: 80rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
}
.day-item {
font-size: 32rpx;
z-index: 200;
}
.not-view-month {
color: #999;
}
.disabled{
color: #ccc;
background-color: #f7f7f7;
}
.more-text {font-size:20rpx; height: 1em; color: #ccc;}
.round {
position: absolute;
background-color: black;
width: 100%;
height: 100%;
top:0;
left:0;
border-radius: 0;
z-index: 0;
}
.restime {
color: white;
.more-text {color: #fff;}
.not-view-month {
color: #e7e7e7;
}
}
}
}
</style>
<template>
<view class="calendar-wrapper">
<view class="calendar-content">
<view class="pick-view">
<view class="pick-img">
<view @tap="toPreMonth"><text class="icon-back"></text></view>
<view @tap="toNextMonth"><text class="icon-right"></text></view>
</view>
<picker @change="changePicker" value="{{viewTime.year}}-{{viewTime.month}}" start="{{startDay?startDay.formatDay:''}}" end="{{endDay?endDay.formatDay:''}}" fields="month" mode="date">
<view class="flex-center">
{{viewTime.year}}年{{viewTime.month}}月
</view>
</picker>
</view>
<view class="week flex-center">
<block wx:for="{{weeksCh}}" wx:key="{{item}}">
<view class="flex-center">{{item}}</view>
</block>
</view>
<view class="day clearfix">
<block wx:for="{{viewDays}}" wx:key="{{item.key}}">
<view @tap="changeResultTime({{item}})" :class="{'disabled':disableDaysIn[item.key] || (startDay && item.key<startDay.key) || (endDay && item.key>endDay.key),'not-view-month':!(item.month === viewTime.month && item.year === viewTime.year), 'restime': item.formatDay === current.formatDay}" class="flex-center">
<view class="day-item">
{{item.day}}
<view class="more-text" wx:if="{{moreText}}">{{moreText[item.key]}}</view>
</view>
<view style="background-color: {{color}}" wx-if="{{(item.formatDay === current.formatDay)}}" class="round"> </view>
</view>
</block>
</view>
</view>
</view>
</template>
<script>
import wepy from 'wepy'
import moment from 'moment'
export default class Calendar extends wepy.component {
// use: <calendar :value.sync="date" :min.sync="minDay" :max.sync="maxDay" :moreText.sync="moreText" :disableDays.sync="disableDays" @hanleConfirm.user="hanleConfirm" color="#f00"></calendar>
props = {
visible : {
type : Boolean,
default: false,
twoWay : true
},
value : {
type : String,
default: moment().format('YYYY-MM-DD')
},
min: {
//yyyy-MM-DD
type : String,
default: ''
},
max: {
//yyyy-MM-DD
type : String,
default: ''
},
moreText: {
type: Object,
default: {}
},
disableDays: {
type: Array,
default: []
},
color : {
type : String,
default: 'black'
}
};
data = {
weeksCh : ['一', '二', '三', '四', '五', '六', '日'],
// 数组最后导出的日期
current : {
year : '',
month : '',
day : '',
formatDay: '',
weekCh : ''
},
viewTime: {
year : '',
month : '',
day : '',
formatDay: '',
weekCh : ''
},
// 当前视图的day数组
viewDays: []
};
watch = {
moreText(n,o){console.log(n,o)},
current(n,o){
this.$emit('hanleConfirm', n);
},
value(newValue, oldValue) {
this._init(newValue)
}
};
computed = {
startDay(){
this.minTime = '';
if (this.min) {
this.minTime = moment(this.min);
return this._dealMoment(this.minTime);
}
return "";
},
endDay(){
this.maxTime = '';
if (this.max) {
this.maxTime = moment(this.max);
return this._dealMoment(this.maxTime);
}
return "";
},
disableDaysIn(){
let o = {};
this.disableDays.forEach(v=>{o[v]=true});
return o;
}
};
methods = {
// pick选择器改变了
changePicker(e) {
// 改变当前视图日期数组
this.viewDays = this._getData(moment(e.detail.value, 'YYYY-MM'))
},
// 点击上个月
toPreMonth() {
let time = moment(this.viewTime.formatDay).subtract(1, 'days');
if (this.startDay) {
//要显示的月小于最小允许的日期的月份
let m =time.startOf('month');
let m2 =this.minTime.startOf('month');
if (m<m2) return false;
}
this.viewDays = this._getData(time)
},
// 点击下个月
toNextMonth() {
let time = moment(this.viewTime.formatDay).endOf('month').add(1, 'days');
if (this.endDay) {
//要显示的月大于最大允许的日期的月份
let m =time.startOf('month');
let m2 =this.maxTime.startOf('month');
if (m>m2) return false;
}
this.viewDays = this._getData(time)
},
// 改变最后导出的时间
changeResultTime(item) {
const {disableDaysIn, startDay, endDay} = this;
if (disableDaysIn[item.key] || (startDay && item.key<startDay.key) || (endDay && item.key>endDay.key)) return false;
this._setTime(moment(item.formatDay));
},
};
/**
* _getData
* 返回当前视图日期数组
* @param time
* @return [{day:31,month:3,year:2018},{day:1,month:4,year:2018},...]
*/
_getData(time = moment()) {
this._setViewTime(time)
// 令时间变为当月1号的
const firstDay = time.startOf('month')
// 计算当月1号是星期几
const firstDayOfWeek = firstDay.format('E')
// 计算上个月多余时间
const last = this._calDate(firstDay.subtract(firstDayOfWeek - 1, 'days'), firstDayOfWeek - 1)
// 计算本月时间
const current = this._calDate(firstDay, firstDay.daysInMonth())
// 令时间变为本月末
const endDay = time.subtract(1, 'days')
// 当月与末是星期几
const endDayOfWeek = endDay.format('E')
// 计算下个月多余时间
const next = this._calDate(endDay.add(1, 'days'), 7 - endDayOfWeek)
return [...last, ...current, ...next]
}
/**
* _setTime
* 设定导出的时间
* @param time moment对象
*/
_setTime(time = moment()) {
let cur = this._dealMoment(time);
if (this.disableDaysIn[cur.key]) return {};
this.current = cur;
}
/**
* _setTime
* 设定当前日历的时间
* @param time moment对象
*/
_setViewTime(time = moment()) {
this.viewTime = this._dealMoment(time.startOf('month'))
}
/**
* _calDate
* 计算日期函数
* @param time moment对象
* @param length 返回数组的长度
* @return 返回日期数组
*/
_calDate(time, length) {
let arr = []
for (let i = 0; i < length; i++) {
arr.push(this._dealMoment(time))
time.add(1, 'days')
}
return arr
}
/**
* _dealMoment
* 处理moment对象
* @param time moment对象
* @return 返回一个Object{year, month, day, formatDay}
*/
_dealMoment(time) {
let formatDay = time.format('YYYY-MM-DD');
let {years, months, date} = time.toObject();
let key = formatDay.replace(/-/g, '');
return {
key,
year : years,
month : months + 1,
day : date,
weekCh : '周' + this.weeksCh[time.format('E') - 1],
formatDay,
}
}
/**
* _init
* 初始化
* @param value '2018-02-02' YYYY-MM-DD
*/
_init(value) {
this.viewDays = this._getData(moment(value));
this._setTime(moment(value));
this.$apply();
}
onLoad() {
this._init(this.value);
}
}
</script>