| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- <!--
- 通用倒计时组件
- 用途:团购详情页活动倒计时、拼团剩余时间等
- 入参 endTime 为秒级 unix;结束后触发 ended 事件
- 剩余 ≥24h 时拆出「天」单位,小时限制在 0–23(与首页活动专区一致)
- -->
- <template>
- <view v-if="visible" class="count-down" :class="theme">
- <text v-if="label" class="cd-label">{{ label }}</text>
- <view class="cd-blocks">
- <!-- 剩余超过 24 小时时展示「天」单位,避免小时数无限增大 -->
- <template v-if="display.showDay">
- <text class="cd-unit">{{ display.d }}</text>
- <text class="cd-day">天</text>
- </template>
- <text class="cd-unit">{{ display.h }}</text>
- <text class="cd-colon">:</text>
- <text class="cd-unit">{{ display.m }}</text>
- <text class="cd-colon">:</text>
- <text class="cd-unit">{{ display.s }}</text>
- </view>
- </view>
- </template>
- <script>
- export default {
- name: 'CountDown',
- props: {
- /** 结束时间(秒级 unix 时间戳) */
- endTime: { type: [Number, String], default: 0 },
- /** 文案前缀,如「距结束」 */
- label: { type: String, default: '' },
- /** 主题:default | pink */
- theme: { type: String, default: 'default' }
- },
- data() {
- return {
- visible: false,
- // showDay:剩余 ≥24h 时展示天;d/h/m/s 为补零后的展示字符串
- display: { showDay: false, d: '00', h: '00', m: '00', s: '00' },
- _timer: null
- }
- },
- watch: {
- endTime: {
- immediate: true,
- handler() {
- this.restart()
- }
- }
- },
- beforeDestroy() {
- this.clearTimer()
- },
- methods: {
- clearTimer() {
- if (this._timer) {
- clearInterval(this._timer)
- this._timer = null
- }
- },
- restart() {
- this.clearTimer()
- this.tick()
- this._timer = setInterval(this.tick, 1000)
- },
- /**
- * 按 endTime 刷新展示;剩余 ≥24h 拆出天单位,小时为当天剩余 0–23
- */
- tick() {
- const end = Number(this.endTime) || 0
- const now = Math.floor(Date.now() / 1000)
- if (end <= 0 || now >= end) {
- this.visible = false
- this.display = { showDay: false, d: '00', h: '00', m: '00', s: '00' }
- this.clearTimer()
- this.$emit('ended')
- return
- }
- this.visible = true
- let diff = end - now
- if (diff < 0) diff = 0
- const d = Math.floor(diff / 86400)
- const h = Math.floor((diff % 86400) / 3600)
- const m = Math.floor((diff % 3600) / 60)
- const s = diff % 60
- const pad = (n) => String(n).padStart(2, '0')
- this.display = {
- showDay: d > 0,
- d: pad(d),
- h: pad(h),
- m: pad(m),
- s: pad(s)
- }
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .count-down {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .cd-label {
- font-size: 22upx;
- color: #999;
- margin-right: 8upx;
- }
- .cd-blocks {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .cd-unit {
- min-width: 40upx;
- height: 36upx;
- line-height: 36upx;
- text-align: center;
- font-size: 22upx;
- color: #fff;
- background: #ff4d6d;
- border-radius: 6upx;
- padding: 0 6upx;
- }
- .cd-colon {
- margin: 0 4upx;
- color: #ff4d6d;
- font-size: 22upx;
- font-weight: 600;
- }
- .cd-day {
- margin: 0 6upx 0 4upx;
- font-size: 22upx;
- color: #ff4d6d;
- font-weight: 600;
- }
- .pink {
- .cd-unit {
- background: #ff6b8a;
- }
- .cd-day,
- .cd-colon {
- color: #ff6b8a;
- }
- }
- </style>
|