1、前言
学习Vue已经有很长一段时间了,也刚刚完成了一个Vue移动端项目,后面会陆续将个人在刚刚完成的这个Vue项目中碰到的坑进行汇总,然后以blog的形式分享出来。
2、vue的日期控制,第三方有很多,但只有年月选择的就很少,我在网上找,就没找到,然后自已就动改直接改造了,改造后的效果如下:
3、我选择的第三方控件是datepicker.vue
<template>
<div class="vue-datepicker">
<input @click.stop="selectYear=!selectYear" :value="current | dateFormat" type="text" readonly>
<div v-if="selectYear" class="vue-datepicker-wrap">
<div class="vue-datepicker-header" @click.stop="">
<span @click.stop="switchMonth(-1)" class="vue-datepicker-header-btn vue-datepicker-header-btn-pre"><span>
<span @click.stop="selectYear=!selectYear"
class="vue-datepicker-header-btn vue-datepicker-header-btn-day">
{{select.year}} 年 {{select.month}} 月
span>
<span @click.stop="switchMonth(1)" class="vue-datepicker-header-btn vue-datepicker-header-btn-next">>span>
div>
<div class="vue-datepicker-content">
<table>
<thead>
<th>日th>
<th>一th>
<th>二th>
<th>三th>
<th>四th>
<th>五th>
<th>六th>
thead>
<tbody>
<tr v-for="week of list">
<td v-for="weekday of week"
:class="{
'flag': weekday.flag,
'active': !weekday.flag && weekday.text == current.date
&& select.month == current.month && select.year == current.year}">
{{weekday.text}}
td>
tr>
tbody>
table>
<div v-if="selectYear" class="vue-date-picker-year-panel">
<ul ref="year">
<li v-for="y of years" @click.stop="pickYear(y)" :class="{'active': y == select.year}">{{y}}li>
ul>
<ul ref="month">
<li v-for="(m, $index) of months"
@click.stop="pickMonth($index + 1)"
:class="{'active': $index + 1 == select.month}">
{{m}}
li>
ul>
div>
div>
div>
div>
template>
<style lang="scss">
.vue-datepicker {
&>input { padding: 5px 10px;
width: 100px;
line-height: 24px;
border: 1px solid #BFCBD7;
border-radius: 3px;
font-size: 14px;
outline: none;
cursor: pointer;
&:focus { border: 1px solid #20a0ff;
}
}
.vue-datepicker-wrap {
width: 240px;
box-shadow: 2px 2px 8px #bdb8b8;
z-index: 999;
.vue-datepicker-header { padding: 0px 15px;
font-size: 14px;
text-align: center;
line-height: 36px;
border-bottom: 1px solid #ccc;
.vue-datepicker-header-btn { cursor: pointer;
&:hover { color: #008afe;
}
}
.vue-datepicker-header-btn-pre {
float: left;
}
.vue-datepicker-header-btn-day {
}
.vue-datepicker-header-btn-next {
float: right;
}
}
.vue-datepicker-content {
z-index: 988;
position: relative;
table { width: 100%;
border-collapse: collapse;
thead { line-height: 30px;
font-size: 12px;
background: #eee;
}
tbody {
tr { line-height: 28px;
td { font-size: 12px;
text-align: center;
cursor: pointer;
&.active, &.active:hover { color: #fff;
background: #008afe;
}
&.flag {
color: #999;
}
}
td:hover {
background: #eee;
}
}
}
}
.vue-date-picker-year-panel {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
ul { width: 50%;
height: 100%;
margin: 0;
padding-left: 0;
box-sizing: border-box;
overflow-y: auto;
float: left;
list-style: none;
li { font-size: 14px;
text-align: center;
line-height: 30px;
cursor: pointer;
&.active { color: #fff;
background: #007acc;
}
}
&:first-child {
border-right: 1px solid #007acc;
}
}
}
}
}
}
style>
<script>
export default {
props: {
moment: {
type: Number,
default() {
return new Date().getTime()
}
}
},
data() {
return {
show: false, // 控制日历面板的显示与隐藏
selectYear: false, // 控制年份的面板的显示和隐藏
current: '', // 已选择的日期时间。yyyy-MM-dd
select: { // 已选择的日期对象
year: '',
month: '',
date: '',
day: ''
},
currentMonthFirstDay: null, // 当前月的1号属于星期几
currentMonthEndDate: null, // 当前月的最后一天是几号
currentMonthEndDay: null, // 当前月的最后一天属于星期几
lastMonthEndDate: null, // 上个月的最后一天是几号
list: [], // 日历的二维数组
years: [], // 1900-2100
months: ['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月']
}
},
watch: {
select: {
handler(newVal) {
let pre
if (newVal.month == 1) {
pre = new Date(newVal.year - 1, 12, 0)
} else {
pre = new Date(newVal.year, newVal.month - 1, 0)
}
this.lastMonthEndDate = pre.getDate()
// 获取日历排表
this.getDateList()
},
deep: true
},
show(newVal) {
if (newVal) {
document.addEventListener('click', this.bindEvent)
} else {
document.removeEventListener('click', this.bindEvent)
}
},
// 打开年份选择器的时候使当前年份、月份出现在窗口顶部
selectYear(newVal) {
if (newVal) {
this.$nextTick(() => {
const year = this.$refs.year
const month = this.$refs.month
const y = year.getElementsByClassName('active')[0].innerHTML
const m = month.getElementsByClassName('active')[0].innerHTML
year.scrollTop = (y - 1900) * 30
month.scrollTop = (this.select.month - 1) * 30
})
}
}
},
created() {
this.transform(this.moment)
this.complete()
// 获得年份列表: 1900-2100
for(let i = 1900; i <= 2100; i++) {
this.years.push(i)
}
},
filters: {
// 日期格式过滤器
dateFormat(val) {
if (!val) {
return ''
}
/*return `${val.year}-${val.month}-${val.date}`.replace(/\d+/g, (a) => {
return (a.length === 4) ? a : ((a.length === 2) ? a : ('0' + a))
})*/
return `${val.year}-${val.month}`.replace(/\d+/g, (a) => {
return (a.length === 4) ? a : ((a.length === 2) ? a : ('0' + a))
})
}
},
methods: {
/**
* 将时间转化为具体的 年、月、日、星期
**/
transform(time) {
const date = new Date(time)
this.select.year = date.getFullYear()
this.select.month = date.getMonth() + 1
this.select.date = date.getDate()
this.select.day = date.getDay()
this.currentMonthFirstDay =
new Date(this.select.year, this.select.month - 1, 1, 0).getDay()
this.currentMonthEndDate =
new Date(this.select.year, this.select.month, 0).getDate()
this.currentMonthEndDay =
new Date(this.select.year, this.select.month, 0).getDay()
},
/*
* 计算出日历列表,二维数组
* 第一层为星期,第二层为每星期的第几天
*/
getDateList() {
this.list = []
// 获取日历第一行的数据(需加上第一个星期中所包含上个月的几天)
let temp = this.lastMonthEndDate - (this.currentMonthFirstDay - 1)
let list =
this.numberList(temp, this.lastMonthEndDate, true)
.concat(this.numberList(1, 7 - this.currentMonthFirstDay))
this.list.push(list)
temp = (7 - this.currentMonthFirstDay) + 1
/*
* 剩下的行数
*/
// 计算除了第一行剩下的天数
const leftDays = this.currentMonthEndDate - (7 - this.currentMonthFirstDay)
// 剩下的星期数
const lineNumber = Math.ceil(leftDays / 7)
// 包含下个月日历的天数
const nextDays = 7 - (leftDays % 7)
for (let i = 0; i < lineNumber; i++) {
if (i === lineNumber - 1 && nextDays > 0 && nextDays !== 7) {
this.list[lineNumber] =
this.numberList(temp, this.currentMonthEndDate)
.concat(this.numberList(1, nextDays, true))
} else {
this.list.push(this.numberList(temp, temp + 6))
}
temp = temp + 7
}
},
/*
* 获得日历数组
* start: 开始日
* end: 结束日
* flag: boolean值,判断是否属于本月
*/
numberList(start, end, flag) {
let list = []
for (let i = start; i <= end; i++) {
list.push({
text: i,
flag: !!flag
})
}
return list
},
/*
* 切换月份, -1为当前月份-1,+1为当前月份+1
*/
switchMonth(n) {
let year = this.select.year
if (n===-1) {
const pre = this.select.month === 1 ? 12 : this.select.month - 1
if (pre === 12) {
this.transform(new Date(year - 1, pre - 1, this.select.date))
} else {
this.transform(new Date(year, pre - 1, this.select.date))
}
} else {
const next = this.select.month === 12 ? 1 : this.select.month + 1
if (next === 1) {
this.transform(new Date(year + 1, next - 1, this.select.date))
} else {
this.transform(new Date(year, next - 1, this.select.date))
}
}
},
pick(day) {
if (!!day.flag) {
// 当页日历上可能还会显示部分上个月或者下个月的部分天数,根据标识来做判断
// 并对月份作出相应的处理
if (parseInt(day.text) > 15) {
this.transform(new Date(this.select.year, this.select.month - 2, parseInt(day.text)))
} else {
this.transform(new Date(this.select.year, this.select.month, parseInt(day.text)))
}
} else {
this.transform(new Date(this.select.year, this.select.month - 1, parseInt(day.text)))
}
this.complete()
},
// 绑定事件:点击关闭日历面板
bindEvent() {
this.show = false
this.selectYear = false
},
// 选取年
pickYear(n) {
this.transform(new Date(n, this.select.month - 1, this.select.date))
this.complete()
},
// 选取月
pickMonth(n) {
this.transform(new Date(this.select.year, n - 1, this.select.date))
this.complete()
this.selectYear = false
},
// 更改选中时间并向父组件派发事件
complete() {
// 触发父组件的传过来的picked事件。三个参数: 年,月,日
//this.$emit('picked', this.select.year, this.select.month, this.select.date)
this.$emit('picked', this.select.year, this.select.month, 2)
this.current = {
year: this.select.year,
month: this.select.month,
date: 2
}
}
}
}
script>
vue的引用页面代码:
<template>
<div>
<datepicker v-on:picked="picked">
datepicker>
div>
template>
<script>
import datepicker from '../components/datepicker.vue'
export default {
data() {
return {
freeMon:'',
}
},
methods: {
picked(year, month, date) {
if(month < 10){
this.freeMon = `${year}-0${month}`;
}else{
this.freeMon = `${year}-${month}`;
}
},
getList(){
},
routerBack(){
this.$router.go(-1);
}
},
components: {
datepicker
},
mounted () {
this.getList()
}
}
script>
3、推荐另一个日期控件vue-datepicker-local
其示例演示地址:https://weifeiyue.github.io/vue-datepicker-local/