用vuejs仿寫一個移動端日曆元件
阿新 • • 發佈:2020-10-26
仿寫一個日曆元件,有些粗糙,需要優化的地方歡迎提出!
參考文章:
https://www.jianshu.com/p/67acaaf7d2f7
allfirst_rank_v2~rank_v28-13-103579415.pc_first_rank_v2_rank_v28&utm_term=%E5%B0%81%E8%A3%85%E6%97%A5%E5%8E%86%E7%BB%84%E4%BB%B6&spm=1018.2118.3001.4187
功能
- 不展開時,滑動切換周
- 展開時,滑動切換月
- 預設選擇當天
- 切換月份後將月份傳遞給父元件(也可以是其他資料)
要點
移動端左右滑動
安裝外掛
npm install vue-touch@next --save
在main.js 中 引入:
import VueTouch from 'vue-touch'
Vue.use(VueTouch, {name: 'v-touch'})
VueTouch.config.swipe = {
threshold: 100 //手指左右滑動距離
}
使用
<v-touch @swipeleft="onSwipeLeft"@swiperight="onSwipeRight" tag="div">
(你的元件)
</v-touch>
獲取每月的天數
首先列出每月的天數(2月除外)
monthDay:[31,'',31,30,31,30,31,31,30,31,30,31],
接著判斷2月是否為閏月再將天數插入該陣列
this.February=this.isLeapYear(this.year)?29:28//獲取二月份的天數
this.monthDay.splice(1,1,this.February) //插入二月份的天數
isLeapYear(year){
return year%4==0&&year%100!==0||year%400==0
},
最後傳入月份的索引,迴圈每個月的天數
<p v-for="(item,idx) in (monthDay[this.month-1] || 30)"
:key="item.id">
{{item}}
</p>
補充前後空格
<p v-for="item in headDays" :key="item.id" class="grey">{{item}}</p>
//中間是當月有效天
<p v-for="item in tailDays" :key="item.id" class="grey">{{item}}</p>
getWholeMonth(){
//獲取某年某月的第一天,由於new Date的月份按索引判斷,所以-1
let firstDay = new Date(this.year,this.month-1,1)
//獲取前方空格數
if(firstDay.getDay() == 0){
this.spaceDay = 6
} else {
this.spaceDay = firstDay.getDay() - 1
}
this.getPrevDays() //補前方空格
this.getCells() //補後方空格
},
//補前方空格
getPrevDays(){
//this.month表示的是月份,
//如果當前月為一月份,獲取十二月份的天數並傳過去。所以傳索引11
if(this.month==1){
this.getHeadDays(this.monthDay[11])
}else{
this.getHeadDays(this.monthDay[this.month-2])
}
},
//補後方空格-中間函式-獲取方格數
getCells(){
let cells=this.spaceDay+this.monthDay[this.month-1]
//餘數不能為0(否則就補一行了),cells%7獲取餘數
//一週有7天,假設餘數為2,那麼後方沒有補的空格就位7-2
if(7-cells%7!==0){
this.getTailDays(7-cells%7)
}
},
//補後方空格
getTailDays(end){
this.tailDays=this.supDays.slice(0,end)
},
其中:
leftDays:[31,30,29,28,27,26,25,24,23,22], //用於擷取的陣列 補前方空格
supDays:[1,2,3,4,5,6,7], //用於擷取的陣列 補後方空格
切換月與周
切換月,實質上就是改變變數month,讓其動態獲取monthDay中的天數
切換周,實質上就是移動日曆的上下位置,當展開時,位置在第一行;當未展開時,動態改變位置,其中weekRow是變數:
<div class="relative" :class="[visible?'row-1':'row-'+weekRow]">(被包裹的日曆)</div>
.row-1{
top:0
}
.row-2{
top:-2.4em
}
.row-3{
top:-4.8em
}
.row-4{
top:-7.2em
}
.row-5{
top:-9.6em
}
.row-6{
top:-12em
}
以左滑為例
onSwipeLeft(){
//1.展開的情況下 滑動切換月份
if(this.visible){
if(this.month==12){
this.year++
this.month=1
}else{
this.month++
}
this.getWholeMonth()
}else{
//2.未展開的情況下 滑動切換周
this.getWholeMonth()//先獲取當前行
//當前周小於行數時,切換下一週
if(this.weekRow<this.rows){
this.weekRow++
}else{
//當前周等於行數時,切換下一個月份,當前周變成第一週。
//由於要切到第一週,所以不用獲取下個月的行
if(this.month==12){
this.year++
this.month=1
this.weekRow=1
}else{
this.month++
this.weekRow=1
}
this.getWholeMonth()//由於更換了月,所以呼叫該函式補空格
}
}
},
父元件監聽子元件資料變化
子元件
updated(){
this.$emit('monthChange', this.month);
},
父元件
<Calendar ref="calendar" @monthChange="updateMonth"/>
current:1//月份
mounted(){
this.current=this.$refs.calendar.month
},
methods:{
updateMonth(month){
this.current=month
},
},
完整原始碼
子元件
<template>
<div>
<div class="calendar" :class="[!visible?'hidden':'']" >
<div class="flex_sb cellbox">
<p v-for="item in weekList" :key="item.id" class="week">{{item}}</p>
</div>
<v-touch @swipeleft="onSwipeLeft" @swiperight="onSwipeRight" tag="div">
<div class="flex_sb cellbox border relative" :class="[visible?'row-1':'row-'+weekRow]">
<p v-for="item in headDays" :key="item.id" class="grey">{{item}}</p>
<p v-for="(item,idx) in (monthDay[this.month-1] || 30)"
@click="setDay(idx)"
:class="idx==activeDay?'active':''"
class="relative"
:key="item.id">
{{item}}
</p>
<p v-for="item in tailDays" :key="item.id" class="grey">{{item}}</p>
</div>
</v-touch>
</div>
<div>
<van-icon name="arrow-down" v-if="!visible" @click="visible=true"/>
<van-icon name="arrow-up" @click="visible=false" v-else/>
</div>
</div>
</template>
<script>
export default {
data(){
return{
year:'', //年
month:'', //月
day:'', //日
weekList:['一','二','三','四','五','六','日'],
monthDay:[31,'',31,30,31,30,31,31,30,31,30,31],
February:'', //判斷2月份的天數
spaceDay: '', //當月日期前方的空格數
leftDays:[31,30,29,28,27,26,25,24,23,22], //用於擷取的陣列 補前方空格
supDays:[1,2,3,4,5,6,7], //用於擷取的陣列 補後方空格
headDays:[], //上個月月尾日期
tailDays:[], //下個月月頭日期
activeDay: '', //選中的日期 索引
visible:false, //判斷日曆是否展開
weekRow:2, //當前周 用於按周切換
rows:'' //當前月的週數
}
},
created(){
this.getTheCurrentDate() //獲取當前日期(年月日)
this.February=this.isLeapYear(this.year)?29:28//獲取二月份的天數
this.monthDay.splice(1,1,this.February) //插入二月份的天數
this.getWholeMonth() //獲取完整月份日曆
this.defaultSet()
},
updated(){
//監聽滑動事件後月份的變化,將月份傳給父元件
this.$emit('monthChange', this.month);
},
methods:{
//判斷是否為閏年
isLeapYear(year){
return year%4==0&&year%100!==0||year%400==0
},
//獲取當前日期
getTheCurrentDate(){
let current=new Date()
this.year = current.getFullYear()
this.month = current.getMonth() + 1
this.day = current.getDate()
},
//預設選擇當前日期
defaultSet(){
//展示周時,獲取當日的行數
if(!this.visible){
this.weekRow=Math.ceil((this.spaceDay+this.day)/7)
}
this.setDay(this.day-1)
},
//獲取空格被填充過的完整的月
getWholeMonth(){
let firstDay = new Date(this.year,this.month-1,1) //獲取某年某月的第一天,由於new Date的月份按索引判斷,所以-1
//獲取前方空格數
if(firstDay.getDay() == 0){
this.spaceDay = 6
} else {
this.spaceDay = firstDay.getDay() - 1
}
this.getPrevDays() //補前方空格
this.getCells() //補後方空格
},
//獲取上個月的天數 並呼叫函式補充開頭空格
getPrevDays(){
//this.month表示的是月份,
//如果當前月為一月份,獲取十二月份的天數並傳過去。所以傳索引11
if(this.month==1){
this.getHeadDays(this.monthDay[11])
}else{
this.getHeadDays(this.monthDay[this.month-2])
}
},
//補開頭空格
getHeadDays(end){
if(end==31){
this.headDays=this.leftDays.slice(0,this.spaceDay).reverse()
}else if(end==30){
this.headDays=this.leftDays.slice(1,this.spaceDay+1).reverse()
}else if(end==29){
this.headDays=this.leftDays.slice(2,this.spaceDay+2).reverse()
}else if(end==28){
this.headDays=this.leftDays.slice(3,this.spaceDay+3).reverse()
}
},
//獲取月份方格數,用於補後方空格 並獲取行/重新獲取行
getCells(){
let cells=this.spaceDay+this.monthDay[this.month-1]
//餘數不能為0(否則就補一行了),cells%7獲取餘數
//一週有7天,假設餘數為2,那麼後方沒有補的空格就位7-2
if(7-cells%7!==0){
this.getTailDays(7-cells%7)
}
//向上取整
this.rows=Math.ceil(cells/7)
},
//補後方空格
getTailDays(end){
this.tailDays=this.supDays.slice(0,end)
},
//選取特定日期
setDay(idx){
this.activeDay = idx
this.day = idx + 1
console.log('選擇的日期是'+this.year+' '+this.month+' '+this.day)
},
//右滑 下一個
onSwipeLeft(){
//1.展開的情況下 滑動切換月份
if(this.visible){
if(this.month==12){
this.year++
this.month=1
}else{
this.month++
}
this.activeDay = 0
this.getWholeMonth()
}else{
//2.未展開的情況下 滑動切換周
this.getWholeMonth()//先獲取當前行
//當前周小於行數時,切換下一週
if(this.weekRow<this.rows){
this.weekRow++
}else{
//當前周等於行數時,切換下一個月份,當前周變成第一週。
//由於要切到第一週,所以不用獲取下個月的行
if(this.month==12){
this.year++
this.month=1
this.weekRow=1
}else{
this.month++
this.weekRow=1
}
this.getWholeMonth()//由於更換了月,所以呼叫該函式補空格
}
}
},
//左滑 上一個
onSwipeRight(){
//1.展開的情況下 滑動切換月份
if(this.visible){
if(this.month==1){
this.year--
this.month=12
}else{
this.month--
}
this.activeDay = 0
this.getWholeMonth()
}else{
//2.未展開的情況下 滑動切換周
//當前周大於1時,切換上一週
if(this.weekRow>1){
this.weekRow--
}else{
//當前周等於1時,切換上一個月,並把當前周變成上個月的最後一週
if(this.month==1){
this.year--
//成功切換到上個月
this.month=12
//呼叫該函式重新獲取行數
this.getWholeMonth()
this.weekRow=this.rows
}else{
this.month--
this.getWholeMonth()
this.weekRow=this.rows
}
}
}
},
}
}
</script>
<style lang="scss" scoped>
.calendar{
font-size: .8em;
width: 80%;
margin: 0 auto;
height: auto;
.flex_sb{
display: flex;
justify-content:space-between;
}
.grey{
background-color: rgb(247, 244, 244);
}
.relative{
position: relative;
}
&.hidden{
height: 4.8em;
overflow: hidden;
}
.week{
z-index: 10;
background: #fff;
}
.cellbox{
flex-wrap: wrap;
margin: 0;
p{
display: inline-block;
width:14.28%;
height:2.4em;
line-height: 2.4em;
box-sizing: border-box;
margin: 0;
&.active{
color: #eee;
background-color: #409EFF;
}
}
}
.border{
p{
border: 1px solid #eee;
}
}
.row-1{
top:0
}
.row-2{
top:-2.4em
}
.row-3{
top:-4.8em
}
.row-4{
top:-7.2em
}
.row-5{
top:-9.6em
}
.row-6{
top:-12em
}
}
</style>
父元件:
<van-cell :title="monthTitle" />
<Calendar ref="calendar" @monthChange="updateMonth"/>
<script>
import Calendar from './../../components/calendar'
export default{
components:{
Calendar
},
data(){
return{
current:8
}
},
mounted(){
this.current=this.$refs.calendar.month
},
methods:{
//只要子元件資料變化了,就呼叫該函式
updateMonth(month){
this.current=month
},
},
computed:{
monthTitle(){
return "出勤月報"+"("+this.month.current+"月)"
}
}
}
</script>