Files
vxe-table/packages/input/src/input.ts
2021-11-07 14:17:03 +08:00

2246 lines
74 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineComponent, h, Teleport, ref, Ref, computed, reactive, nextTick, watch, onUnmounted, PropType } from 'vue'
import XEUtils from 'xe-utils'
import GlobalConfig from '../../v-x-e-table/src/conf'
import { useSize } from '../../hooks/size'
import { getFuncText, getLastZIndex, nextZIndex } from '../../tools/utils'
import { hasClass, getAbsolutePos, getEventTargetNode } from '../../tools/dom'
import { GlobalEvent, hasEventKey, EVENT_KEYS } from '../../tools/event'
import { VNodeStyle, VxeInputConstructor, VxeInputEmits, InputReactData, InputMethods, VxeInputPropTypes, InputPrivateRef } from '../../../types/all'
interface DateYearItem {
date: Date;
isCurrent: boolean;
isNow: boolean;
year: number;
}
interface DateMonthItem {
date: Date;
isPrev: boolean;
isCurrent: boolean;
isNow: boolean;
isNext: boolean;
month: number;
}
interface DateQuarterItem {
date: Date;
isPrev: boolean;
isCurrent: boolean;
isNow: boolean;
isNext: boolean;
quarter: number;
}
interface DateDayItem {
date: Date;
isWeekNumber?: boolean;
isPrev: boolean;
isCurrent: boolean;
isNow: boolean;
isNext: boolean;
label: number;
}
interface DateHourMinuteSecondItem {
value: number;
label: string;
}
const yearSize = 20
const monthSize = 20
const quarterSize = 8
function toStringTimeDate (str: VxeInputPropTypes.ModelValue) {
if (str) {
const rest = new Date()
let h = 0
let m = 0
let s = 0
if (XEUtils.isDate(str)) {
h = str.getHours()
m = str.getMinutes()
s = str.getSeconds()
} else {
str = XEUtils.toValueString(str)
const parses = str.match(/^(\d{1,2})(:(\d{1,2}))?(:(\d{1,2}))?/)
if (parses) {
h = XEUtils.toNumber(parses[1])
m = XEUtils.toNumber(parses[3])
s = XEUtils.toNumber(parses[5])
}
}
rest.setHours(h)
rest.setMinutes(m)
rest.setSeconds(s)
return rest
}
return new Date('')
}
export default defineComponent({
name: 'VxeInput',
props: {
modelValue: [String, Number, Date] as PropType<VxeInputPropTypes.ModelValue>,
immediate: { type: Boolean as PropType<VxeInputPropTypes.Immediate>, default: true },
name: String as PropType<VxeInputPropTypes.Name>,
type: { type: String as PropType<VxeInputPropTypes.Type>, default: 'text' },
clearable: { type: Boolean as PropType<VxeInputPropTypes.Clearable>, default: () => GlobalConfig.input.clearable },
readonly: Boolean as PropType<VxeInputPropTypes.Readonly>,
disabled: Boolean as PropType<VxeInputPropTypes.Disabled>,
placeholder: String as PropType<VxeInputPropTypes.Placeholder>,
maxlength: [String, Number] as PropType<VxeInputPropTypes.Maxlength>,
autocomplete: { type: String as PropType<VxeInputPropTypes.Autocomplete>, default: 'off' },
align: String as PropType<VxeInputPropTypes.Align>,
form: String as PropType<VxeInputPropTypes.Form>,
className: String as PropType<VxeInputPropTypes.ClassName>,
size: { type: String as PropType<VxeInputPropTypes.Size>, default: () => GlobalConfig.input.size || GlobalConfig.size },
// number、integer、float
min: { type: [String, Number] as PropType<VxeInputPropTypes.Min>, default: null },
max: { type: [String, Number] as PropType<VxeInputPropTypes.Max>, default: null },
step: [String, Number] as PropType<VxeInputPropTypes.Step>,
exponential: { type: Boolean as PropType<VxeInputPropTypes.Exponential>, default: () => GlobalConfig.input.exponential },
// number、integer、float、password
controls: { type: Boolean as PropType<VxeInputPropTypes.Controls>, default: () => GlobalConfig.input.controls },
// float
digits: { type: [String, Number] as PropType<VxeInputPropTypes.Digits>, default: () => GlobalConfig.input.digits },
// date、week、month、quarter、year
minDate: { type: [String, Number, Date] as PropType<VxeInputPropTypes.MinDate>, default: () => GlobalConfig.input.minDate },
maxDate: { type: [String, Number, Date] as PropType<VxeInputPropTypes.MaxDate>, default: () => GlobalConfig.input.maxDate },
// 已废弃 startWeek被 startDay 替换
startWeek: Number as PropType<VxeInputPropTypes.StartDay>,
startDay: { type: [String, Number] as PropType<VxeInputPropTypes.StartDay>, default: () => GlobalConfig.input.startDay },
labelFormat: { type: String as PropType<VxeInputPropTypes.LabelFormat>, default: () => GlobalConfig.input.labelFormat },
valueFormat: { type: String as PropType<VxeInputPropTypes.ValueFormat>, default: () => GlobalConfig.input.valueFormat },
editable: { type: Boolean as PropType<VxeInputPropTypes.Editable>, default: true },
festivalMethod: { type: Function as PropType<VxeInputPropTypes.FestivalMethod>, default: () => GlobalConfig.input.festivalMethod },
disabledMethod: { type: Function as PropType<VxeInputPropTypes.DisabledMethod>, default: () => GlobalConfig.input.disabledMethod },
// week
selectDay: { type: [String, Number] as PropType<VxeInputPropTypes.SelectDay>, default: () => GlobalConfig.input.selectDay },
prefixIcon: String as PropType<VxeInputPropTypes.PrefixIcon>,
suffixIcon: String as PropType<VxeInputPropTypes.SuffixIcon>,
placement: String as PropType<VxeInputPropTypes.Placement>,
transfer: { type: Boolean as PropType<VxeInputPropTypes.Transfer>, default: () => GlobalConfig.input.transfer }
},
emits: [
'update:modelValue',
'input',
'change',
'keydown',
'keyup',
'wheel',
'click',
'focus',
'blur',
'clear',
'search-click',
'toggle-visible',
'prev-number',
'next-number',
'prefix-click',
'suffix-click',
'date-prev',
'date-today',
'date-next'
] as VxeInputEmits,
setup (props, context) {
const { slots, emit } = context
const xID = XEUtils.uniqueId()
const computeSize = useSize(props)
const reactData = reactive<InputReactData>({
inited: false,
panelIndex: 0,
showPwd: false,
visiblePanel: false,
animatVisible: false,
panelStyle: null,
panelPlacement: '',
isActivated: false,
inputValue: props.modelValue,
datetimePanelValue: null,
datePanelValue: null,
datePanelLabel: '',
datePanelType: 'day',
selectMonth: null,
currentDate: null
})
const refElem = ref() as Ref<HTMLDivElement>
const refInputTarget = ref() as Ref<HTMLInputElement>
const refInputPanel = ref() as Ref<HTMLDivElement>
const refInputTimeBody = ref() as Ref<HTMLDivElement>
const refMaps: InputPrivateRef = {
refElem,
refInput: refInputTarget
}
const $xeinput = {
xID,
props,
context,
reactData,
getRefMaps: () => refMaps
} as unknown as VxeInputConstructor
let inputMethods = {} as InputMethods
const computeIsDateTimeType = computed(() => {
const { type } = props
return type === 'time' || type === 'datetime'
})
const computeIsNumType = computed(() => {
return ['number', 'integer', 'float'].indexOf(props.type) > -1
})
const computeIsDatePickerType = computed(() => {
const isDateTimeType = computeIsDateTimeType.value
return isDateTimeType || ['date', 'week', 'month', 'quarter', 'year'].indexOf(props.type) > -1
})
const computeIsPawdType = computed(() => {
return props.type === 'password'
})
const computeIsSearchType = computed(() => {
return props.type === 'search'
})
const computeDigitsValue = computed(() => {
return XEUtils.toInteger(props.digits) || 1
})
const computeStepValue = computed(() => {
const { type } = props
const digitsValue = computeDigitsValue.value
const step = props.step
if (type === 'integer') {
return XEUtils.toInteger(step) || 1
} else if (type === 'float') {
return XEUtils.toNumber(step) || (1 / Math.pow(10, digitsValue))
}
return XEUtils.toNumber(step) || 1
})
const computeIsClearable = computed(() => {
const { type } = props
const isNumType = computeIsNumType.value
const isDatePickerType = computeIsDatePickerType.value
const isPawdType = computeIsPawdType.value
return props.clearable && (isPawdType || isNumType || isDatePickerType || type === 'text' || type === 'search')
})
const computeDateMinTime = computed(() => {
return props.minDate ? XEUtils.toStringDate(props.minDate) : null
})
const computeDateMaxTime = computed(() => {
return props.maxDate ? XEUtils.toStringDate(props.maxDate) : null
})
const computeDateValueFormat = computed(() => {
const { type } = props
return type === 'time' ? 'HH:mm:ss' : (props.valueFormat || (type === 'datetime' ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd'))
})
const computeDateValue = computed(() => {
const { modelValue, type } = props
const isDatePickerType = computeIsDatePickerType.value
const dateValueFormat = computeDateValueFormat.value
let val = null
if (modelValue && isDatePickerType) {
let date
if (type === 'time') {
date = toStringTimeDate(modelValue)
} else {
date = XEUtils.toStringDate(modelValue, dateValueFormat)
}
if (XEUtils.isValidDate(date)) {
val = date
}
}
return val
})
const computeIsDisabledPrevDateBtn = computed(() => {
const dateMinTime = computeDateMinTime.value
const { selectMonth } = reactData
if (selectMonth && dateMinTime) {
return selectMonth <= dateMinTime
}
return false
})
const computeIsDisabledNextDateBtn = computed(() => {
const dateMaxTime = computeDateMaxTime.value
const { selectMonth } = reactData
if (selectMonth && dateMaxTime) {
return selectMonth >= dateMaxTime
}
return false
})
const computeDateTimeLabel = computed(() => {
const { datetimePanelValue } = reactData
if (datetimePanelValue) {
return XEUtils.toDateString(datetimePanelValue, 'HH:mm:ss')
}
return ''
})
const computeDateHMSTime = computed(() => {
const dateValue = computeDateValue.value
const isDateTimeType = computeIsDateTimeType.value
return dateValue && isDateTimeType ? (dateValue.getHours() * 3600 + dateValue.getMinutes() * 60 + dateValue.getSeconds()) * 1000 : 0
})
const computeDateLabelFormat = computed(() => {
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
return props.labelFormat || GlobalConfig.i18n(`vxe.input.date.labelFormat.${props.type}`)
}
return null
})
const computeYearList = computed(() => {
const { selectMonth, currentDate } = reactData
const years: DateYearItem[] = []
if (selectMonth && currentDate) {
const currFullYear = currentDate.getFullYear()
const startYear = new Date(XEUtils.toNumber(('' + 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()
years.push({
date,
isCurrent: true,
isNow: currFullYear === itemFullYear,
year: itemFullYear
})
}
}
return years
})
const computeSelectDatePanelLabel = computed(() => {
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
const { datePanelType, selectMonth } = reactData
const yearList = computeYearList.value
let year = ''
let month
if (selectMonth) {
year = selectMonth.getFullYear()
month = selectMonth.getMonth() + 1
}
if (datePanelType === 'quarter') {
return GlobalConfig.i18n('vxe.input.date.quarterLabel', [year])
} else 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 ''
})
const computeWeekDatas = computed(() => {
const weeks = []
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
const { startDay, startWeek } = props
let sWeek = XEUtils.toNumber(XEUtils.isNumber(startDay) || XEUtils.isString(startDay) ? startDay : startWeek)
weeks.push(sWeek)
for (let index = 0; index < 6; index++) {
if (sWeek >= 6) {
sWeek = 0
} else {
sWeek++
}
weeks.push(sWeek)
}
}
return weeks
})
const computeDateHeaders = computed(() => {
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
const weekDatas = computeWeekDatas.value
return weekDatas.map((day) => {
return {
value: day,
label: GlobalConfig.i18n(`vxe.input.date.weeks.w${day}`)
}
})
}
return []
})
const computeWeekHeaders = computed(() => {
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
const dateHeaders = computeDateHeaders.value
return [{ label: GlobalConfig.i18n('vxe.input.date.weeks.w') }].concat(dateHeaders)
}
return []
})
const computeYearDatas = computed(() => {
const yearList = computeYearList.value
return XEUtils.chunk(yearList, 4)
})
const getDateQuarter = (date: Date) => {
const month = date.getMonth()
if (month < 3) {
return 1
} else if (month < 6) {
return 2
} else if (month < 9) {
return 3
}
return 4
}
const computeQuarterList = computed(() => {
const { selectMonth, currentDate } = reactData
const quarters: DateQuarterItem[] = []
if (selectMonth && currentDate) {
const currFullYear = currentDate.getFullYear()
const currQuarter = getDateQuarter(currentDate)
const firstYear = XEUtils.getWhatYear(selectMonth, 0, 'first')
const selFullYear = firstYear.getFullYear()
for (let index = -2; index < quarterSize - 2; index++) {
const date = XEUtils.getWhatQuarter(firstYear, index)
const itemFullYear = date.getFullYear()
const itemQuarter = getDateQuarter(date)
const isPrev = itemFullYear < selFullYear
quarters.push({
date,
isPrev,
isCurrent: itemFullYear === selFullYear,
isNow: itemFullYear === currFullYear && itemQuarter === currQuarter,
isNext: !isPrev && itemFullYear > selFullYear,
quarter: itemQuarter
})
}
}
return quarters
})
const computeQuarterDatas = computed(() => {
const quarterList = computeQuarterList.value
return XEUtils.chunk(quarterList, 2)
})
const computeMonthList = computed(() => {
const { selectMonth, currentDate } = reactData
const months: DateMonthItem[] = []
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
})
const computeMonthDatas = computed(() => {
const monthList = computeMonthList.value
return XEUtils.chunk(monthList, 4)
})
const computeDayList = computed(() => {
const { selectMonth, currentDate } = reactData
const days: DateDayItem[] = []
if (selectMonth && currentDate) {
const dateHMSTime = computeDateHMSTime.value
const weekDatas = computeWeekDatas.value
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() + dateHMSTime)
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
})
const computeDayDatas = computed(() => {
const dayList = computeDayList.value
return XEUtils.chunk(dayList, 7)
})
const computeWeekDates = computed(() => {
const dayDatas = computeDayDatas.value
return dayDatas.map((list) => {
const firstItem = list[0]
const item: DateDayItem = {
date: firstItem.date,
isWeekNumber: true,
isPrev: false,
isCurrent: false,
isNow: false,
isNext: false,
label: XEUtils.getYearWeek(firstItem.date)
}
return [item].concat(list)
})
})
const computeHourList = computed(() => {
const list: DateHourMinuteSecondItem[] = []
const isDateTimeType = computeIsDateTimeType.value
if (isDateTimeType) {
for (let index = 0; index < 24; index++) {
list.push({
value: index,
label: ('' + index).padStart(2, '0')
})
}
}
return list
})
const computeMinuteList = computed(() => {
const list: DateHourMinuteSecondItem[] = []
const isDateTimeType = computeIsDateTimeType.value
if (isDateTimeType) {
for (let index = 0; index < 60; index++) {
list.push({
value: index,
label: ('' + index).padStart(2, '0')
})
}
}
return list
})
const computeSecondList = computed(() => {
const minuteList = computeMinuteList.value
return minuteList
})
const computeInpReadonly = computed(() => {
const { type, readonly, editable } = props
return readonly || !editable || type === 'week' || type === 'quarter'
})
const computeInputType = computed(() => {
const { type } = props
const { showPwd } = reactData
const isNumType = computeIsNumType.value
const isDatePickerType = computeIsDatePickerType.value
const isPawdType = computeIsPawdType.value
if (isDatePickerType || isNumType || (isPawdType && showPwd) || type === 'number') {
return 'text'
}
return type
})
const computeInpPlaceholder = computed(() => {
const { placeholder } = props
if (placeholder) {
return getFuncText(placeholder)
}
return ''
})
const computeInpMaxlength = computed(() => {
const { maxlength } = props
const isNumType = computeIsNumType.value
// 数值最大长度限制 16 位,包含小数
return isNumType && !XEUtils.toNumber(maxlength) ? 16 : maxlength
})
const computeInpImmediate = computed(() => {
const { type, immediate } = props
return immediate || !(type === 'text' || type === 'number' || type === 'integer' || type === 'float')
})
function toFloatValueFixed (inputValue: string | number, digitsValue: number) {
if (/^-/.test('' + inputValue)) {
return XEUtils.toFixed(XEUtils.ceil(inputValue, digitsValue), digitsValue)
}
return XEUtils.toFixed(XEUtils.floor(inputValue, digitsValue), digitsValue)
}
function getNumberValue (val: any) {
const { type, exponential } = props
const inpMaxlength = computeInpMaxlength.value
const digitsValue = computeDigitsValue.value
const restVal = (type === 'float' ? toFloatValueFixed(val, digitsValue) : XEUtils.toValueString(val))
if (exponential && (val === restVal || XEUtils.toValueString(val).toLowerCase() === XEUtils.toNumber(restVal).toExponential())) {
return val
}
return restVal.slice(0, inpMaxlength)
}
const triggerEvent = (evnt: Event & { type: 'input' | 'change' | 'keydown' | 'keyup' | 'wheel' | 'click' | 'focus' | 'blur' }) => {
const { inputValue } = reactData
inputMethods.dispatchEvent(evnt.type, { value: inputValue }, evnt)
}
const emitModel = (value: string, evnt: Event | { type: string }) => {
reactData.inputValue = value
emit('update:modelValue', value)
inputMethods.dispatchEvent('input', { value }, evnt)
if (XEUtils.toValueString(props.modelValue) !== value) {
inputMethods.dispatchEvent('change', { value }, evnt)
}
}
const emitInputEvent = (value: any, evnt: Event) => {
const isDatePickerType = computeIsDatePickerType.value
const inpImmediate = computeInpImmediate.value
reactData.inputValue = value
if (!isDatePickerType) {
if (inpImmediate) {
emitModel(value, evnt)
} else {
inputMethods.dispatchEvent('input', { value }, evnt)
}
}
}
const inputEvent = (evnt: Event & { type: 'input' }) => {
const inputElem = evnt.target as HTMLInputElement
const value = inputElem.value
emitInputEvent(value, evnt)
}
const changeEvent = (evnt: Event & { type: 'change' }) => {
const inpImmediate = computeInpImmediate.value
if (!inpImmediate) {
triggerEvent(evnt)
}
}
const focusEvent = (evnt: Event & { type: 'focus' }) => {
reactData.isActivated = true
triggerEvent(evnt)
}
const clickPrefixEvent = (evnt: Event) => {
const { disabled } = props
if (!disabled) {
const { inputValue } = reactData
inputMethods.dispatchEvent('prefix-click', { value: inputValue }, evnt)
}
}
let hidePanelTimeout: number
const hidePanel = () => {
reactData.visiblePanel = false
hidePanelTimeout = window.setTimeout(() => {
reactData.animatVisible = false
}, 350)
}
const clearValueEvent = (evnt: Event, value: VxeInputPropTypes.ModelValue) => {
const { type } = props
const isNumType = computeIsNumType.value
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
hidePanel()
}
if (isNumType || ['text', 'search', 'password'].indexOf(type) > -1) {
focus()
}
inputMethods.dispatchEvent('clear', { value }, evnt)
}
const clickSuffixEvent = (evnt: Event) => {
const { disabled } = props
if (!disabled) {
if (hasClass(evnt.currentTarget, 'is--clear')) {
emitModel('', evnt)
clearValueEvent(evnt, '')
} else {
const { inputValue } = reactData
inputMethods.dispatchEvent('suffix-click', { value: inputValue }, evnt)
}
}
}
const dateParseValue = (value?: VxeInputPropTypes.ModelValue) => {
const { type } = props
const { valueFormat } = props
const dateLabelFormat = computeDateLabelFormat.value
let dValue: Date | null = null
let dLabel = ''
if (value) {
if (type === 'time') {
dValue = toStringTimeDate(value)
} else {
dValue = XEUtils.toStringDate(value, valueFormat)
}
}
if (XEUtils.isValidDate(dValue)) {
dLabel = XEUtils.toDateString(dValue, dateLabelFormat)
} else {
dValue = null
}
reactData.datePanelValue = dValue
reactData.datePanelLabel = dLabel
}
/**
* 值变化时处理
*/
const changeValue = () => {
const isDatePickerType = computeIsDatePickerType.value
const { inputValue } = reactData
if (isDatePickerType) {
dateParseValue(inputValue)
reactData.inputValue = reactData.datePanelLabel
}
}
/**
* 检查初始值
*/
const initValue = () => {
const { type } = props
const { inputValue } = reactData
const isDatePickerType = computeIsDatePickerType.value
const digitsValue = computeDigitsValue.value
if (isDatePickerType) {
changeValue()
} else if (type === 'float') {
if (inputValue) {
const validValue = toFloatValueFixed(inputValue, digitsValue)
if (inputValue !== validValue) {
emitModel(validValue, { type: 'init' })
}
}
}
}
const vaildMaxNum = (num: number | string) => {
return props.max === null || XEUtils.toNumber(num) <= XEUtils.toNumber(props.max)
}
const vaildMinNum = (num: number | string) => {
return props.min === null || XEUtils.toNumber(num) >= XEUtils.toNumber(props.min)
}
const dateRevert = () => {
reactData.inputValue = reactData.datePanelLabel
}
const dateCheckMonth = (date: Date) => {
const month = XEUtils.getWhatMonth(date, 0, 'first')
if (!XEUtils.isEqual(month, reactData.selectMonth)) {
reactData.selectMonth = month
}
}
const dateChange = (date: Date) => {
const { modelValue } = props
const { datetimePanelValue } = reactData
const isDateTimeType = computeIsDateTimeType.value
const dateValueFormat = computeDateValueFormat.value
if (props.type === 'week') {
const sWeek = XEUtils.toNumber(props.selectDay)
date = XEUtils.getWhatWeek(date, 0, sWeek)
} else if (isDateTimeType) {
date.setHours(datetimePanelValue.getHours())
date.setMinutes(datetimePanelValue.getMinutes())
date.setSeconds(datetimePanelValue.getSeconds())
}
const inpVal = XEUtils.toDateString(date, dateValueFormat)
dateCheckMonth(date)
if (!XEUtils.isEqual(modelValue, inpVal)) {
emitModel(inpVal, { type: 'update' })
}
}
const afterCheckValue = () => {
const { type, min, max, exponential } = props
const { inputValue, datetimePanelValue } = reactData
const isNumType = computeIsNumType.value
const isDatePickerType = computeIsDatePickerType.value
const dateLabelFormat = computeDateLabelFormat.value
const inpReadonly = computeInpReadonly.value
if (!inpReadonly) {
if (isNumType) {
if (inputValue) {
let inpNumVal: number | string = type === 'integer' ? XEUtils.toInteger(inputValue) : XEUtils.toNumber(inputValue)
if (!vaildMinNum(inpNumVal)) {
inpNumVal = min
} else if (!vaildMaxNum(inpNumVal)) {
inpNumVal = max
}
if (exponential) {
const inpStringVal = XEUtils.toValueString(inputValue).toLowerCase()
if (inpStringVal === XEUtils.toNumber(inpNumVal).toExponential()) {
inpNumVal = inpStringVal
}
}
emitModel(getNumberValue(inpNumVal), { type: 'check' })
}
} else if (isDatePickerType) {
if (inputValue) {
let inpDateVal: VxeInputPropTypes.ModelValue
if (type === 'time') {
inpDateVal = toStringTimeDate(inputValue)
} else {
inpDateVal = XEUtils.toStringDate(inputValue, dateLabelFormat)
}
if (XEUtils.isValidDate(inpDateVal)) {
if (type === 'time') {
inpDateVal = XEUtils.toDateString(inpDateVal, dateLabelFormat)
if (inputValue !== inpDateVal) {
emitModel(inpDateVal, { type: 'check' })
}
reactData.inputValue = inpDateVal
} else {
let isChange = false
if (type === 'datetime') {
const dateValue = computeDateValue.value
if (inputValue !== XEUtils.toDateString(dateValue, dateLabelFormat) || inputValue !== XEUtils.toDateString(inpDateVal, dateLabelFormat)) {
isChange = true
datetimePanelValue.setHours(inpDateVal.getHours())
datetimePanelValue.setMinutes(inpDateVal.getMinutes())
datetimePanelValue.setSeconds(inpDateVal.getSeconds())
}
} else {
isChange = true
}
reactData.inputValue = XEUtils.toDateString(inpDateVal, dateLabelFormat)
if (isChange) {
dateChange(inpDateVal)
}
}
} else {
dateRevert()
}
} else {
emitModel('', { type: 'check' })
}
}
}
}
const blurEvent = (evnt: Event & { type: 'blur' }) => {
const { inputValue } = reactData
const inpImmediate = computeInpImmediate.value
if (!inpImmediate) {
emitModel(inputValue, evnt)
}
afterCheckValue()
if (!reactData.visiblePanel) {
reactData.isActivated = false
}
inputMethods.dispatchEvent('blur', { value: inputValue }, evnt)
}
// 密码
const passwordToggleEvent = (evnt: Event) => {
const { readonly, disabled } = props
const { showPwd } = reactData
if (!disabled && !readonly) {
reactData.showPwd = !showPwd
}
inputMethods.dispatchEvent('toggle-visible', { visible: reactData.showPwd }, evnt)
}
// 密码
// 搜索
const searchEvent = (evnt: Event) => {
inputMethods.dispatchEvent('search-click', {}, evnt)
}
// 搜索
// 数值
const numberChange = (isPlus: boolean, evnt: Event) => {
const { min, max, type } = props
const { inputValue } = reactData
const stepValue = computeStepValue.value
const numValue = type === 'integer' ? XEUtils.toInteger(inputValue) : XEUtils.toNumber(inputValue)
const newValue = isPlus ? XEUtils.add(numValue, stepValue) : XEUtils.subtract(numValue, stepValue)
let restNum: number | string
if (!vaildMinNum(newValue)) {
restNum = min
} else if (!vaildMaxNum(newValue)) {
restNum = max
} else {
restNum = newValue
}
emitInputEvent(getNumberValue(restNum), evnt as (Event & { type: 'input' }))
}
let downbumTimeout: number
const numberNextEvent = (evnt: Event) => {
const { readonly, disabled } = props
clearTimeout(downbumTimeout)
if (!disabled && !readonly) {
numberChange(false, evnt)
}
inputMethods.dispatchEvent('next-number', {}, evnt)
}
const numberDownNextEvent = (evnt: Event) => {
downbumTimeout = window.setTimeout(() => {
numberNextEvent(evnt)
numberDownNextEvent(evnt)
}, 60)
}
const numberPrevEvent = (evnt: Event) => {
const { readonly, disabled } = props
clearTimeout(downbumTimeout)
if (!disabled && !readonly) {
numberChange(true, evnt)
}
inputMethods.dispatchEvent('prev-number', {}, evnt)
}
const numberKeydownEvent = (evnt: KeyboardEvent) => {
const isUpArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_UP)
const isDwArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_DOWN)
if (isUpArrow || isDwArrow) {
evnt.preventDefault()
if (isUpArrow) {
numberPrevEvent(evnt)
} else {
numberNextEvent(evnt)
}
}
}
const keydownEvent = (evnt: KeyboardEvent & { type: 'keydown' }) => {
const { exponential, controls } = props
const isNumType = computeIsNumType.value
if (isNumType) {
const isCtrlKey = evnt.ctrlKey
const isShiftKey = evnt.shiftKey
const isAltKey = evnt.altKey
const keyCode = evnt.keyCode
if (!isCtrlKey && !isShiftKey && !isAltKey && (hasEventKey(evnt, EVENT_KEYS.SPACEBAR) || ((!exponential || keyCode !== 69) && (keyCode >= 65 && keyCode <= 90)) || (keyCode >= 186 && keyCode <= 188) || keyCode >= 191)) {
evnt.preventDefault()
}
if (controls) {
numberKeydownEvent(evnt)
}
}
triggerEvent(evnt)
}
const keyupEvent = (evnt: KeyboardEvent & { type: 'keyup' }) => {
triggerEvent(evnt)
}
// 数值
const numberStopDown = () => {
clearTimeout(downbumTimeout)
}
const numberDownPrevEvent = (evnt: Event) => {
downbumTimeout = window.setTimeout(() => {
numberPrevEvent(evnt)
numberDownPrevEvent(evnt)
}, 60)
}
const numberMousedownEvent = (evnt: MouseEvent) => {
numberStopDown()
if (evnt.button === 0) {
const isPrevNumber = hasClass(evnt.currentTarget, 'is--prev')
if (isPrevNumber) {
numberPrevEvent(evnt)
} else {
numberNextEvent(evnt)
}
downbumTimeout = window.setTimeout(() => {
if (isPrevNumber) {
numberDownPrevEvent(evnt)
} else {
numberDownNextEvent(evnt)
}
}, 500)
}
}
const wheelEvent = (evnt: WheelEvent & {
type: 'wheel';
wheelDelta: number;
}) => {
const isNumType = computeIsNumType.value
if (isNumType && props.controls) {
if (reactData.isActivated) {
const delta = evnt.deltaY
if (delta > 0) {
numberNextEvent(evnt)
} else if (delta < 0) {
numberPrevEvent(evnt)
}
evnt.preventDefault()
}
}
triggerEvent(evnt)
}
// 日期
const dateMonthHandle = (date: Date, offsetMonth: number) => {
reactData.selectMonth = XEUtils.getWhatMonth(date, offsetMonth, 'first')
}
const dateNowHandle = () => {
const currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first')
reactData.currentDate = currentDate
dateMonthHandle(currentDate, 0)
}
const dateToggleTypeEvent = () => {
let { datePanelType } = reactData
if (datePanelType === 'month' || datePanelType === 'quarter') {
datePanelType = 'year'
} else {
datePanelType = 'month'
}
reactData.datePanelType = datePanelType
}
const datePrevEvent = (evnt: Event) => {
const { type } = props
const { datePanelType, selectMonth } = reactData
const isDisabledPrevDateBtn = computeIsDisabledPrevDateBtn.value
if (!isDisabledPrevDateBtn) {
if (type === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else if (type === 'month' || type === 'quarter') {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -1, 'first')
}
} else {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -yearSize, 'first')
} else if (datePanelType === 'month') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, -1, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatMonth(selectMonth, -1, 'first')
}
}
inputMethods.dispatchEvent('date-prev', { type }, evnt)
}
}
const dateTodayMonthEvent = (evnt: Event) => {
dateNowHandle()
dateChange(reactData.currentDate)
hidePanel()
inputMethods.dispatchEvent('date-today', { type: props.type }, evnt)
}
const dateNextEvent = (evnt: Event) => {
const { type } = props
const { datePanelType, selectMonth } = reactData
const isDisabledNextDateBtn = computeIsDisabledNextDateBtn.value
if (!isDisabledNextDateBtn) {
if (type === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else if (type === 'month' || type === 'quarter') {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, 1, 'first')
}
} else {
if (datePanelType === 'year') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, yearSize, 'first')
} else if (datePanelType === 'month') {
reactData.selectMonth = XEUtils.getWhatYear(selectMonth, 1, 'first')
} else {
reactData.selectMonth = XEUtils.getWhatMonth(selectMonth, 1, 'first')
}
}
inputMethods.dispatchEvent('date-next', { type }, evnt)
}
}
const isDateDisabled = (item: { date: Date }) => {
const { disabledMethod } = props
const { datePanelType } = reactData
return disabledMethod && disabledMethod({ type: datePanelType, viewType: datePanelType, date: item.date, $input: $xeinput })
}
const dateSelectItem = (date: Date) => {
const { type } = props
const { datePanelType } = reactData
if (type === 'month') {
if (datePanelType === 'year') {
reactData.datePanelType = 'month'
dateCheckMonth(date)
} else {
dateChange(date)
hidePanel()
}
} else if (type === 'year') {
dateChange(date)
hidePanel()
} else if (type === 'quarter') {
if (datePanelType === 'year') {
reactData.datePanelType = 'quarter'
dateCheckMonth(date)
} else {
dateChange(date)
hidePanel()
}
} else {
if (datePanelType === 'month') {
reactData.datePanelType = type === 'week' ? type : 'day'
dateCheckMonth(date)
} else if (datePanelType === 'year') {
reactData.datePanelType = 'month'
dateCheckMonth(date)
} else {
dateChange(date)
hidePanel()
}
}
}
const dateSelectEvent = (item: DateYearItem | DateQuarterItem | DateMonthItem | DateDayItem) => {
if (!isDateDisabled(item)) {
dateSelectItem(item.date)
}
}
const dateMoveDay = (offsetDay: Date) => {
if (!isDateDisabled({ date: offsetDay })) {
const dayList = computeDayList.value
if (!dayList.some((item) => XEUtils.isDateSame(item.date, offsetDay, 'yyyyMMdd'))) {
dateCheckMonth(offsetDay)
}
dateParseValue(offsetDay)
}
}
const dateMoveYear = (offsetYear: Date) => {
if (!isDateDisabled({ date: offsetYear })) {
const yearList = computeYearList.value
if (!yearList.some((item) => XEUtils.isDateSame(item.date, offsetYear, 'yyyy'))) {
dateCheckMonth(offsetYear)
}
dateParseValue(offsetYear)
}
}
const dateMoveQuarter = (offsetQuarter: Date) => {
if (!isDateDisabled({ date: offsetQuarter })) {
const quarterList = computeQuarterList.value
if (!quarterList.some((item) => XEUtils.isDateSame(item.date, offsetQuarter, 'yyyyq'))) {
dateCheckMonth(offsetQuarter)
}
dateParseValue(offsetQuarter)
}
}
const dateMoveMonth = (offsetMonth: Date) => {
if (!isDateDisabled({ date: offsetMonth })) {
const monthList = computeMonthList.value
if (!monthList.some((item) => XEUtils.isDateSame(item.date, offsetMonth, 'yyyyMM'))) {
dateCheckMonth(offsetMonth)
}
dateParseValue(offsetMonth)
}
}
const dateMouseenterEvent = (item: DateYearItem | DateQuarterItem | DateMonthItem | DateDayItem) => {
if (!isDateDisabled(item)) {
const { datePanelType } = reactData
if (datePanelType === 'month') {
dateMoveMonth(item.date)
} else if (datePanelType === 'quarter') {
dateMoveQuarter(item.date)
} else if (datePanelType === 'year') {
dateMoveYear(item.date)
} else {
dateMoveDay(item.date)
}
}
}
const updateTimePos = (liElem: Element) => {
if (liElem) {
const height = (liElem as HTMLElement).offsetHeight
const ulElem = liElem.parentNode as HTMLElement
ulElem.scrollTop = (liElem as HTMLElement).offsetTop - height * 4
}
}
const dateTimeChangeEvent = (evnt: Event) => {
reactData.datetimePanelValue = new Date(reactData.datetimePanelValue.getTime())
updateTimePos(evnt.currentTarget as HTMLLIElement)
}
const dateHourEvent = (evnt: MouseEvent, item: DateHourMinuteSecondItem) => {
reactData.datetimePanelValue.setHours(item.value)
dateTimeChangeEvent(evnt)
}
const dateConfirmEvent = () => {
const dateValue = computeDateValue.value
dateChange(dateValue || reactData.currentDate)
hidePanel()
}
const dateMinuteEvent = (evnt: MouseEvent, item: DateHourMinuteSecondItem) => {
reactData.datetimePanelValue.setMinutes(item.value)
dateTimeChangeEvent(evnt)
}
const dateSecondEvent = (evnt: MouseEvent, item: DateHourMinuteSecondItem) => {
reactData.datetimePanelValue.setSeconds(item.value)
dateTimeChangeEvent(evnt)
}
const dateOffsetEvent = (evnt: KeyboardEvent) => {
const { isActivated, datePanelValue, datePanelType } = reactData
if (isActivated) {
evnt.preventDefault()
const isLeftArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_LEFT)
const isUpArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_UP)
const isRightArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_RIGHT)
const isDwArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_DOWN)
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)
}
dateMoveYear(offsetYear)
} else if (datePanelType === 'quarter') {
let offsetQuarter = XEUtils.getWhatQuarter(datePanelValue || Date.now(), 0, 'first')
if (isLeftArrow) {
offsetQuarter = XEUtils.getWhatQuarter(offsetQuarter, -1)
} else if (isUpArrow) {
offsetQuarter = XEUtils.getWhatQuarter(offsetQuarter, -2)
} else if (isRightArrow) {
offsetQuarter = XEUtils.getWhatQuarter(offsetQuarter, 1)
} else if (isDwArrow) {
offsetQuarter = XEUtils.getWhatQuarter(offsetQuarter, 2)
}
dateMoveQuarter(offsetQuarter)
} 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)
}
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)
}
dateMoveDay(offsetDay)
}
}
}
const datePgOffsetEvent = (evnt: KeyboardEvent) => {
const { isActivated } = reactData
if (isActivated) {
const isPgUp = hasEventKey(evnt, EVENT_KEYS.PAGE_UP)
evnt.preventDefault()
if (isPgUp) {
datePrevEvent(evnt)
} else {
dateNextEvent(evnt)
}
}
}
const dateOpenPanel = () => {
const { type } = props
const isDateTimeType = computeIsDateTimeType.value
const dateValue = computeDateValue.value
if (['year', 'quarter', 'month', 'week'].indexOf(type) > -1) {
reactData.datePanelType = type as 'year' | 'quarter' | 'month' | 'week'
} else {
reactData.datePanelType = 'day'
}
reactData.currentDate = XEUtils.getWhatDay(Date.now(), 0, 'first')
if (dateValue) {
dateMonthHandle(dateValue, 0)
dateParseValue(dateValue)
} else {
dateNowHandle()
}
if (isDateTimeType) {
reactData.datetimePanelValue = reactData.datePanelValue || XEUtils.getWhatDay(Date.now(), 0, 'first')
nextTick(() => {
const timeBodyElem = refInputTimeBody.value
XEUtils.arrayEach(timeBodyElem.querySelectorAll('li.is--selected'), updateTimePos)
})
}
}
// 日期
// 弹出面板
const updateZindex = () => {
if (reactData.panelIndex < getLastZIndex()) {
reactData.panelIndex = nextZIndex()
}
}
const updatePlacement = () => {
return nextTick().then(() => {
const { transfer, placement } = props
const { panelIndex } = reactData
const targetElem = refInputTarget.value
const panelElem = refInputPanel.value
if (targetElem && panelElem) {
const targetHeight = targetElem.offsetHeight
const targetWidth = targetElem.offsetWidth
const panelHeight = panelElem.offsetHeight
const panelWidth = panelElem.offsetWidth
const marginSize = 5
const panelStyle: VNodeStyle = {
zIndex: panelIndex
}
const { boundingTop, boundingLeft, visibleHeight, visibleWidth } = getAbsolutePos(targetElem)
let panelPlacement: VxeInputPropTypes.Placement = 'bottom'
if (transfer) {
let left = boundingLeft
let top = boundingTop + targetHeight
if (placement === 'top') {
panelPlacement = 'top'
top = boundingTop - panelHeight
} else if (!placement) {
// 如果下面不够放,则向上
if (top + panelHeight + marginSize > visibleHeight) {
panelPlacement = 'top'
top = boundingTop - panelHeight
}
// 如果上面不够放,则向下(优先)
if (top < marginSize) {
panelPlacement = 'bottom'
top = boundingTop + targetHeight
}
}
// 如果溢出右边
if (left + panelWidth + marginSize > visibleWidth) {
left -= left + panelWidth + marginSize - visibleWidth
}
// 如果溢出左边
if (left < marginSize) {
left = marginSize
}
Object.assign(panelStyle, {
left: `${left}px`,
top: `${top}px`,
minWidth: `${targetWidth}px`
})
} else {
if (placement === 'top') {
panelPlacement = 'top'
panelStyle.bottom = `${targetHeight}px`
} else if (!placement) {
// 如果下面不够放,则向上
if (boundingTop + targetHeight + panelHeight > visibleHeight) {
// 如果上面不够放,则向下(优先)
if (boundingTop - targetHeight - panelHeight > marginSize) {
panelPlacement = 'top'
panelStyle.bottom = `${targetHeight}px`
}
}
}
}
reactData.panelStyle = panelStyle
reactData.panelPlacement = panelPlacement
return nextTick()
}
})
}
const showPanel = () => {
const { disabled } = props
const { visiblePanel } = reactData
const isDatePickerType = computeIsDatePickerType.value
if (!disabled && !visiblePanel) {
if (!reactData.inited) {
reactData.inited = true
}
clearTimeout(hidePanelTimeout)
reactData.isActivated = true
reactData.animatVisible = true
if (isDatePickerType) {
dateOpenPanel()
}
setTimeout(() => {
reactData.visiblePanel = true
}, 10)
updateZindex()
updatePlacement()
}
}
const datePickerOpenEvent = (evnt: Event) => {
const { readonly } = props
if (!readonly) {
evnt.preventDefault()
showPanel()
}
}
const clickEvent = (evnt: Event & { type: 'click' }) => {
const isDatePickerType = computeIsDatePickerType.value
if (isDatePickerType) {
datePickerOpenEvent(evnt)
}
triggerEvent(evnt)
}
// 弹出面板
// 全局事件
const handleGlobalMousedownEvent = (evnt: Event) => {
const { disabled } = props
const { visiblePanel, isActivated } = reactData
const isDatePickerType = computeIsDatePickerType.value
const el = refElem.value
const panelElem = refInputPanel.value
if (!disabled && isActivated) {
reactData.isActivated = getEventTargetNode(evnt, el).flag || getEventTargetNode(evnt, panelElem).flag
if (!reactData.isActivated) {
// 如果是日期类型
if (isDatePickerType) {
if (visiblePanel) {
hidePanel()
afterCheckValue()
}
} else {
afterCheckValue()
}
}
}
}
const handleGlobalKeydownEvent = (evnt: KeyboardEvent) => {
const { clearable, disabled } = props
const { visiblePanel } = reactData
const isDatePickerType = computeIsDatePickerType.value
if (!disabled) {
const isTab = hasEventKey(evnt, EVENT_KEYS.TAB)
const isDel = hasEventKey(evnt, EVENT_KEYS.DELETE)
const isEsc = hasEventKey(evnt, EVENT_KEYS.ESCAPE)
const isEnter = hasEventKey(evnt, EVENT_KEYS.ENTER)
const isLeftArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_LEFT)
const isUpArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_UP)
const isRightArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_RIGHT)
const isDwArrow = hasEventKey(evnt, EVENT_KEYS.ARROW_DOWN)
const isPgUp = hasEventKey(evnt, EVENT_KEYS.PAGE_UP)
const isPgDn = hasEventKey(evnt, EVENT_KEYS.PAGE_DOWN)
const operArrow = isLeftArrow || isUpArrow || isRightArrow || isDwArrow
let isActivated = reactData.isActivated
if (isTab) {
if (isActivated) {
afterCheckValue()
}
isActivated = false
reactData.isActivated = isActivated
} else if (operArrow) {
if (isDatePickerType) {
if (isActivated) {
if (visiblePanel) {
dateOffsetEvent(evnt)
} else if (isUpArrow || isDwArrow) {
datePickerOpenEvent(evnt)
}
}
}
} else if (isEnter) {
if (isDatePickerType) {
if (visiblePanel) {
if (reactData.datePanelValue) {
dateSelectItem(reactData.datePanelValue)
} else {
hidePanel()
}
} else if (isActivated) {
datePickerOpenEvent(evnt)
}
}
} else if (isPgUp || isPgDn) {
if (isDatePickerType) {
if (isActivated) {
datePgOffsetEvent(evnt)
}
}
}
if (isTab || isEsc) {
if (visiblePanel) {
hidePanel()
}
} else if (isDel && clearable) {
if (isActivated) {
clearValueEvent(evnt, null)
}
}
}
}
const handleGlobalMousewheelEvent = (evnt: Event) => {
const { disabled } = props
const { visiblePanel } = reactData
if (!disabled) {
if (visiblePanel) {
const panelElem = refInputPanel.value
if (getEventTargetNode(evnt, panelElem).flag) {
updatePlacement()
} else {
hidePanel()
afterCheckValue()
}
}
}
}
const handleGlobalBlurEvent = () => {
const { isActivated, visiblePanel } = reactData
if (visiblePanel) {
hidePanel()
afterCheckValue()
} else if (isActivated) {
afterCheckValue()
}
}
const renderDateLabel = (item: DateYearItem | DateQuarterItem | DateMonthItem | DateDayItem, label: string | number) => {
const { festivalMethod } = props
if (festivalMethod) {
const { datePanelType } = reactData
const festivalRest = festivalMethod({ type: datePanelType, viewType: datePanelType, date: item.date, $input: $xeinput })
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.toValueString(extraItem.label))
] : label)
]
const festivalLabel = festivalItem.label
if (festivalLabel) {
// 默认最多支持3个节日重叠
const festivalLabels = XEUtils.toValueString(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
}
const renderDateDayTable = () => {
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const dateHeaders = computeDateHeaders.value
const dayDatas = computeDayDatas.value
const matchFormat = 'yyyyMMdd'
return [
h('table', {
class: `vxe-input--date-${datePanelType}-view`,
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(item),
'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat),
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
},
onClick: () => dateSelectEvent(item),
onMouseenter: () => dateMouseenterEvent(item)
}, renderDateLabel(item, item.label))
}))
}))
])
]
}
const renderDateWeekTable = () => {
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const weekHeaders = computeWeekHeaders.value
const weekDates = computeWeekDates.value
const matchFormat = 'yyyyMMdd'
return [
h('table', {
class: `vxe-input--date-${datePanelType}-view`,
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(item),
'is--selected': isSelected,
'is--hover': isHover
},
// event
onClick: () => dateSelectEvent(item),
onMouseenter: () => dateMouseenterEvent(item)
}, renderDateLabel(item, item.label))
}))
}))
])
]
}
const renderDateMonthTable = () => {
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const monthDatas = computeMonthDatas.value
const matchFormat = 'yyyyMM'
return [
h('table', {
class: `vxe-input--date-${datePanelType}-view`,
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(item),
'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat),
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
},
onClick: () => dateSelectEvent(item),
onMouseenter: () => dateMouseenterEvent(item)
}, renderDateLabel(item, GlobalConfig.i18n(`vxe.input.date.months.m${item.month}`)))
}))
}))
])
]
}
const renderDateQuarterTable = () => {
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const quarterDatas = computeQuarterDatas.value
const matchFormat = 'yyyyq'
return [
h('table', {
class: `vxe-input--date-${datePanelType}-view`,
cellspacing: 0,
cellpadding: 0,
border: 0
}, [
h('tbody', quarterDatas.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(item),
'is--selected': XEUtils.isDateSame(dateValue, item.date, matchFormat),
'is--hover': XEUtils.isDateSame(datePanelValue, item.date, matchFormat)
},
onClick: () => dateSelectEvent(item),
onMouseenter: () => dateMouseenterEvent(item)
}, renderDateLabel(item, GlobalConfig.i18n(`vxe.input.date.quarters.q${item.quarter}`)))
}))
}))
])
]
}
const renderDateYearTable = () => {
const { datePanelType, datePanelValue } = reactData
const dateValue = computeDateValue.value
const yearDatas = computeYearDatas.value
const matchFormat = 'yyyy'
return [
h('table', {
class: `vxe-input--date-${datePanelType}-view`,
cellspacing: 0,
cellpadding: 0,
border: 0
}, [
h('tbody', yearDatas.map((rows) => {
return h('tr', rows.map((item) => {
return h('td', {
class: {
'is--disabled': isDateDisabled(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)
},
onClick: () => dateSelectEvent(item),
onMouseenter: () => dateMouseenterEvent(item)
}, renderDateLabel(item, item.year))
}))
}))
])
]
}
const renderDateTable = () => {
const { datePanelType } = reactData
switch (datePanelType) {
case 'week' :
return renderDateWeekTable()
case 'month' :
return renderDateMonthTable()
case 'quarter' :
return renderDateQuarterTable()
case 'year' :
return renderDateYearTable()
}
return renderDateDayTable()
}
const renderDatePanel = () => {
const { datePanelType } = reactData
const isDisabledPrevDateBtn = computeIsDisabledPrevDateBtn.value
const isDisabledNextDateBtn = computeIsDisabledNextDateBtn.value
const selectDatePanelLabel = computeSelectDatePanelLabel.value
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',
onClick: 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
}],
onClick: datePrevEvent
}, [
h('i', {
class: 'vxe-icon--caret-left'
})
]),
h('span', {
class: 'vxe-input--date-picker-btn vxe-input--date-picker-current-btn',
onClick: dateTodayMonthEvent
}, [
h('i', {
class: 'vxe-icon--dot'
})
]),
h('span', {
class: ['vxe-input--date-picker-btn vxe-input--date-picker-next-btn', {
'is--disabled': isDisabledNextDateBtn
}],
onClick: dateNextEvent
}, [
h('i', {
class: 'vxe-icon--caret-right'
})
])
])
]),
h('div', {
class: 'vxe-input--date-picker-body'
}, renderDateTable())
]
}
const renderTimePanel = () => {
const { datetimePanelValue } = reactData
const dateTimeLabel = computeDateTimeLabel.value
const hourList = computeHourList.value
const minuteList = computeMinuteList.value
const secondList = computeSecondList.value
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',
type: 'button',
onClick: dateConfirmEvent
}, GlobalConfig.i18n('vxe.button.confirm'))
]),
h('div', {
ref: refInputTimeBody,
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
},
onClick: (evnt: MouseEvent) => 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
},
onClick: (evnt: MouseEvent) => 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
},
onClick: (evnt: MouseEvent) => dateSecondEvent(evnt, item)
}, item.label)
}))
])
]
}
const renderPanel = () => {
const { type, transfer } = props
const { inited, animatVisible, visiblePanel, panelPlacement, panelStyle } = reactData
const vSize = computeSize.value
const isDatePickerType = computeIsDatePickerType.value
const renders = []
if (isDatePickerType) {
if (type === 'datetime') {
renders.push(
h('div', {
class: 'vxe-input--panel-layout-wrapper'
}, [
h('div', {
class: 'vxe-input--panel-left-wrapper'
}, renderDatePanel()),
h('div', {
class: 'vxe-input--panel-right-wrapper'
}, renderTimePanel())
])
)
} else if (type === 'time') {
renders.push(
h('div', {
class: 'vxe-input--panel-wrapper'
}, renderTimePanel())
)
} else {
renders.push(
h('div', {
class: 'vxe-input--panel-wrapper'
}, renderDatePanel())
)
}
return h(Teleport, {
to: 'body',
disabled: transfer ? !inited : true
}, [
h('div', {
ref: refInputPanel,
class: ['vxe-table--ignore-clear vxe-input--panel', `type--${type}`, {
[`size--${vSize}`]: vSize,
'is--transfer': transfer,
'animat--leave': animatVisible,
'animat--enter': visiblePanel
}],
placement: panelPlacement,
style: panelStyle
}, renders)
])
}
return null
}
const renderNumberIcon = () => {
return h('span', {
class: 'vxe-input--number-suffix'
}, [
h('span', {
class: 'vxe-input--number-prev is--prev',
onMousedown: numberMousedownEvent,
onMouseup: numberStopDown,
onMouseleave: numberStopDown
}, [
h('i', {
class: ['vxe-input--number-prev-icon', GlobalConfig.icon.INPUT_PREV_NUM]
})
]),
h('span', {
class: 'vxe-input--number-next is--next',
onMousedown: numberMousedownEvent,
onMouseup: numberStopDown,
onMouseleave: numberStopDown
}, [
h('i', {
class: ['vxe-input--number-next-icon', GlobalConfig.icon.INPUT_NEXT_NUM]
})
])
])
}
const renderDatePickerIcon = () => {
return h('span', {
class: 'vxe-input--date-picker-suffix',
onClick: datePickerOpenEvent
}, [
h('i', {
class: ['vxe-input--date-picker-icon', GlobalConfig.icon.INPUT_DATE]
})
])
}
const renderSearchIcon = () => {
return h('span', {
class: 'vxe-input--search-suffix',
onClick: searchEvent
}, [
h('i', {
class: ['vxe-input--search-icon', GlobalConfig.icon.INPUT_SEARCH]
})
])
}
const renderPasswordIcon = () => {
const { showPwd } = reactData
return h('span', {
class: 'vxe-input--password-suffix',
onClick: passwordToggleEvent
}, [
h('i', {
class: ['vxe-input--password-icon', showPwd ? GlobalConfig.icon.INPUT_SHOW_PWD : GlobalConfig.icon.INPUT_PWD]
})
])
}
const rendePrefixIcon = () => {
const { prefixIcon } = props
const prefixSlot = slots.prefix
const icons = []
if (prefixSlot) {
icons.push(
h('span', {
class: 'vxe-input--prefix-icon'
}, prefixSlot({}))
)
} else if (prefixIcon) {
icons.push(
h('i', {
class: ['vxe-input--prefix-icon', prefixIcon]
})
)
}
return icons.length ? h('span', {
class: 'vxe-input--prefix',
onClick: clickPrefixEvent
}, icons) : null
}
const renderSuffixIcon = () => {
const { disabled, suffixIcon } = props
const { inputValue } = reactData
const suffixSlot = slots.suffix
const isClearable = computeIsClearable.value
const icons = []
if (suffixSlot) {
icons.push(
h('span', {
class: 'vxe-input--suffix-icon'
}, suffixSlot({}))
)
} 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 && !(inputValue === '' || XEUtils.eqNull(inputValue))
}],
onClick: clickSuffixEvent
}, icons) : null
}
const renderExtraSuffixIcon = () => {
const { controls } = props
const isNumType = computeIsNumType.value
const isDatePickerType = computeIsDatePickerType.value
const isPawdType = computeIsPawdType.value
const isSearchType = computeIsSearchType.value
let icons
if (isPawdType) {
icons = renderPasswordIcon()
} else if (isNumType) {
if (controls) {
icons = renderNumberIcon()
}
} else if (isDatePickerType) {
icons = renderDatePickerIcon()
} else if (isSearchType) {
icons = renderSearchIcon()
}
return icons ? h('span', {
class: 'vxe-input--extra-suffix'
}, [icons]) : null
}
inputMethods = {
dispatchEvent (type, params, evnt) {
emit(type, Object.assign({ $input: $xeinput, $event: evnt }, params))
},
focus () {
const inputElem = refInputTarget.value
reactData.isActivated = true
inputElem.focus()
return nextTick()
},
blur () {
const inputElem = refInputTarget.value
inputElem.blur()
reactData.isActivated = false
return nextTick()
}
}
Object.assign($xeinput, inputMethods)
watch(() => props.modelValue, (val) => {
reactData.inputValue = val
changeValue()
})
watch(computeDateLabelFormat, () => {
dateParseValue(reactData.datePanelValue)
reactData.inputValue = reactData.datePanelLabel
})
nextTick(() => {
GlobalEvent.on($xeinput, 'mousewheel', handleGlobalMousewheelEvent)
GlobalEvent.on($xeinput, 'mousedown', handleGlobalMousedownEvent)
GlobalEvent.on($xeinput, 'keydown', handleGlobalKeydownEvent)
GlobalEvent.on($xeinput, 'blur', handleGlobalBlurEvent)
})
onUnmounted(() => {
numberStopDown()
GlobalEvent.off($xeinput, 'mousewheel')
GlobalEvent.off($xeinput, 'mousedown')
GlobalEvent.off($xeinput, 'keydown')
GlobalEvent.off($xeinput, 'blur')
})
initValue()
const renderVN = () => {
const { className, controls, type, align, name, disabled, readonly, autocomplete } = props
const { inputValue, visiblePanel, isActivated } = reactData
const vSize = computeSize.value
const isDatePickerType = computeIsDatePickerType.value
const inpReadonly = computeInpReadonly.value
const inpMaxlength = computeInpMaxlength.value
const inputType = computeInputType.value
const inpPlaceholder = computeInpPlaceholder.value
const childs = []
const prefix = rendePrefixIcon()
const suffix = renderSuffixIcon()
// 前缀图标
if (prefix) {
childs.push(prefix)
}
// 输入框
childs.push(
h('input', {
ref: refInputTarget,
class: 'vxe-input--inner',
value: inputValue,
name,
type: inputType,
placeholder: inpPlaceholder,
maxlength: inpMaxlength,
readonly: inpReadonly,
disabled,
autocomplete,
onKeydown: keydownEvent,
onKeyup: keyupEvent,
onWheel: wheelEvent,
onClick: clickEvent,
onInput: inputEvent,
onChange: changeEvent,
onFocus: focusEvent,
onBlur: blurEvent
})
)
// 后缀图标
if (suffix) {
childs.push(suffix)
}
// 特殊功能图标
childs.push(renderExtraSuffixIcon())
// 面板容器
if (isDatePickerType) {
childs.push(renderPanel())
}
return h('div', {
ref: refElem,
class: ['vxe-input', `type--${type}`, className, {
[`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)
}
$xeinput.renderVN = renderVN
return $xeinput
},
render () {
return this.renderVN()
}
})