Skip to content

Instantly share code, notes, and snippets.

@sliwey-zz
Last active August 23, 2017 09:00
Show Gist options
  • Save sliwey-zz/a9303e0fc66a5acec2322b0fdfdaa416 to your computer and use it in GitHub Desktop.
Save sliwey-zz/a9303e0fc66a5acec2322b0fdfdaa416 to your computer and use it in GitHub Desktop.
data-picker
<template>
<div class="calendar-wrap" :class="size" v-clickOutside="handleClose">
<input
type="text"
readonly="readonly"
class="calendar-input"
:value="selectDateString"
@click="handleToggle">
<transition name="slide-down" @after-leave="handleAfterLeave">
<div class="calendar-drop" :style="dropStyle" v-show="visible">
<div class="calendar-head">
<span class="prevYear" @click="handleAdd(currentView === 'year' ? -10 : -1, 'years')"></span>
<span class="prevMonth" @click="handleAdd(-1, 'months')" v-show="currentView === 'date'"></span>
<span class="calendar-year" @click="currentView = 'year'">{{ yearString }}</span>
<span class="calendar-month" v-show="currentView === 'date'" @click="currentView = 'month'">{{ currDate.format('M') }}月</span>
<span class="nextYear" @click="handleAdd(currentView === 'year' ? 10 : 1, 'years')"></span>
<span class="nextMonth" @click="handleAdd(1, 'months')" v-show="currentView === 'date'"></span>
</div>
<ul class="calendar-list" v-show="currentView === 'date'">
<li class="calendar-item week">日</li>
<li class="calendar-item week">一</li>
<li class="calendar-item week">二</li>
<li class="calendar-item week">三</li>
<li class="calendar-item week">四</li>
<li class="calendar-item week">五</li>
<li class="calendar-item week">六</li>
<li
class="calendar-item"
v-for="day in days"
:class="{today: day.isToday, selected: day.isSelected, 'prev-month': day.isPrevMonth, 'next-month': day.isNextMonth, disabled: day.isDisabled}"
@click="handleDaySelect(day.value, day.isPrevMonth, day.isNextMonth, day.isDisabled)">
{{ day.value }}
</li>
</ul>
<ul class="calendar-list month" v-show="currentView === 'month'">
<li
class="calendar-item"
v-for="month in months"
:class="{selected: month.isSelected}"
@click="handleMonthSelect(month.value)">
{{ month.value }}月
</li>
</ul>
<ul class="calendar-list year" v-show="currentView === 'year'">
<li
class="calendar-item"
v-for="year in years"
:class="{selected: year.isSelected}"
@click="handleYearSelect(year.value)">
{{ year.value }}
</li>
</ul>
</div>
</transition>
</div>
</template>
<script>
import moment from 'moment'
import { oneOf, getType } from '@/utils'
import { clickOutside } from '@/directives'
const DEFAULT_FORMATS = {
year: 'YYYY',
month: 'YYYY-MM',
date: 'YYYY-MM-DD',
}
export default {
name: 'app-calendar',
props: {
format: String,
options: {
type: Object,
default: () => ({}),
},
size: {
validator: value => oneOf(value, ['small', 'normal', 'large']),
default: 'normal',
},
placement: {
validator: value => oneOf(value, ['top', 'top-start', 'top-end', 'right', 'right-start', 'right-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end']),
default: 'bottom-start',
},
type: {
validator: value => oneOf(value, ['date', 'year', 'month', ]),
default: 'date',
},
value: {
validator: value => moment(value).isValid(),
default: () => moment(),
},
},
directives: {
clickOutside
},
data() {
return {
visible: false,
width: 90,
height: 30,
selectDate: moment(this.value),
currDate: moment(this.value),
currentView: this.type,
}
},
computed: {
dropStyle() {
return getDropPosition(this.width, this.height, this.placement)
},
selectDateString() {
return this.selectDate.format(this.format || DEFAULT_FORMATS[this.type])
},
yearString() {
const firstYear = ~~(this.currDate.year() / 10) * 10
return this.currentView === 'year' ? `${firstYear}-${firstYear + 9}` : `${this.currDate.format('YYYY')}年`
},
days() {
return getDays(this.currDate.clone(), this.selectDate.clone(), this.options.disabledDate)
},
months() {
const monthArr = []
const currDate = this.currDate.clone()
for (let i = 0; i < 12; i++) {
currDate.month(i)
monthArr.push({
value: i + 1,
isSelected: currDate.isSame(this.selectDate, 'month'),
})
}
return monthArr
},
years() {
const yearArr = []
const firstYear = ~~(this.currDate.year() / 10) * 10
for (let i = 0; i < 10; i++) {
yearArr.push({
value: firstYear + i,
isSelected: this.selectDate.year() === firstYear + i
})
}
return yearArr
},
},
methods: {
handleToggle() {
this.visible = !this.visible
},
handleClose() {
this.visible = false
},
handleAfterLeave() {
this.currDate = this.selectDate.clone()
this.currentView = this.type
},
handleAdd(num, key) {
this.currDate = this.currDate.clone().add(num, key)
},
handleDaySelect(day, isPrevMonth, isNextMonth, isDisabled) {
if (isDisabled) {
return
}
if (isPrevMonth) {
this.currDate.add(-1, 'months')
}
if (isNextMonth) {
this.currDate.add(1, 'months')
}
this.currDate.date(day)
this.handleClose()
if (!this.selectDate.isSame(this.currDate)) {
this.selectDate = this.currDate.clone()
this.$emit('change', this.selectDate.format(this.format || DEFAULT_FORMATS[this.type]))
}
},
handleMonthSelect(month) {
this.currDate = this.currDate.clone().month(month - 1)
if (this.type === 'month') {
this.handleClose()
if (!this.selectDate.isSame(this.currDate)) {
this.selectDate = this.currDate.clone()
this.$emit('change', this.selectDate.format(this.format || DEFAULT_FORMATS[this.type]))
}
} else {
this.currentView = 'date'
}
},
handleYearSelect(year) {
this.currDate = this.currDate.clone().year(year)
if (this.type === 'year') {
this.handleClose()
if (!this.selectDate.isSame(this.currDate)) {
this.selectDate = this.currDate.clone()
this.$emit('change', this.selectDate.format(this.format || DEFAULT_FORMATS[this.type]))
}
} else {
this.currentView = 'month'
}
},
},
mounted() {
const computedStyle = window.getComputedStyle(this.$el)
this.width = Number(computedStyle.width.replace('px', ''))
this.height = Number(computedStyle.height.replace('px', ''))
},
}
function getDropPosition(offsetLeft, offsetTop, placement) {
const offset = 5
const position = {}
const postfix = placement.split('-')[1]
if (placement.indexOf('top') > -1) {
position.bottom = `${offset}px`
if (postfix) {
if (postfix === 'start') {
position.left = '0'
}
if (postfix === 'end') {
position.right = '0'
}
} else {
position.left = `${offsetLeft / 2}`
position.transform = 'translate3d(-50%, 0, 0)'
}
}
if (placement.indexOf('right') > -1) {
position.left = `${offsetLeft + offset}px`
if (postfix) {
if (postfix === 'start') {
position.top = '0'
}
if (postfix === 'end') {
position.bottom = '0'
}
} else {
position.top = `${offsetTop / 2}`
position.transform = 'translate3d(0, -50%, 0)'
}
}
if (placement.indexOf('bottom') > -1) {
position.top = `${(offsetTop + offset)}px`
if (postfix) {
if (postfix === 'start') {
position.left = '0'
}
if (postfix === 'end') {
position.right = '0'
}
} else {
position.left = `${offsetLeft / 2}`
position.transform = 'translate3d(-50%, 0, 0)'
}
}
if (placement.indexOf('left') > -1) {
position.right = `${offsetLeft + offset}px`
if (postfix) {
if (postfix === 'start') {
position.top = '0'
}
if (postfix === 'end') {
position.bottom = '0'
}
} else {
position.top = `${offsetTop / 2}`
position.transform = 'translate3d(0, -50%, 0)'
}
}
return position
}
function getDays(currDate, selectDate, disabledDate) {
const today = moment()
const firstDayInWeek = moment(currDate.format('YYYY-MM') + '-01').day()
const days = currDate.daysInMonth()
const prevMonthDays = firstDayInWeek === 0 ? 7 : firstDayInWeek
const nextMonthDays = 42 - prevMonthDays - days
const dayArr = []
const prevMonth = currDate.clone().add(-1, 'months')
const nextMonth = currDate.clone().add(1, 'months')
const lastDayOfPrevMonth = prevMonth.daysInMonth()
for (let i = lastDayOfPrevMonth; i > lastDayOfPrevMonth - prevMonthDays; i--) {
prevMonth.date(i)
dayArr.unshift({
value: i,
isPrevMonth: true,
isDisabled: disabledDate && getType(disabledDate) === 'function' && disabledDate(prevMonth.toDate()),
})
}
for (let i = 1; i <= days; i++) {
currDate.date(i)
dayArr.push({
value: i,
isSelected: currDate.isSame(selectDate, 'day'),
isToday: currDate.isSame(today, 'day'),
isDisabled: disabledDate && getType(disabledDate) === 'function' && disabledDate(currDate.toDate()),
})
}
for (let i = 1; i <= nextMonthDays; i++) {
nextMonth.date(i)
dayArr.push({
value: i,
isNextMonth: true,
isDisabled: disabledDate && getType(disabledDate) === 'function' && disabledDate(nextMonth.toDate()),
})
}
return dayArr
}
</script>
<style lang="scss" scoped>
.calendar{
&-wrap{
position: relative;
display: inline-block;
width: 90px;
height: 30px;
color: #4ccaac;
font-size: 12px;
border: 1px solid rgba(255, 255, 255, .5);
border-radius: 5px;
box-sizing: border-box;
&.small{
height: 24px;
}
&.large{
height: 36px;
font-size: 14px;
}
}
&-input{
display: block;
width: 100%;
height: 100%;
background: transparent;
color: #999;
text-align: center;
cursor: pointer;
border: 0;
}
&-drop{
position: absolute;
width: 210px;
background: #fff;
color: #333;
font-size: 14px;
border: 1px solid #4ccaac;
border-radius: 5px;
box-sizing: border-box;
z-index: 99999;
}
&-head{
height: 30px;
margin-bottom: 5px;
padding: 0 5px;
text-align: center;
line-height: 30px;
overflow: hidden;
border-bottom: 1px solid #ddd;
%btn{
width: 20px;
height: 100%;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
}
.prevYear{
@extend %btn;
float: left;
background-image: url(images/prev2.png);
}
.prevMonth{
@extend %btn;
float: left;
background-image: url(images/prev.png);
}
.nextYear{
@extend %btn;
float: right;
background-image: url(images/next2.png);
}
.nextMonth{
@extend %btn;
float: right;
background-image: url(images/next.png);
}
.calendar-year,
.calendar-month{
cursor: pointer;
&:hover{
color: #4ccaac;
}
}
}
&-list{
width: 189px;
margin: 0 auto;
padding-bottom: 10px;
overflow: hidden;
&.month,
&.year{
padding-bottom: 0;
.calendar-item{
width: 63px;
height: 30px;
margin-bottom: 5px;
}
}
}
&-item{
float: left;
width: 27px;
height: 27px;
line-height: 27px;
text-align: center;
border: 1px solid transparent;
box-sizing: border-box;
transition: .3s;
cursor: pointer;
&:hover{
background: #cdf5ef;
}
&.week{
color: #666;
cursor: default;
&:hover{
background: transparent;
}
}
&.today{
border-color: #4ccaac;
}
&.selected{
color: #fff;
background: #4ccaac;
&:hover{
background: #4ccaac;
}
}
&.prev-month,
&.next-month{
color: #bbb;
}
&.disabled{
background: #f4f4f4;
color: #bbb;
cursor: not-allowed;
}
}
}
.slide-down-enter,
.slide-down-leave-to{
transform: translate3d(0, -5px, 0);
opacity: 0;
}
.slide-down-enter-to,
.slide-down-leave{
transform: translate3d(0, 0, 0);
opacity: 1;
}
.slide-down-enter-active,
.slide-down-leave-active{
transition: .3s;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment