UNPKG

vxe-pc-ui

Version:
261 lines (226 loc) • 7.74 kB
import { defineComponent, ref, h, reactive, watch, computed, PropType, onUnmounted, onMounted, createCommentVNode } from 'vue' import { getConfig, getI18n, createEvent, useSize } from '../../ui' import { getSlotVNs } from '../../ui/src/vn' import VxeTextComponent from '../../text/src/text' import XEUtils from 'xe-utils' import type { VxeCountdownPropTypes, CountdownReactData, CountdownInternalData, CountdownPrivateRef, VxeCountdownEmits, VxeCountdownPrivateComputed, VxeCountdownConstructor, VxeCountdownPrivateMethods, ValueOf, CountdownMethods, CountdownPrivateMethods } from '../../../types' export default defineComponent({ name: 'VxeCountdown', props: { modelValue: [Number, String] as PropType<VxeCountdownPropTypes.ModelValue>, format: String as PropType<VxeCountdownPropTypes.Format>, prefixConfig: Object as PropType<VxeCountdownPropTypes.PrefixConfig>, suffixConfig: Object as PropType<VxeCountdownPropTypes.SuffixConfig>, size: { type: String as PropType<VxeCountdownPropTypes.Size>, default: () => getConfig().countdown.size || getConfig().size } }, emits: [ 'update:modelValue', 'start', 'end' ] as VxeCountdownEmits, setup (props, context) { const { slots, emit } = context const xID = XEUtils.uniqueId() const refElem = ref<HTMLDivElement>() const { computeSize } = useSize(props) const reactData = reactive<CountdownReactData>({ currNum: 0, secondNum: 0 }) const internalData: CountdownInternalData = { dnTimeout: undefined } const refMaps: CountdownPrivateRef = { refElem } const computeTimeFormats = computed<Array<'yyyy' | 'MM' | 'dd' | 'HH' | 'mm' | 'ss'>>(() => { const { secondNum } = reactData if (secondNum >= 31622400000) { return ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss'] } if (secondNum >= 2678400000) { return ['MM', 'dd', 'HH', 'mm', 'ss'] } if (secondNum >= 86400000) { return ['dd', 'HH', 'mm', 'ss'] } if (secondNum >= 3600000) { return ['HH', 'mm', 'ss'] } if (secondNum >= 60000) { return ['mm', 'ss'] } return ['ss'] }) const computeDiffConf = computed(() => { const { currNum } = reactData return XEUtils.getDateDiff(Date.now(), Date.now() + currNum) }) const computeFormatLabel = computed(() => { const { format } = props const diffConf = computeDiffConf.value let rest = '' if (format) { rest = `${format}` XEUtils.each(diffConf, (val, key) => { rest = rest.replace(new RegExp(key, 'g'), XEUtils.padStart(val, key.length, '0')) }) return rest } return rest }) const computePrefixOpts = computed(() => { return Object.assign({}, props.prefixConfig, getConfig().countdown.prefixConfig) }) const computeSuffixOpts = computed(() => { return Object.assign({}, props.suffixConfig, getConfig().countdown.suffixConfig) }) const computeMaps: VxeCountdownPrivateComputed = { computeSize } const $xeCountdown = { xID, props, context, reactData, internalData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps } as unknown as VxeCountdownConstructor & VxeCountdownPrivateMethods const dispatchEvent = (type: ValueOf<VxeCountdownEmits>, params: Record<string, any>, evnt: Event | null) => { emit(type, createEvent(evnt, { $carousel: $xeCountdown }, params)) } const updateCount = () => { const secondNum = XEUtils.toNumber(props.modelValue || 0) reactData.secondNum = secondNum reactData.currNum = secondNum } const handleTime = () => { const { currNum } = reactData if (currNum > 1000) { reactData.currNum -= 1000 internalData.dnTimeout = setTimeout(() => { handleTime() }, 1000) } else { reactData.currNum = 0 handleStop() } } const countdownMethods: CountdownMethods = { dispatchEvent } const handleStart = () => { dispatchEvent('start', {}, null) handleTime() } const handleStop = () => { const { dnTimeout } = internalData if (dnTimeout) { clearTimeout(dnTimeout) internalData.dnTimeout = undefined dispatchEvent('end', {}, null) } } const countdownPrivateMethods: CountdownPrivateMethods = { } Object.assign($xeCountdown, countdownMethods, countdownPrivateMethods) const renderDefaultContentVNs = () => { const { format } = props const timeFormats = computeTimeFormats.value const diffConf = computeDiffConf.value const formatLabel = computeFormatLabel.value if (format) { return [ h('div', { key: 'format', class: 'vxe-countdown--content-format' }, formatLabel) ] } return timeFormats.map((key, index) => { return h('div', { key: index, class: 'vxe-countdown--content-item' }, [ h('div', { class: 'vxe-countdown--content-num' }, `${diffConf[key] || 0}`), h('div', { class: 'vxe-countdown--content-unit' }, getI18n(`vxe.countdown.formats.${key}`)) ]) }) } const renderVN = () => { const { prefixConfig, suffixConfig } = props const { currNum } = reactData const vSize = computeSize.value const diffConf = computeDiffConf.value const prefixOpts = computePrefixOpts.value const suffixOpts = computeSuffixOpts.value const prefixSlot = slots.prefix const suffixSlot = slots.suffix const defaultSlot = slots.default return h('div', { ref: refElem, class: ['vxe-countdown', diffConf.done ? 'is--progress' : 'is-end', { [`size--${vSize}`]: vSize }] }, [ prefixSlot || prefixConfig ? h('div', { class: 'vxe-countdown--prefix' }, prefixSlot ? getSlotVNs(prefixSlot({ currentValue: currNum, diffConf })) : [ h(VxeTextComponent, { content: prefixOpts.content, icon: prefixOpts.icon, status: prefixOpts.status }) ]) : createCommentVNode(), h('div', { class: 'vxe-countdown--content' }, defaultSlot ? getSlotVNs(defaultSlot({ currentValue: currNum, diffConf })) : renderDefaultContentVNs() ), suffixSlot || suffixConfig ? h('div', { class: 'vxe-countdown--suffix' }, suffixSlot ? getSlotVNs(suffixSlot({ currentValue: currNum, diffConf })) : [ h(VxeTextComponent, { content: suffixOpts.content, icon: suffixOpts.icon, status: suffixOpts.status }) ]) : createCommentVNode() ]) } watch(() => props.modelValue, () => { updateCount() handleStop() handleStart() }) onUnmounted(() => { handleStop() }) onMounted(() => { handleStart() }) updateCount() $xeCountdown.renderVN = renderVN return $xeCountdown }, render () { return this.renderVN() } })