日历组件:
<template><div class="calendar" @click.stop><div class="input-wrap"><inputtype="text"v-if="dateChangeSets":placeholder="placeholder"class="input dateChangeSets middle-input"@focus="foc"v-model="sel"/><input type="text" v-else class="input middle-input" @input="changees" @focus="foc" v-model="sel" /></div><div class="dp" v-show="show"><div class="dp-header"><a class="dp-h-1" @click="cy(-1)">«</a><a class="dp-h-2" @click="cm(-1)">‹</a><span class="dp-ym">{{y}}年 {{m}}月</span><a class="dp-h-3" @click="cm(1)">›</a><a class="dp-h-4" @click="cy(1)">»</a></div><table class="dp-table"><thead><tr><th><span>日</span></th><th><span>一</span></th><th><span>二</span></th><th><span>三</span></th><th><span>四</span></th><th><span>五</span></th><th><span>六</span></th></tr></thead><tbody><tr v-for="(cell, index) in data" :key="index"><tdv-for="(item, index) in cell":key="index":class="{'dp-last': m != item.month, 'dp-today': cur == item.data, 'dp-select': sel == item.data}"><span @click="addClickOn(item.data)">{{ item.day }}</span></td></tr></tbody></table><!-- <div class="dp-footer"><a @click="clickNow">{{sel}}</a> <span class="btn btn-ok" @click="show = false">确定</span></div> --></div></div>
</template><script>
import getCalendar from './getCalendar.js'
export default {props: ['nowDate', 'dateChangeSets', 'placeholder'],model: {prop: 'nowDate',event: 'dateChange'},data () {let d = '' // 用于显示的日历let len = ('' + this.nowDate).length// 为空if (!this.nowDate || (len !== 13 && len !== 10)) {d = new Date()} else {d = len === 13 ? new Date(parseInt(this.nowDate)) : new Date(this.nowDate * 1000)}let sel = ''if (Object.prototype.toString.call(d) === '[object Date]') {// it is a dateif (isNaN(d.getTime())) { // d.valueOf() could also work// date is not validd = new Date()} else {// date is validsel = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate()}} else {// not a dated = new Date()}if (!this.nowDate) {sel = ''}let curTime = new Date()let cur = '' + curTime.getFullYear() + '-' + (curTime.getMonth() + 1) + '-' + curTime.getDate() // 当前日期let y = d.getFullYear()let m = d.getMonth() + 1let data = getCalendar(d.getFullYear(), d.getMonth() + 1) // 显示的日历return {cur,sel,y,m,data,firstClick: true,show: false}},created () { },methods: {// flag === -1 月份的后退, flag === 1 月份的前进cm (flag) {// 前进后退月if (flag === -1) {if (this.m === 1) {this.addInitOn(parseInt(this.y) - 1, 12)} else {this.addInitOn(this.y, parseInt(this.m) - 1)}} else {if (this.m === 12) {this.addInitOn(parseInt(this.y) + 1, 1)} else {this.addInitOn(this.y, parseInt(this.m) + 1)}}setTimeout(() => {if (this.firstClick) {this.cm(flag)this.firstClick = falsesetTimeout(() => {this.firstClick = true}, 200)}}, 50)},cy (flag) {// 前进后退年if (flag === -1) {this.addInitOn(parseInt(this.y) - 1, this.m)} else {this.addInitOn(parseInt(this.y) + 1, this.m)}setTimeout(() => {if (this.firstClick) {this.cy(flag)this.firstClick = falsesetTimeout(() => {this.firstClick = true}, 200)}}, 50)},clickNow () {let t = new Date()let y = t.getFullYear()let m = t.getMonth() + 1// let d = t.getDate()this.addInitOn(y, m)},foc () {let self = thisthis.show = truedocument.body.onclick = function () {self.show = falsedocument.body.onclick = null}},changees () {console.log(this.sel, 'sel')this.$emit('changees', this.sel)},addInitOn (y, m) {// TODO:有时会出现13月,切换月份和学年 需要点击俩次this.$emit('init', y, m)this.$on('init', function () {this.data = getCalendar(y, m)this.y = ythis.m = m})},addClickOn (data) {this.sel = datavar ar = data.split('-')var m = ar[1]var y = ar[0]let newDate = new Date(ar[0], ar[1] - 1, ar[2]).getTime() // 更新时间戳if (m !== this.m) {this.y = ythis.m = mthis.data = getCalendar(y, m)}this.show = falsethis.$emit('dateChange', newDate)}}
}
</script><style lang="scss" scoped>
.calendar {position: relative;user-select: none;display: inline-block;
}
.input-wrap {display: inline-block;
}
ul,
li {padding: 0;margin: 0;
}.dp {width: 220px;// height: 280px;height: 250px;border: 1px solid black;box-shadow: 0 0 4px rgba(0, 0, 0, 0.17);border: 1px solid #d9d9d9;border-radius: 6px;position: absolute;top: 38px;background: #fff;z-index: 999;font-size: 12px;color: #666;
}.dp-table {width: 100%;text-align: center;border-collapse: collapse;
}
.dp-table th {padding: 6px 0;border: 0;
}
.dp-table td {padding: 4px 0;line-height: 18px;border: 0;
}
.dp-table th span {display: block;font-weight: normal;
}
.dp-table td span {display: block;width: 23px;height: 23px;line-height: 23px;border: 1px solid transparent;margin: 0 auto;transition: background 0.3s ease;border-radius: 4px;padding: 0;box-sizing: border-box;
}
.dp-table td span:hover {background: #fcf2e9;cursor: pointer;
}.dp-table .dp-last span {color: #ccc;
}.dp-table .dp-today span {border-color: #f5bb50;font-weight: bold;color: #f5bb50;
}.dp-table .dp-select span {background: #f5bb50;color: #fff;border: 1px solid transparent;
}
.dp-table .dp-select span:hover {background: #f5bb50;
}.dp-header {position: relative;text-align: center;height: 34px;line-height: 34px;text-align: center;border-bottom: 1px solid #e9e9e9;
}
.dp-header .dp-ym {font-weight: bold;
}
.dp-header a {color: #999;line-height: 34px;height: 34px;font-size: 24px;display: inline-block;padding: 0 5px;position: absolute;
}
.dp-header a:hover {color: #2db7f5;cursor: pointer;
}
.dp-header .dp-h-1 {left: 7px;
}
.dp-header .dp-h-2 {left: 29px;
}
.dp-header .dp-h-3 {right: 29px;
}
.dp-header .dp-h-4 {right: 7px;
}.dp-footer {height: 34px;border-top: 1px solid #e9e9e9;text-align: center;position: relative;
}
.dp-footer a {display: inline-block;line-height: 34px;height: 34px;cursor: pointer;
}
.dp-footer span {position: absolute;right: 16px;top: 7px;
}
.dp .btn {display: inline-block;text-align: center;vertical-align: middle;cursor: pointer;border: 1px solid #d9d9d9;white-space: nowrap;line-height: 1.5;padding: 1px 6px;font-size: 12px;border-radius: 6px;background-color: #f4f4f4;outline: 0;opacity: 1;color: #fff;background-color: #2db7f5;
}
.dateChangeSets {font-size: 14px !important;font-family: PingFangTC-Regular, PingFangTC !important;font-weight: 400 !important;color: #d6d6d6 !important;
}
</style>
getCalendar.js文件
function getCalendar (y, m) {// 输出一个日历数据源 月份不必减1y = parseInt(y)m = parseInt(m)let time = new Date(y, m - 1, 1)let lastDatelet nextDatelet lastMonth = m - 1let nextMonth = m + 1if (m === 1) {lastDate = '' + (y - 1) + '-' + 12 + '-'nextDate = '' + y + '-' + 2 + '-'lastMonth = 12} else if (m === 12) {lastDate = '' + y + '-' + 11 + '-'nextDate = '' + (y + 1) + '-' + 1 + '-'nextMonth = 1} else {lastDate = '' + y + '-' + (m - 1) + '-'nextDate = '' + y + '-' + (m + 1) + '-'}let maxNumber = 42let r1 = []let r2 = []let r3 = []// 日 ~ 一let lastFix = time.getDay()/*** 格式 一 ~ 日 写法:* let lastFix = time.getDay() - 1* lastFix = lastFix < 0 ? lastFix + 7 : lastFix**/let lastMaxDate = new Date(y, m - 1, 0).getDate() // 上个月份最大天数let maxDate = new Date(y, m, 0).getDate() // 当前月份的let i, t// 统计上个月显示天数for (i = 0; i < lastFix; i++) {t = lastMaxDate - lastFix + i + 1r1[i] = { month: lastMonth, day: t, data: lastDate + t }}// 统计这个月显示天数for (i = 0; i < maxDate; i++) {t = i + 1r2[i] = { month: m, day: t, data: '' + y + '-' + m + '-' + t }}// 统计下个月显示天数let nextFix = maxNumber - maxDate - lastFixfor (i = 0; i < nextFix; i++) {t = i + 1r3[i] = { month: nextMonth, day: t, data: nextDate + t }}let result = r1.concat(r2, r3)let ar = []for (i = 0; i < 6; i++) {ar.push(result.splice(0, 7))}return ar
}export default getCalendar
index.js文件,全局注册
import Calendar from './Calendar.vue'const theCalendar = {install: function (Vue) {Vue.component('Calendar', Calendar)}
}
export default theCalendar
全局注册组件(main.js文件中):
import Calendar from './components/common/calendar'
...
Vue.use(Calendar)
使用(无需引用直接使用):
<Calendarclass="date_choose":nowDate="new Date()"@dateChange="dateChange"></Calendar>