mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-06-11 05:02:27 +03:00
279 lines
6.9 KiB
Vue
279 lines
6.9 KiB
Vue
<template>
|
|
<div class="datepicker"
|
|
:style="{'width': width + 'px','min-width':range ? '210px' : '140px'}"
|
|
v-clickoutside="closePopup">
|
|
<input readonly
|
|
class="input"
|
|
:value="text"
|
|
:placeholder="innerPlaceholder"
|
|
ref="input"
|
|
@click="togglePopup"
|
|
@mousedown="$event.preventDefault()">
|
|
<i class="input-icon"
|
|
:class="showCloseIcon ? 'input-icon__close' : 'input-icon__calendar'"
|
|
@mouseenter="hoverIcon"
|
|
@mouseleave="hoverIcon"
|
|
@click="clickIcon" ></i>
|
|
<div class="datepicker-popup"
|
|
:class="{'range':range}"
|
|
v-show="showPopup">
|
|
<template v-if="!range">
|
|
<calendar-panel v-model="currentValue" :show="showPopup"></calendar-panel>
|
|
</template>
|
|
<template v-else>
|
|
<div class="datepicker-top">
|
|
<span v-for="range in ranges" @click="selectRange(range)">{{range.text}}</span>
|
|
</div>
|
|
<calendar-panel style="width:50%" v-model="currentValue[0]" :end-at="currentValue[1]" :show="showPopup"></calendar-panel>
|
|
<calendar-panel style="width:50%" v-model="currentValue[1]" :start-at="currentValue[0]" :show="showPopup"></calendar-panel>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { formatDate } from './utils.js'
|
|
import CalendarPanel from './calendar-panel.vue'
|
|
|
|
export default {
|
|
components: { CalendarPanel },
|
|
props: {
|
|
format: {
|
|
type: String,
|
|
default: 'yyyy-MM-dd',
|
|
},
|
|
range: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
width: {
|
|
type: [String, Number],
|
|
default: 210,
|
|
},
|
|
palceholder: String,
|
|
value: null,
|
|
},
|
|
data() {
|
|
return {
|
|
showPopup: false,
|
|
showCloseIcon: false,
|
|
currentValue: this.value,
|
|
ranges: [],
|
|
}
|
|
},
|
|
watch: {
|
|
value: {
|
|
handler(val) {
|
|
if (!this.range) {
|
|
this.currentValue = this.isValidDate(val) ? val : undefined
|
|
} else {
|
|
this.currentValue = this.isValidRange(val) ? val : [undefined, undefined]
|
|
}
|
|
},
|
|
immediate: true,
|
|
},
|
|
currentValue(val) {
|
|
if ((!this.range && val) || (this.range && val[0] && val[1])) {
|
|
this.$emit('input', val)
|
|
}
|
|
},
|
|
},
|
|
computed: {
|
|
innerPlaceholder() {
|
|
return this.placeholder ? this.placeholder : (this.range ? '请选择日期范围' : '请选择日期')
|
|
},
|
|
text() {
|
|
if (!this.range && this.currentValue) {
|
|
return this.stringify(this.currentValue)
|
|
}
|
|
if (this.range && this.currentValue[0] && this.currentValue[1]) {
|
|
return this.stringify(this.currentValue[0]) + ' ~ ' + this.stringify(this.currentValue[1])
|
|
}
|
|
return ''
|
|
},
|
|
},
|
|
created() {
|
|
this.initRanges()
|
|
},
|
|
methods: {
|
|
closePopup() {
|
|
this.showPopup = false
|
|
},
|
|
togglePopup() {
|
|
if (this.showPopup) {
|
|
this.$refs.input.blur()
|
|
this.showPopup = false
|
|
} else {
|
|
this.$refs.input.focus()
|
|
this.showPopup = true
|
|
}
|
|
},
|
|
hoverIcon(e) {
|
|
if (e.type === 'mouseenter' && this.text) {
|
|
this.showCloseIcon = true
|
|
}
|
|
if (e.type === 'mouseleave') {
|
|
this.showCloseIcon = false
|
|
}
|
|
},
|
|
clickIcon() {
|
|
if (this.showCloseIcon) {
|
|
this.$emit('input', '')
|
|
} else {
|
|
this.togglePopup()
|
|
}
|
|
},
|
|
stringify(date) {
|
|
return formatDate(date, this.format)
|
|
},
|
|
isValidDate(date) {
|
|
return !!new Date(date).getTime()
|
|
},
|
|
isValidRange(date) {
|
|
return Array.isArray(date) &&
|
|
date.length === 2 &&
|
|
this.isValidDate(date[0]) &&
|
|
this.isValidDate(date[1])
|
|
},
|
|
selectRange(range) {
|
|
this.$emit('input', [range.start, range.end])
|
|
},
|
|
initRanges() {
|
|
const time = new Date()
|
|
time.setMonth(time.getMonth() + 1, 0) // 切换到本月最后一天
|
|
this.ranges.push({
|
|
text: '今天',
|
|
start: new Date(),
|
|
end: new Date(),
|
|
}, {
|
|
text: '最近一周',
|
|
start: new Date(Date.now() - 3600 * 1000 * 24 * 7),
|
|
end: new Date(),
|
|
}, {
|
|
text: '今后一周',
|
|
start: new Date(),
|
|
end: new Date(Date.now() + 3600 * 1000 * 24 * 7),
|
|
}, {
|
|
text: '本月',
|
|
start: new Date(time.getFullYear(), time.getMonth(), 1),
|
|
end: time,
|
|
}, {
|
|
text: '最近一个月',
|
|
start: new Date(Date.now() - 3600 * 1000 * 24 * 30),
|
|
end: new Date(),
|
|
}, {
|
|
text: '最近三个月',
|
|
start: new Date(Date.now() - 3600 * 1000 * 24 * 90),
|
|
end: new Date()
|
|
})
|
|
},
|
|
},
|
|
directives: {
|
|
clickoutside: {
|
|
bind(el, binding, vnode) {
|
|
el['@clickoutside'] = (e) => {
|
|
if (!el.contains(e.target) && binding.expression && vnode.context[binding.expression]) {
|
|
binding.value()
|
|
}
|
|
}
|
|
document.addEventListener('click', el['@clickoutside'], true)
|
|
},
|
|
unbind(el) {
|
|
document.removeEventListener('click', el['@clickoutside'], true)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
.datepicker {
|
|
position: relative;
|
|
display: inline-block;
|
|
color:#73879c;
|
|
font: 14px/1.5 "Helvetica Neue", Helvetica, Arial, "Microsoft Yahei", sans-serif;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.datepicker * {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.datepicker-popup {
|
|
position: absolute;
|
|
min-width: 234px;
|
|
margin-top: 1px;
|
|
border: 1px solid #d9d9d9;
|
|
background-color: #fff;
|
|
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
|
|
z-index: 1000;
|
|
}
|
|
|
|
.range {
|
|
min-width: 468px;
|
|
}
|
|
|
|
.input {
|
|
display: inline-block;
|
|
width: 100%;
|
|
height: 34px;
|
|
padding: 6px 30px 6px 10px;
|
|
font-size: 14px;
|
|
line-height: 1.4;
|
|
color: #555;
|
|
background-color: #fff;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
|
}
|
|
|
|
.input-icon {
|
|
top: 0;
|
|
right: 0;
|
|
position: absolute;
|
|
width: 30px;
|
|
height: 100%;
|
|
color: #888;
|
|
text-align: center;
|
|
font-style: normal;
|
|
}
|
|
.input-icon::after{
|
|
content:'';
|
|
display: inline-block;
|
|
width: 0;
|
|
height: 100%;
|
|
vertical-align: middle;
|
|
}
|
|
.input-icon__calendar{
|
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA00lEQVQ4T72SzQ2CQBCF54UGKIES6EAswQq0BS/A3PQ0hAt0oKVQgiVYAkcuZMwSMOyCyRKNe9uf+d6b2Qf6csGtL8sy7vu+Zebn/E5EoiAIwjRNH/PzBUBEGiJqmPniAMw+YeZkFSAiJwA3j45aVT0wsxGitwOjDGDnASBVvU4OLQARRURk9e4CAcSqWn8CLHp3Ae6MXAe/B4yzUeMkz/P9ZgdFUQzFIwD/B4yKgwMTos0OtvzCHcDRJ0gAzlmW1VYSq6oKu66LfQBTjC2AT+Hamxcml5IRpPq3VQAAAABJRU5ErkJggg==);
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
}
|
|
.input-icon__close::before {
|
|
content: '\2716';
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.datepicker-top {
|
|
margin: 0 12px;
|
|
line-height: 34px;
|
|
border-bottom: 1px solid rgba(0, 0, 0, .05);
|
|
}
|
|
|
|
.datepicker-top>span {
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.datepicker-top>span:hover {
|
|
color: #1284e7;
|
|
}
|
|
|
|
.datepicker-top>span:after {
|
|
content: "|";
|
|
margin: 0 10px;
|
|
color: #48576a;
|
|
}
|
|
</style>
|