2
0
mirror of https://github.com/tenrok/vue2-datepicker.git synced 2026-06-24 21:00:35 +03:00

添加手动输入功能

This commit is contained in:
mxie
2018-03-21 19:40:27 +08:00
parent c2ec32ca09
commit 83ece88336
2 changed files with 143 additions and 76 deletions
+140 -73
View File
@@ -1,55 +1,62 @@
<template> <template>
<div class="mx-datepicker" <div class="mx-datepicker"
:class="{'disabled': disabled}" :class="{'disabled': disabled}"
:style="{'width': width + 'px','min-width':range ? (type === 'datetime' ? '320px' : '210px') : '140px'}" :style="{'width': width + 'px','min-width':range ? (type === 'datetime' ? '320px' : '210px') : '140px'}"
v-clickoutside="closePopup"> v-clickoutside="closePopup">
<input readonly <input name="date"
name="date" :disabled="disabled"
:disabled="disabled" :class="inputClass"
:class="inputClass" :value="text"
:value="text" :readonly="!editable || range"
:placeholder="innerPlaceholder" :placeholder="innerPlaceholder"
ref="input" ref="input"
@mouseenter="hoverIcon" @mouseenter="hoverIcon"
@mouseleave="hoverIcon" @mouseleave="hoverIcon"
@click="togglePopup" @click="togglePopup"
@mousedown="$event.preventDefault()"> @input="handleInput"
<i class="mx-input-icon" @change="handleChange"
:class="showCloseIcon ? 'mx-input-icon__close' : 'mx-input-icon__calendar'" @mousedown="$event.preventDefault()">
@mouseenter="hoverIcon" <i class="mx-input-icon"
@mouseleave="hoverIcon" :class="showCloseIcon ? 'mx-input-icon__close' : 'mx-input-icon__calendar'"
@click="clickIcon" ></i> @mouseenter="hoverIcon"
<div class="mx-datepicker-popup" @mouseleave="hoverIcon"
:class="{'range':range}" @click="clickIcon"></i>
:style="position" <div class="mx-datepicker-popup"
ref="calendar" :class="{'range':range}"
v-show="showPopup"> :style="position"
ref="calendar"
<calendar-panel v-show="showPopup">
v-if="!range" <calendar-panel v-if="!range"
v-model="currentValue" v-model="currentValue"
@select="selectDate"
:show="showPopup"></calendar-panel>
<div v-else
style="overflow:hidden">
<div class="mx-datepicker-top"
v-if="ranges.length">
<span v-for="range in ranges"
@click="selectRange(range)">{{range.text}}</span>
</div>
<calendar-panel style="width:50%;box-shadow:1px 0 rgba(0, 0, 0, .1)"
v-model="currentValue[0]"
:end-at="currentValue[1]"
@select="selectDate" @select="selectDate"
:show="showPopup"></calendar-panel> :show="showPopup"></calendar-panel>
<div v-else style="overflow:hidden" > <calendar-panel style="width:50%;"
<div class="mx-datepicker-top" v-if="ranges.length"> v-model="currentValue[1]"
<span v-for="range in ranges" @click="selectRange(range)">{{range.text}}</span> :start-at="currentValue[0]"
</div> @select="selectDate"
<calendar-panel style="width:50%;box-shadow:1px 0 rgba(0, 0, 0, .1)" :show="showPopup"></calendar-panel>
v-model="currentValue[0]" </div>
:end-at="currentValue[1]" <div class="mx-datepicker-footer"
@select="selectDate" v-if="confirm">
:show="showPopup"></calendar-panel> <button type="button"
<calendar-panel style="width:50%;" class="mx-datepicker-btn mx-datepicker-btn-confirm"
v-model="currentValue[1]" @click="confirmDate"> {{ confirmText }}</button>
:start-at="currentValue[0]"
@select="selectDate"
:show="showPopup"></calendar-panel>
</div>
<div class="mx-datepicker-footer" v-if="confirm">
<button type="button" class="mx-datepicker-btn mx-datepicker-btn-confirm" @click="confirmDate"> {{ confirmText }}</button>
</div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script> <script>
@@ -88,7 +95,7 @@ export default {
}, },
disabledDays: { disabledDays: {
type: Array, type: Array,
default: function() { default: function () {
return [] return []
} }
}, },
@@ -129,20 +136,25 @@ export default {
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false
},
editable: {
type: Boolean,
default: false
} }
}, },
data() { data () {
return { return {
showPopup: false, showPopup: false,
showCloseIcon: false, showCloseIcon: false,
currentValue: this.value, currentValue: this.value,
position: null, position: null,
userInput: '',
ranges: [] // 快捷选项 ranges: [] // 快捷选项
} }
}, },
watch: { watch: {
value: { value: {
handler(val) { handler (val) {
if (!this.range) { if (!this.range) {
this.currentValue = this.isValidDate(val) ? val : undefined this.currentValue = this.isValidDate(val) ? val : undefined
} else { } else {
@@ -153,17 +165,19 @@ export default {
}, },
immediate: true immediate: true
}, },
showPopup(val) { showPopup (val) {
if (val) { if (val) {
this.$nextTick(this.displayPopup) this.$nextTick(this.displayPopup)
} else {
this.userInput = null
} }
} }
}, },
computed: { computed: {
translation() { translation () {
return Languages[this.lang] || Languages['en'] return Languages[this.lang] || Languages['en']
}, },
innerPlaceholder() { innerPlaceholder () {
return ( return (
this.placeholder || this.placeholder ||
(this.range (this.range
@@ -171,9 +185,9 @@ export default {
: this.translation.placeholder.date) : this.translation.placeholder.date)
) )
}, },
text() { text () {
if (!this.range && this.isValidDate(this.value)) { if (!this.range && this.isValidDate(this.value)) {
return this.stringify(this.value) return this.userInput !== null ? this.userInput : this.stringify(this.value)
} }
if (this.range && this.isValidRange(this.value)) { if (this.range && this.isValidRange(this.value)) {
return ( return (
@@ -184,18 +198,43 @@ export default {
} }
}, },
methods: { methods: {
updateDate() { handleInput (event) {
this.userInput = event.target.value
},
handleChange (event) {
const value = event.target.value
const date = this.parseDate(value, this.format)
if (date && this.editable && !this.range) {
if (this.notBefore && date < new Date(this.notBefore)) {
return
}
if (this.notAfter && date > new Date(this.notAfter)) {
return
}
for (let i = 0, len = this.disabledDays.length; i < len; i++) {
if (date.getTime() === new Date(this.disabledDays[i]).getTime()) {
return
}
}
this.$emit('input', date)
this.$emit('change', date)
this.closePopup()
}
},
updateDate () {
const val = this.currentValue const val = this.currentValue
if ((!this.range && val) || (this.range && val[0] && val[1])) { if ((!this.range && val) || (this.range && val[0] && val[1])) {
this.$emit('input', val) this.$emit('input', val)
this.$emit('change', val)
this.closePopup()
} }
}, },
confirmDate() { confirmDate () {
this.updateDate() this.updateDate()
this.closePopup() this.closePopup()
this.$emit('confirm', this.currentValue) this.$emit('confirm', this.currentValue)
}, },
selectDate(show = false) { selectDate (show = false) {
if (!this.confirm && !this.disabled) { if (!this.confirm && !this.disabled) {
this.updateDate() this.updateDate()
if (!show && this.type === 'date' && !this.range) { if (!show && this.type === 'date' && !this.range) {
@@ -203,10 +242,10 @@ export default {
} }
} }
}, },
closePopup() { closePopup () {
this.showPopup = false this.showPopup = false
}, },
togglePopup() { togglePopup () {
if (this.showPopup) { if (this.showPopup) {
this.$refs.input.blur() this.$refs.input.blur()
this.showPopup = false this.showPopup = false
@@ -215,7 +254,7 @@ export default {
this.showPopup = true this.showPopup = true
} }
}, },
hoverIcon(e) { hoverIcon (e) {
if (this.disabled) { if (this.disabled) {
return return
} }
@@ -226,17 +265,40 @@ export default {
this.showCloseIcon = false this.showCloseIcon = false
} }
}, },
clickIcon() { clickIcon () {
if (this.disabled) { if (this.disabled) {
return return
} }
if (this.showCloseIcon) { if (this.showCloseIcon) {
this.$emit('input', '') this.$emit('input', '')
this.$emit('change', '')
} else { } else {
this.togglePopup() this.togglePopup()
} }
}, },
formatDate(date, fmt = 'YYYY-MM-dd HH:mm:ss') { parseDate (str, fmt = 'yyyy-MM-dd') {
let isValid = true
const obj = { y: 0, M: 1, d: 0, H: 0, h: 0, m: 0, s: 0 }
fmt.replace(/([^yMdHhms]*?)(([yMdHhms])\3*)([^yMdHhms]*?)/g, function (m, $1, $2, $3, $4, idx, old) {
const rgs = new RegExp($1 + '(\\d{' + $2.length + '})' + $4)
const index = str.search(rgs)
if (index === -1) {
isValid = false
} else {
str = str.replace(rgs, function (_m, _$1) {
obj[$3] = parseInt(_$1);
return ''
});
}
return ''
});
if (!isValid) {
return false
}
obj.M--
return new Date(obj.y, obj.M, obj.d, obj.H || obj.h, obj.m, obj.s)
},
formatDate (date, fmt = 'yyyy-MM-dd HH:mm:ss') {
const hour = date.getHours() const hour = date.getHours()
const map = { const map = {
'M+': date.getMonth() + 1, // 月份 'M+': date.getMonth() + 1, // 月份
@@ -261,13 +323,13 @@ export default {
}) })
return str return str
}, },
stringify(date) { stringify (date) {
return this.formatDate(new Date(date), this.format) return this.formatDate(new Date(date), this.format)
}, },
isValidDate(date) { isValidDate (date) {
return !!new Date(date).getTime() return !!new Date(date).getTime()
}, },
isValidRange(date) { isValidRange (date) {
return ( return (
Array.isArray(date) && Array.isArray(date) &&
date.length === 2 && date.length === 2 &&
@@ -275,10 +337,11 @@ export default {
this.isValidDate(date[1]) this.isValidDate(date[1])
) )
}, },
selectRange(range) { selectRange (range) {
this.$emit('input', [range.start, range.end]) this.$emit('input', [range.start, range.end])
this.$emit('change', [range.start, range.end])
}, },
initRanges() { initRanges () {
if (Array.isArray(this.shortcuts)) { if (Array.isArray(this.shortcuts)) {
this.ranges = this.shortcuts this.ranges = this.shortcuts
} else if (this.shortcuts) { } else if (this.shortcuts) {
@@ -311,7 +374,7 @@ export default {
this.ranges = [] this.ranges = []
} }
}, },
displayPopup() { displayPopup () {
if (this.disabled) { if (this.disabled) {
return return
} }
@@ -342,12 +405,12 @@ export default {
} }
} }
}, },
created() { created () {
this.initRanges() this.initRanges()
}, },
directives: { directives: {
clickoutside: { clickoutside: {
bind(el, binding, vnode) { bind (el, binding, vnode) {
el['@clickoutside'] = e => { el['@clickoutside'] = e => {
if ( if (
!el.contains(e.target) && !el.contains(e.target) &&
@@ -359,7 +422,7 @@ export default {
} }
document.addEventListener('click', el['@clickoutside'], true) document.addEventListener('click', el['@clickoutside'], true)
}, },
unbind(el) { unbind (el) {
document.removeEventListener('click', el['@clickoutside'], true) document.removeEventListener('click', el['@clickoutside'], true)
} }
} }
@@ -409,10 +472,14 @@ export default {
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
&:disabled, &.disabled { &:disabled,
&.disabled {
opacity: 0.7; opacity: 0.7;
cursor: not-allowed; cursor: not-allowed;
} }
&:focus {
outline: none;
}
} }
.mx-input-icon { .mx-input-icon {
+2 -2
View File
@@ -3,7 +3,7 @@
<div class="example"> <div class="example">
<section class="demo"> <section class="demo">
<span class="label">default:</span> <span class="label">default:</span>
<date-picker v-model="value1" lang="en"></date-picker> <date-picker v-model="value1" lang="en" editable></date-picker>
</section> </section>
<section class="demo"> <section class="demo">
<span class="label">range:</span> <span class="label">range:</span>
@@ -87,7 +87,7 @@ export default {
value6: '', value6: '',
value7: '', value7: '',
value8: '', value8: '',
demo1: '<date-picker v-model="value1" lang="en"></date-picker>\n<date-picker v-model="value3" range lang="en"></date-picker>', demo1: '<date-picker v-model="value1" editable lang="en"></date-picker>\n<date-picker v-model="value3" range lang="en"></date-picker>',
demo2: `<date-picker v-model="value3" type="datetime" format="yyyy-MM-dd HH:mm:ss" lang="en"></date-picker>\n<date-picker v-model="value4" type="datetime" format="yyyy-MM-dd hh:mm:ss a" :time-picker-options="{start: '00:00',step: '00:30',end: '23:30'}" lang="en"></date-picker>\n<date-picker v-model="value4" range type="datetime" format="yyyy-MM-dd HH:mm:ss" lang="en"></date-picker>`, demo2: `<date-picker v-model="value3" type="datetime" format="yyyy-MM-dd HH:mm:ss" lang="en"></date-picker>\n<date-picker v-model="value4" type="datetime" format="yyyy-MM-dd hh:mm:ss a" :time-picker-options="{start: '00:00',step: '00:30',end: '23:30'}" lang="en"></date-picker>\n<date-picker v-model="value4" range type="datetime" format="yyyy-MM-dd HH:mm:ss" lang="en"></date-picker>`,
demo3: '<date-picker v-model="value6" format="yyyy-MM-dd" lang="en" confirm></date-picker>\n<date-picker v-model="value7" lang="en" type="datetime" format="yyyy-MM-dd hh:mm:ss" confirm></date-picker>\n<date-picker v-model="value8" lang="en" range format="yyyy-MM-dd" confirm></date-picker>' demo3: '<date-picker v-model="value6" format="yyyy-MM-dd" lang="en" confirm></date-picker>\n<date-picker v-model="value7" lang="en" type="datetime" format="yyyy-MM-dd hh:mm:ss" confirm></date-picker>\n<date-picker v-model="value8" lang="en" range format="yyyy-MM-dd" confirm></date-picker>'
} }