| .. | .. |
|---|
| 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> |
|---|
| .. | .. |
|---|
| 70 | 71 | </view> |
|---|
| 71 | 72 | </view> |
|---|
| 72 | 73 | </view> |
|---|
| 73 | | - <view @click="goLottery" class="lottery_banner" v-if="info && info.isLottery"> |
|---|
| 74 | | - <image class="lottery_bannerImg" |
|---|
| 75 | | - src="https://ossacmall.bluedeer.com.cn/app/static/images/zone_lotteryBanner.gif" mode="widthFix"> |
|---|
| 74 | + <view @click="goLottery" class="lottery_banner" |
|---|
| 75 | + v-if="info && info.isLottery && info.lotteryBanner && lotteryBannerReady"> |
|---|
| 76 | + <image class="lottery_bannerImg" :src="info.lotteryBanner" mode="widthFix" |
|---|
| 77 | + :style="{ height: lotteryBannerHeight }"> |
|---|
| 76 | 78 | </image> |
|---|
| 77 | 79 | </view> |
|---|
| 78 | 80 | <view class="tag-img-container" v-if="info.zoneType == 7"> |
|---|
| .. | .. |
|---|
| 589 | 591 | showMenu: false, |
|---|
| 590 | 592 | selectedMenuArr: [], |
|---|
| 591 | 593 | expireTime: [], |
|---|
| 592 | | - expireTimeUnit: [], |
|---|
| 594 | + countdownToken: 0, |
|---|
| 593 | 595 | isPageShow: false, |
|---|
| 594 | 596 | loginBackFn: null, |
|---|
| 595 | 597 | errText: '', |
|---|
| .. | .. |
|---|
| 622 | 624 | stockEmpty: true, |
|---|
| 623 | 625 | urlOption: {}, |
|---|
| 624 | 626 | homeTop: '200', |
|---|
| 627 | + lotteryBannerHeight: '', //抽奖banner预计算高度(rpx),空字符串表示未取到,按原逻辑自适应 |
|---|
| 628 | + lotteryBannerReady: false, //是否已拿到banner尺寸(或已确认拿不到),未就绪前不渲染,避免首帧撑高 |
|---|
| 625 | 629 | hasNewPop: false, |
|---|
| 626 | 630 | leftCoupon: {}, |
|---|
| 627 | 631 | rightCoupon: {}, |
|---|
| .. | .. |
|---|
| 1296 | 1300 | }) |
|---|
| 1297 | 1301 | }, |
|---|
| 1298 | 1302 | validAlert(data) { |
|---|
| 1303 | + // 每次重新计算前作废旧的倒计时链,避免多条定时器同时刷新导致数字跳动 |
|---|
| 1304 | + this.countdownToken++ |
|---|
| 1305 | + const token = this.countdownToken |
|---|
| 1299 | 1306 | const { |
|---|
| 1300 | 1307 | startTime, |
|---|
| 1301 | 1308 | expireTime |
|---|
| .. | .. |
|---|
| 1314 | 1321 | let minutes = startTimetamp.getMinutes() |
|---|
| 1315 | 1322 | minutes = minutes < 10 ? '0' + minutes : minutes; |
|---|
| 1316 | 1323 | |
|---|
| 1317 | | - this.countdownTimer(startTime.split('-').join('/'), 'start') |
|---|
| 1324 | + this.countdownTimer(startTime.split('-').join('/'), 'start', token) |
|---|
| 1318 | 1325 | this.startOrEnd = "后开启" |
|---|
| 1319 | 1326 | return |
|---|
| 1320 | | - } |
|---|
| 1321 | | - } |
|---|
| 1322 | | - if (expireTime) { |
|---|
| 1323 | | - const expireTimetamp = new Date(expireTime.split('-').join('/')).getTime(); |
|---|
| 1324 | | - if (expireTimetamp < nowTimestamp) { |
|---|
| 1327 | + } |
|---|
| 1328 | + } |
|---|
| 1329 | + if (expireTime) { |
|---|
| 1330 | + const expireTimetamp = new Date(expireTime.split('-').join('/')).getTime(); |
|---|
| 1331 | + if (expireTimetamp < nowTimestamp) { |
|---|
| 1325 | 1332 | return |
|---|
| 1326 | | - } |
|---|
| 1327 | | - this.countdownTimer(expireTime.split('-').join('/'), 'end') |
|---|
| 1333 | + } |
|---|
| 1334 | + this.countdownTimer(expireTime.split('-').join('/'), 'end', token) |
|---|
| 1328 | 1335 | this.startOrEnd = "后结束" |
|---|
| 1329 | 1336 | } |
|---|
| 1330 | 1337 | }, |
|---|
| .. | .. |
|---|
| 1446 | 1453 | this.$refs.popNewuserRef.show() |
|---|
| 1447 | 1454 | } |
|---|
| 1448 | 1455 | }, |
|---|
| 1456 | + // 预取抽奖banner真实宽高,计算出高度后再渲染,避免加载前被组件默认高度(225px)撑大 |
|---|
| 1457 | + setLotteryBannerSize(info) { |
|---|
| 1458 | + let that = this |
|---|
| 1459 | + that.lotteryBannerHeight = '' |
|---|
| 1460 | + that.lotteryBannerReady = false |
|---|
| 1461 | + if (!info || !info.isLottery || !info.lotteryBanner) { |
|---|
| 1462 | + that.lotteryBannerReady = true |
|---|
| 1463 | + return |
|---|
| 1464 | + } |
|---|
| 1465 | + uni.getImageInfo({ |
|---|
| 1466 | + src: info.lotteryBanner, |
|---|
| 1467 | + success: (img) => { |
|---|
| 1468 | + if (img.width > 0 && img.height > 0) { |
|---|
| 1469 | + that.lotteryBannerHeight = (690 * img.height / img.width).toFixed(2) + 'rpx' |
|---|
| 1470 | + } |
|---|
| 1471 | + that.lotteryBannerReady = true |
|---|
| 1472 | + }, |
|---|
| 1473 | + fail: (err) => { |
|---|
| 1474 | + console.warn('抽奖banner尺寸预取失败,回退为自适应', err) |
|---|
| 1475 | + that.lotteryBannerReady = true |
|---|
| 1476 | + } |
|---|
| 1477 | + }) |
|---|
| 1478 | + }, |
|---|
| 1449 | 1479 | getZoneDetail: function () { |
|---|
| 1450 | 1480 | let that = this; |
|---|
| 1451 | 1481 | let belong = this.$store.getters.belong; |
|---|
| .. | .. |
|---|
| 1453 | 1483 | console.log('专区详情入参::', params) |
|---|
| 1454 | 1484 | getZoneDetail(that.id, params).then(res => { |
|---|
| 1455 | 1485 | that.info = res.data; |
|---|
| 1486 | + //that.info.zoneType=8; |
|---|
| 1487 | + that.setLotteryBannerSize(res.data); |
|---|
| 1456 | 1488 | that.updateZoneShareMenu(); |
|---|
| 1457 | 1489 | if (res.data.zoneType == 6) { |
|---|
| 1458 | 1490 | that.setYueyangZone() |
|---|
| .. | .. |
|---|
| 1462 | 1494 | that.homeTop = that.getHomeTop(0.4) |
|---|
| 1463 | 1495 | } |
|---|
| 1464 | 1496 | that.expireTime = []; |
|---|
| 1465 | | - that.expireTimeUnit = []; |
|---|
| 1497 | + that.countdownToken++; |
|---|
| 1466 | 1498 | that.where.page = 1; |
|---|
| 1467 | 1499 | that.getGoodList() |
|---|
| 1468 | 1500 | that.validAlert(res.data) |
|---|
| .. | .. |
|---|
| 1488 | 1520 | } |
|---|
| 1489 | 1521 | }); |
|---|
| 1490 | 1522 | }, |
|---|
| 1491 | | - countdownTimer(expireTime, type) { |
|---|
| 1523 | + countdownTimer(expireTime, type, token) { |
|---|
| 1492 | 1524 | if (!expireTime) return |
|---|
| 1525 | + if (token !== undefined && token !== this.countdownToken) return |
|---|
| 1493 | 1526 | let that = this; |
|---|
| 1494 | 1527 | // 结束时间的时间戳(需传入一个有效的日期对象或时间戳) |
|---|
| 1495 | 1528 | const endTimestamp = new Date(expireTime).getTime(); |
|---|
| .. | .. |
|---|
| 1501 | 1534 | if (type == 'end') { |
|---|
| 1502 | 1535 | if (diff <= 0) { |
|---|
| 1503 | 1536 | this.expireTime = [] |
|---|
| 1504 | | - this.expireTimeUnit = [] |
|---|
| 1505 | 1537 | return |
|---|
| 1506 | 1538 | } else { } |
|---|
| 1507 | 1539 | } else if (type == 'start') { |
|---|
| 1508 | 1540 | if (diff <= 0) { |
|---|
| 1509 | 1541 | this.expireTime = [] |
|---|
| 1510 | | - this.expireTimeUnit = [] |
|---|
| 1511 | 1542 | const expireTime2 = that.info.expireTime |
|---|
| 1512 | 1543 | if (expireTime2) { |
|---|
| 1513 | 1544 | const expireTimetamp = new Date(expireTime2.split('-').join('/')).getTime(); |
|---|
| 1514 | 1545 | if (expireTimetamp < nowTimestamp) { |
|---|
| 1515 | 1546 | return |
|---|
| 1516 | 1547 | } |
|---|
| 1517 | | - this.countdownTimer(expireTime2.split('-').join('/'), 'end') |
|---|
| 1548 | + this.countdownTimer(expireTime2.split('-').join('/'), 'end', token) |
|---|
| 1518 | 1549 | this.startOrEnd = "后结束" |
|---|
| 1519 | 1550 | } |
|---|
| 1520 | 1551 | return |
|---|
| 1521 | 1552 | } |
|---|
| 1522 | 1553 | } |
|---|
| 1554 | + const pad = num => (num < 10 ? '0' + num : String(num)) |
|---|
| 1523 | 1555 | const res = [] |
|---|
| 1524 | | - const unit = [] |
|---|
| 1525 | 1556 | const days = Math.floor(diff / (1000 * 60 * 60 * 24)) |
|---|
| 1526 | 1557 | diff %= (1000 * 60 * 60 * 24) |
|---|
| 1527 | | - if (days > 0) { |
|---|
| 1528 | | - res.push(days); |
|---|
| 1529 | | - unit.push('日') |
|---|
| 1530 | | - } |
|---|
| 1531 | 1558 | // 转换为小时、分钟、秒 |
|---|
| 1532 | 1559 | const hours = Math.floor(diff / (1000 * 60 * 60)); |
|---|
| 1533 | 1560 | diff %= (1000 * 60 * 60); // 剩余毫秒数 |
|---|
| 1534 | | - if (hours > 0) { |
|---|
| 1535 | | - res.push(hours < 10 ? '0' + hours : hours); |
|---|
| 1536 | | - unit.push('时') |
|---|
| 1537 | | - } |
|---|
| 1538 | 1561 | const minutes = Math.floor(diff / (1000 * 60)); |
|---|
| 1539 | | - if (minutes > 0) { |
|---|
| 1540 | | - res.push(minutes < 10 ? '0' + minutes : minutes); |
|---|
| 1541 | | - unit.push('分') |
|---|
| 1542 | | - } |
|---|
| 1543 | 1562 | diff %= (1000 * 60); // 剩余毫秒数 |
|---|
| 1544 | 1563 | const seconds = Math.floor(diff / 1000); |
|---|
| 1545 | | - if (seconds > 0) { |
|---|
| 1546 | | - res.push(seconds < 10 ? '0' + seconds : seconds); |
|---|
| 1547 | | - unit.push('秒') |
|---|
| 1564 | + // 天数为 0 时不展示,时/分/秒始终展示(补 0),保证倒计时长度稳定 |
|---|
| 1565 | + if (days > 0) { |
|---|
| 1566 | + res.push({ |
|---|
| 1567 | + digits: pad(days).split(''), |
|---|
| 1568 | + unit: '日' |
|---|
| 1569 | + }) |
|---|
| 1548 | 1570 | } |
|---|
| 1571 | + res.push({ |
|---|
| 1572 | + digits: pad(hours).split(''), |
|---|
| 1573 | + unit: '时' |
|---|
| 1574 | + }) |
|---|
| 1575 | + res.push({ |
|---|
| 1576 | + digits: pad(minutes).split(''), |
|---|
| 1577 | + unit: '分' |
|---|
| 1578 | + }) |
|---|
| 1579 | + res.push({ |
|---|
| 1580 | + digits: pad(seconds).split(''), |
|---|
| 1581 | + unit: '秒' |
|---|
| 1582 | + }) |
|---|
| 1549 | 1583 | this.expireTime = res |
|---|
| 1550 | | - this.expireTimeUnit = unit |
|---|
| 1551 | 1584 | if (this.isPageShow) { |
|---|
| 1552 | 1585 | setTimeout(() => { |
|---|
| 1553 | | - that.countdownTimer(expireTime, type) |
|---|
| 1586 | + that.countdownTimer(expireTime, type, token) |
|---|
| 1554 | 1587 | }, 1000) |
|---|
| 1555 | 1588 | } |
|---|
| 1556 | 1589 | }, |
|---|
| .. | .. |
|---|
| 1637 | 1670 | .lottery_banner { |
|---|
| 1638 | 1671 | margin: 15rpx auto; |
|---|
| 1639 | 1672 | width: 690rpx; |
|---|
| 1640 | | - height: 120rpx; |
|---|
| 1641 | 1673 | text-align: center; |
|---|
| 1642 | 1674 | } |
|---|
| 1643 | 1675 | |
|---|
| 1644 | 1676 | .lottery_bannerImg { |
|---|
| 1645 | 1677 | width: 690rpx; |
|---|
| 1646 | | - height: 120rpx; |
|---|
| 1647 | 1678 | } |
|---|
| 1648 | 1679 | |
|---|
| 1649 | 1680 | .tag-wrap-container { |
|---|
| .. | .. |
|---|
| 2085 | 2116 | padding: 15rpx; |
|---|
| 2086 | 2117 | padding-left: 30rpx; |
|---|
| 2087 | 2118 | color: #979797; |
|---|
| 2119 | + font-size: 26rpx; |
|---|
| 2088 | 2120 | |
|---|
| 2089 | 2121 | .num { |
|---|
| 2122 | + display: inline-block; |
|---|
| 2090 | 2123 | color: #000000; |
|---|
| 2091 | 2124 | background: #FFE20E; |
|---|
| 2092 | | - padding: 0 10rpx; |
|---|
| 2125 | + padding: 0 6rpx; |
|---|
| 2126 | + margin: 0 4rpx; |
|---|
| 2127 | + vertical-align: middle; |
|---|
| 2128 | + } |
|---|
| 2129 | + |
|---|
| 2130 | + /* 每个数字固定宽度,避免倒计时跳动时宽度忽长忽短 */ |
|---|
| 2131 | + .digit { |
|---|
| 2132 | + display: inline-block; |
|---|
| 2133 | + width: 19rpx; |
|---|
| 2134 | + text-align: center; |
|---|
| 2093 | 2135 | } |
|---|
| 2094 | 2136 | } |
|---|
| 2095 | 2137 | </style> |
|---|