vxe-pc-ui
Version:
A vue based PC component library
261 lines (226 loc) • 7.74 kB
text/typescript
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()
}
})