共计 4541 个字符,预计需要花费 12 分钟才能阅读完成。
日历组件
日历组件在平时我的项目中是十分常见的一个组件,那么咱们就来实现一下。实现之前要晓得有哪几个须要留神的点。
- 一、日期要怎么显示?
每个月须要展现不同的日期,那么咱们就来找找法则吧。
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>
正文完