日历组件
日历组件在平时项目中是非常常见的一个组件,那么我们就来实现一下。实现之前要知道有哪几个需要注意的点。
-一、日期要怎么显示?
每个月需要展示不同的日期,那么我们就来找找规律吧。
1.每个月需要展示固定的42天。
2.每个月的一号是周几,比如上图中的一号是周二,那么我们可以使用时间的getDay函数获取一号的是2,然后让第一天往前推2天,就是我们需要展示在日历上的第一天了。
- 二、日期框里的年月不能根据输入框里的日期变化,因为后面还会涉及到切换问题
- 三、每次切换都要更新日历板中的日期,使其与对应月份展示的相同
- 四、每次点击日历中的日期需要隐藏日历板(点击切换年月按钮不隐藏)
- 五、点击日历板上的日期之后再次点击输入框会展示对应月份对应时间,对应时间需要给一个特殊展示(如上图蓝色部分)
- 六、不是当月的时间需要和当前月的日期有区分(颜色区分)
- 七、选中了某一天需要更新父组件中的数据
- 八、输入框中录入年月日,点击enter键,再次点击input会弹出对应的日期并且标蓝色
上面就是封装这个日历所需要注意到的点,那么下面看代码吧;
代码实现
父组件
<template>
<div id="datePicker">
<Calendar :time="time" v-model="time"></Calendar>
<div style="display: inline-block">{{time}}</div>
</div>
</template>
<script>
import Calendar from '../components/calendar';
export default {
name: 'datePicker',
components: {
Calendar
},
data() {
return {
time: new Date()
};
},
methods: {}
};
</script>
<style>
</style>
子组件
<template>
<div id="calendar" v-click-outside>
<input type="text" ref="input" class="input" :value="formatterTime" @focus="handlClick" @keydown="enterInput">
<div class="content" v-if="isShow">
<div class="title">
<div class="oper">
<span class="iconfont icon-shuangjiantouzuo" @click="handlChangeDate('year',-1)"></span>
<span class="iconfont icon-zuojiantou" @click="handlChangeDate('month',-1)"></span>
</div>
<div class="date">{{`${timeCopy.year}年${timeCopy.month}月`}}</div>
<div class="oper">
<span class="iconfont icon-youjiantou" @click="handlChangeDate('month',+1)"></span>
<span class="iconfont icon-shuangjiantouyou" @click="handlChangeDate('year',+1)"></span>
</div>
</div>
<div class="week">
<span class="week-item" v-for="(v,i) in weekList" :key="i">{{v}}</span>
</div>
<ul class="days">
<li class="days-row" v-for="(v,i) in 6" :key="i">
<span class="days-item" v-for="(vs,j) in 7" :key="j"
@click="chooseDay(getCurrentMonthDays[i*7+j])"
:class="[
{'gray-day':getCurrentMonthDays[i*7+j].getMonth()+1!=timeCopy.month},
{'high-light':getCurrentMonthDays[i*7+j].getDate()==timeCopy.day}
]"
>
{{getCurrentMonthDays[i*7+j].getDate()}}
</span>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: 'calendar',
props: {
time: {type: Date}
},
directives: {
'clickOutside': {
bind(el, builing, vNode) {
let handler = (e) => {
let input = document.getElementsByClassName('input')[0];
let title = document.getElementsByClassName('title')[0];
if (!title) return;
if (!input.contains(e.target) && !title.contains(e.target)) {
vNode.context.isShow = false;
}
};
document.addEventListener('click', handler);
}
}
},
computed: {
formatterTime() {
let {year, month, day} = this.timeCopy;
return `${year}-${month}-${day}`;
},
getCurrentMonthDays() {
let {year, month} = this.timeCopy;
let firstDay = new Date(`${year}-${month}-01`);
let week = firstDay.getDay();
let beginDay;
if (week === 0) {
beginDay = firstDay - 7 * 1000 * 60 * 60 * 24;
} else {
beginDay = firstDay - week * 1000 * 60 * 60 * 24;
}
let dateArr = [];
for (let i = 0; i < 42; i++) {
dateArr.push(new Date(beginDay + i * 1000 * 60 * 60 * 24));
}
return dateArr;
}
},
data() {
return {
isShow: false,
timeCopy: {},
weekList: ['日', '一', '二', '三', '四', '五', '六']
};
},
methods: {
enterInput(e) {
if (e.keyCode == '13') {
let val = e.srcElement.value;
let {month, day} = this.setData(new Date(val));
if (isNaN(month) || isNaN(day)) {
alert('输入不合法哦');
return;
}
this.timeCopy = this.setData(new Date(e.srcElement.value));
this.$emit('input', new Date(e.srcElement.value));
this.isShow = false;
this.$refs.input.blur();
}
},
handlChangeDate(flag, val) {
this.timeCopy[flag] += val;
if (this.timeCopy.month > 12) {
this.timeCopy.month = 1;
this.timeCopy.year += 1;
}
if (this.timeCopy.month < 1) {
this.timeCopy.month = 12;
this.timeCopy.year -= 1;
}
},
chooseDay(date) {
this.$emit('input', date);
this.timeCopy = this.setData(date);
},
setData(time) {
let year = time.getFullYear();
let month = time.getMonth() + 1;
let day = time.getDate();
return {year, month, day};
},
handlClick() {
this.isShow = true;
}
},
created() {
this.timeCopy = this.setData(this.time);
}
};
</script>
<style lang="less" scoped>
* {
margin: 0;
padding: 0;
}
ul li {
list-style: none;
}
#calendar {
@commonColor: #00a0ff;
text-align: center;
display: inline-block;
input {
width: 268px;
background-color: #fff;
border-radius: 4px;
border: 1px solid #dcdfe6;
color: #606266;
height: 40px;
line-height: 40px;
outline: none;
padding: 0 15px;
}
.content {
user-select: none;
position: absolute;
width: 300px;
z-index: 100;
background: #fff;
color: #606266;
border: 1px solid #e4e7ed;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
border-radius: 4px;
line-height: 30px;
margin: 5px 0;
.title {
display: flex;
height: 25px;
line-height: 25px;
margin-bottom: 15px;
.oper {
font-size: 16px;
cursor: pointer;
flex: 1;
}
.date {
flex: 1;
}
}
.week {
display: flex;
border-bottom: 1px solid #bfc4cc;
.week-item {
flex: 1;
font-size: 14px;
}
}
.days {
display: flex;
flex-direction: column;
padding: 5px;
font-size: 12px;
justify-content: space-evenly;
.days-row {
display: flex;
.days-item {
flex: 1;
cursor: pointer;
&:hover {
color: @commonColor;
}
}
}
}
}
.gray-day {
color: #bfc4cc !important;
}
.high-light {
color: @commonColor;
}
}
</style>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。