UNPKG

vxe-table

Version:

一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、虚拟列表、模态窗口、自定义模板、渲染器、贼灵活的配置项、扩展接口等...

1,570 lines (1,537 loc) 57.9 kB
import XEUtils from 'xe-utils/ctor' import GlobalConfig from '../../conf' import vSize from '../../mixins/size' import { UtilTools, DomTools, GlobalEvent } from '../../tools' const browse = DomTools.browse const wheelName = browse.firefox ? 'DOMMouseScroll' : 'mousewheel' const yearSize = 20 const monthSize = 20 function toStringTimeDate (str) { if (str) { const rest = new Date() let h, m, s if (XEUtils.isDate(str)) { h = str.getHours() m = str.getMinutes() s = str.getSeconds() } else { str = XEUtils.toString(str) const parses = str.match(/^(\d{1,2})(:(\d{1,2}))?(:(\d{1,2}))?/) if (parses) { h = parses[1] m = parses[3] s = parses[5] } } rest.setHours(h || 0) rest.setMinutes(m || 0) rest.setSeconds(s || 0) return rest } return new Date('') } function renderDefaultInput (h, _vm) { const { inpAttrs, inpEvents, value } = _vm return h('input', { ref: 'input', class: 'vxe-input--inner', domProps: { value }, attrs: inpAttrs, on: inpEvents }) } function renderDateInput (h, _vm) { const { inpAttrs, inpEvents, inputValue } = _vm return h('input', { ref: 'input', class: 'vxe-input--inner', domProps: { value: inputValue }, attrs: inpAttrs, on: inpEvents }) } function renderDateLabel (h, _vm, item, label) { const festivalMethod = _vm.festivalMethod if (festivalMethod) { const festivalRest = festivalMethod({ type: _vm.datePanelType, ...item }) const festivalItem = festivalRest ? (XEUtils.isString(festivalRest) ? { label: festivalRest } : festivalRest) : {} const extraItem = festivalItem.extra ? (XEUtils.isString(festivalItem.extra) ? { label: festivalItem.extra } : festivalItem.extra) : null const labels = [ h('span', { class: ['vxe-input--date-label', { 'is-notice': festivalItem.notice }] }, extraItem && extraItem.label ? [ h('span', label), h('span', { class: ['vxe-input--date-label--extra', extraItem.important ? 'is-important' : '', extraItem.className], style: extraItem.style }, XEUtils.toString(extraItem.label)) ] : label) ] const festivalLabel = festivalItem.label if (festivalLabel) { // 默认最多支持3个节日重叠 const festivalLabels = XEUtils.toString(festivalLabel).split(',') labels.push( h('span', { class: ['vxe-input--date-festival', festivalItem.important ? 'is-important' : '', festivalItem.className], style: festivalItem.style }, [ festivalLabels.length > 1 ? h('span', { class: ['vxe-input--date-festival--overlap', `overlap--${festivalLabels.length}`] }, festivalLabels.map(label => h('span', label.substring(0, 3)))) : h('span', { class: 'vxe-input--date-festival--label' }, festivalLabels[0].substring(0, 3)) ]) ) } return labels } return label } function isDateDisabled (_vm, item) { const disabledMethod = _vm.disabledMethod return disabledMethod && disabledMethod({ type: _vm.type, date: item.date }) } function renderDateDayTable (h, _vm) { const { datePanelType, dateValue, datePanelValue, dateHeaders, dayDatas } = _vm const matchFormat = 'yyyy-MM-dd' return [ h('table', { class: `vxe-input--date-${datePanelType}-view`, attrs: { cellspacing: 0, cellpadding: 0, border: 0 } }, [ h('thead', [ h('tr', dateHeaders.map(item => { return h('th', item.label) })) ]), h('tbody', dayDatas.map(rows => { return h('tr', rows.map(item => { return h('td', { class: { 'is--prev': item.isPrev, 'is--current': item.isCurrent, 'is--now': item.isNow, 'is--next': item.isNext, 'is--disabled': isDateDisabled(_vm, item), 'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat), 'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat) }, on: { click: () => _vm.dateSelectEvent(item), mouseenter: () => _vm.dateMouseenterEvent(item) } }, renderDateLabel(h, _vm, item, item.label)) })) })) ]) ] } function renderDateWeekTable (h, _vm) { const { datePanelType, dateValue, datePanelValue, weekHeaders, weekDates } = _vm const matchFormat = 'yyyy-MM-dd' return [ h('table', { class: `vxe-input--date-${datePanelType}-view`, attrs: { cellspacing: 0, cellpadding: 0, border: 0 } }, [ h('thead', [ h('tr', weekHeaders.map(item => { return h('th', item.label) })) ]), h('tbody', weekDates.map(rows => { const isSelected = rows.some(item => XEUtils.isDateSame(dateValue, item.date, matchFormat)) const isHover = rows.some(item => XEUtils.isDateSame(datePanelValue, item.date, matchFormat)) return h('tr', rows.map(item => { return h('td', { class: { 'is--prev': item.isPrev, 'is--current': item.isCurrent, 'is--now': item.isNow, 'is--next': item.isNext, 'is--disabled': isDateDisabled(_vm, item), 'is--selected': isSelected, 'is--hover': isHover }, on: { click: () => _vm.dateSelectEvent(item), mouseenter: () => _vm.dateMouseenterEvent(item) } }, renderDateLabel(h, _vm, item, item.label)) })) })) ]) ] } function renderDateMonthTable (h, _vm) { const { dateValue, datePanelType, monthDatas, datePanelValue } = _vm const matchFormat = 'yyyy-MM' return [ h('table', { class: `vxe-input--date-${datePanelType}-view`, attrs: { cellspacing: 0, cellpadding: 0, border: 0 } }, [ h('tbody', monthDatas.map(rows => { return h('tr', rows.map(item => { return h('td', { class: { 'is--prev': item.isPrev, 'is--current': item.isCurrent, 'is--now': item.isNow, 'is--next': item.isNext, 'is--disabled': isDateDisabled(_vm, item), 'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat), 'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat) }, on: { click: () => _vm.dateSelectEvent(item), mouseenter: () => _vm.dateMouseenterEvent(item) } }, renderDateLabel(h, _vm, item, GlobalConfig.i18n(`vxe.input.date.months.m${item.month}`))) })) })) ]) ] } function renderDateYearTable (h, _vm) { const { dateValue, datePanelType, yearDatas, datePanelValue } = _vm const matchFormat = 'yyyy' return [ h('table', { class: `vxe-input--date-${datePanelType}-view`, attrs: { cellspacing: 0, cellpadding: 0, border: 0 } }, [ h('tbody', yearDatas.map(rows => { return h('tr', rows.map(item => { return h('td', { class: { 'is--disabled': isDateDisabled(_vm, item), 'is--current': item.isCurrent, 'is--now': item.isNow, 'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat), 'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat) }, on: { click: () => _vm.dateSelectEvent(item), mouseenter: () => _vm.dateMouseenterEvent(item) } }, renderDateLabel(h, _vm, item, item.year)) })) })) ]) ] } function renderDateTable (h, _vm) { const { datePanelType } = _vm switch (datePanelType) { case 'week' : return renderDateWeekTable(h, _vm) case 'month' : return renderDateMonthTable(h, _vm) case 'year' : return renderDateYearTable(h, _vm) } return renderDateDayTable(h, _vm) } function renderDatePanel (h, _vm) { const { datePanelType, selectDatePanelLabel, isDisabledPrevDateBtn, isDisabledNextDateBtn } = _vm return [ h('div', { class: 'vxe-input--date-picker-header' }, [ h('div', { class: 'vxe-input--date-picker-type-wrapper' }, [ datePanelType === 'year' ? h('span', { class: 'vxe-input--date-picker-label' }, selectDatePanelLabel) : h('span', { class: 'vxe-input--date-picker-btn', on: { click: _vm.dateToggleTypeEvent } }, selectDatePanelLabel) ]), h('div', { class: 'vxe-input--date-picker-btn-wrapper' }, [ h('span', { class: ['vxe-input--date-picker-btn vxe-input--date-picker-prev-btn', { 'is--disabled': isDisabledPrevDateBtn }], on: { click: _vm.datePrevEvent } }, [ h('i', { class: 'vxe-icon--caret-left' }) ]), h('span', { class: 'vxe-input--date-picker-btn vxe-input--date-picker-current-btn', on: { click: _vm.dateTodayMonthEvent } }, [ h('i', { class: 'vxe-icon--dot' }) ]), h('span', { class: ['vxe-input--date-picker-btn vxe-input--date-picker-next-btn', { 'is--disabled': isDisabledNextDateBtn }], on: { click: _vm.dateNextEvent } }, [ h('i', { class: 'vxe-icon--caret-right' }) ]) ]) ]), h('div', { class: 'vxe-input--date-picker-body' }, renderDateTable(h, _vm)) ] } function renderTimePanel (h, _vm) { const { dateTimeLabel, datetimePanelValue, hourList, minuteList, secondList } = _vm return [ h('div', { class: 'vxe-input--time-picker-header' }, [ h('span', { class: 'vxe-input--time-picker-title' }, dateTimeLabel), h('button', { class: 'vxe-input--time-picker-confirm', attrs: { type: 'button' }, on: { click: _vm.dateConfirmEvent } }, GlobalConfig.i18n('vxe.button.confirm')) ]), h('div', { ref: 'timeBody', class: 'vxe-input--time-picker-body' }, [ h('ul', { class: 'vxe-input--time-picker-hour-list' }, hourList.map((item, index) => { return h('li', { key: index, class: { 'is--selected': datetimePanelValue && datetimePanelValue.getHours() === item.value }, on: { click: (evnt) => _vm.dateHourEvent(evnt, item) } }, item.label) })), h('ul', { class: 'vxe-input--time-picker-minute-list' }, minuteList.map((item, index) => { return h('li', { key: index, class: { 'is--selected': datetimePanelValue && datetimePanelValue.getMinutes() === item.value }, on: { click: (evnt) => _vm.dateMinuteEvent(evnt, item) } }, item.label) })), h('ul', { class: 'vxe-input--time-picker-second-list' }, secondList.map((item, index) => { return h('li', { key: index, class: { 'is--selected': datetimePanelValue && datetimePanelValue.getSeconds() === item.value }, on: { click: (evnt) => _vm.dateSecondEvent(evnt, item) } }, item.label) })) ]) ] } function renderPanel (h, _vm) { const { type, vSize, isDatePicker, transfer, animatVisible, visiblePanel, panelPlacement, panelStyle } = _vm const renders = [] if (isDatePicker) { if (type === 'datetime') { renders.push( h('div', { class: 'vxe-input--panel-layout-wrapper' }, [ h('div', { class: 'vxe-input--panel-left-wrapper' }, renderDatePanel(h, _vm)), h('div', { class: 'vxe-input--panel-right-wrapper' }, renderTimePanel(h, _vm)) ]) ) } else if (type === 'time') { renders.push( h('div', { class: 'vxe-input--panel-wrapper' }, renderTimePanel(h, _vm)) ) } else { renders.push( h('div', { class: 'vxe-input--panel-wrapper' }, renderDatePanel(h, _vm)) ) } return h('div', { ref: 'panel', class: ['vxe-table--ignore-clear vxe-input--panel', `type--${type}`, { [`size--${vSize}`]: vSize, 'is--transfer': transfer, 'animat--leave': animatVisible, 'animat--enter': visiblePanel }], attrs: { 'data-placement': panelPlacement }, style: panelStyle }, renders) } return null } function renderNumberIcon (h, _vm) { return h('span', { class: 'vxe-input--number-suffix' }, [ h('span', { class: 'vxe-input--number-prev is--prev', on: { mousedown: _vm.numberMousedownEvent, mouseup: _vm.numberStopDown, mouseleave: _vm.numberStopDown } }, [ h('i', { class: ['vxe-input--number-prev-icon', GlobalConfig.icon.INPUT_PREV_NUM] }) ]), h('span', { class: 'vxe-input--number-next is--next', on: { mousedown: _vm.numberMousedownEvent, mouseup: _vm.numberStopDown, mouseleave: _vm.numberStopDown } }, [ h('i', { class: ['vxe-input--number-next-icon', GlobalConfig.icon.INPUT_NEXT_NUM] }) ]) ]) } function renderDatePickerIcon (h, _vm) { return h('span', { class: 'vxe-input--date-picker-suffix', on: { click: _vm.datePickerOpenEvent } }, [ h('i', { class: ['vxe-input--date-picker-icon', GlobalConfig.icon.INPUT_DATE] }) ]) } function renderSearchIcon (h, _vm) { return h('span', { class: 'vxe-input--search-suffix', on: { click: _vm.searchEvent } }, [ h('i', { class: ['vxe-input--search-icon', GlobalConfig.icon.INPUT_SEARCH] }) ]) } function renderPasswordIcon (h, _vm) { const { showPwd } = _vm return h('span', { class: 'vxe-input--password-suffix', on: { click: _vm.passwordToggleEvent } }, [ h('i', { class: ['vxe-input--password-icon', showPwd ? GlobalConfig.icon.INPUT_SHOW_PWD : GlobalConfig.icon.INPUT_PWD] }) ]) } function rendePrefixIcon (h, _vm) { const { $scopedSlots, prefixIcon } = _vm const icons = [] if ($scopedSlots.prefix) { icons.push( h('span', { class: 'vxe-input--prefix-icon' }, $scopedSlots.prefix.call(this, {}, h)) ) } else if (prefixIcon) { icons.push( h('i', { class: ['vxe-input--prefix-icon', prefixIcon] }) ) } return icons.length ? h('span', { class: 'vxe-input--prefix', on: { click: _vm.clickPrefixEvent } }, icons) : null } function renderSuffixIcon (h, _vm) { const { $scopedSlots, value, isClearable, disabled, suffixIcon } = _vm const icons = [] if ($scopedSlots.suffix) { icons.push( h('span', { class: 'vxe-input--suffix-icon' }, $scopedSlots.suffix.call(this, {}, h)) ) } else if (suffixIcon) { icons.push( h('i', { class: ['vxe-input--suffix-icon', suffixIcon] }) ) } if (isClearable) { icons.push( h('i', { class: ['vxe-input--clear-icon', GlobalConfig.icon.INPUT_CLEAR] }) ) } return icons.length ? h('span', { class: ['vxe-input--suffix', { 'is--clear': isClearable && !disabled && !(value === '' || XEUtils.eqNull(value)) }], on: { click: _vm.clickSuffixEvent } }, icons) : null } function renderExtraSuffixIcon (h, _vm) { const { controls, isPassword, isNumber, isDatePicker, isSearch } = _vm let icons if (controls) { if (isPassword) { icons = renderPasswordIcon(h, _vm) } else if (isNumber) { icons = renderNumberIcon(h, _vm) } else if (isDatePicker) { icons = renderDatePickerIcon(h, _vm) } else if (isSearch) { icons = renderSearchIcon(h, _vm) } } return icons ? h('span', { class: 'vxe-input--extra-suffix' }, [icons]) : null } export default { name: 'VxeInput', mixins: [vSize], props: { value: [String, Number, Date], name: String, type: { type: String, default: 'text' }, clearable: { type: Boolean, default: () => GlobalConfig.input.clearable }, readonly: Boolean, disabled: Boolean, placeholder: String, maxlength: [String, Number], autocomplete: { type: String, default: 'off' }, align: String, form: String, size: { type: String, default: () => GlobalConfig.input.size || GlobalConfig.size }, // number、integer、float min: { type: [String, Number], default: null }, max: { type: [String, Number], default: null }, step: [String, Number], // number、integer、float、password controls: { type: Boolean, default: () => GlobalConfig.input.controls }, // float digits: { type: [String, Number], default: () => GlobalConfig.input.digits }, // date、week、month、year dateConfig: Object, minDate: { type: [String, Number, Date], default: () => GlobalConfig.input.minDate }, maxDate: { type: [String, Number, Date], default: () => GlobalConfig.input.maxDate }, startWeek: { type: Number, default: () => GlobalConfig.input.startWeek }, labelFormat: { type: String, default: () => GlobalConfig.input.labelFormat }, valueFormat: { type: String, default: () => GlobalConfig.input.valueFormat }, editable: { type: Boolean, default: true }, festivalMethod: { type: Function, default: () => GlobalConfig.input.festivalMethod }, disabledMethod: { type: Function, default: () => GlobalConfig.input.disabledMethod }, prefixIcon: String, suffixIcon: String, placement: String, transfer: { type: Boolean, default: () => GlobalConfig.input.transfer } }, data () { return { panelIndex: 0, showPwd: false, visiblePanel: false, animatVisible: false, panelStyle: null, panelPlacement: null, isActivated: false, inputValue: '', datetimePanelValue: null, datePanelValue: null, datePanelLabel: '', datePanelType: 'day', selectMonth: null, currentDate: null } }, computed: { isNumber () { return ['number', 'integer', 'float'].indexOf(this.type) > -1 }, isDatePicker () { return this.hasTime || ['date', 'week', 'month', 'year'].indexOf(this.type) > -1 }, hasTime () { const { type } = this return type === 'time' || type === 'datetime' }, isPassword () { return this.type === 'password' }, isSearch () { return this.type === 'search' }, stepValue () { const { type, step } = this if (type === 'integer') { return XEUtils.toInteger(step) || 1 } else if (type === 'float') { return XEUtils.toNumber(step) || (1 / Math.pow(10, this.digitsValue)) } return XEUtils.toNumber(step) || 1 }, digitsValue () { return XEUtils.toInteger(this.digits) || 1 }, isClearable () { return this.clearable && (this.isPassword || this.isNumber || this.isDatePicker || this.type === 'text' || this.type === 'search') }, isDisabledPrevDateBtn () { const { selectMonth, dateMinTime } = this if (selectMonth) { return selectMonth <= dateMinTime } return false }, isDisabledNextDateBtn () { const { selectMonth, dateMaxTime } = this if (selectMonth) { return selectMonth >= dateMaxTime } return false }, dateMinTime () { return this.minDate ? XEUtils.toStringDate(this.minDate) : null }, dateMaxTime () { return this.maxDate ? XEUtils.toStringDate(this.maxDate) : null }, dateValue () { const { value, isDatePicker, type, dateValueFormat } = this let val = null if (value && isDatePicker) { let date if (type === 'time') { date = toStringTimeDate(value) } else { date = XEUtils.toStringDate(value, dateValueFormat) } if (XEUtils.isValidDate(date)) { val = date } } return val }, dateTimeLabel () { const { datetimePanelValue } = this if (datetimePanelValue) { return XEUtils.toDateString(datetimePanelValue, 'HH:mm:ss') } return '' }, hmsTime () { const { dateValue } = this return dateValue && (this.hasTime) ? (dateValue.getHours() * 3600 + dateValue.getMinutes() * 60 + dateValue.getSeconds()) * 1000 : 0 }, dateLabelFormat () { if (this.isDatePicker) { return this.labelFormat || GlobalConfig.i18n(`vxe.input.date.labelFormat.${this.type}`) } return null }, dateValueFormat () { const { type } = this return type === 'time' ? 'HH:mm:ss' : (this.valueFormat || (type === 'datetime' ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd')) }, selectDatePanelLabel () { if (this.isDatePicker) { const { datePanelType, selectMonth, yearList } = this let year = '' let month if (selectMonth) { year = selectMonth.getFullYear() month = selectMonth.getMonth() + 1 } if (datePanelType === 'month') { return GlobalConfig.i18n('vxe.input.date.monthLabel', [year]) } else if (datePanelType === 'year') { return yearList.length ? `${yearList[0].year} - ${yearList[yearList.length - 1].year}` : '' } return GlobalConfig.i18n('vxe.input.date.dayLabel', [year, month ? GlobalConfig.i18n(`vxe.input.date.m${month}`) : '-']) } return '' }, weekDatas () { const weeks = [] if (this.isDatePicker) { let sWeek = XEUtils.toNumber(this.startWeek) weeks.push(sWeek) for (let index = 0; index < 6; index++) { if (sWeek >= 6) { sWeek = 0 } else { sWeek++ } weeks.push(sWeek) } } return weeks }, dateHeaders () { if (this.isDatePicker) { return this.weekDatas.map(day => { return { value: day, label: GlobalConfig.i18n(`vxe.input.date.weeks.w${day}`) } }) } return [] }, weekHeaders () { if (this.isDatePicker) { return [{ label: GlobalConfig.i18n('vxe.input.date.weeks.w') }].concat(this.dateHeaders) } return [] }, yearList () { const { selectMonth, currentDate } = this const months = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const startYear = new Date(('' + selectMonth.getFullYear()).replace(/\d{1}$/, '0'), 0, 1) for (let index = -10; index < yearSize - 10; index++) { const date = XEUtils.getWhatYear(startYear, index, 'first') const itemFullYear = date.getFullYear() months.push({ date, isCurrent: true, isNow: currFullYear === itemFullYear, year: itemFullYear }) } } return months }, yearDatas () { return XEUtils.chunk(this.yearList, 4) }, monthList () { const { selectMonth, currentDate } = this const months = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const currMonth = currentDate.getMonth() const selFullYear = XEUtils.getWhatYear(selectMonth, 0, 'first').getFullYear() for (let index = -4; index < monthSize - 4; index++) { const date = XEUtils.getWhatYear(selectMonth, 0, index) const itemFullYear = date.getFullYear() const itemMonth = date.getMonth() const isPrev = itemFullYear < selFullYear months.push({ date, isPrev, isCurrent: itemFullYear === selFullYear, isNow: itemFullYear === currFullYear && itemMonth === currMonth, isNext: !isPrev && itemFullYear > selFullYear, month: itemMonth }) } } return months }, monthDatas () { return XEUtils.chunk(this.monthList, 4) }, dayList () { const { weekDatas, selectMonth, currentDate, hmsTime } = this const days = [] if (selectMonth && currentDate) { const currFullYear = currentDate.getFullYear() const currMonth = currentDate.getMonth() const currDate = currentDate.getDate() const selFullYear = selectMonth.getFullYear() const selMonth = selectMonth.getMonth() const selDay = selectMonth.getDay() const prevOffsetDate = -weekDatas.indexOf(selDay) const startDate = new Date(XEUtils.getWhatDay(selectMonth, prevOffsetDate).getTime() + hmsTime) for (let index = 0; index < 42; index++) { const date = XEUtils.getWhatDay(startDate, index) const itemFullYear = date.getFullYear() const itemMonth = date.getMonth() const itemDate = date.getDate() const isPrev = date < selectMonth days.push({ date, isPrev, isCurrent: itemFullYear === selFullYear && itemMonth === selMonth, isNow: itemFullYear === currFullYear && itemMonth === currMonth && itemDate === currDate, isNext: !isPrev && selMonth !== itemMonth, label: itemDate }) } } return days }, dayDatas () { return XEUtils.chunk(this.dayList, 7) }, weekDates () { return this.dayDatas.map(list => { const firstItem = list[0] const item = { date: firstItem.date, isWeekNumber: true, isPrev: false, isCurrent: false, isNow: false, isNext: false, label: XEUtils.getYearWeek(firstItem.date) } return [item].concat(list) }) }, hourList () { const list = [] if (this.hasTime) { for (let index = 0; index < 24; index++) { list.push({ value: index, label: ('' + index).padStart(2, 0) }) } } return list }, minuteList () { const list = [] if (this.hasTime) { for (let index = 0; index < 60; index++) { list.push({ value: index, label: ('' + index).padStart(2, 0) }) } } return list }, secondList () { return this.minuteList }, inpAttrs () { const { isDatePicker, isNumber, isPassword, type, name, placeholder, readonly, disabled, maxlength, form, autocomplete, showPwd, editable } = this let inputType = type if (isDatePicker || isNumber || (isPassword && showPwd) || type === 'number') { inputType = 'text' } const attrs = { name, form, type: inputType, placeholder, maxlength: isNumber && !XEUtils.toNumber(maxlength) ? 16 : maxlength, // 数值最大长度限制 16 位,包含小数 readonly: readonly || type === 'week' || !editable, disabled, autocomplete } if (placeholder) { attrs.placeholder = UtilTools.getFuncText(placeholder) } return attrs }, inpEvents () { const evnts = {} XEUtils.each(this.$listeners, (cb, name) => { if (['change', 'clear', 'prefix-click', 'suffix-click'].indexOf(name) === -1) { evnts[name] = this.triggerEvent } }) if (this.isNumber) { evnts.keydown = this.keydownEvent evnts[wheelName] = this.mousewheelEvent } else if (this.isDatePicker) { evnts.click = this.clickEvent } evnts.input = this.inputEvent evnts.focus = this.focusEvent evnts.blur = this.blurEvent return evnts } }, watch: { value () { this.changeValue() }, dateLabelFormat () { this.dateParseValue(this.datePanelValue) this.inputValue = this.datePanelLabel } }, created () { this.initValue() GlobalEvent.on(this, 'mousewheel', this.handleGlobalMousewheelEvent) GlobalEvent.on(this, 'mousedown', this.handleGlobalMousedownEvent) GlobalEvent.on(this, 'keydown', this.handleGlobalKeydownEvent) GlobalEvent.on(this, 'blur', this.handleGlobalBlurEvent) }, mounted () { if (this.dateConfig) { UtilTools.warn('vxe.error.removeProp', ['date-config']) } if (this.isDatePicker) { if (this.transfer) { document.body.appendChild(this.$refs.panel) } } }, beforeDestroy () { const panelElem = this.$refs.panel if (panelElem && panelElem.parentNode) { panelElem.parentNode.removeChild(panelElem) } }, destroyed () { this.numberStopDown() GlobalEvent.off(this, 'mousewheel') GlobalEvent.off(this, 'mousedown') GlobalEvent.off(this, 'keydown') GlobalEvent.off(this, 'blur') }, render (h) { const { controls, isDatePicker, visiblePanel, isActivated, vSize, type, align, readonly, disabled } = this const childs = [] const prefix = rendePrefixIcon(h, this) const suffix = renderSuffixIcon(h, this) // 前缀图标 if (prefix) { childs.push(prefix) } // 输入框 childs.push(isDatePicker ? renderDateInput(h, this) : renderDefaultInput(h, this)) // 后缀图标 if (suffix) { childs.push(suffix) } // 特殊功能图标 childs.push(renderExtraSuffixIcon(h, this)) // 面板容器 if (isDatePicker) { childs.push(renderPanel(h, this)) } return h('div', { class: ['vxe-input', `type--${type}`, { [`size--${vSize}`]: vSize, [`is--${align}`]: align, 'is--controls': controls, 'is--prefix': !!prefix, 'is--suffix': !!suffix, 'is--readonly': readonly, 'is--visivle': visiblePanel, 'is--disabled': disabled, 'is--active': isActivated }] }, childs) }, methods: { focus () { this.isActivated = true this.$refs.input.focus() return this.$nextTick() }, blur () { this.$refs.input.blur() this.isActivated = false return this.$nextTick() }, triggerEvent (evnt) { const { $refs, value } = this this.$emit(evnt.type, { $panel: $refs.panel, value, $event: evnt }) }, emitUpdate (value, evnt) { if (XEUtils.toString(this.value) !== value) { this.$emit('input', value) this.$emit('change', { value, $event: evnt }) } }, inputEvent (evnt) { const { isDatePicker } = this const value = evnt.target.value this.inputValue = value if (!isDatePicker) { this.emitUpdate(value, evnt) } }, focusEvent (evnt) { this.isActivated = true this.triggerEvent(evnt) }, blurEvent (evnt) { this.afterCheckValue() if (!this.visiblePanel) { this.isActivated = false } this.triggerEvent(evnt) }, keydownEvent (evnt) { if (this.isNumber) { const isCtrlKey = evnt.ctrlKey const isShiftKey = evnt.shiftKey const isAltKey = evnt.altKey const keyCode = evnt.keyCode if (!isCtrlKey && !isShiftKey && !isAltKey && (keyCode === 32 || (keyCode >= 65 && keyCode <= 90))) { evnt.preventDefault() } this.numberKeydownEvent(evnt) } this.triggerEvent(evnt) }, mousewheelEvent (evnt) { if (this.isNumber && this.controls) { if (this.isActivated) { const delta = -evnt.wheelDelta || evnt.detail if (delta > 0) { this.numberNextEvent(evnt) } else if (delta < 0) { this.numberPrevEvent(evnt) } evnt.preventDefault() } } }, clickEvent (evnt) { const { isDatePicker } = this if (isDatePicker) { this.datePickerOpenEvent(evnt) } this.triggerEvent(evnt) }, clickPrefixEvent (evnt) { const { $refs, disabled, value } = this if (!disabled) { this.$emit('prefix-click', { $panel: $refs.panel, value, $event: evnt }) } }, clickSuffixEvent (evnt) { const { $refs, disabled, value } = this if (!disabled) { if (DomTools.hasClass(evnt.currentTarget, 'is--clear')) { this.emitUpdate('', evnt) this.clearValueEvent(evnt, '') } else { this.$emit('suffix-click', { $panel: $refs.panel, value, $event: evnt }) } } }, clearValueEvent (evnt, value) { const { $refs, type, isNumber } = this if (this.isDatePicker) { this.hidePanel() } if (isNumber || ['text', 'search', 'password'].indexOf(type) > -1) { this.focus() } this.$emit('clear', { $panel: $refs.panel, value, $event: evnt }) }, /** * 检查初始值 */ initValue () { const { type, isDatePicker, value, digitsValue } = this if (isDatePicker) { this.changeValue() } else if (type === 'float') { if (value) { const validValue = XEUtils.toFixed(XEUtils.floor(value, digitsValue), digitsValue) if (value !== validValue) { this.emitUpdate(validValue, { type: 'init' }) } } } }, /** * 值变化时处理 */ changeValue () { if (this.isDatePicker) { this.dateParseValue(this.value) this.inputValue = this.datePanelLabel } }, afterCheckValue () { const { type, inpAttrs, value, inputValue, isDatePicker, isNumber, datetimePanelValue, dateLabelFormat, min, max, digitsValue } = this if (!inpAttrs.readonly) { if (isNumber) { if (value) { let inpVal = type === 'integer' ? XEUtils.toInteger(value) : XEUtils.toNumber(value) if (!this.vaildMinNum(inpVal)) { inpVal = min } else if (!this.vaildMaxNum(inpVal)) { inpVal = max } this.emitUpdate(type === 'float' ? XEUtils.toFixed(XEUtils.floor(inpVal, digitsValue), digitsValue) : XEUtils.toString(inpVal), { type: 'check' }) } } else if (isDatePicker) { let inpVal = inputValue if (inpVal) { if (type === 'time') { inpVal = toStringTimeDate(inpVal, dateLabelFormat) } else { inpVal = XEUtils.toStringDate(inpVal, dateLabelFormat) } if (XEUtils.isValidDate(inpVal)) { if (type === 'time') { inpVal = XEUtils.toDateString(inpVal, dateLabelFormat) if (value !== inpVal) { this.emitUpdate(inpVal, { type: 'check' }) } this.inputValue = inpVal } else { if (!XEUtils.isDateSame(value, inpVal, dateLabelFormat)) { if (type === 'datetime') { datetimePanelValue.setHours(inpVal.getHours()) datetimePanelValue.setMinutes(inpVal.getMinutes()) datetimePanelValue.setSeconds(inpVal.getSeconds()) } this.dateChange(inpVal) } else { this.inputValue = XEUtils.toDateString(value, dateLabelFormat) } } } else { this.dateRevert() } } else { this.emitUpdate('', { type: 'check' }) } } } }, // 密码 passwordToggleEvent (evnt) { const { disabled, readonly, showPwd } = this if (!disabled && !readonly) { this.showPwd = !showPwd } this.$emit('toggle-visible', { visible: this.showPwd, $event: evnt }) }, // 密码 // 搜索 searchEvent (evnt) { this.$emit('search-click', { $event: evnt }) }, // 搜索 // 数值 vaildMinNum (num) { return this.min === null || num >= XEUtils.toNumber(this.min) }, vaildMaxNum (num) { return this.max === null || num <= XEUtils.toNumber(this.max) }, numberStopDown () { clearTimeout(this.downbumTimeout) }, numberDownPrevEvent (evnt) { this.downbumTimeout = setTimeout(() => { this.numberPrevEvent(evnt) this.numberDownPrevEvent(evnt) }, 60) }, numberDownNextEvent (evnt) { this.downbumTimeout = setTimeout(() => { this.numberNextEvent(evnt) this.numberDownNextEvent(evnt) }, 60) }, numberKeydownEvent (evnt) { const { keyCode } = evnt const isUpArrow = keyCode === 38 const isDwArrow = keyCode === 40 if (isUpArrow || isDwArrow) { evnt.preventDefault() if (isUpArrow) { this.numberPrevEvent(evnt) } else { this.numberNextEvent(evnt) } } }, numberMousedownEvent (evnt) { this.numberStopDown() if (evnt.button === 0) { const isPrevNumber = DomTools.hasClass(evnt.currentTarget, 'is--prev') if (isPrevNumber) { this.numberPrevEvent(evnt) } else { this.numberNextEvent(evnt) } this.downbumTimeout = setTimeout(() => { if (isPrevNumber) { this.numberDownPrevEvent(evnt) } else { this.numberDownNextEvent(evnt) } }, 500) } }, numberPrevEvent (evnt) { const { disabled, readonly } = this clearTimeout(this.downbumTimeout) if (!disabled && !readonly) { this.numberChange(true, evnt) } this.$emit('prev-number', { $event: evnt }) }, numberNextEvent (evnt) { const { disabled, readonly } = this clearTimeout(this.downbumTimeout) if (!disabled && !readonly) { this.numberChange(false, evnt) } this.$emit('next-number', { $event: evnt }) }, numberChange (isPlus, evnt) { const { type, digitsValue, value, stepValue } = this const inputValue = type === 'integer' ? XEUtils.toInteger(value) : XEUtils.toNumber(value) const newValue = isPlus ? XEUtils.add(inputValue, stepValue) : XEUtils.subtract(inputValue, stepValue) if (this.vaildMinNum(newValue) && this.vaildMaxNum(newValue)) { this.emitUpdate(type === 'float' ? XEUtils.toFixed(XEUtils.floor(newValue, digitsValue), digitsValue) : XEUtils.toString(newValue), evnt) } }, // 数值 // 日期 datePickerOpenEvent (evnt) { evnt.preventDefault() this.showPanel() }, dateMonthHandle (date, offsetMonth) { this.selectMonth = XEUtils.getWhatMonth(date, offsetMonth, 'first') }, dateNowHandle () { const currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first') this.currentDate = currentDate this.dateMonthHandle(currentDate, 0) }, dateToggleTypeEvent () { let { datePanelType } = this if (datePanelType === 'month') { datePanelType = 'year' } else { datePanelType = 'month' } this.datePanelType = datePanelType }, datePrevEvent (evnt) { const { isDisabledPrevDateBtn, type, datePanelType } = this if (!isDisabledPrevDateBtn) { if (type === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, -yearSize, 'first') } else if (type === 'month') { if (datePanelType === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, -yearSize, 'first') } else { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, -1, 'first') } } else { if (datePanelType === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, -yearSize, 'first') } else if (datePanelType === 'month') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, -1, 'first') } else { this.selectMonth = XEUtils.getWhatMonth(this.selectMonth, -1, 'first') } } this.$emit('date-prev', { type, $event: evnt }) } }, dateTodayMonthEvent (evnt) { this.dateNowHandle() this.dateChange(this.currentDate) this.hidePanel() this.$emit('date-today', { type: this.type, $event: evnt }) }, dateNextEvent (evnt) { const { isDisabledNextDateBtn, type, datePanelType } = this if (!isDisabledNextDateBtn) { if (type === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, yearSize, 'first') } else if (type === 'month') { if (datePanelType === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, yearSize, 'first') } else { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, 1, 'first') } } else { if (datePanelType === 'year') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, yearSize, 'first') } else if (datePanelType === 'month') { this.selectMonth = XEUtils.getWhatYear(this.selectMonth, 1, 'first') } else { this.selectMonth = XEUtils.getWhatMonth(this.selectMonth, 1, 'first') } } this.$emit('date-next', { type, $event: evnt }) } }, dateSelectEvent (item) { if (!isDateDisabled(this, item)) { this.dateSelectItem(item.date) } }, dateSelectItem (date) { const { type, datePanelType } = this if (type === 'month') { if (datePanelType === 'year') { this.datePanelType = 'month' this.dateCheckMonth(date) } else { this.dateChange(date) this.hidePanel() } } else if (type === 'year') { this.hidePanel() this.dateChange(date) } else { if (datePanelType === 'month') { this.datePanelType = type === 'week' ? type : 'day' this.dateCheckMonth(date) } else if (datePanelType === 'year') { this.datePanelType = 'month' this.dateCheckMonth(date) } else { this.dateChange(date) this.hidePanel() } } }, dateMouseenterEvent (item) { if (!isDateDisabled(this, item)) { const { datePanelType } = this if (datePanelType === 'month') { this.dateMoveMonth(item.date) } else if (datePanelType === 'year') { this.dateMoveYear(item.date) } else { this.dateMoveDay(item.date) } } }, dateHourEvent (evnt, item) { this.datetimePanelValue.setHours(item.value) this.dateTimeChangeEvent(evnt) }, dateConfirmEvent () { this.dateChange(this.dateValue || this.currentDate) this.hidePanel() }, dateMinuteEvent (evnt, item) { this.datetimePanelValue.setMinutes(item.value) this.dateTimeChangeEvent(evnt) }, dateSecondEvent (evnt, item) { this.datetimePanelValue.setSeconds(item.value) this.dateTimeChangeEvent(evnt) }, dateTimeChangeEvent (evnt) { this.datetimePanelValue = new Date(this.datetimePanelValue.getTime()) this.updateTimePos(evnt.currentTarget) }, updateTimePos (liElem) { if (liElem) { const height = liElem.offsetHeight liElem.parentNode.scrollTop = liElem.offsetTop - height * 4 } }, dateMoveDay (offsetDay) { if (!isDateDisabled(this, { date: offsetDay })) { if (!this.dayList.some(item => XEUtils.isDateSame(item.date, offsetDay, 'yyyy-MM-dd'))) { this.dateCheckMonth(offsetDay) } this.dateParseValue(offsetDay) } }, dateMoveMonth (offsetMonth) { if (!isDateDisabled(this, { date: offsetMonth })) { if (!this.monthList.some(item => XEUtils.isDateSame(item.date, offsetMonth, 'yyyy-MM'))) { this.dateCheckMonth(offsetMonth) } this.dateParseValue(offsetMonth) } }, dateMoveYear (offsetYear) { if (!isDateDisabled(this, { date: offsetYear })) { if (!this.yearList.some(item => XEUtils.isDateSame(item.date, offsetYear, 'yyyy'))) { this.dateCheckMonth(offsetYear) } this.dateParseValue(offsetYear) } }, dateParseValue (date) { const { type, dateLabelFormat, valueFormat } = this let dValue = null let dLabel = '' if (date) { if (type === 'time') { dValue = toStringTimeDate(date, valueFormat) } else { dValue = XEUtils.toStringDate(date, valueFormat) } } if (XEUtils.isValidDate(dValue)) { dLabel = XEUtils.toDateString(dValue, dateLabelFormat) } else { dValue = null } this.datePanelValue = dValue this.datePanelLabel = dLabel }, dateOffsetEvent (evnt) { const { isActivated, datePanelValue, datePanelType } = this if (isActivated) { evnt.preventDefault() const keyCode = evnt.keyCode const isLeftArrow = keyCode === 37 const isUpArrow = keyCode === 38 const isRightArrow = keyCode === 39 const isDwArrow = keyCode === 40 if (datePanelType === 'year') { let offsetYear = XEUtils.getWhatYear(datePanelValue || Date.now(), 0, 'first') if (isLeftArrow) { offsetYear = XEUtils.getWhatYear(offsetYear, -1) } else if (isUpArrow) { offsetYear = XEUtils.getWhatYear(offsetYear, -4) } else if (isRightArrow) { offsetYear = XEUtils.getWhatYear(offsetYear, 1) } else if (isDwArrow) { offsetYear = XEUtils.getWhatYear(offsetYear, 4) } this.dateMoveYear(offsetYear) } else if (datePanelType === 'month') { let offsetMonth = XEUtils.getWhatMonth(datePanelValue || Date.now(), 0, 'first') if (isLeftArrow) { offsetMonth = XEUtils.getWhatMonth(offsetMonth, -1) } else if (isUpArrow) { offsetMonth = XEUtils.getWhatMonth(offsetMonth, -4) } else if (isRightArrow) { offsetMonth = XEUtils.getWhatMonth(offsetMonth, 1) } else if (isDwArrow) { offsetMonth = XEUtils.getWhatMonth(offsetMonth, 4) } this.dateMoveMonth(offsetMonth) } else { let offsetDay = datePanelValue || XEUtils.getWhatDay(Date.now(), 0, 'first') if (isLeftArrow) { offsetDay = XEUtils.getWhatDay(offsetDay, -1) } else if (isUpArrow) { offsetDay = XEUtils.getWhatWeek(offsetDay, -1) } else if (isRightArrow) { offsetDay = XEUtils.getWhatDay(offsetDay, 1) } else if (isDwArrow) { offsetDay = XEUtils.getWhatWeek(offsetDay, 1) } this.dateMoveDay(offsetDay) } } }, datePgOffsetEvent (evnt) { const { isActivated } = this if (isActivated) { const isPgUp = evnt.keyCode === 33 evnt.preventDefault() if (isPgUp) { this.datePrevEvent(evnt) } else { this.dateNextEvent(evnt) } } }, dateChange (date) { const { value, datetimePanelValue, dateValueFormat } = this if (this.type === 'week') { const sWeek = XEUtils.toNumber(this.startWeek) date = XEUtils.getWhatWeek(date, 0, sWeek) } else if (this.hasTime) { date.setHours(datetimePanelValue.getHours()) date.setMinutes(datetimePanelValue.getMinutes()) date.setSeconds(datetimePanelValue.getSeconds()) } const inpVal = XEUtils.toDateString(date, dateValueFormat) this.dateCheckMonth(date) if (!XEUtils.isEqual(value, inpVal)) { this.emitUpdate(inpVal, { type: 'update' }) } }, dateCheckMonth (date) { const month = XEUtils.getWhatMonth(date, 0, 'first') if (!XEUtils.isEqual(month, this.selectMonth)) {