iview/src/components/date-picker/picker.vue

528 lines
19 KiB
Vue
Raw Normal View History

2016-12-12 10:37:52 +08:00
<template>
2017-07-20 11:53:18 +08:00
<div :class="[prefixCls]" v-clickoutside="handleClose">
<div ref="reference" :class="[prefixCls + '-rel']">
<slot>
<i-input
:class="[prefixCls + '-editor']"
:readonly="!editable || readonly"
:disabled="disabled"
:size="size"
:placeholder="placeholder"
:value="visualValue"
@on-input-change="handleInputChange"
@on-focus="handleFocus"
@on-click="handleIconClick"
@mouseenter.native="handleInputMouseenter"
@mouseleave.native="handleInputMouseleave"
:icon="iconType"></i-input>
</slot>
</div>
<transition :name="transition">
2017-07-20 11:53:18 +08:00
<Drop
@click.native="handleTransferClick"
v-show="opened"
:class="{ [prefixCls + '-transfer']: transfer }"
:placement="placement"
ref="drop"
:data-transfer="transfer"
v-transfer-dom>
<div ref="picker"></div>
</Drop>
</transition>
2016-12-12 20:34:28 +08:00
</div>
2016-12-12 10:37:52 +08:00
</template>
<script>
2016-12-12 20:34:28 +08:00
import Vue from 'vue';
import iInput from '../../components/input/input.vue';
import Drop from '../../components/select/dropdown.vue';
import clickoutside from '../../directives/clickoutside';
2017-07-20 11:53:18 +08:00
import TransferDom from '../../directives/transfer-dom';
2016-12-12 20:34:28 +08:00
import { oneOf } from '../../utils/assist';
2016-12-27 17:41:35 +08:00
import { formatDate, parseDate } from './util';
import Emitter from '../../mixins/emitter';
2016-12-12 20:34:28 +08:00
const prefixCls = 'ivu-date-picker';
const DEFAULT_FORMATS = {
date: 'yyyy-MM-dd',
month: 'yyyy-MM',
2016-12-15 20:16:58 +08:00
year: 'yyyy',
2016-12-12 20:34:28 +08:00
datetime: 'yyyy-MM-dd HH:mm:ss',
time: 'HH:mm:ss',
timerange: 'HH:mm:ss',
daterange: 'yyyy-MM-dd',
datetimerange: 'yyyy-MM-dd HH:mm:ss'
};
2016-12-15 20:16:58 +08:00
const RANGE_SEPARATOR = ' - ';
const DATE_FORMATTER = function(value, format) {
return formatDate(value, format);
};
const DATE_PARSER = function(text, format) {
return parseDate(text, format);
};
const RANGE_FORMATTER = function(value, format) {
if (Array.isArray(value) && value.length === 2) {
const start = value[0];
const end = value[1];
if (start && end) {
return formatDate(start, format) + RANGE_SEPARATOR + formatDate(end, format);
}
}
return '';
};
const RANGE_PARSER = function(text, format) {
const array = text.split(RANGE_SEPARATOR);
if (array.length === 2) {
const range1 = array[0];
const range2 = array[1];
return [parseDate(range1, format), parseDate(range2, format)];
}
return [];
};
const TYPE_VALUE_RESOLVER_MAP = {
default: {
formatter(value) {
if (!value) return '';
return '' + value;
},
parser(text) {
if (text === undefined || text === '') return null;
return text;
}
},
date: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
datetime: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
daterange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
datetimerange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
timerange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
time: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
month: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
year: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
number: {
formatter(value) {
if (!value) return '';
return '' + value;
},
parser(text) {
let result = Number(text);
if (!isNaN(text)) {
return result;
} else {
return null;
}
}
}
};
2016-12-12 10:37:52 +08:00
export default {
name: 'CalendarPicker',
mixins: [ Emitter ],
2016-12-12 20:34:28 +08:00
components: { iInput, Drop },
2017-07-20 11:53:18 +08:00
directives: { clickoutside, TransferDom },
2016-12-12 20:34:28 +08:00
props: {
format: {
type: String
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
editable: {
type: Boolean,
default: true
},
clearable: {
type: Boolean,
default: true
},
confirm: {
type: Boolean,
default: false
},
open: {
type: Boolean,
default: null
},
2016-12-12 20:34:28 +08:00
size: {
validator (value) {
return oneOf(value, ['small', 'large']);
}
},
placeholder: {
type: String,
default: ''
},
2016-12-22 09:18:11 +08:00
placement: {
2016-12-12 20:34:28 +08:00
validator (value) {
2016-12-22 09:18:11 +08:00
return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
2016-12-12 20:34:28 +08:00
},
2016-12-22 09:18:11 +08:00
default: 'bottom-start'
2016-12-12 20:34:28 +08:00
},
options: {
type: Object
2017-07-20 11:53:18 +08:00
},
transfer: {
type: Boolean,
default: false
2016-12-12 20:34:28 +08:00
}
},
2016-12-12 10:37:52 +08:00
data () {
2016-12-12 20:34:28 +08:00
return {
prefixCls: prefixCls,
showClose: false,
visible: false,
2016-12-14 23:08:57 +08:00
picker: null,
2016-12-22 09:18:11 +08:00
internalValue: '',
disableClickOutSide: false, // fixed when click a date,trigger clickoutside to close picker
2017-07-20 11:53:18 +08:00
disableCloseUnderTransfer: false, // transfer 模式下点击Drop也会触发关闭
currentValue: this.value
2016-12-25 22:49:42 +08:00
};
2016-12-12 20:34:28 +08:00
},
computed: {
opened () {
return this.open === null ? this.visible : this.open;
},
2016-12-12 20:34:28 +08:00
iconType () {
2016-12-26 14:50:39 +08:00
let icon = 'ios-calendar-outline';
2016-12-28 15:21:25 +08:00
if (this.type === 'time' || this.type === 'timerange') icon = 'ios-clock-outline';
2016-12-26 14:50:39 +08:00
if (this.showClose) icon = 'ios-close';
return icon;
2016-12-12 20:34:28 +08:00
},
2016-12-22 15:08:05 +08:00
transition () {
if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') {
return 'slide-up';
} else {
return 'slide-down';
}
},
2016-12-15 20:16:58 +08:00
selectionMode() {
2016-12-19 16:21:54 +08:00
if (this.type === 'month') {
2016-12-15 20:16:58 +08:00
return 'month';
} else if (this.type === 'year') {
return 'year';
}
return 'day';
},
visualValue: {
get () {
const value = this.internalValue;
if (!value) return;
const formatter = (
TYPE_VALUE_RESOLVER_MAP[this.type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
const format = DEFAULT_FORMATS[this.type];
return formatter(value, this.format || format);
},
set (value) {
if (value) {
const type = this.type;
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
const parsedValue = parser(value, this.format || DEFAULT_FORMATS[type]);
if (parsedValue) {
if (this.picker) this.picker.value = parsedValue;
}
return;
}
if (this.picker) this.picker.value = value;
}
2016-12-12 20:34:28 +08:00
}
},
methods: {
2017-07-20 11:53:18 +08:00
// 开启 transfer 时,点击 Drop 即会关闭,这里不让其关闭
handleTransferClick () {
if (this.transfer) this.disableCloseUnderTransfer = true;
},
2016-12-12 20:34:28 +08:00
handleClose () {
2017-07-20 11:53:18 +08:00
if (this.disableCloseUnderTransfer) {
this.disableCloseUnderTransfer = false;
return false;
}
2017-01-09 19:57:01 +08:00
if (this.open !== null) return;
2017-03-30 10:52:01 +08:00
// if (!this.disableClickOutSide) this.visible = false;
this.visible = false;
2016-12-22 09:18:11 +08:00
this.disableClickOutSide = false;
2016-12-12 20:34:28 +08:00
},
handleFocus () {
2016-12-15 23:29:31 +08:00
if (this.readonly) return;
2016-12-12 20:34:28 +08:00
this.visible = true;
},
2016-12-15 23:29:31 +08:00
handleInputChange (event) {
const oldValue = this.visualValue;
const value = event.target.value;
let correctValue = '';
2016-12-19 23:40:39 +08:00
let correctDate = '';
const type = this.type;
const format = this.format || DEFAULT_FORMATS[type];
2016-12-15 23:29:31 +08:00
2016-12-19 23:40:39 +08:00
if (type === 'daterange' || type === 'timerange' || type === 'datetimerange') {
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
const formatter = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
const parsedValue = parser(value, format);
2016-12-20 09:29:48 +08:00
if (parsedValue[0] instanceof Date && parsedValue[1] instanceof Date) {
if (parsedValue[0].getTime() > parsedValue[1].getTime()) {
correctValue = oldValue;
} else {
correctValue = formatter(parsedValue, format);
}
2016-12-20 09:35:23 +08:00
// todo 判断disabledDate
2016-12-15 23:29:31 +08:00
} else {
2016-12-19 23:40:39 +08:00
correctValue = oldValue;
2016-12-15 23:29:31 +08:00
}
2016-12-19 23:40:39 +08:00
2016-12-20 09:29:48 +08:00
correctDate = parser(correctValue, format);
2016-12-27 17:41:35 +08:00
} else if (type === 'time') {
const parsedDate = parseDate(value, format);
if (parsedDate instanceof Date) {
if (this.disabledHours.length || this.disabledMinutes.length || this.disabledSeconds.length) {
const hours = parsedDate.getHours();
const minutes = parsedDate.getMinutes();
const seconds = parsedDate.getSeconds();
if ((this.disabledHours.length && this.disabledHours.indexOf(hours) > -1) ||
(this.disabledMinutes.length && this.disabledMinutes.indexOf(minutes) > -1) ||
(this.disabledSeconds.length && this.disabledSeconds.indexOf(seconds) > -1)) {
correctValue = oldValue;
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = oldValue;
}
correctDate = parseDate(correctValue, format);
2016-12-15 23:29:31 +08:00
} else {
2016-12-19 23:40:39 +08:00
const parsedDate = parseDate(value, format);
if (parsedDate instanceof Date) {
const options = this.options || false;
if (options && options.disabledDate && typeof options.disabledDate === 'function' && options.disabledDate(new Date(parsedDate))) {
2016-12-19 23:40:39 +08:00
correctValue = oldValue;
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = oldValue;
}
2016-12-15 23:29:31 +08:00
2016-12-19 23:40:39 +08:00
correctDate = parseDate(correctValue, format);
}
2016-12-15 23:29:31 +08:00
this.visualValue = correctValue;
event.target.value = correctValue;
this.internalValue = correctDate;
this.currentValue = correctDate;
2016-12-15 23:41:06 +08:00
if (correctValue !== oldValue) this.emitChange(correctDate);
2016-12-15 20:16:58 +08:00
},
handleInputMouseenter () {
2016-12-12 20:34:28 +08:00
if (this.readonly || this.disabled) return;
if (this.visualValue && this.clearable) {
2016-12-12 20:34:28 +08:00
this.showClose = true;
}
},
2016-12-15 20:16:58 +08:00
handleInputMouseleave () {
2016-12-12 20:34:28 +08:00
this.showClose = false;
},
handleIconClick () {
2017-03-29 10:29:12 +08:00
if (this.showClose) {
this.handleClear();
2017-06-07 10:49:15 +08:00
} else if (!this.disabled) {
2017-03-29 10:29:12 +08:00
this.handleFocus();
}
},
handleClear () {
2016-12-15 20:16:58 +08:00
this.visible = false;
this.internalValue = '';
this.currentValue = '';
2016-12-22 15:08:05 +08:00
this.$emit('on-clear');
this.dispatch('FormItem', 'on-form-change', '');
2016-12-12 20:34:28 +08:00
},
showPicker () {
if (!this.picker) {
let isConfirm = this.confirm;
const type = this.type;
this.picker = new Vue(this.panel).$mount(this.$refs.picker);
if (type === 'datetime' || type === 'datetimerange') {
isConfirm = true;
this.picker.showTime = true;
}
2016-12-14 23:08:57 +08:00
this.picker.value = this.internalValue;
this.picker.confirm = isConfirm;
2016-12-15 20:16:58 +08:00
this.picker.selectionMode = this.selectionMode;
2016-12-14 23:08:57 +08:00
if (this.format) this.picker.format = this.format;
2016-12-26 14:50:39 +08:00
// TimePicker
if (this.disabledHours) this.picker.disabledHours = this.disabledHours;
if (this.disabledMinutes) this.picker.disabledMinutes = this.disabledMinutes;
if (this.disabledSeconds) this.picker.disabledSeconds = this.disabledSeconds;
if (this.hideDisabledOptions) this.picker.hideDisabledOptions = this.hideDisabledOptions;
2016-12-14 23:08:57 +08:00
const options = this.options;
for (const option in options) {
this.picker[option] = options[option];
}
2016-12-15 20:16:58 +08:00
this.picker.$on('on-pick', (date, visible = false) => {
if (!isConfirm) this.visible = visible;
this.currentValue = date;
2016-12-15 23:29:31 +08:00
this.picker.value = date;
2016-12-15 20:16:58 +08:00
this.picker.resetView && this.picker.resetView();
2017-01-04 14:22:27 +08:00
this.emitChange(date);
2016-12-15 20:16:58 +08:00
});
this.picker.$on('on-pick-clear', () => {
this.handleClear();
});
this.picker.$on('on-pick-success', () => {
this.visible = false;
2016-12-22 15:08:05 +08:00
this.$emit('on-ok');
});
2016-12-22 09:18:11 +08:00
this.picker.$on('on-pick-click', () => this.disableClickOutSide = true);
2016-12-12 20:34:28 +08:00
}
2016-12-15 20:16:58 +08:00
if (this.internalValue instanceof Date) {
this.picker.date = new Date(this.internalValue.getTime());
} else {
this.picker.value = this.internalValue;
}
this.picker.resetView && this.picker.resetView();
2016-12-15 23:41:06 +08:00
},
emitChange (date) {
const newDate = this.formattingDate(date);
this.$emit('on-change', newDate);
this.$nextTick(() => {
this.dispatch('FormItem', 'on-form-change', newDate);
});
},
formattingDate (date) {
2016-12-28 15:21:25 +08:00
const type = this.type;
const format = this.format || DEFAULT_FORMATS[type];
2016-12-20 09:35:23 +08:00
const formatter = (
2016-12-28 15:21:25 +08:00
TYPE_VALUE_RESOLVER_MAP[type] ||
2016-12-20 09:35:23 +08:00
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
let newDate = formatter(date, format);
if (type === 'daterange' || type === 'timerange' || type === 'datetimerange') {
newDate = [newDate.split(RANGE_SEPARATOR)[0], newDate.split(RANGE_SEPARATOR)[1]];
}
return newDate;
2016-12-12 20:34:28 +08:00
}
},
watch: {
visible (val) {
if (val) {
this.showPicker();
this.$refs.drop.update();
2016-12-22 15:08:05 +08:00
if (this.open === null) this.$emit('on-open-change', true);
2016-12-12 20:34:28 +08:00
} else {
if (this.picker) this.picker.resetView && this.picker.resetView(true);
2016-12-12 20:34:28 +08:00
this.$refs.drop.destroy();
2016-12-22 15:08:05 +08:00
if (this.open === null) this.$emit('on-open-change', false);
2016-12-15 20:16:58 +08:00
}
},
internalValue(val) {
if (!val && this.picker && typeof this.picker.handleClear === 'function') {
this.picker.handleClear();
2016-12-12 20:34:28 +08:00
}
// this.$emit('input', val);
2016-12-14 23:08:57 +08:00
},
value (val) {
this.currentValue = val;
},
currentValue: {
2016-12-14 23:08:57 +08:00
immediate: true,
handler (val) {
2016-12-27 17:16:11 +08:00
const type = this.type;
2016-12-28 17:30:34 +08:00
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
2016-12-28 16:07:11 +08:00
2016-12-29 12:11:22 +08:00
if (val && type === 'time' && !(val instanceof Date)) {
2016-12-28 17:30:34 +08:00
val = parser(val, this.format || DEFAULT_FORMATS[type]);
2016-12-29 12:11:22 +08:00
} else if (val && type === 'timerange' && Array.isArray(val) && val.length === 2 && !(val[0] instanceof Date) && !(val[1] instanceof Date)) {
2016-12-28 17:30:34 +08:00
val = val.join(RANGE_SEPARATOR);
2016-12-27 17:16:11 +08:00
val = parser(val, this.format || DEFAULT_FORMATS[type]);
}
2016-12-28 17:30:34 +08:00
2016-12-15 20:16:58 +08:00
this.internalValue = val;
this.$emit('input', val);
2016-12-14 23:08:57 +08:00
}
2016-12-22 15:08:05 +08:00
},
open (val) {
if (val === true) {
this.visible = val;
this.$emit('on-open-change', true);
} else if (val === false) {
this.$emit('on-open-change', false);
}
2016-12-12 20:34:28 +08:00
}
2016-12-12 10:37:52 +08:00
},
2016-12-12 20:34:28 +08:00
beforeDestroy () {
if (this.picker) {
this.picker.$destroy();
}
},
mounted () {
2016-12-22 15:08:05 +08:00
if (this.open !== null) this.visible = this.open;
2016-12-12 20:34:28 +08:00
}
2016-12-25 22:49:42 +08:00
};
</script>