| .. | .. |
|---|
| 20 | 20 | <view v-else> |
|---|
| 21 | 21 | <view class="expireTime" v-if="expireTime && expireTime.length"> |
|---|
| 22 | 22 | <text>专区将在</text> |
|---|
| 23 | | - <text v-for="(val, index) in expireTime" :key="index"> |
|---|
| 24 | | - <text class="num">{{ val }}</text>{{ expireTimeUnit[index] }} |
|---|
| 23 | + <text v-for="(item, index) in expireTime" :key="index"> |
|---|
| 24 | + <text class="num"><text v-for="(char, ci) in item.digits" :key="ci" |
|---|
| 25 | + class="digit">{{ char }}</text></text>{{ item.unit }} |
|---|
| 25 | 26 | </text> |
|---|
| 26 | 27 | <text>{{ startOrEnd }}</text> |
|---|
| 27 | 28 | </view> |
|---|
| .. | .. |
|---|
| 606 | 607 | showMenu: false, |
|---|
| 607 | 608 | selectedMenuArr: [], |
|---|
| 608 | 609 | expireTime: [], |
|---|
| 609 | | - expireTimeUnit: [], |
|---|
| 610 | + countdownToken: 0, |
|---|
| 610 | 611 | isPageShow: false, |
|---|
| 611 | 612 | loginBackFn: null, |
|---|
| 612 | 613 | errText: '', |
|---|
| .. | .. |
|---|
| 1326 | 1327 | }) |
|---|
| 1327 | 1328 | }, |
|---|
| 1328 | 1329 | validAlert(data) { |
|---|
| 1330 | + // 每次重新计算前作废旧的倒计时链,避免多条定时器同时刷新导致数字跳动 |
|---|
| 1331 | + this.countdownToken++ |
|---|
| 1332 | + const token = this.countdownToken |
|---|
| 1329 | 1333 | const { |
|---|
| 1330 | 1334 | startTime, |
|---|
| 1331 | 1335 | expireTime |
|---|
| .. | .. |
|---|
| 1344 | 1348 | let minutes = startTimetamp.getMinutes() |
|---|
| 1345 | 1349 | minutes = minutes < 10 ? '0' + minutes : minutes; |
|---|
| 1346 | 1350 | |
|---|
| 1347 | | - this.countdownTimer(startTime.split('-').join('/'), 'start') |
|---|
| 1351 | + this.countdownTimer(startTime.split('-').join('/'), 'start', token) |
|---|
| 1348 | 1352 | this.startOrEnd = "后开启" |
|---|
| 1349 | 1353 | return |
|---|
| 1350 | | - } |
|---|
| 1351 | | - } |
|---|
| 1352 | | - if (expireTime) { |
|---|
| 1353 | | - const expireTimetamp = new Date(expireTime.split('-').join('/')).getTime(); |
|---|
| 1354 | | - if (expireTimetamp < nowTimestamp) { |
|---|
| 1354 | + } |
|---|
| 1355 | + } |
|---|
| 1356 | + if (expireTime) { |
|---|
| 1357 | + const expireTimetamp = new Date(expireTime.split('-').join('/')).getTime(); |
|---|
| 1358 | + if (expireTimetamp < nowTimestamp) { |
|---|
| 1355 | 1359 | return |
|---|
| 1356 | | - } |
|---|
| 1357 | | - this.countdownTimer(expireTime.split('-').join('/'), 'end') |
|---|
| 1360 | + } |
|---|
| 1361 | + this.countdownTimer(expireTime.split('-').join('/'), 'end', token) |
|---|
| 1358 | 1362 | this.startOrEnd = "后结束" |
|---|
| 1359 | 1363 | } |
|---|
| 1360 | 1364 | }, |
|---|
| .. | .. |
|---|
| 1493 | 1497 | that.homeTop = that.getHomeTop(0.4) |
|---|
| 1494 | 1498 | } |
|---|
| 1495 | 1499 | that.expireTime = []; |
|---|
| 1496 | | - that.expireTimeUnit = []; |
|---|
| 1500 | + that.countdownToken++; |
|---|
| 1497 | 1501 | that.where.page = 1; |
|---|
| 1498 | 1502 | that.getGoodList() |
|---|
| 1499 | 1503 | that.validAlert(res.data) |
|---|
| .. | .. |
|---|
| 1519 | 1523 | } |
|---|
| 1520 | 1524 | }); |
|---|
| 1521 | 1525 | }, |
|---|
| 1522 | | - countdownTimer(expireTime, type) { |
|---|
| 1526 | + countdownTimer(expireTime, type, token) { |
|---|
| 1523 | 1527 | if (!expireTime) return |
|---|
| 1528 | + if (token !== undefined && token !== this.countdownToken) return |
|---|
| 1524 | 1529 | let that = this; |
|---|
| 1525 | 1530 | // 结束时间的时间戳(需传入一个有效的日期对象或时间戳) |
|---|
| 1526 | 1531 | const endTimestamp = new Date(expireTime).getTime(); |
|---|
| .. | .. |
|---|
| 1532 | 1537 | if (type == 'end') { |
|---|
| 1533 | 1538 | if (diff <= 0) { |
|---|
| 1534 | 1539 | this.expireTime = [] |
|---|
| 1535 | | - this.expireTimeUnit = [] |
|---|
| 1536 | 1540 | return |
|---|
| 1537 | 1541 | } else { } |
|---|
| 1538 | 1542 | } else if (type == 'start') { |
|---|
| 1539 | 1543 | if (diff <= 0) { |
|---|
| 1540 | 1544 | this.expireTime = [] |
|---|
| 1541 | | - this.expireTimeUnit = [] |
|---|
| 1542 | 1545 | const expireTime2 = that.info.expireTime |
|---|
| 1543 | 1546 | if (expireTime2) { |
|---|
| 1544 | 1547 | const expireTimetamp = new Date(expireTime2.split('-').join('/')).getTime(); |
|---|
| 1545 | 1548 | if (expireTimetamp < nowTimestamp) { |
|---|
| 1546 | 1549 | return |
|---|
| 1547 | 1550 | } |
|---|
| 1548 | | - this.countdownTimer(expireTime2.split('-').join('/'), 'end') |
|---|
| 1551 | + this.countdownTimer(expireTime2.split('-').join('/'), 'end', token) |
|---|
| 1549 | 1552 | this.startOrEnd = "后结束" |
|---|
| 1550 | 1553 | } |
|---|
| 1551 | 1554 | return |
|---|
| 1552 | 1555 | } |
|---|
| 1553 | 1556 | } |
|---|
| 1557 | + const pad = num => (num < 10 ? '0' + num : String(num)) |
|---|
| 1554 | 1558 | const res = [] |
|---|
| 1555 | | - const unit = [] |
|---|
| 1556 | 1559 | const days = Math.floor(diff / (1000 * 60 * 60 * 24)) |
|---|
| 1557 | 1560 | diff %= (1000 * 60 * 60 * 24) |
|---|
| 1558 | | - if (days > 0) { |
|---|
| 1559 | | - res.push(days); |
|---|
| 1560 | | - unit.push('日') |
|---|
| 1561 | | - } |
|---|
| 1562 | 1561 | // 转换为小时、分钟、秒 |
|---|
| 1563 | 1562 | const hours = Math.floor(diff / (1000 * 60 * 60)); |
|---|
| 1564 | 1563 | diff %= (1000 * 60 * 60); // 剩余毫秒数 |
|---|
| 1565 | | - if (hours > 0) { |
|---|
| 1566 | | - res.push(hours < 10 ? '0' + hours : hours); |
|---|
| 1567 | | - unit.push('时') |
|---|
| 1568 | | - } |
|---|
| 1569 | 1564 | const minutes = Math.floor(diff / (1000 * 60)); |
|---|
| 1570 | | - if (minutes > 0) { |
|---|
| 1571 | | - res.push(minutes < 10 ? '0' + minutes : minutes); |
|---|
| 1572 | | - unit.push('分') |
|---|
| 1573 | | - } |
|---|
| 1574 | 1565 | diff %= (1000 * 60); // 剩余毫秒数 |
|---|
| 1575 | 1566 | const seconds = Math.floor(diff / 1000); |
|---|
| 1576 | | - if (seconds > 0) { |
|---|
| 1577 | | - res.push(seconds < 10 ? '0' + seconds : seconds); |
|---|
| 1578 | | - unit.push('秒') |
|---|
| 1567 | + // 天数为 0 时不展示,时/分/秒始终展示(补 0),保证倒计时长度稳定 |
|---|
| 1568 | + if (days > 0) { |
|---|
| 1569 | + res.push({ |
|---|
| 1570 | + digits: pad(days).split(''), |
|---|
| 1571 | + unit: '日' |
|---|
| 1572 | + }) |
|---|
| 1579 | 1573 | } |
|---|
| 1574 | + res.push({ |
|---|
| 1575 | + digits: pad(hours).split(''), |
|---|
| 1576 | + unit: '时' |
|---|
| 1577 | + }) |
|---|
| 1578 | + res.push({ |
|---|
| 1579 | + digits: pad(minutes).split(''), |
|---|
| 1580 | + unit: '分' |
|---|
| 1581 | + }) |
|---|
| 1582 | + res.push({ |
|---|
| 1583 | + digits: pad(seconds).split(''), |
|---|
| 1584 | + unit: '秒' |
|---|
| 1585 | + }) |
|---|
| 1580 | 1586 | this.expireTime = res |
|---|
| 1581 | | - this.expireTimeUnit = unit |
|---|
| 1582 | 1587 | if (this.isPageShow) { |
|---|
| 1583 | 1588 | setTimeout(() => { |
|---|
| 1584 | | - that.countdownTimer(expireTime, type) |
|---|
| 1589 | + that.countdownTimer(expireTime, type, token) |
|---|
| 1585 | 1590 | }, 1000) |
|---|
| 1586 | 1591 | } |
|---|
| 1587 | 1592 | }, |
|---|
| .. | .. |
|---|
| 2156 | 2161 | padding: 15rpx; |
|---|
| 2157 | 2162 | padding-left: 30rpx; |
|---|
| 2158 | 2163 | color: #979797; |
|---|
| 2164 | + font-size: 26rpx; |
|---|
| 2159 | 2165 | |
|---|
| 2160 | 2166 | .num { |
|---|
| 2167 | + display: inline-block; |
|---|
| 2161 | 2168 | color: #000000; |
|---|
| 2162 | 2169 | background: #FFE20E; |
|---|
| 2163 | | - padding: 0 10rpx; |
|---|
| 2170 | + padding: 0 6rpx; |
|---|
| 2171 | + margin: 0 4rpx; |
|---|
| 2172 | + vertical-align: middle; |
|---|
| 2173 | + } |
|---|
| 2174 | + |
|---|
| 2175 | + /* 每个数字固定宽度,避免倒计时跳动时宽度忽长忽短 */ |
|---|
| 2176 | + .digit { |
|---|
| 2177 | + display: inline-block; |
|---|
| 2178 | + width: 19rpx; |
|---|
| 2179 | + text-align: center; |
|---|
| 2164 | 2180 | } |
|---|
| 2165 | 2181 | } |
|---|
| 2166 | 2182 | </style> |
|---|