mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-06-24 21:00:35 +03:00
添加手动输入功能
This commit is contained in:
+140
-73
@@ -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
@@ -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>'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user