vue 时间选择器组件
组件效果:
单文件组件:
<template> <div class="date-pickers"> <!--date为computed计算属性中得到的值 focus调用初始化日期方法--> <input type="text" placeholder="选择日期" @focus="trueDateBox" :value="date" readonly/> <!--基于ATUI的输入框,和input没有太大区别--> <!--<at-input type="data" placeholder="输入提示" @focus="trueDateBox" :value="date" readonly style="width:200px;display: inline-block;"></at-input>--> <transition name="fade"> <div class="date-box" v-if="dateBoxFlag"> <div class="day-select" style="height:40px;text-align: center"> <div> <button @click="reduceYear"><<</button> <button @click="reduceMonth"><</button> </div> <div> <input type="text" @click="selected" v-model="year"/>年 <input type="text" @click="selected" v-model="month"/>月 </div> <div> <button @click="addMonth">></button> <button @click="addYear">>></button> </div> </div> <div class="day-screen"> <div style="padding: 0;margin: 0"> <span v-for="week in week">{{ week }}</span> </div> <div @click="selectDay" style="padding: 0;margin: 0"> <span v-for="day in previousMonth" class="previousMonth"> {{ day }} </span> <span v-for="day in monthDay[month - 1]" :class="isActive(day)" class="currentMonth">{{ day }}</span> <span v-for="day in nextMonth" class="nextMonth">{{ day }}</span> </div> </div> </div> </transition> </div> </template> <script> export default { name: 'datePickers', data () { return { dateBoxFlag: false, year: 0, month: 0, day: 0, previousMonth: [], nextMonth: [], week: ['日', '一', '二', '三', '四', '五', '六'], monthDay: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], dateTime:"" } }, props: { oldTime: "" }, mounted:function () { console.log(this.oldTime) if (this.oldTime != null) { var date = new Date(parseInt(this.oldTime.replace("/Date(", "").replace(")/", ""), 10)); //月份为0-11,所以+1,月份小于10时补个0 var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; var currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); this.year=date.getFullYear(); this.month=month; this.day=currentDate; this.$emit('datatime',this.date); } }, computed: { date () { if (this.year == 0 || this.month == 0 || this.day == 0) { return ''; } return this.year + '-' + this.month + '-' + this.day; } }, watch: { year: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.year = date.getFullYear(); } if (val < 0) { this.year = 1; } if (val > 10000) { this.year = 10000; } this.dayScreen(); }, month: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.month = date.getMonth() + 1; } if (val < 1) { this.month = 1; } if (val > 12) { this.month = 12; } this.dayScreen(); }, }, methods: { // 突出显示当前日期 isActive (index) { if (index == this.day) { return { active: true, } } }, // 显示日期盒子并初始化 trueDateBox () { if (this.date === '') { let date = new Date(); this.year = date.getFullYear(); if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } this.month = date.getMonth() + 1; this.day = date.getDate(); } this.dayScreen(); this.dateBoxFlag = true; }, // 增减年份 addYear () { this.year++; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, reduceYear () { this.year--; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, // 增减月份 addMonth () { this.month++; if (this.month > 12) { this.month = 1; this.year++; } }, reduceMonth () { this.month--; if (this.month < 1) { this.month = 12; this.year--; } }, // 获取input里的文字 selected (e) { e.target.select(); }, // 选择日期 selectDay (e) { let targetClass = e.target.className; if (targetClass == 'previousMonth') { if (this.month == 1) { this.month = 12; this.year--; } else { this.month = this.month - 1; } this.day = parseInt(e.target.innerText); } else if (targetClass == 'nextMonth') { if (this.month == 12) { this.month = 1; this.year++; } else { this.month = this.month + 1; } this.day = parseInt(e.target.innerText); } else { this.day = parseInt(e.target.innerText); } this.$emit('datatime',this.date); this.dateBoxFlag = false; }, // 日期显示 dayScreen () { // 上一个月 let firstDate = new Date(this.year, this.month - 1, 1); let firstWeek = firstDate.getDay(); let preMonthDay = null; if (this.month == 1) { preMonthDay = this.monthDay[11]; } else { preMonthDay = this.monthDay[this.month - 2]; } for (let i = 0; i < preMonthDay; i++) { this.previousMonth[i] = i + 1; } if (firstWeek == 0) { this.previousMonth = this.previousMonth.slice(-7); } else { this.previousMonth = this.previousMonth.slice(-firstWeek); } // 下一个月 let endDate = new Date(this.year, this.month - 1, this.monthDay[this.month - 1]); let endWeek = endDate.getDay(); let nextMonthDay = null; if (this.month == 12) { nextMonthDay = this.monthDay[0]; } else { nextMonthDay = this.monthDay[this.month]; } for (let i = 0; i < nextMonthDay; i++) { this.nextMonth[i] = i + 1; } if (endWeek == 6) { this.nextMonth = this.nextMonth.slice(0, 7); } else { this.nextMonth = this.nextMonth.slice(0, 6 - endWeek); } }, // 判断是否是闰年 isLeapYear (year) { return (year % 100 == 0 ? (year % 400 == 0 ? true : false) : (year % 4 == 0 ? true : false)); }, } } </script> <style> .date-pickers { width: 280px; position: relative; } .date-pickers > input { width: 50%; height: 20px; padding: 5px; } .date-pickers .fade-enter-active, .date-pickers .fade-leave-active { transition: all 0.5s; } .date-pickers .fade-enter, .date-pickers .fade-leave-active { opacity: 0; transform: translateY(-10px); } .date-pickers > .date-box { width: 100%; border: 1px solid #EAEAEA; border-radius: 5px; box-shadow: 2px 2px 2px #eee; background: white; position: absolute; bottom: 38px; left: 0px; z-index: 99; } .date-pickers > div div.day-select { display: flex; padding: 5px 0; height: 30px; line-height: 30px; color: #888888; border-bottom: 1px solid #ccc; } .date-pickers > div div.day-select input, .date-pickers > div div.day-select button { border: none; background: white; text-align: center; color: #888888; cursor: pointer; } .date-pickers > div div.day-select > div:nth-child(1), .date-pickers > div div.day-select > div:nth-child(3) { width: 20%; } .date-pickers > div div.day-select > div:nth-child(2) { width: 60%; display: flex; justify-content: center; } .date-pickers > div div.day-select > div:nth-child(2) input:hover { background: #eee; } .date-pickers > div div.day-select > div:nth-child(2) input:nth-child(1) { width: 50px; } .date-pickers > div div.day-select > div:nth-child(2) input:nth-child(2) { width: 30px; } .date-pickers > div div.day-screen > div { width: 280px; padding: 0 5px; display: flex; font-size: 14px; justify-content: flex-start; flex-wrap: wrap; } .date-pickers > div div.day-screen > div span { width: 40px; height: 40px; text-align: center; line-height: 40px; border-bottom: 1px solid #ccc; } .date-pickers > div div.day-screen > div:nth-child(1) { font-weight: bold; background: #F8F8F8; } .date-pickers > div div.day-screen > div:nth-child(2) span { cursor: pointer; color: black; } .date-pickers > div div.day-screen > div:nth-child(2) span:hover, .date-pickers > div div.day-screen > div:nth-child(2) span.active { background: #21A5EF; color: white; } .date-pickers > div div.day-screen > div:nth-child(2) span.previousMonth, .date-pickers > div div.day-screen > div:nth-child(2) span.nextMonth { color: #888888; } /*# sourceMappingURL=style.css.map */ </style>
vue引用该单文件组件,会在页面显示一个输入框,点击后出现日期选择器。但是页面调用需要获取父组件的默认值并向父组件传递日期选择结果。(默认为空时可以不向子组件传值)
<datePickers v-on:datatime="datatime" :oldTime="$store.state.Jtnc.seeobjs.DJSJ" v-if="isRouterAlive" class="inputStyle" ></datePickers>
v-on:datatime="datatime"向子组件传递了一个方法,子组件调用方法后可将选定日期值值传回父组件。
父组件方法:datatime
/*时间处理*/ datatime(theTime){ this.modifyModel.DJSJ = theTime; },
子组件调用(在选择日期的方法selectDay中调用):
this.$emit('datatime',this.date);
:oldTime="$store.state.Jtnc.seeobjs.DJSJ"将默认的日期值传入子组件,子组件通过props接收,看到组件时将看到默认日期。
props: { oldTime: "" }
v-if="isRouterAlive"为强制刷新组件的开关,由于该组件会在打开页面时加载,而默认日期如果在打开模态框时传入,所以在打开模态框时强行刷新组件,可看到默认日期。
将数据渲染在模态框中是调用this.reload()刷新组件。(https://www.cnblogs.com/s313139232/p/9176820.html)
/*组件刷新*/ reload () { this.isRouterAlive = false; this.$nextTick(() => (this.isRouterAlive = true)) },
组件源代码:
.vue文件:
<template> <div class="date-pickers"> <input type="text" placeholder="选择日期" @focus="trueDateBox" :value="date" readonly /> <transition name="fade"> <div class="date-box" v-if="dateBoxFlag"> <div class="day-select"> <div> <button @click="reduceYear">«</button> <button @click="reduceMonth"><</button> </div> <div> <input type="text" @click="selected" v-model="year" />年 <input type="text" @click="selected" v-model="month" />月 </div> <div> <button @click="addMonth">></button> <button @click="addYear">»</button> </div> </div> <div class="day-screen"> <div> <span v-for="week in week">{{ week }}</span> </div> <div @click="selectDay"> <span v-for="day in previousMonth" class="previousMonth"> {{ day }} </span> <span v-for="day in monthDay[month - 1]" v-bind:class="isActive(day)" class="currentMonth">{{ day }}</span> <span v-for="day in nextMonth" class="nextMonth">{{ day }}</span> </div> </div> </div> </transition> </div> </template> <script> export default { name: 'datePickers', data () { return { dateBoxFlag: false, year: 0, month: 0, day: 0, previousMonth: [], nextMonth: [], week: ['日', '一', '二', '三', '四', '五', '六'], monthDay: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], } }, computed: { date () { if (this.year == 0 || this.month == 0 || this.day == 0) { return ''; } return this.year + '-' + this.month + '-' + this.day; } }, watch: { year: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.year = date.getFullYear(); } if (val < 0) { this.year = 1; } if (val > 10000) { this.year = 10000; } this.dayScreen(); }, month: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.month = date.getMonth() + 1; } if (val < 1) { this.month = 1; } if (val > 12) { this.month = 12; } this.dayScreen(); }, }, methods: { // 突出显示当前日期 isActive (index) { if (index == this.day) { return { active: true, } } }, // 显示日期盒子并初始化 trueDateBox () { if (this.date === '') { let date = new Date(); this.year = date.getFullYear(); if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } this.month = date.getMonth() + 1; this.day = date.getDate(); } this.dayScreen(); this.dateBoxFlag = true; }, // 增减年份 addYear () { this.year++; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, reduceYear () { this.year--; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, // 增减月份 addMonth () { this.month++; if (this.month > 12) { this.month = 1; this.year++; } }, reduceMonth () { this.month--; if (this.month < 1) { this.month = 12; this.year--; } }, // 获取input里的文字 selected (e) { e.target.select(); }, // 选择日期 selectDay (e) { let targetClass = e.target.className; if (targetClass == 'previousMonth') { if (this.month == 1) { this.month = 12; this.year--; } else { this.month = this.month - 1; } this.day = parseInt(e.target.innerText); } else if (targetClass == 'nextMonth') { if (this.month == 12) { this.month = 1; this.year++; } else { this.month = this.month + 1; } this.day = parseInt(e.target.innerText); } else { this.day = parseInt(e.target.innerText); } this.dateBoxFlag = false; }, // 日期显示 dayScreen () { // 上一个月 let firstDate = new Date(this.year, this.month - 1, 1); let firstWeek = firstDate.getDay(); let preMonthDay = null; if (this.month == 1) { preMonthDay = this.monthDay[11]; } else { preMonthDay = this.monthDay[this.month - 2]; } for (let i = 0; i < preMonthDay; i++) { this.previousMonth[i] = i + 1; } if (firstWeek == 0) { this.previousMonth = this.previousMonth.slice(-7); } else { this.previousMonth = this.previousMonth.slice(-firstWeek); } // 下一个月 let endDate = new Date(this.year, this.month - 1, this.monthDay[this.month - 1]); let endWeek = endDate.getDay(); let nextMonthDay = null; if (this.month == 12) { nextMonthDay = this.monthDay[0]; } else { nextMonthDay = this.monthDay[this.month]; } for (let i = 0; i < nextMonthDay; i++) { this.nextMonth[i] = i + 1; } if (endWeek == 6) { this.nextMonth = this.nextMonth.slice(0, 7); } else { this.nextMonth = this.nextMonth.slice(0, 6 - endWeek); } }, // 判断是否是闰年 isLeapYear (year) { return (year % 100 == 0 ? (year % 400 == 0 ? true : false) : (year % 4 == 0 ? true : false)); }, } } </script> <style lang="scss"> .date-pickers { width: 280px; padding: 5px; position: relative; >input { width: 50%; height: 20px; padding: 5px; } .fade-enter-active, .fade-leave-active { transition: all 0.5s; } .fade-enter, .fade-leave-active { opacity: 0; transform: translateY(-10px); } >div { width: 100%; border: 1px solid #EAEAEA; border-radius: 5px; box-shadow: 2px 2px 2px #eee; background: white; position: absolute; top: 50px; left: 0px; z-index: 99; div.day-select { display: flex; padding: 5px 0; height: 30px; line-height: 30px; color: #888888; border-bottom: 1px solid #ccc; input, button { border: none; background: white; text-align: center; color: #888888; cursor: pointer; } >div:nth-child(1), >div:nth-child(3) { width: 20%; } >div:nth-child(2) { width: 60%; display: flex; justify-content: center; input:hover { background: #eee; } input:nth-child(1) { width: 50px; } input:nth-child(2) { width: 30px; } } } div.day-screen { >div { width: 280px; padding: 0 5px; display: flex; font-size: 14px; justify-content: flex-start; flex-wrap: wrap; span { width: 40px; height: 40px; text-align: center; line-height: 40px; border-bottom: 1px solid #ccc; } } >div:nth-child(1) { font-weight: bold; background: #F8F8F8; } >div:nth-child(2) { span { cursor: pointer; color: black; &:hover, &.active { background: #21A5EF; color: white; } } span.previousMonth, span.nextMonth { color: #888888; } } } } } </style>
组件js文件:
Vue.component('datepickers', { template: ` <div class="date-pickers"> <input type="text" placeholder="选择日期" @focus="trueDateBox" :value="date" readonly /> <transition name="fade"> <div class="date-box" v-if="dateBoxFlag"> <div class="day-select"> <div> <button @click="reduceYear">«</button> <button @click="reduceMonth"><</button> </div> <div> <input type="text" @click="selected" v-model="year" />年 <input type="text" @click="selected" v-model="month" />月 </div> <div> <button @click="addMonth">></button> <button @click="addYear">»</button> </div> </div> <div class="day-screen"> <div> <span v-for="week in week">{{ week }}</span> </div> <div @click="selectDay"> <span v-for="day in previousMonth" class="previousMonth"> {{ day }} </span> <span v-for="day in monthDay[month - 1]" v-bind:class="isActive(day)" class="currentMonth">{{ day }}</span> <span v-for="day in nextMonth" class="nextMonth">{{ day }}</span> </div> </div> </div> </transition> </div> `, name: 'datePickers', data() { return { dateBoxFlag: false, year: 0, month: 0, day: 0, previousMonth: [], nextMonth: [], week: ['日', '一', '二', '三', '四', '五', '六'], monthDay: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], } }, computed: { date () { if (this.year == 0 || this.month == 0 || this.day == 0) { return ''; } return this.year + '-' + this.month + '-' + this.day; }, }, watch: { year: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.year = date.getFullYear(); } if (val < 0) { this.year = 1; } if (val > 10000) { this.year = 10000; } this.dayScreen(); }, month: function (val) { let reg = /^[1-9]\d*$/g; if (!reg.test(val)) { let date = new Date(); this.month = date.getMonth() + 1; } if (val < 1) { this.month = 1; } if (val > 12) { this.month = 12; } this.dayScreen(); }, }, methods: { // 突出显示当前日期 isActive (index) { if (index == this.day) { return { active: true, } } }, // 显示日期盒子并初始化 trueDateBox() { if (this.date == '') { let date = new Date(); this.year = date.getFullYear(); if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } this.month = date.getMonth() + 1; this.day = date.getDate(); } this.dayScreen(); this.dateBoxFlag = true; }, // 增减年份 addYear() { this.year++; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, reduceYear() { this.year--; if (this.isLeapYear(this.year)) { this.monthDay[1] = 29; } else { this.monthDay[1] = 28; } }, // 增减月份 addMonth() { this.month++; if (this.month > 12) { this.month = 1; this.year++; } }, reduceMonth() { this.month--; if (this.month < 1) { this.month = 12; this.year--; } }, // 获取input里的文字 selected(e) { e.target.select(); }, // 选择日期 selectDay(e) { let targetClass = e.target.className; if (targetClass == 'previousMonth') { if (this.month == 1) { this.month = 12; this.year--; } else { this.month = this.month - 1; } this.day = parseInt(e.target.innerText); } else if (targetClass == 'nextMonth') { if (this.month == 12) { this.month = 1; this.year++; } else { this.month = this.month + 1; } this.day = parseInt(e.target.innerText); } else { this.day = parseInt(e.target.innerText); } this.dateBoxFlag = false; }, // 日期显示 dayScreen() { // 上一个月 let firstDate = new Date(this.year, this.month - 1, 1); let firstWeek = firstDate.getDay(); let preMonthDay = null; if (this.month == 1) { preMonthDay = this.monthDay[11]; } else { preMonthDay = this.monthDay[this.month - 2]; } for (let i = 0; i < preMonthDay; i++) { this.previousMonth[i] = i + 1; } if (firstWeek == 0) { this.previousMonth = this.previousMonth.slice(-7); } else { this.previousMonth = this.previousMonth.slice(-firstWeek); } // 下一个月 let endDate = new Date(this.year, this.month - 1, this.monthDay[this.month - 1]); let endWeek = endDate.getDay(); let nextMonthDay = null; if (this.month == 12) { nextMonthDay = this.monthDay[0]; } else { nextMonthDay = this.monthDay[this.month]; } for (let i = 0; i < nextMonthDay; i++) { this.nextMonth[i] = i + 1; } if (endWeek == 6) { this.nextMonth = this.nextMonth.slice(0, 7); } else { this.nextMonth = this.nextMonth.slice(0, 6 - endWeek); } }, // 判断是否是闰年 isLeapYear(year) { return (year % 100 == 0 ? (year % 400 == 0 ? true : false) : (year % 4 == 0 ? true : false)); }, } });
css文件:
.date-pickers { width: 280px; padding: 5px; position: relative; } .date-pickers > input { width: 50%; height: 20px; padding: 5px; } .date-pickers .fade-enter-active, .date-pickers .fade-leave-active { transition: all 0.5s; } .date-pickers .fade-enter, .date-pickers .fade-leave-active { opacity: 0; transform: translateY(-10px); } .date-pickers > div { width: 100%; border: 1px solid #EAEAEA; border-radius: 5px; box-shadow: 2px 2px 2px #eee; background: white; position: absolute; top: 50px; left: 0px; z-index: 99; } .date-pickers > div div.day-select { display: flex; padding: 5px 0; height: 30px; line-height: 30px; color: #888888; border-bottom: 1px solid #ccc; } .date-pickers > div div.day-select input, .date-pickers > div div.day-select button { border: none; background: white; text-align: center; color: #888888; cursor: pointer; } .date-pickers > div div.day-select > div:nth-child(1), .date-pickers > div div.day-select > div:nth-child(3) { width: 20%; } .date-pickers > div div.day-select > div:nth-child(2) { width: 60%; display: flex; justify-content: center; } .date-pickers > div div.day-select > div:nth-child(2) input:hover { background: #eee; } .date-pickers > div div.day-select > div:nth-child(2) input:nth-child(1) { width: 50px; } .date-pickers > div div.day-select > div:nth-child(2) input:nth-child(2) { width: 30px; } .date-pickers > div div.day-screen > div { width: 280px; padding: 0 5px; display: flex; font-size: 14px; justify-content: flex-start; flex-wrap: wrap; } .date-pickers > div div.day-screen > div span { width: 40px; height: 40px; text-align: center; line-height: 40px; border-bottom: 1px solid #ccc; } .date-pickers > div div.day-screen > div:nth-child(1) { font-weight: bold; background: #F8F8F8; } .date-pickers > div div.day-screen > div:nth-child(2) span { cursor: pointer; color: black; } .date-pickers > div div.day-screen > div:nth-child(2) span:hover, .date-pickers > div div.day-screen > div:nth-child(2) span.active { background: #21A5EF; color: white; } .date-pickers > div div.day-screen > div:nth-child(2) span.previousMonth, .date-pickers > div div.day-screen > div:nth-child(2) span.nextMonth { color: #888888; } /*# sourceMappingURL=style.css.map */
项目源代码:https://github.com/huanghaibin91/Date-Pickers
原项目效果:https://huanghaibin91.github.io/Date-Pickers/