element/packages/date-picker/src/picker.vue

448 lines
10 KiB
Vue
Raw Normal View History

2016-07-27 06:15:02 +00:00
<template>
<el-input
2016-07-27 06:15:02 +00:00
class="el-date-editor"
:readonly="!editable || readonly"
:disabled="disabled"
:size="size"
2016-08-09 02:10:33 +00:00
v-clickoutside="handleClose"
:placeholder="placeholder"
@focus="handleFocus"
@blur="handleBlur"
@keydown.native="handleKeydown"
:value="visualValue"
@change.native="visualValue = $event.target.value"
ref="reference">
<i slot="icon"
class="el-input__icon"
@click="handleClickIcon"
:class="[showClose ? 'el-icon-close' : triggerClass]"
@mouseenter="handleMouseEnterIcon"
@mouseleave="showClose = false"
2016-07-27 06:15:02 +00:00
v-if="haveTrigger">
</i>
</el-input>
2016-07-27 06:15:02 +00:00
</template>
<script>
import Vue from 'vue';
2016-10-13 03:12:24 +00:00
import Clickoutside from 'element-ui/src/utils/clickoutside';
2016-10-13 08:11:06 +00:00
import { formatDate, parseDate, getWeekNumber } from './util';
2016-10-13 03:12:24 +00:00
import Popper from 'element-ui/src/utils/vue-popper';
2016-10-27 09:31:22 +00:00
import Emitter from 'element-ui/src/mixins/emitter';
import ElInput from 'element-ui/packages/input';
2016-07-27 06:15:02 +00:00
2016-10-27 09:31:22 +00:00
const NewPopper = {
props: {
appendToBody: Popper.props.appendToBody,
offset: Popper.props.offset,
boundariesPadding: Popper.props.boundariesPadding
},
methods: Popper.methods,
data: Popper.data,
beforeDestroy: Popper.beforeDestroy
};
2016-07-27 06:15:02 +00:00
const RANGE_SEPARATOR = ' - ';
const DEFAULT_FORMATS = {
date: 'yyyy-MM-dd',
month: 'yyyy-MM',
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',
year: 'yyyy'
2016-07-27 06:15:02 +00:00
};
const HAVE_TRIGGER_TYPES = [
'date',
'datetime',
'time',
'time-select',
'week',
'month',
'year',
'daterange',
'timerange',
'datetimerange'
];
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];
2016-07-27 06:15:02 +00:00
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;
}
},
week: {
formatter(value) {
if (value instanceof Date) {
const weekNumber = getWeekNumber(value);
return value.getFullYear() + 'w' + (weekNumber > 9 ? weekNumber : '0' + weekNumber);
}
return value;
},
parser(text) {
const array = (text || '').split('w');
if (array.length === 2) {
const year = Number(array[0]);
const month = Number(array[1]);
if (!isNaN(year) && !isNaN(month) && month < 54) {
return text;
}
}
return null;
}
},
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
2016-07-27 06:15:02 +00:00
},
number: {
formatter(value) {
if (!value) return '';
return '' + value;
},
parser(text) {
let result = Number(text);
if (!isNaN(text)) {
return result;
} else {
return null;
}
}
}
};
2016-09-21 04:46:53 +00:00
const PLACEMENT_MAP = {
left: 'bottom-start',
center: 'bottom-center',
right: 'bottom-end'
};
2016-07-27 06:15:02 +00:00
export default {
2016-10-27 09:31:22 +00:00
mixins: [Emitter, NewPopper],
2016-08-29 11:19:14 +00:00
2016-07-27 06:15:02 +00:00
props: {
size: String,
2016-07-27 06:15:02 +00:00
format: String,
readonly: Boolean,
placeholder: String,
disabled: Boolean,
editable: {
type: Boolean,
default: true
},
2016-09-21 04:46:53 +00:00
align: {
type: String,
default: 'left'
},
2016-07-27 06:15:02 +00:00
value: {},
haveTrigger: {},
pickerOptions: {}
},
components: { ElInput },
directives: { Clickoutside },
2016-07-27 06:15:02 +00:00
data() {
return {
pickerVisible: false,
showClose: false,
internalValue: ''
2016-07-27 06:15:02 +00:00
};
},
watch: {
pickerVisible(val) {
if (this.readonly || this.disabled) return;
val ? this.showPicker() : this.hidePicker();
2016-08-29 11:19:14 +00:00
},
internalValue(val) {
if (!val && this.picker && typeof this.picker.handleClear === 'function') {
this.picker.handleClear();
}
},
value: {
immediate: true,
handler(val) {
this.internalValue = val;
}
2016-07-27 06:15:02 +00:00
}
},
computed: {
reference() {
return this.$refs.reference.$el;
},
refInput() {
if (this.reference) return this.reference.querySelector('input');
return {};
},
valueIsEmpty() {
const val = this.internalValue;
if (Array.isArray(val)) {
for (let i = 0, len = val.length; i < len; i++) {
if (val[i]) {
return false;
}
}
} else {
if (val) {
return false;
}
}
return true;
},
2016-07-27 06:15:02 +00:00
triggerClass() {
return this.type.indexOf('time') !== -1 ? 'el-icon-time' : 'el-icon-date';
},
selectionMode() {
if (this.type === 'week') {
return 'week';
} else if (this.type === 'month') {
return 'month';
} else if (this.type === 'year') {
return 'year';
}
return 'day';
},
haveTrigger() {
if (typeof this.showTrigger !== 'undefined') {
return this.showTrigger;
}
return HAVE_TRIGGER_TYPES.indexOf(this.type) !== -1;
},
visualValue: {
get() {
const value = this.internalValue;
if (!value) return;
2016-07-27 06:15:02 +00:00
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) {
this.picker.value = parsedValue;
2016-07-27 06:15:02 +00:00
}
return;
}
this.picker.value = value;
2016-07-27 06:15:02 +00:00
}
}
},
created() {
// vue-popper
this.options = {
boundariesPadding: 0,
gpuAcceleration: false
};
this.placement = PLACEMENT_MAP[this.align] || PLACEMENT_MAP.left;
},
2016-07-27 06:15:02 +00:00
methods: {
handleMouseEnterIcon() {
if (this.readonly || this.disabled) return;
if (!this.valueIsEmpty) {
this.showClose = true;
}
},
handleClickIcon() {
if (this.readonly || this.disabled) return;
if (this.valueIsEmpty) {
this.pickerVisible = !this.pickerVisible;
} else {
this.internalValue = '';
this.$emit('input', '');
}
},
2016-08-09 02:10:33 +00:00
handleClose() {
this.pickerVisible = false;
},
2016-07-27 06:15:02 +00:00
handleFocus() {
const type = this.type;
if (HAVE_TRIGGER_TYPES.indexOf(type) !== -1 && !this.pickerVisible) {
this.pickerVisible = true;
2016-07-27 06:15:02 +00:00
}
this.$emit('focus', this);
},
handleBlur() {
this.$emit('blur', this);
2016-11-26 07:18:38 +00:00
this.dispatch('ElFormItem', 'el.form.blur');
2016-07-27 06:15:02 +00:00
},
handleKeydown(event) {
const keyCode = event.keyCode;
2016-10-13 08:11:06 +00:00
// tab
if (keyCode === 9) {
this.pickerVisible = false;
2016-07-27 06:15:02 +00:00
}
},
hidePicker() {
if (this.picker) {
this.picker.resetView && this.picker.resetView();
this.pickerVisible = this.picker.visible = false;
this.destroyPopper();
}
},
showPicker() {
if (!this.picker) {
this.panel.defaultValue = this.internalValue;
2016-10-13 08:11:06 +00:00
this.picker = new Vue(this.panel).$mount(document.createElement('div'));
this.popperElm = this.picker.$el;
this.picker.width = this.reference.getBoundingClientRect().width;
2016-07-27 06:15:02 +00:00
this.picker.showTime = this.type === 'datetime' || this.type === 'datetimerange';
this.picker.selectionMode = this.selectionMode;
if (this.format) {
this.picker.format = this.format;
}
const options = this.pickerOptions;
if (options && options.selectableRange) {
let ranges = options.selectableRange;
const parser = TYPE_VALUE_RESOLVER_MAP.datetimerange.parser;
const format = DEFAULT_FORMATS.timerange;
ranges = Array.isArray(ranges) ? ranges : [ranges];
this.picker.selectableRange = ranges.map(range => parser(range, format));
}
if (this.type === 'time-select' && options) {
this.$watch('pickerOptions.minTime', val => {
this.picker.minTime = val;
});
}
for (const option in options) {
2016-09-01 03:45:44 +00:00
if (options.hasOwnProperty(option) &&
// 忽略 time-picker 的该配置项
option !== 'selectableRange') {
2016-07-27 06:15:02 +00:00
this.picker[option] = options[option];
}
}
this.$el.appendChild(this.picker.$el);
this.pickerVisible = this.picker.visible = true;
this.picker.resetView && this.picker.resetView();
this.picker.$on('dodestroy', this.doDestroy);
2016-07-27 06:15:02 +00:00
this.picker.$on('pick', (date, visible = false) => {
2016-08-09 02:10:33 +00:00
this.$emit('input', date);
this.pickerVisible = this.picker.visible = visible;
2016-07-27 06:15:02 +00:00
this.picker.resetView && this.picker.resetView();
});
this.picker.$on('select-range', (start, end) => {
this.refInput.setSelectionRange(start, end);
this.refInput.focus();
2016-07-27 06:15:02 +00:00
});
} else {
this.pickerVisible = this.picker.visible = true;
}
this.updatePopper();
2016-07-27 06:15:02 +00:00
if (this.internalValue instanceof Date) {
this.picker.date = new Date(this.internalValue.getTime());
2016-07-27 06:15:02 +00:00
} else {
this.picker.value = this.internalValue;
2016-07-27 06:15:02 +00:00
}
this.picker.resetView && this.picker.resetView();
2016-07-27 06:15:02 +00:00
this.$nextTick(() => {
this.picker.ajustScrollTop && this.picker.ajustScrollTop();
});
}
}
};
</script>