vhall-mini-sdk 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +17 -20
  2. package/lib/components/announcement/announ-banner/index.js +5 -0
  3. package/lib/components/announcement/announ-banner/index.wxml +2 -2
  4. package/lib/components/announcement/announ-banner/index.wxss +1 -1
  5. package/lib/components/announcement/announ-icon/index.wxml +2 -2
  6. package/lib/components/announcement/index.js +7 -0
  7. package/lib/components/announcement/index.wxml +1 -1
  8. package/lib/components/chat/chat-item/chat-item.js +11 -5
  9. package/lib/components/chat/chat-item/chat-item.wxml +38 -0
  10. package/lib/components/chat/chat-item/chat-item.wxss +1 -1
  11. package/lib/components/chat/chat-list/chat-list.js +40 -2
  12. package/lib/components/chat/chat-list/chat-list.wxml +1 -1
  13. package/lib/components/chat/chat.js +4 -0
  14. package/lib/components/chat/chat.wxml +2 -3
  15. package/lib/components/chat/chat.wxss +1 -1
  16. package/lib/components/chat-recommend/index.js +113 -0
  17. package/lib/components/chat-recommend/index.json +4 -0
  18. package/lib/components/chat-recommend/index.wxml +10 -0
  19. package/lib/components/chat-recommend/index.wxss +1 -0
  20. package/lib/components/live-landscape/live-landscape.js +15 -0
  21. package/lib/components/live-landscape/live-landscape.json +2 -0
  22. package/lib/components/live-landscape/live-landscape.wxml +3 -1
  23. package/lib/components/live-portrait/live-portrait.js +14 -0
  24. package/lib/components/live-portrait/live-portrait.json +3 -1
  25. package/lib/components/live-portrait/live-portrait.wxml +4 -2
  26. package/lib/components/live-portrait/live-portrait.wxss +1 -1
  27. package/lib/components/lottery/lottery-shake/lottery-shake.js +6 -5
  28. package/lib/components/lottery/lottery-shake/lottery-shake.wxml +6 -1
  29. package/lib/components/lottery/lottery-slot/lottery-slot.js +9 -5
  30. package/lib/components/lottery/lottery-slot/lottery-slot.wxml +6 -1
  31. package/lib/components/lottery/lottery-wheel/lottery-wheel.js +16 -16
  32. package/lib/components/lottery/lottery-wheel/lottery-wheel.wxml +10 -4
  33. package/lib/components/lottery/lottery-wheel/lottery-wheel.wxss +1 -1
  34. package/lib/components/lottery/lottery.js +36 -16
  35. package/lib/components/lottery/lottery.wxml +5 -4
  36. package/lib/components/lottery/lottery.wxss +1 -1
  37. package/lib/components/player/components/main/index.wxml +2 -2
  38. package/lib/components/player/components/poster/poster.wxml +1 -1
  39. package/lib/components/player/components/poster/poster.wxss +1 -1
  40. package/lib/components/player/extensions/data.js +11 -1
  41. package/lib/components/player/extensions/methods.js +104 -3
  42. package/lib/components/player/extensions/observers.js +13 -8
  43. package/lib/components/player/player.wxml +16 -5
  44. package/lib/components/player/player.wxss +1 -1
  45. package/lib/components/product/product-card/product-card.js +9 -1
  46. package/lib/components/product/product-card/product-card.wxml +9 -16
  47. package/lib/components/product/product-card/product-card.wxss +1 -1
  48. package/lib/components/product/product-detail/product-detail.js +9 -1
  49. package/lib/components/product/product-item/product-item.js +14 -1
  50. package/lib/components/product/product-item/product-item.wxss +1 -1
  51. package/lib/components/product/product-list/product-list.wxml +1 -1
  52. package/lib/components/product/product-order/product-order.js +20 -13
  53. package/lib/components/product/product-order/product-order.wxss +1 -1
  54. package/lib/pubComponents/countdown/countdown.wxss +1 -1
  55. package/lib/pubComponents/popup/popup.js +1 -0
  56. package/lib/sdk/main/index.js +2 -2
  57. package/lib/sdk/player/index.js +1 -1
  58. package/lib/sdk/plugins/LotteryServer.js +1 -1
  59. package/lib/sdk/plugins/ProductServer.js +1 -1
  60. package/lib/shared/utils.js +1 -1
  61. package/package.json +1 -1
@@ -38,6 +38,10 @@ export function createMethods() {
38
38
  currentQuality: vodPlayInfo.currentQuality,
39
39
  });
40
40
  this.triggerEvent('ready', null);
41
+ // 暖场视频:自动播放由我们手动控制(受 advancePlayTime 时间限制约束),不直接依赖 video 的 autoplay
42
+ if (this.data.warmVideoIds.length > 0 && this.data.autoplay && this.canPlayWarmup()) {
43
+ this.play();
44
+ }
41
45
  } catch (e) {
42
46
  console.error(e);
43
47
  }
@@ -176,6 +180,7 @@ export function createMethods() {
176
180
  showLoading: false,
177
181
  showError: false,
178
182
  showPoster: false,
183
+ showBigPlay: false,
179
184
  });
180
185
  },
181
186
  /**
@@ -185,10 +190,15 @@ export function createMethods() {
185
190
  onpause(e) {
186
191
  console.log(`暂停pause事件:`, e.detail);
187
192
  this.triggerEvent('pause', e.detail);
188
- this.setData({
193
+ const data = {
189
194
  playStatus: 'paused',
190
195
  pausedFlag: true,
191
- });
196
+ };
197
+ // 暖场视频暂停时,只显示大播放按钮(不显示封面)
198
+ if (this.data.warmVideoIds.length > 0) {
199
+ data.showBigPlay = true;
200
+ }
201
+ this.setData(data);
192
202
  },
193
203
  /**
194
204
  * 结束事件(仅点播)
@@ -197,13 +207,69 @@ export function createMethods() {
197
207
  onended(e) {
198
208
  console.log(`播放ended事件:`, e.detail);
199
209
  this.triggerEvent('ended', e.detail);
210
+ // 暖场视频连续播放/循环播放处理
211
+ const { warmVideoIds, warmVideoIndex, warmupPlayerType } = this.data;
212
+ if (warmVideoIds.length === 0) return;
213
+ const isLast = warmVideoIndex >= warmVideoIds.length - 1;
214
+ if (!isLast) {
215
+ // 还有下一个,直接续播
216
+ this.playNextWarmVideo(warmVideoIndex + 1);
217
+ } else if (warmupPlayerType === 2) {
218
+ // 循环播放:回到第一个继续(单视频时即重播当前视频)
219
+ this.playNextWarmVideo(0);
220
+ } else {
221
+ // 单次播放(warmupPlayerType===1)播完最后一个,回到封面+大播放按钮
222
+ // 重置回第一个视频(仅更新地址,不自动播放,等用户点击大播放按钮)
223
+ this.setData({
224
+ showPoster: true,
225
+ showBigPlay: true,
226
+ playStatus: 'paused',
227
+ pausedFlag: true,
228
+ currentVodPlayTime: 0,
229
+ });
230
+ this.playNextWarmVideo(0, false);
231
+ }
232
+ },
233
+ /**
234
+ * 播放指定索引的暖场视频(连续/循环播放)
235
+ * @param {number} nextIndex 下一个视频索引
236
+ */
237
+ async playNextWarmVideo(nextIndex, autoPlay = true) {
238
+ const nextId = this.data.warmVideoIds[nextIndex];
239
+ // 更新调度实例中的点播 id
240
+ if (this.vhallPlayerCore && this.vhallPlayerCore.scheduler) {
241
+ this.vhallPlayerCore.scheduler.option.paasVodId = nextId;
242
+ }
243
+ // 切换视频前先隐藏自定义大播放按钮,避免切换间隙(尤其横屏全屏)
244
+ // video 原生居中播放按钮闪现时与自定义按钮重叠。
245
+ // 仅 autoPlay(续播/循环)时隐藏;autoPlay=false(单次播完重置地址)时保留外层设置的 showBigPlay
246
+ this.setData({ warmVideoIndex: nextIndex });
247
+ if (autoPlay) {
248
+ this.setData({ showBigPlay: false });
249
+ }
250
+ // 重新拉取播放地址并播放
251
+ if (this.data.type == 'vod') {
252
+ try {
253
+ const vodPlayInfo = await this.vhallPlayerCore.initVhallyunVod();
254
+ this.setData({
255
+ videoSrc: vodPlayInfo.videoSrc,
256
+ qualitys: vodPlayInfo.qualitys,
257
+ currentQuality: vodPlayInfo.currentQuality,
258
+ }, () => {
259
+ if (autoPlay) {
260
+ this.play();
261
+ }
262
+ });
263
+ } catch (err) {
264
+ console.error('播放下一个暖场视频失败', err);
265
+ }
266
+ }
200
267
  },
201
268
  /**
202
269
  * loadedmetadata事件(仅点播)
203
270
  * @param {*} e
204
271
  */
205
272
  onloadedmetadata(e) {
206
- console.log(`播放loadedmetadata事件:`, e.detail);
207
273
  this.triggerEvent('loadedmetadata', e.detail);
208
274
  const { duration, width, height } = e.detail;
209
275
  this.setData({
@@ -249,6 +315,41 @@ export function createMethods() {
249
315
  onreconnecting(e) {
250
316
  this.triggerEvent('reconnecting', e.detail);
251
317
  },
318
+ /**
319
+ * 判断暖场视频当前是否到达可观看时间
320
+ * advancePlayTime===0:开播前随时可看(默认)
321
+ * advancePlayTime>0:开播前 advancePlayTime 秒才可观看
322
+ * @returns {boolean}
323
+ */
324
+ canPlayWarmup() {
325
+ const { advancePlayTime, webinarStartTime } = this.data;
326
+ if (advancePlayTime <= 0) return true; // 0 表示开播前随时可看
327
+ if (!webinarStartTime) return true; // 无开播时间则放行
328
+ const startTs = new Date(String(webinarStartTime).replace(/-/g, '/')).getTime();
329
+ if (isNaN(startTs)) return true;
330
+ const nowTs = Date.now();
331
+ // 距离开播还有多少秒
332
+ const remainSec = (startTs - nowTs) / 1000;
333
+ // remainSec <= 0 表示已开播;remainSec <= advancePlayTime 表示已进入可观看窗口
334
+ return remainSec <= advancePlayTime;
335
+ },
336
+ /**
337
+ * 点播封面上的播放按钮点击
338
+ */
339
+ handlePosterPlay() {
340
+ // 暖场视频:点击播放前校验可观看时间
341
+ if (this.data.warmVideoIds.length > 0 && !this.canPlayWarmup()) {
342
+ wx.showToast({
343
+ title: '尚未到暖场视频播放时间',
344
+ icon: 'none',
345
+ });
346
+ return;
347
+ }
348
+ this.play();
349
+ if (this.data.type == 'live') {
350
+ this.resume();
351
+ }
352
+ },
252
353
  startWatchExpire(info) {
253
354
  if (!info) return;
254
355
  const webinar = this.sdkInstance.store.get('watchInitData.webinar');
@@ -10,18 +10,16 @@ export function createObservers() {
10
10
  _that.setData({
11
11
  liveInfo,
12
12
  });
13
- // const options = _that.store.get('');
14
- // console.log('liveInfo:', JSON.stringify(options));
15
- // _that.initVhallPlayer(options);
16
13
  _that.setHot();
17
14
  },
18
15
  webinarStatus: (status) => {
19
- _that.setData({
20
- webinarStatus: status,
21
- });
16
+ console.log('webinarStatus', status);
17
+ // 直播状态变化处理
18
+ _that.setData({ webinarStatus: status });
22
19
  _that.initVhallPlayer(_that.store.get(''));
23
20
  _that.setHot();
24
21
  if (status === 3) {
22
+ // 直播结束
25
23
  _that.destroy();
26
24
  }
27
25
  },
@@ -85,10 +83,17 @@ export function createObservers() {
85
83
  'posterOption.**': function (option) {
86
84
  const { enable, url } = option;
87
85
  if (enable && url.length > 0) {
88
- this.setData({
86
+ const data = {
89
87
  showPoster: true,
90
88
  posterUrl: url,
91
- });
89
+ };
90
+ // 暖场视频:未到可观看时间时不显示大播放按钮
91
+ if (this.data.warmVideoIds.length > 0) {
92
+ data.showBigPlay = this.canPlayWarmup ? this.canPlayWarmup() : true;
93
+ } else {
94
+ data.showBigPlay = true;
95
+ }
96
+ this.setData(data);
92
97
  }
93
98
  },
94
99
  audioMode: function (audioMode) {
@@ -7,7 +7,7 @@
7
7
  }
8
8
  </wxs>
9
9
  <view class='vhall-layout-root' style='{{_style}}'>
10
- <player-main catchtap="onToggleControls" wx:if="{{webinarStatus!=3}}" id='{{id}}' class='vhall-player-root' src='{{videoSrc}}' type='{{type}}' autoplay='{{autoplay}}' objectFit='{{objectFit}}' muted='{{mutedFlag}}' mode='{{liveDelayMode}}' pictureInPictureMode='{{pictureInPictureMode}}' poster='{{posterUrl}}' controls='{{controls}}' show-progress="{{showProgress}}" loop="{{loop}}" initialSeekTime='{{initialSeekTime}}' bindfullscreenchange='onfullscreenchange' bindpictureinpicturechange='onpictureinpicturechange' bindtimeupdate='ontimeupdate' bindplay='onplay' bindpause='onpause' bindended='onended' bindloadedmetadata='onloadedmetadata' binderror='onerror' bindwaiting='onwaiting' bindloadStart='onloadStart' bindreconnecting='onreconnecting'>
10
+ <player-main catchtap="onToggleControls" wx:if="{{webinarStatus!=3}}" id='{{id}}' class='vhall-player-root' src='{{videoSrc}}' type='{{type}}' autoplay='{{isWarmVideo ? false : autoplay}}' objectFit='{{objectFit}}' muted='{{mutedFlag}}' mode='{{liveDelayMode}}' pictureInPictureMode='{{pictureInPictureMode}}' poster='{{type=="vod" ? "" : posterUrl}}' controls='{{controls}}' show-progress="{{isWarmVideo ? false : showProgress}}" show-bottom-progress="{{isWarmVideo ? false : showBottomProgress}}" show-center-play-btn="{{!isWarmVideo}}" loop="{{loop}}" initialSeekTime='{{initialSeekTime}}' bindfullscreenchange='onfullscreenchange' bindpictureinpicturechange='onpictureinpicturechange' bindtimeupdate='ontimeupdate' bindplay='onplay' bindpause='onpause' bindended='onended' bindloadedmetadata='onloadedmetadata' binderror='onerror' bindwaiting='onwaiting' bindloadStart='onloadStart' bindreconnecting='onreconnecting'>
11
11
  <block wx:if="{{type=='live'}}">
12
12
  <!-- 纯音频播放蒙层图片 -->
13
13
  <image wx:if='{{showAudioCover}}' class='vhall-player-audio-cover' mode='scaleToFill' src='https://cnstatic01.e.vhall.com/common-static/saas-watch/static/img/video.110b96d1.gif' />
@@ -33,14 +33,25 @@
33
33
  <online-hot wx:if="{{!controlsHiddenFlag}}" fullscreen="{{fullscreenFlag}}" showPerson="{{showPerson}}" personValue="{{personValue}}" showHot="{{showHot}}" hotValue="{{hotValue}}"></online-hot>
34
34
  </block>
35
35
  <block wx:else>
36
- <!-- 点播:在线人数和热度。 点播使用的video原生控制栏,没法判断展示还是隐藏状态 -->
37
- <online-hot fullscreen="{{fullscreenFlag}}" showPerson="{{showPerson}}" personValue="{{personValue}}" showHot="{{showHot}}" hotValue="{{hotValue}}"></online-hot>
36
+ <!-- 预告||点播||回放:自定义封面(铺满),不使用 video 原生 poster -->
37
+ <vhall-player-poster wx:if="{{showPoster && tools.canShow(ignores, 'vhall-player-poster')}}" url='{{posterUrl}}' />
38
+ <!-- 预告||点播||回放:封面上的居中播放按钮(可独立于封面显示,如暂停时) -->
39
+ <view class="vod-poster-play" wx:if="{{showBigPlay}}" catchtap="handlePosterPlay">
40
+ <view class="play-triangle"></view>
41
+ </view>
42
+ <!-- 回放:在线人数和热度。 点播使用的video原生控制栏,没法判断展示还是隐藏状态 -->
43
+ <online-hot wx:if="{{webinarStatus==5}}" fullscreen="{{fullscreenFlag}}" showPerson="{{showPerson}}" personValue="{{personValue}}" showHot="{{showHot}}" hotValue="{{hotValue}}"></online-hot>
38
44
  </block>
39
45
  </player-main>
40
46
  <slot name='vhall-player-01' />
41
- <view wx:if="{{webinarStatus==2||webinarStatus==3}}" class="webinar_warn">
47
+ <!-- 直播预告 && 无暖场视频 -->
48
+ <view wx:if="{{webinarStatus==2 && !isWarmVideo }}" class="webinar_not_live">
42
49
  <image class="cover_img" src="{{liveInfo.coverImg}}" />
43
- <view wx:if="{{webinarStatus==3}}" class="webinar_status">
50
+ </view>
51
+ <!-- 直播结束 -->
52
+ <view wx:elif="{{ webinarStatus==3}}" class="webinar_not_live">
53
+ <image class="cover_img" src="{{liveInfo.coverImg}}" />
54
+ <view class="webinar_status">
44
55
  <image class="status-img" mode="widthFix" src="https://cnstatic01.e.vhall.com/common-static/images/mp/live_end.png" />
45
56
  <text class="status-text">直播已结束</text>
46
57
  </view>
@@ -1 +1 @@
1
- .vhall-layout-root{height:100%;transition:width .3s ease-in-out,height .3s ease-in-out;width:100%}.webinar_warn{align-items:center;background-color:rgba(0,0,0,.9);bottom:0;color:#fff;display:flex;font-size:14px;height:100%;justify-content:center;left:0;position:relative;right:0;top:0}.webinar_warn .cover_img{height:100%;position:absolute;width:100%;z-index:1}.webinar_warn .webinar_status{align-items:center;background-color:rgba(0,0,0,.6);bottom:0;display:flex;flex-direction:column;justify-content:center;left:0;position:absolute;right:0;text-align:center;top:0;z-index:2}.webinar_warn .webinar_status .status-img{display:block;margin-left:-10px;width:140rpx}.webinar_warn .webinar_status .status-text{display:block;font-size:1rem;padding-top:10px}.vhall-player-audio-cover{height:100%;left:0;position:absolute;top:0;width:100%}
1
+ .vhall-layout-root{height:100%;transition:width .3s ease-in-out,height .3s ease-in-out;width:100%}.webinar_not_live{align-items:center;background-color:rgba(0,0,0,.9);bottom:0;color:#fff;display:flex;font-size:14px;height:100%;justify-content:center;left:0;position:relative;right:0;top:0}.webinar_not_live .cover_img{height:100%;position:absolute;width:100%;z-index:1}.webinar_not_live .webinar_status{align-items:center;background-color:rgba(0,0,0,.6);bottom:0;display:flex;flex-direction:column;justify-content:center;left:0;position:absolute;right:0;text-align:center;top:0;z-index:2}.webinar_not_live .webinar_status .status-img{display:block;margin-left:-10px;width:140rpx}.webinar_not_live .webinar_status .status-text{display:block;font-size:1rem;padding-top:10px}.vhall-player-audio-cover{height:100%;left:0;position:absolute;top:0;width:100%}.vod-poster-play{align-items:center;background:rgba(0,0,0,.45);border-radius:50%;display:flex;height:140rpx;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:140rpx;z-index:3}.vod-poster-play .play-triangle{border-color:transparent transparent transparent #fff;border-style:solid;border-width:30rpx 0 30rpx 48rpx;height:0;margin-left:12rpx;width:0}
@@ -83,6 +83,13 @@ Component({
83
83
  wx.navigateTo({
84
84
  url: appendParamsToPath(product?.mini_program_config?.mini_program_path, { webinarId })
85
85
  });
86
+
87
+ this.properties.sdkInstance?.get('productServer')?.reportGoodsView({
88
+ goods_id: product.goods_id,
89
+ third_goods_id: product.third_goods_id,
90
+ act: 'popup_click',
91
+ notice_type: 'buy'
92
+ });
86
93
  } else if (product.buy_type === 1) {
87
94
  // 触发购买事件,由父组件显示下单弹窗
88
95
  this.triggerEvent('buyClick', product);
@@ -106,4 +113,5 @@ Component({
106
113
  });
107
114
  }
108
115
  }
109
- });
116
+ });
117
+
@@ -1,45 +1,38 @@
1
1
  <view class="product-card-container">
2
2
  <block wx:for="{{products}}" wx:key="{{item.goods_id}}">
3
- <view
4
- class="product-card {{(item.sale_status === 0 || item.covered_status === 1) ? 'product-card-disabled' : ''}} {{products.length > 1 ? 'product-card-small' : ''}}"
5
- >
3
+ <view class="product-card {{(item.sale_status === 0 || item.covered_status === 1) ? 'product-card-disabled' : ''}} {{products.length > 1 ? 'product-card-small' : ''}}">
6
4
  <!-- 讲解中徽章 -->
7
5
  <view class="product-card-badge">
8
6
  <image class="product-card-badge-icon" src="https://cnstatic01.e.vhall.com/common-static/saas-wap/static/img/live.b07d13f4.gif" mode="heightFix"></image>
9
7
  讲解中
10
8
  </view>
11
-
12
9
  <!-- 关闭按钮 -->
13
- <view class="product-card-close" bindtap="onCloseCard" data-product-id="{{item.goods_id}}">x</view>
14
-
10
+ <view class="product-card-close" bindtap="onCloseCard" data-product-id="{{item.goods_id}}">
11
+ &#10005;
12
+ </view>
15
13
  <!-- 商品图片(点击打开详情) -->
16
14
  <view class="product-card-image" bindtap="onCardClick" data-index="{{index}}">
17
- <image class="product-card-img"
18
- src="{{item.cover_img || 'https://cnstatic01.e.vhall.com/common-static/images/default_avatar.png'}}"
19
- mode="aspectFill"
20
- ></image>
15
+ <image class="product-card-img" src="{{item.cover_img || 'https://cnstatic01.e.vhall.com/common-static/images/default_avatar.png'}}" mode="aspectFill"></image>
21
16
  <!-- 已抢光遮罩层 -->
22
17
  <view wx:if="{{item.sale_status === 0}}" class="product-card-mask">
23
18
  <text class="product-card-mask-text">已抢光</text>
24
19
  </view>
25
20
  </view>
26
-
27
21
  <!-- 商品信息 -->
28
22
  <view class="product-card-info">
29
23
  <!-- 商品名称(点击打开详情) -->
30
- <text class="product-card-title" bindtap="onCardClick" data-index="{{index}}">{{item.name || '商品名称'}}</text>
31
-
24
+ <view class="product-card-title" bindtap="onCardClick" data-index="{{index}}">
25
+ {{item.name || '商品名称'}}
26
+ </view>
32
27
  <!-- 价格和购买按钮 -->
33
28
  <view class="product-card-price-container" bindtap="onBuyClick" data-index="{{index}}">
34
-
35
29
  <block wx:if="{{item.covered_status === 1}}">
36
30
  <text class="product-card-price">¥?</text>
37
31
  </block>
38
32
  <block wx:else>
39
33
  <text class="product-card-price">¥{{item.discount_price || item.price || 0}}</text>
40
34
  </block>
41
-
42
- <image class="buy-icon" src="//cnstatic01.e.vhall.com/common-static/saas-wap/static/img/rob.4306d2b5.png" mode="heightFix" ></image>
35
+ <image class="buy-icon" src="//cnstatic01.e.vhall.com/common-static/saas-wap/static/img/rob.4306d2b5.png" mode="heightFix"></image>
43
36
  </view>
44
37
  </view>
45
38
  </view>
@@ -1 +1 @@
1
- .product-card-container{align-items:flex-end;bottom:180rpx;display:flex;flex-direction:column;gap:16rpx;position:fixed;right:20rpx;z-index:999}.product-card{background-color:#fff;border-radius:12rpx;box-shadow:0 4rpx 12rpx rgba(0,0,0,.15);overflow:hidden;position:relative;width:240rpx}.product-card-badge{background-color:rgba(0,0,0,.6);border-radius:30rpx;color:#fff;font-size:.71rem;left:8rpx;padding:4rpx 20rpx;position:absolute;top:8rpx;z-index:10}.product-card-badge .product-card-badge-icon{height:20rpx}.product-card-close{color:#fff;font-size:1.14rem;padding:0 14rpx;position:absolute;right:0rpx;top:0rpx;z-index:10}.product-card-image{height:240rpx;position:relative;width:100%}.product-card-image .product-card-img{height:100%;object-fit:cover;width:100%}.product-card-info{padding:12rpx}.product-card-title{color:#333;display:block;font-size:.86rem;font-weight:500;margin-bottom:12rpx;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.product-card-price-container{align-items:center;background:#fb2626;border-radius:8rpx;color:#fff;display:flex;height:50rpx;justify-content:space-between;margin-top:12rpx;padding-left:10rpx}.product-card-buy{background-color:#ff4d4f;border-radius:6rpx;color:#fff;cursor:pointer;font-size:.71rem;padding:8rpx 16rpx}.product-card-mask{align-items:center;background-color:rgba(0,0,0,.4);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.product-card-mask-text{border-radius:20rpx;color:#fff;font-size:.86rem;padding:8rpx 16rpx}.product-card-disabled{opacity:.7}.product-card-disabled .product-card-price{color:#fff}.product-card-disabled .product-card-price-container{opacity:.6}.buy-icon{display:block;height:100%}.product-card-small{width:180rpx}.product-card-small .product-card-image{height:180rpx}.product-card-small .product-card-price-container{height:40rpx}.product-card-small .product-card-badge{font-size:.64rem;padding:3rpx 16rpx}.product-card-small .product-card-close{font-size:1rem;padding:0 10rpx}.product-card-small .buy-icon{display:none}
1
+ .product-card-container{align-items:flex-end;bottom:180rpx;display:flex;flex-direction:column;gap:16rpx;position:fixed;right:20rpx;z-index:999}.product-card{background-color:#fff;border-radius:12rpx;box-shadow:0 4rpx 12rpx rgba(0,0,0,.15);overflow:hidden;position:relative;width:240rpx}.product-card-badge{background-color:rgba(0,0,0,.6);border-radius:30rpx;color:#fff;font-size:.71rem;left:8rpx;padding:4rpx 20rpx;position:absolute;top:8rpx;z-index:10}.product-card-badge .product-card-badge-icon{height:20rpx}.product-card-close{align-items:center;background:rgba(0,0,0,.6);border-radius:50%;color:#fff;display:flex;font-size:24rpx;height:36rpx;justify-content:center;position:absolute;right:4rpx;top:8rpx;width:36rpx;z-index:10}.product-card-image{height:240rpx;position:relative;width:100%}.product-card-image .product-card-img{height:100%;object-fit:cover;width:100%}.product-card-info{padding:12rpx}.product-card-title{-webkit-box-orient:vertical;-webkit-line-clamp:2;color:#333;display:-webkit-box;font-size:24rpx;font-weight:500;margin-bottom:12rpx;overflow:hidden}.product-card-price-container{align-items:center;background:#fb2626;border-radius:8rpx;color:#fff;display:flex;height:50rpx;justify-content:space-between;margin-top:12rpx;padding-left:10rpx}.product-card-buy{background-color:#ff4d4f;border-radius:6rpx;color:#fff;cursor:pointer;font-size:.71rem;padding:8rpx 16rpx}.product-card-mask{align-items:center;background-color:rgba(0,0,0,.4);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.product-card-mask-text{border-radius:20rpx;color:#fff;font-size:.86rem;padding:8rpx 16rpx}.product-card-disabled{opacity:.7}.product-card-disabled .product-card-price{color:#fff}.product-card-disabled .product-card-price-container{opacity:.6}.buy-icon{display:block;height:100%}.product-card-small{width:180rpx}.product-card-small .product-card-image{height:180rpx}.product-card-small .product-card-price-container{height:40rpx}.product-card-small .product-card-badge{font-size:.64rem;padding:3rpx 16rpx}.product-card-small .product-card-close{font-size:1rem;padding:0 10rpx}.product-card-small .buy-icon{display:none}
@@ -103,6 +103,13 @@ Component({
103
103
  url: appendParamsToPath(product?.mini_program_config?.mini_program_path, { webinarId })
104
104
  });
105
105
  this.closeDetail();
106
+
107
+ this.properties.sdkInstance?.get('productServer')?.reportGoodsView({
108
+ goods_id: product.goods_id,
109
+ third_goods_id: product.third_goods_id,
110
+ act: 'popup_click',
111
+ notice_type: 'buy'
112
+ });
106
113
  } else if (product.buy_type === 1) {
107
114
  // 先关闭详情弹框
108
115
  this.closeDetail();
@@ -124,4 +131,5 @@ Component({
124
131
  });
125
132
  }
126
133
  }
127
- });
134
+ });
135
+
@@ -14,6 +14,11 @@ Component({
14
14
  webinarId: {
15
15
  type: String,
16
16
  value: 0
17
+ },
18
+ // SDK实例
19
+ sdkInstance: {
20
+ type: Object,
21
+ value: null,
17
22
  }
18
23
  },
19
24
  data: {
@@ -40,6 +45,13 @@ Component({
40
45
  wx.navigateTo({
41
46
  url: appendParamsToPath(product?.mini_program_config?.mini_program_path, { webinarId })
42
47
  });
48
+
49
+ this.properties.sdkInstance?.get('productServer')?.reportGoodsView({
50
+ goods_id: product.goods_id,
51
+ third_goods_id: product.third_goods_id,
52
+ act: 'popup_click',
53
+ notice_type: 'buy'
54
+ });
43
55
  } else if (product.buy_type === 1) {
44
56
  // 触发购买事件,由父组件显示下单弹窗
45
57
  this.triggerEvent('buyClick', product);
@@ -53,4 +65,5 @@ Component({
53
65
  }
54
66
  }
55
67
  }
56
- });
68
+ });
69
+
@@ -1 +1 @@
1
- .product-image{border-radius:10rpx;height:180rpx;margin-right:20rpx;overflow:hidden;position:relative;width:180rpx}.product-index{border-radius:8rpx 0 8rpx 0;color:#fff;font-size:1rem;height:40rpx;padding:0 16rpx;z-index:1}.product-index,.product-mask{align-items:center;background-color:rgba(0,0,0,.6);display:flex;justify-content:center;left:0;position:absolute;top:0}.product-mask{bottom:0;right:0;z-index:2}.product-mask-text{color:#fff;font-size:1rem}.buy-btn.disabled{cursor:not-allowed;opacity:.6}.covered-price{color:#999;font-size:1rem;margin-bottom:10rpx}.product-image image{height:100%;object-fit:cover;width:100%}.product-info{display:flex;flex:1;flex-direction:column;justify-content:space-between;width:0}.product-name{color:#333;font-size:1.12rem;line-height:1.43rem}.product-desc,.product-name{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;text-overflow:ellipsis}.product-desc{color:#666;font-size:.9rem}.price-container{align-items:baseline;display:flex}.product-discount-price{color:#fb2626;font-size:1.14rem}.product-original-price{color:#999;font-size:.86rem;text-decoration:line-through}.product-price{color:#fb2626;font-size:1.14rem}.buy-btn{background-color:#fb2626;border-radius:40rpx;bottom:20rpx;color:#fff;font-size:.86rem;padding:10rpx 32rpx;position:absolute;right:20rpx}.product-item{border-bottom:1rpx solid #f0f0f0;cursor:pointer;display:flex;padding:20rpx 0;position:relative}.product-item:last-child{border-bottom:none}.product-sales{color:#999;font-size:.86rem}.product-badge{align-items:center;background-color:rgba(0,0,0,.6);bottom:0;color:#fff;display:flex;font-size:.71rem;justify-content:center;left:0;padding:8rpx;position:absolute;right:0;text-align:center;z-index:5}.product-badge-text{display:inline-block}
1
+ .product-image{border-radius:10rpx;height:180rpx;margin-right:20rpx;overflow:hidden;position:relative;width:180rpx}.product-index{border-radius:8rpx 0 8rpx 0;color:#fff;font-size:1rem;height:40rpx;padding:0 16rpx;z-index:1}.product-index,.product-mask{align-items:center;background-color:rgba(0,0,0,.6);display:flex;justify-content:center;left:0;position:absolute;top:0}.product-mask{bottom:0;right:0;z-index:2}.product-mask-text{color:#fff;font-size:1rem}.buy-btn.disabled{cursor:not-allowed;opacity:.6}.covered-price{color:#999;font-size:1rem;margin-bottom:10rpx}.product-image image{height:100%;object-fit:cover;width:100%}.product-info{display:flex;flex:1;flex-direction:column;justify-content:space-between;width:0}.product-name{color:#333;font-size:1.12rem;line-height:1.43rem}.product-desc,.product-name{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden;text-overflow:ellipsis}.product-desc{color:#666;font-size:.9rem}.price-container{align-items:baseline;display:flex;line-height:1.8}.product-discount-price{color:#fb2626;font-size:1.14rem}.product-original-price{color:#999;font-size:.86rem;text-decoration:line-through}.product-price{color:#fb2626;font-size:1.14rem}.buy-btn{background-color:#fb2626;border-radius:40rpx;bottom:20rpx;color:#fff;font-size:.86rem;padding:10rpx 32rpx;position:absolute;right:20rpx}.product-item{border-bottom:1rpx solid #f0f0f0;cursor:pointer;display:flex;padding:20rpx 0;position:relative}.product-item:last-child{border-bottom:none}.product-sales{color:#999;font-size:.86rem}.product-badge{align-items:center;background-color:rgba(0,0,0,.6);bottom:0;color:#fff;display:flex;font-size:.71rem;justify-content:center;left:0;padding:8rpx;position:absolute;right:0;text-align:center;z-index:5}.product-badge-text{display:inline-block}
@@ -2,7 +2,7 @@
2
2
  <scroll-view scroll-y class="product-list">
3
3
  <view wx:if="{{productList.length === 0}}" class="product-empty">暂无商品</view>
4
4
  <view wx:else>
5
- <product-item webinarId="{{webinarId}}" wx:for="{{productList}}" wx:key="{{item.id}}" index="{{index}}" product="{{item}}" bind:openDetail="openProductDetail" bind:buyClick="onBuyClick" />
5
+ <product-item webinarId="{{webinarId}}" wx:for="{{productList}}" wx:key="{{item.id}}" index="{{index}}" product="{{item}}" sdkInstance="{{ sdkInstance }}" bind:openDetail="openProductDetail" bind:buyClick="onBuyClick" />
6
6
  </view>
7
7
  </scroll-view>
8
8
  </view>
@@ -48,7 +48,7 @@ Component({
48
48
  },
49
49
  observers: {
50
50
  // 监听显示状态变化(兼容旧用法)
51
- show: function(show) {
51
+ show: function (show) {
52
52
  if (show) {
53
53
  // 重置数量
54
54
  this.setData({
@@ -62,11 +62,11 @@ Component({
62
62
  }
63
63
  },
64
64
  // 监听数量变化
65
- quantity: function(val) {
65
+ quantity: function (val) {
66
66
  this.updateAmount();
67
67
  },
68
68
  // 监听商品变化
69
- product: function(newVal) {
69
+ product: function (newVal) {
70
70
  if (newVal) {
71
71
  this.updateAmount();
72
72
  }
@@ -89,7 +89,7 @@ Component({
89
89
 
90
90
  try {
91
91
  const res = await wxApi.sendGet(`/v3/goods/webinar-setting/get-info?webinar_id=${webinarId}`);
92
-
92
+
93
93
  if (res && res.code === 200 && res.data) {
94
94
  this.setData({
95
95
  enablePhone: res.data.enable_phone || 0,
@@ -126,7 +126,7 @@ Component({
126
126
  const product = this.properties.product;
127
127
  const quantity = this.data.quantity;
128
128
  const price = parseFloat(product.discount_price || product.price || 0);
129
-
129
+
130
130
  // 确保价格是有效数字
131
131
  const validPrice = isNaN(price) ? 0 : price;
132
132
  const subtotal = validPrice * quantity;
@@ -201,6 +201,13 @@ Component({
201
201
  url: appendParamsToPath(product?.mini_program_config?.mini_program_path, { webinarId })
202
202
  });
203
203
  this.closeOrder();
204
+
205
+ this.properties.sdkInstance?.get('productServer')?.reportGoodsView({
206
+ goods_id: product.goods_id,
207
+ third_goods_id: product.third_goods_id,
208
+ act: 'popup_click',
209
+ notice_type: 'buy'
210
+ });
204
211
  return;
205
212
  }
206
213
 
@@ -292,7 +299,7 @@ Component({
292
299
 
293
300
  try {
294
301
  wx.showLoading({ title: '提交中...' });
295
-
302
+
296
303
  const res = await wxApi.sendPost('/v3/goods/order/create', params);
297
304
 
298
305
  wx.hideLoading();
@@ -330,7 +337,7 @@ Component({
330
337
  });
331
338
  return;
332
339
  }
333
-
340
+
334
341
  let payParams;
335
342
  try {
336
343
  payParams = JSON.parse(orderInfo.ext);
@@ -346,12 +353,12 @@ Component({
346
353
 
347
354
  try {
348
355
  wx.showLoading({ title: '支付中...' });
349
-
356
+
350
357
  // 使用 await 方式,不使用 success/fail 回调
351
358
  const result = await wx.requestPayment(payParams);
352
359
 
353
360
  wx.hideLoading();
354
-
361
+
355
362
  if (result && result.errMsg === 'requestPayment:ok') {
356
363
  // 支付成功
357
364
  wx.showToast({
@@ -359,10 +366,10 @@ Component({
359
366
  icon: 'success',
360
367
  duration: 2000
361
368
  });
362
-
369
+
363
370
  // 关闭订单页面
364
371
  this.closeOrder();
365
-
372
+
366
373
  // 触发支付成功事件
367
374
  this.triggerEvent('paySuccess', {
368
375
  product: this.properties.product,
@@ -374,7 +381,7 @@ Component({
374
381
  } catch (error) {
375
382
  wx.hideLoading();
376
383
  console.error('支付失败:', error);
377
-
384
+
378
385
  if (error && error.errMsg && error.errMsg.includes('cancel')) {
379
386
  wx.showToast({
380
387
  title: '已取消支付',
@@ -391,4 +398,4 @@ Component({
391
398
  }
392
399
  }
393
400
  }
394
- });
401
+ });
@@ -1 +1 @@
1
- .order-mask{align-items:flex-end;background-color:rgba(0,0,0,.5);bottom:0;display:flex;left:0;position:fixed;right:0;top:0;z-index:1000}.order-container{background-color:#fff;border-radius:24rpx 24rpx 0 0;display:flex;flex-direction:column;max-height:80vh;width:100%}.order-header{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;justify-content:space-between}.order-title{color:#333;font-size:1.29rem;font-weight:700}.order-close{align-items:center;display:flex;height:60rpx;justify-content:center;width:60rpx}.close-icon{color:#999;font-size:1.71rem;line-height:1}.order-content{box-sizing:border-box;flex:1;overflow-y:auto;padding:30rpx 40rpx}.product-section{margin-bottom:30rpx}.product-card{background-color:#fafafa;border-radius:16rpx;display:flex;padding:20rpx}.product-img{border-radius:12rpx;height:160rpx;margin-right:20rpx;width:160rpx}.product-detail{display:flex;flex:1;flex-direction:column;justify-content:space-between}.product-name{-webkit-line-clamp:2;-webkit-box-orient:vertical;color:#333;display:-webkit-box;font-size:1.14rem;font-weight:500;line-height:1.43rem;overflow:hidden}.product-desc{color:#999;font-size:.93rem;margin-top:8rpx}.product-price-row{align-items:baseline;display:flex;margin-top:12rpx}.product-price{color:#fb2626;font-size:1.29rem;font-weight:700}.product-discount-price{color:#fb2626;font-size:1.14rem}.product-original-price{color:#999;font-size:.86rem;margin-left:12rpx;text-decoration:line-through}.covered-price{color:#999;font-size:1rem}.quantity-section{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;justify-content:space-between;padding:20rpx 0}.section-title{color:#333;font-size:1.07rem}.qty-btn,.quantity-control{align-items:center;display:flex}.qty-btn{background-color:#f5f5f5;border-radius:50%;height:56rpx;justify-content:center;width:56rpx}.qty-btn .qty-btn-text{color:#333;font-size:1.29rem;line-height:1}.qty-value{color:#333;font-size:1.14rem;margin:0 20rpx;min-width:60rpx;text-align:center}.amount-section{margin-top:20rpx}.amount-row{align-items:center;display:flex;justify-content:space-between;padding:16rpx 0}.amount-row.total{border-top:1rpx dashed #e0e0e0;margin-top:10rpx;padding-top:26rpx}.amount-label{color:#666;font-size:1rem}.amount-row.total .amount-label{color:#333;font-size:1.07rem}.amount-value{color:#333;font-size:1rem}.amount-row.total .amount-value{color:#fb2626;font-size:1.29rem;font-weight:700}.order-footer{align-items:center;background-color:#fff;border-top:1rpx solid #f5f5f5;display:flex;justify-content:space-between;padding:24rpx 40rpx}.footer-left{align-items:baseline;display:flex}.footer-label{color:#666;font-size:1rem}.footer-total{color:#fb2626;font-size:1.43rem;font-weight:700;margin-left:8rpx}.submit-btn{background:linear-gradient(135deg,#fb2626,#ff6b6b);border-radius:48rpx;color:#fff;font-size:1.14rem;font-weight:500;padding:24rpx 80rpx}.submit-btn:active{opacity:.8}.user-info-section{border-top:1rpx solid #f5f5f5;margin-top:30rpx;padding-top:30rpx}.input-item{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;padding:20rpx 0}.input-item:last-child{border-bottom:none}.input-item-top{align-items:flex-start}.input-label{color:#333;font-size:1.07rem;margin-right:20rpx;text-align:right;width:138rpx}.input-value,.textarea-value{color:#333;flex:1;font-size:1.07rem}.textarea-value{min-height:100rpx}.required-mark{color:#fb2626;margin-left:4rpx}
1
+ .order-mask{align-items:flex-end;background-color:rgba(0,0,0,.5);bottom:0;display:flex;left:0;position:fixed;right:0;top:0;z-index:1000}.order-container{background-color:#fff;border-radius:24rpx 24rpx 0 0;display:flex;flex-direction:column;max-height:80vh;width:100%}.order-header{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;justify-content:space-between}.order-title{color:#333;font-size:1.29rem;font-weight:700}.order-close{align-items:center;display:flex;height:60rpx;justify-content:center;width:60rpx}.close-icon{color:#999;font-size:1.71rem;line-height:1}.order-content{box-sizing:border-box;flex:1;overflow-y:auto;padding:24rpx 12rpx}.product-section{margin-bottom:30rpx}.product-card{background-color:#fafafa;border-radius:16rpx;display:flex;padding:12rpx}.product-img{border-radius:12rpx;height:160rpx;margin-right:20rpx;width:160rpx}.product-detail{display:flex;flex:1;flex-direction:column;justify-content:space-between}.product-name{-webkit-line-clamp:2;-webkit-box-orient:vertical;color:#333;display:-webkit-box;font-size:1.14rem;font-weight:500;line-height:1.43rem;overflow:hidden}.product-desc{color:#999;font-size:.93rem;margin-top:8rpx}.product-price-row{align-items:baseline;display:flex;margin-top:12rpx}.product-price{color:#fb2626;font-size:1.29rem;font-weight:700}.product-discount-price{color:#fb2626;font-size:1.14rem}.product-original-price{color:#999;font-size:.86rem;margin-left:12rpx;text-decoration:line-through}.covered-price{color:#999;font-size:1rem}.quantity-section{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;justify-content:space-between;padding:20rpx 0}.section-title{color:#333;font-size:1.07rem}.qty-btn,.quantity-control{align-items:center;display:flex}.qty-btn{background-color:#f5f5f5;border-radius:50%;height:56rpx;justify-content:center;width:56rpx}.qty-btn .qty-btn-text{color:#333;font-size:1.29rem;line-height:1}.qty-value{color:#333;font-size:1.14rem;margin:0 20rpx;min-width:60rpx;text-align:center}.amount-section{margin-top:20rpx}.amount-row{align-items:center;display:flex;justify-content:space-between;padding:16rpx 0}.amount-row.total{border-top:1rpx dashed #e0e0e0;margin-top:10rpx;padding-top:26rpx}.amount-label{color:#666;font-size:1rem}.amount-row.total .amount-label{color:#333;font-size:1.07rem}.amount-value{color:#333;font-size:1rem}.amount-row.total .amount-value{color:#fb2626;font-size:1.29rem;font-weight:700}.order-footer{align-items:center;background-color:#fff;border-top:1rpx solid #f5f5f5;display:flex;justify-content:space-between;padding:24rpx 12rpx}.footer-left{align-items:baseline;display:flex}.footer-label{color:#666;font-size:1rem}.footer-total{color:#fb2626;font-size:1.43rem;font-weight:700;margin-left:8rpx}.submit-btn{background:linear-gradient(135deg,#fb2626,#ff6b6b);border-radius:48rpx;color:#fff;font-size:32rpx;font-weight:500;padding:12rpx 20rpx}.submit-btn:active{opacity:.8}.user-info-section{border-top:1rpx solid #f5f5f5;margin-top:30rpx;padding-top:30rpx}.input-item{align-items:center;border-bottom:1rpx solid #f5f5f5;display:flex;padding:20rpx 0}.input-item:last-child{border-bottom:none}.input-item-top{align-items:flex-start}.input-label{color:#333;font-size:1.07rem;margin-right:20rpx;text-align:right;width:160rpx}.input-value,.textarea-value{color:#333;flex:1;font-size:1.07rem}.textarea-value{min-height:100rpx}.required-mark{color:#fb2626;margin-left:4rpx}
@@ -1 +1 @@
1
- .countdown-container{gap:8rpx;justify-content:center;padding:16rpx}.countdown-container,.countdown-item{align-items:baseline;display:flex}.number{font-size:2.14rem;font-weight:600;margin:0 10rpx}.unit{font-size:.86rem}
1
+ .countdown-container{gap:8rpx;justify-content:center}.countdown-container,.countdown-item{align-items:baseline;display:flex}.number{font-size:56rpx;font-weight:600;margin:0 10rpx}.unit{font-size:.86rem}
@@ -129,6 +129,7 @@ Component({
129
129
  popupManager.unregisterZIndex(this.data.currentZIndex);
130
130
  this.setData({
131
131
  currentZIndex: null,
132
+ zIndex: 0,
132
133
  });
133
134
  }
134
135
  },