时间日历控件不管在Pc端仍是移动端,都是十分重要且有用的一个组件,最近重拾了本身的UI库,按照ElementUI
的组件种类,以Pc端为核心进行组件开发【 适配Vue + Less 】,目前项目已有近20个组件,若是你们有兴趣,欢迎来GIT上踩踩哦vue
附上GIt地址 - github.com/Jason9708/C…git
本文带来的日历控件与以往的日历控件不太相同,我将以Window自带的日历做为大概的UI设计,摸出一个可拖拽的浮窗型日历控件github
效果以下:web
index.js
:工具函数文件index.less
:样式表index.vue
:组件文件calendar.vue
:测试组件文件index.js
工具函数// 定义月份英文缩写
const englishMonthList = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sept',
'Oct',
'Nov',
'Dec'
]
// 根据传入日期获取指定年月日时分秒
const getNewDate = (date) => {
let year = date.getFullYear()
let month = date.getMonth()
let day = date.getDate()
let hour = date.getHours()
let minute = date.getMinutes()
let second = date.getSeconds()
return { year, month, day, hour, minute, second }
}
// 获取指定年月日获取日期
const getDate = (year, month, day) => {
return new Date(year, month, day);
}
// 获取当前月的英文拼写
const englishMonth = (month) => {
let engMonth = englishMonthList[month]
return engMonth
}
// 导出函数
export {
getNewDate,
getDate,
englishMonth
}
复制代码
index.vue
组件文件<template>
<div class='cai-calendar-wrapper' id='cai-calendar-wrapper'>
<!-- 头部,即未展开的组件 -->
<div class='cai-calendar-header'>
<div class='cai-calendar-header-time'>{{currentTime.hour}}:{{formatDate(currentTime.minute)}}:{{formatDate(currentTime.second)}}</div>
<div class='cai-calendar-header-date'>
{{currentTime.year}}年{{currentTime.month + 1}}月{{currentTime.day}}日
</div>
</div>
<!-- 日历容器 transition实现动画效果-->
<transition name='calendar'>
<div class='cai-calendar-container' v-if='showCalendar'>
<!-- 容器头部,实现上下月切换,以及返回今天 -->
<div class='container-header'>
<span class='container-header-date'>
{{choosenMonthDec}}
</span>
<i class='cai-icon-up' @click='handlePrevMonth'></i>
<i class='cai-icon-down' @click='handleNextMonth'></i>
<span class='container-header-today' @click='handleToday'>今天</span>
</div>
<!-- 列表头行,显示周一到周日 -->
<div class="calendar-week">
<div v-for="(item, index) in calendarTitleArr" :key="index" class="week-item">{{item}}</div>
</div>
<!-- 列表 排列每个日期 -->
<div class="calendar-week">
<div v-for="(item, index) in calendarList" :key="index" class="week-item date-item" :class='[{today:isCurrentDay(item.date)}]' @click='chooseDate(item)'>
<span>{{item.day}}</span>
</div>
</div>
</div>
</transition>
<!-- 自定义弹窗 transition实现动画效果 -->
<transition name='calendar-dialog'>
<div class='cai-calendar-dialog' v-if='showCalendarDialog'>
<i class='cai-icon-close' @click='showCalendarDialog = false'></i>
<!-- 具名插槽实现用户自定义内容 -->
<slot name="content">
</slot>
</div>
</transition>
</div>
</template>
<script>
import * as utils from './index.js';
export default {
name:'CaiCalendar',
data(){
return{
currentTime:{ // 当前时间年月日,时分秒,用于头部显示实时时间
year:'',
month:'',
day:'',
hour:'',
second:''
},
calendarTitleArr: [ // 星期英文拼写
'MON',
'TUE',
'WED',
'THU',
'FRI',
'SAT',
'SUN '
],
calendarList: [], // 日历数组
choosenMonthDec:'', // 日历容器头部的可选月份描述
choosenMonth:'', // 日历容器头部的可选月份
interval:'', // 存储定时器
showCalendar:false, // 是否显式完整日历
showCalendarDialog:false, // 是否显示自定义弹窗
}
},
mounted(){
// 拖拽
this.drag()
// 第一次不延时
this.handleNowDate()
this.interval = setInterval( () => {
this.handleNowDate()
}, 1000)
},
methods:{
// 拖拽函数
drag(){
var that = this
var drag = document.getElementById('cai-calendar-wrapper')
// //点击某物体时,用drag对象便可,move和up是全局区域,
// 也就是整个文档通用,应该使用document对象而不是drag对象(不然,采用drag对象时物体只能往右方或下方移动)
drag.onmousedown = function(event){
var event = event || window.event //兼容IE浏览器
// 鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
var diffX = event.clientX - drag.offsetLeft
var diffY = event.clientY - drag.offsetTop
var startX = event.clientX
var startY = event.clientY
if(typeof drag.setCapture !== 'undefined'){
drag.setCapture()
}
// 鼠标移动,修改定位
document.onmousemove = function(event){
var event = event || window.event
var moveX = event.clientX - diffX
var moveY = event.clientY - diffY
if(moveX < 0){
moveX = 0
}else if(moveX > window.innerWidth - drag.offsetWidth){
moveX = window.innerWidth - drag.offsetWidth
console.log(moveX)
}
if(moveY < 0){
moveY = 0
}else if(moveY > window.innerHeight - drag.offsetHeight){
moveY = window.innerHeight - drag.offsetHeight
}
drag.style.left = moveX + 'px'
drag.style.top = moveY + 'px'
}
document.onmouseup = function(event){
var targetClass = event.target.className
var event = event || window.event
var endX = event.clientX
var endY = event.clientY
if(startX === endX && startY === endY){
if(targetClass === 'cai-calendar-header' || targetClass === 'cai-calendar-header-time' || targetClass === 'cai-calendar-header-date')
that.turnCalendar()
}
console.log(this) // #document
this.onmousemove = null
this.onmouseup = null
// 适配低版本 ie
if(typeof drag.releaseCapture!='undefined'){
drag.releaseCapture()
}
}
}
},
// 切换完整日历
turnCalendar(){
this.visibleCalendar(this.currentTime.year,this.currentTime.month)
this.showCalendar = !this.showCalendar
this.showCalendarDialog = false
},
// 是不是今天
isCurrentDay (date) {
let {year: currentYear, month: currentMonth, day: currentDay} = utils.getNewDate(new Date())
let {year, month, day} = utils.getNewDate(date)
return currentYear == year && currentMonth == month && currentDay == day
},
// 点击上一个月
handlePrevMonth () {
let prevMonth = utils.getDate(this.choosenMonth.year,this.choosenMonth.month,1)
prevMonth.setMonth(prevMonth.getMonth() - 1)
this.choosenMonth.year = utils.getNewDate(prevMonth).year
this.choosenMonth.month = utils.getNewDate(prevMonth).month
this.visibleCalendar(this.choosenMonth.year,this.choosenMonth.month)
this.$emit('handlePrevMonth',this.choosenMonth.year,this.choosenMonth.month) // 传给父组件,上个年和月份做为传递数据
},
// 点击下一个月
handleNextMonth () {
let nextMonth = utils.getDate(this.choosenMonth.year,this.choosenMonth.month,1)
nextMonth.setMonth(nextMonth.getMonth() + 1)
this.choosenMonth.year = utils.getNewDate(nextMonth).year
this.choosenMonth.month = utils.getNewDate(nextMonth).month
this.visibleCalendar(this.choosenMonth.year,this.choosenMonth.month)
this.$emit('handleNextMonth',this.choosenMonth.year,this.choosenMonth.month) // 传给父组件,上个年和月份做为传递数据
},
// 点击回到今天
handleToday () {
this.choosenMonth.year = utils.getNewDate(new Date()).year
this.choosenMonth.month = utils.getNewDate(new Date()).month
this.visibleCalendar(this.choosenMonth.year,this.choosenMonth.month)
this.$emit('handleToday',this.choosenMonth.year,this.choosenMonth.month) // 传给父组件,上个年和月份做为传递数据
},
// 点击日期
chooseDate(item){
this.showCalendarDialog = true
this.$emit('handleClickDay', item);
},
// 加载日历
visibleCalendar(YEAR,MONTH){
let calendatArr = []
// 获取指定时间年月日
let {year, month, day} = utils.getNewDate(utils.getDate(YEAR, MONTH, 1))
// 获取指定月第一天星期几
let currentFirstDay = utils.getDate(year, month, 1)
let weekDay = currentFirstDay.getDay()
console.log('weekDay',weekDay)
// 计算开始时间
let startTime = currentFirstDay - (weekDay - 1) * 24 * 60 * 60 * 1000
console.log('startTime',startTime)
// 获取当前月份中日历显示几天 ( 第一天是周5或周6,则显示6行,不然显示5行)
let monthDayNum
if (weekDay == 5 || weekDay == 6){
monthDayNum = 42
}else {
monthDayNum = 35
}
// 从第一天开始计算,每进行一次循环,日期加一天
for (let i = 0; i < monthDayNum; i++) {
calendatArr.push({
date: new Date(startTime + i * 24 * 60 * 60 * 1000),
year: year,
month: month + 1,
day: new Date(startTime + i * 24 * 60 * 60 * 1000).getDate(),
clickDay: false,
})
}
// 赋值日历列表
this.calendarList = calendatArr
this.choosenMonth = {
year:year,
month:month
}
this.choosenMonthDec = `${utils.englishMonth(month)} ${year}` // 日历容器头部的可选月份
},
// 获取当前时间,并赋值currentTime
handleNowDate(){
let {year, month, day, hour, minute, second} = utils.getNewDate(new Date())
this.currentTime = {year, month, day, hour, minute, second}
},
// 格式化日期 我的很多天期以 0X 的格式显示
formatDate(date){
date = Number(date)
return date < 10 ? `0${date}` : date
}
},
destroyed(){
// 清除定时器
clearInterval('interval')
}
}
</script>
<style lang="less" scoped>
@import './index.less'; // 加入样式表
@import '../../CaiIcon/component/index.less'; // 图标样式表(可无论)
</style>
复制代码
关于以上代码,须要注意如下:数组
Interval
,在调用前,必须手动调用一次,避免第一次也延时致使效果不佳index.less
:样式表.cai-calendar-wrapper{
width:540px;
display: flex;
flex-direction: column;
align-items: center;
position: fixed;
user-select:none;
.cai-calendar-header{
width:100%;
display: flex;
flex-direction: column;
justify-content:center;
padding:20px;
background:rgba(9,9,9,0.8);
position:relative;
&:before{
content:'';
position: absolute;
top:0px;
left:0px;
height:100%;
width:5px;
background: #0097e6;
}
.cai-calendar-header-time{
text-align: left;
font-size:30px;
font-weight: 500;
color: #74b9ff;
margin-left:20px;
margin-bottom:10px;
}
.cai-calendar-header-date{
text-align: left;
font-size:15px;
color: #fff;
margin-left:30px;
}
}
.cai-calendar-container{
width:100%;
margin-top:5px;
opacity: 1;
padding:20px;
background:rgba(9,9,9,0.8);
transform-origin:top;
transform:rotateX(0deg);
display:flex;
flex-direction: column;
.container-header{
display: flex;
justify-content: flex-end;
align-items: center;
color:#fff;
margin-bottom:10px;
.container-header-date{
margin-right:10px;
}
.container-header-today{
margin:0px 10px;
padding:5px 10px;
color: #74b9ff;
font-size:12px;
border:1px solid #74b9ff;
border-radius:5px;
cursor:pointer;
transition: all .2s linear;
}
.container-header-today:hover{
color:#0984e3;
border-color:#0984e3;
}
.cai-icon-up {
margin-right:10px;
cursor:pointer;
transition:all .2s linear;
}
.cai-icon-down {
cursor:pointer;
transition:all .2s linear;
}
.cai-icon-up:hover {
color:#0984e3;
}
.cai-icon-down:hover {
color:#0984e3;
}
}
.calendar-week{
width: 100%;
display: flex;
flex-wrap: wrap;
border-right: none;
list-style: none;
margin: 0;
padding: 0;
.week-item{
width: 57px;
text-align: center;
font-size: 16px;
color: #fff;
font-weight: 600;
padding:0px;
margin:10px;
}
.date-item{
cursor:pointer;
transition:all .2s linear;
position: relative;
}
.date-item:hover{
color:#0984e3;
}
.today{
color:#74b9ff;
}
}
}
.cai-calendar-dialog{
position: absolute;
top:0px;
bottom:0px;
left:-330px;
width:300px;
background:rgba(9,9,9,0.8);
color:#fff;
overflow: auto;
.cai-icon-close{
position: absolute;
top:5px;
right:5px;
color:#fff;
cursor: pointer;
transition: all .2s linear;
}
.cai-icon-close:hover{
color:#0097e6;
}
}
// 关于弹窗的滚动条样式覆盖
.cai-calendar-dialog::-webkit-scrollbar {
width: 5px;
margin:2px;
height: 5px;
}
.cai-calendar-dialog::-webkit-scrollbar-button {
display: none;
}
.cai-calendar-dialog::-webkit-scrollbar-track {
background-color: transparent;
}
.cai-calendar-dialog::-webkit-scrollbar-thumb {
width: 3px;
background-color: #0097e6;
-webkit-border-radius: 3em;
-moz-border-radius: 3em;
border-radius: 3em;
}
// 日历 - 进入/离开 动画
.calendar-enter-active, .calendar-leave-active {
transition: all 1s
}
.calendar-enter, .calendar-leave-active {
transform:rotateX(-90deg);
opacity:0;
}
// 日历弹窗 - 进入/离开 动画
.calendar-dialog-enter-active, .calendar-dialog-leave-active {
transition: all 1s
}
.calendar-dialog-enter, .calendar-dialog-leave-active {
transform:translateX(30px);
opacity:0;
}
}
复制代码
calendar.vue
:测试组件文件开源地址浏览器
该开源git是一个开源UI库项目,目前开发近20款适配Vue
的UI组件,有兴趣的小伙伴给点⭐app
部分组件截图less