pb-sxp-ui 1.20.49 → 1.20.51

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.
package/dist/index.cjs CHANGED
@@ -20990,8 +20990,74 @@ const StructurePage = (_a) => {
20990
20990
  }
20991
20991
  });
20992
20992
  }, [bffEventReport]);
20993
+ // Hero Section 图片浏览事件上报
20994
+ const heroImageStartTime = React.useRef(0);
20995
+ const heroImageStartReported = React.useRef(false); // 标记是否已上报 start 事件
20996
+ const handleHeroImageStartEvent = React.useCallback(() => {
20997
+ if (!(data === null || data === void 0 ? void 0 : data.heroSection))
20998
+ return;
20999
+ const item = data.heroSection;
21000
+ // 只对图片类型上报(不包括视频)
21001
+ if (item.url)
21002
+ return;
21003
+ const startTime = Date.now();
21004
+ heroImageStartTime.current = startTime;
21005
+ heroImageStartReported.current = true; // 标记已上报
21006
+ console.log('[handleHeroImageStartEvent] 上报 viewImageCarouselStart');
21007
+ bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21008
+ eventInfo: {
21009
+ eventSubject: 'viewImageCarouselStart',
21010
+ eventDescription: 'User start view the image carousel',
21011
+ sceneId: '',
21012
+ contentId: item.itemId || '',
21013
+ contentName: item.title || '',
21014
+ imageStartTime: `${startTime}`,
21015
+ contentTags: item.tags ? JSON.stringify(item.tags) : '[]',
21016
+ position: '0',
21017
+ contentFormat: 'image',
21018
+ rtc: '',
21019
+ traceInfo: item.traceInfo || ''
21020
+ }
21021
+ });
21022
+ }, [data, bffEventReport]);
21023
+ const handleHeroImageEndEvent = React.useCallback(() => {
21024
+ if (!(data === null || data === void 0 ? void 0 : data.heroSection))
21025
+ return;
21026
+ const item = data.heroSection;
21027
+ // 只对图片类型上报(不包括视频)
21028
+ if (item.url)
21029
+ return;
21030
+ // 只有上报过 start 事件才上报 end 事件
21031
+ if (!heroImageStartReported.current) {
21032
+ console.log('[handleHeroImageEndEvent] 未上报过 start 事件,跳过 end 事件');
21033
+ return;
21034
+ }
21035
+ const endTime = Date.now();
21036
+ const duration = heroImageStartTime.current === 0 ? 0 : (endTime - heroImageStartTime.current) / 1000;
21037
+ console.log('[handleHeroImageEndEvent] 上报 viewImageCarouselEnd, duration:', duration);
21038
+ bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21039
+ eventInfo: {
21040
+ eventSubject: 'viewImageCarouselEnd',
21041
+ eventDescription: 'User end view the image carousel',
21042
+ sceneId: '',
21043
+ contentId: item.itemId || '',
21044
+ contentName: item.title || '',
21045
+ imageEndTime: `${endTime}`,
21046
+ playDuration: `${duration}`,
21047
+ contentTags: item.tags ? JSON.stringify(item.tags) : '[]',
21048
+ position: '0',
21049
+ contentFormat: 'image',
21050
+ rtc: '',
21051
+ traceInfo: item.traceInfo || ''
21052
+ }
21053
+ });
21054
+ // 重置标记,允许下次重新上报
21055
+ heroImageStartReported.current = false;
21056
+ heroImageStartTime.current = 0;
21057
+ }, [data, bffEventReport]);
20993
21058
  // Carousel 导航
20994
21059
  // Carousel 图片浏览开始事件上报
21060
+ const carouselImageStartReported = React.useRef({}); // 记录每个索引是否已上报 start
20995
21061
  const handleCarouselImageStartEvent = React.useCallback((index, loadTime) => {
20996
21062
  var _a, _b;
20997
21063
  if (!((_a = data === null || data === void 0 ? void 0 : data.carouselSection) === null || _a === void 0 ? void 0 : _a[index]))
@@ -21000,8 +21066,15 @@ const StructurePage = (_a) => {
21000
21066
  // 只对图片类型上报(不包括视频)
21001
21067
  if (item.url)
21002
21068
  return;
21069
+ // 如果该索引已经上报过 start 事件(且还没有 end),跳过重复上报
21070
+ if (carouselImageStartReported.current[index]) {
21071
+ console.log('[handleCarouselImageStartEvent] 索引', index, '已上报过 start 事件,跳过重复上报');
21072
+ return;
21073
+ }
21003
21074
  const startTime = Date.now();
21004
21075
  carouselImageStartTime.current = startTime;
21076
+ carouselImageStartReported.current[index] = true; // 标记已上报
21077
+ console.log('[handleCarouselImageStartEvent] 上报 viewImageCarouselStart, index:', index);
21005
21078
  bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21006
21079
  eventInfo: Object.assign(Object.assign({ eventSubject: 'viewImageCarouselStart', eventDescription: 'User start view the image carousel', sceneId: '', contentId: item.itemId || '', contentName: item.title || '', imageStartTime: `${startTime}`, contentTags: item.tags ? JSON.stringify(item.tags) : '[]', position: `${index}`, contentFormat: 'image', rtc: '', traceInfo: item.traceInfo || '' }, (loadTime && { loadTime: `${loadTime}` })), { contentSize: ((_b = item.imgUrls) === null || _b === void 0 ? void 0 : _b[0]) ? '1' : '0' })
21007
21080
  });
@@ -21015,8 +21088,14 @@ const StructurePage = (_a) => {
21015
21088
  // 只对图片类型上报(不包括视频)
21016
21089
  if (item.url)
21017
21090
  return;
21091
+ // 只有上报过 start 事件才上报 end 事件
21092
+ if (!carouselImageStartReported.current[index]) {
21093
+ console.log('[handleCarouselImageEndEvent] 索引', index, '未上报过 start 事件,跳过 end 事件');
21094
+ return;
21095
+ }
21018
21096
  const endTime = Date.now();
21019
21097
  const duration = carouselImageStartTime.current === 0 ? 0 : (endTime - carouselImageStartTime.current) / 1000;
21098
+ console.log('[handleCarouselImageEndEvent] 上报 viewImageCarouselEnd, index:', index, 'duration:', duration);
21020
21099
  bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21021
21100
  eventInfo: {
21022
21101
  eventSubject: 'viewImageCarouselEnd',
@@ -21033,6 +21112,9 @@ const StructurePage = (_a) => {
21033
21112
  traceInfo: item.traceInfo || ''
21034
21113
  }
21035
21114
  });
21115
+ // 重置标记,允许下次重新上报
21116
+ carouselImageStartReported.current[index] = false;
21117
+ carouselImageStartTime.current = 0;
21036
21118
  }, [data, bffEventReport]);
21037
21119
  const handleCarouselPrev = () => {
21038
21120
  var _a;
@@ -21126,55 +21208,121 @@ const StructurePage = (_a) => {
21126
21208
  handleCarouselImageStartEvent(newIndex, carouselImageLoadTime.current[newIndex]);
21127
21209
  }
21128
21210
  };
21211
+ // Hero Section 的曝光检测 ref
21212
+ const heroSectionRef = React.useRef(null);
21213
+ // Hero Section 进入可视区域时的回调
21214
+ const handleHeroVisible = React.useCallback(() => {
21215
+ var _a, _b;
21216
+ if (!(data === null || data === void 0 ? void 0 : data.heroSection))
21217
+ return;
21218
+ const item = data.heroSection;
21219
+ console.log('[Hero onVisible] 是否是视频:', !!item.url, '是否是图片:', !!((_a = item.imgUrls) === null || _a === void 0 ? void 0 : _a[0]));
21220
+ // 只对图片类型上报(不包括视频),允许重复上报
21221
+ if (!item.url && ((_b = item.imgUrls) === null || _b === void 0 ? void 0 : _b[0])) {
21222
+ console.log('[Hero onVisible] 上报 viewImageCarouselStart');
21223
+ handleHeroImageStartEvent();
21224
+ }
21225
+ }, [data === null || data === void 0 ? void 0 : data.heroSection, handleHeroImageStartEvent]);
21226
+ // Hero Section 离开可视区域时的回调
21227
+ const handleHeroHidden = React.useCallback(() => {
21228
+ var _a, _b;
21229
+ if (!(data === null || data === void 0 ? void 0 : data.heroSection))
21230
+ return;
21231
+ const item = data.heroSection;
21232
+ console.log('[Hero onHidden] 是否是视频:', !!item.url, '是否是图片:', !!((_a = item.imgUrls) === null || _a === void 0 ? void 0 : _a[0]));
21233
+ // 只对图片类型上报(不包括视频)
21234
+ if (!item.url && ((_b = item.imgUrls) === null || _b === void 0 ? void 0 : _b[0])) {
21235
+ console.log('[Hero onHidden] 上报 viewImageCarouselEnd');
21236
+ handleHeroImageEndEvent();
21237
+ }
21238
+ }, [data === null || data === void 0 ? void 0 : data.heroSection, handleHeroImageEndEvent]);
21239
+ // 使用高级曝光检测监听 Hero Section
21240
+ useAdvancedOnScreen(heroSectionRef, {
21241
+ threshold: 0.67, // 2/3 区域可见才开始计时
21242
+ delay: 1000, // 停留 1 秒后触发
21243
+ onVisible: handleHeroVisible,
21244
+ onHidden: handleHeroHidden
21245
+ });
21129
21246
  // Carousel Section 的曝光检测 ref
21130
21247
  const carouselSectionRef = React.useRef(null);
21131
- const hasCarouselReported = React.useRef(false);
21248
+ const carouselIsVisible = React.useRef(false); // 记录 carousel section 是否在可视区域内
21249
+ // 调试:检查 carouselSectionRef 是否正确挂载
21250
+ React.useEffect(() => {
21251
+ var _a;
21252
+ console.log('[Carousel Debug] carouselSectionRef.current:', !!carouselSectionRef.current);
21253
+ console.log('[Carousel Debug] carouselSection 数据存在:', !!(data === null || data === void 0 ? void 0 : data.carouselSection));
21254
+ console.log('[Carousel Debug] carouselSection 长度:', (_a = data === null || data === void 0 ? void 0 : data.carouselSection) === null || _a === void 0 ? void 0 : _a.length);
21255
+ }, [data === null || data === void 0 ? void 0 : data.carouselSection]);
21132
21256
  // Carousel Section 进入可视区域时的回调
21133
21257
  const handleCarouselVisible = React.useCallback(() => {
21134
- if (!(data === null || data === void 0 ? void 0 : data.carouselSection) || data.carouselSection.length === 0)
21258
+ var _a;
21259
+ console.log('[Carousel onVisible] 回调被触发');
21260
+ console.log('[Carousel onVisible] data?.carouselSection:', !!(data === null || data === void 0 ? void 0 : data.carouselSection));
21261
+ console.log('[Carousel onVisible] carouselSection.length:', (_a = data === null || data === void 0 ? void 0 : data.carouselSection) === null || _a === void 0 ? void 0 : _a.length);
21262
+ if (!(data === null || data === void 0 ? void 0 : data.carouselSection) || data.carouselSection.length === 0) {
21263
+ console.log('[Carousel onVisible] 没有 carouselSection 数据,返回');
21135
21264
  return;
21136
- const currentItem = data.carouselSection[carouselIndex];
21137
- console.log('[Carousel onVisible] 当前索引:', carouselIndex, '是否是视频:', !!(currentItem === null || currentItem === void 0 ? void 0 : currentItem.url));
21138
- // 只在首次进入可视区域时上报第一张图片
21139
- if (!hasCarouselReported.current) {
21140
- hasCarouselReported.current = true;
21141
- // 只对图片类型上报(不包括视频)
21142
- if (!currentItem.url) {
21143
- handleCarouselImageStartEvent(carouselIndex);
21144
- }
21265
+ }
21266
+ const currentIndex = currentCarouselIndexRef.current;
21267
+ const currentItem = data.carouselSection[currentIndex];
21268
+ carouselIsVisible.current = true;
21269
+ console.log('[Carousel onVisible] 当前索引:', currentIndex, '是否是视频:', !!(currentItem === null || currentItem === void 0 ? void 0 : currentItem.url));
21270
+ // 只对图片类型上报(不包括视频),允许重复上报
21271
+ if (!currentItem.url) {
21272
+ console.log('[Carousel onVisible] 上报 viewImageCarouselStart, 索引:', currentIndex);
21273
+ handleCarouselImageStartEvent(currentIndex);
21145
21274
  }
21146
21275
  // 如果当前项是视频,自动播放
21147
- if ((currentItem === null || currentItem === void 0 ? void 0 : currentItem.url) && carouselVideoRefs.current[carouselIndex]) {
21148
- const currentVideo = carouselVideoRefs.current[carouselIndex];
21276
+ if ((currentItem === null || currentItem === void 0 ? void 0 : currentItem.url) && carouselVideoRefs.current[currentIndex]) {
21277
+ const currentVideo = carouselVideoRefs.current[currentIndex];
21149
21278
  if (currentVideo) {
21150
- console.log('[Carousel onVisible] 开始播放视频,索引:', carouselIndex);
21279
+ console.log('[Carousel onVisible] 开始播放视频,索引:', currentIndex);
21151
21280
  // 更新当前正在播放的视频索引
21152
- currentPlayingCarouselIndex.current = carouselIndex;
21281
+ currentPlayingCarouselIndex.current = currentIndex;
21153
21282
  // 标记这次播放是自动触发的
21154
- carouselAutoPlayTriggers.current[carouselIndex] = true;
21283
+ carouselAutoPlayTriggers.current[currentIndex] = true;
21155
21284
  currentVideo.play().catch(err => {
21156
21285
  console.warn('[Carousel] Video autoplay failed (onVisible):', err);
21157
21286
  // 播放失败时重置标记
21158
- carouselAutoPlayTriggers.current[carouselIndex] = false;
21287
+ carouselAutoPlayTriggers.current[currentIndex] = false;
21159
21288
  });
21160
21289
  // 更新播放状态
21161
21290
  setCarouselVideoPausedStates(prev => {
21162
21291
  const newStates = [...prev];
21163
- newStates[carouselIndex] = false;
21292
+ newStates[currentIndex] = false;
21164
21293
  return newStates;
21165
21294
  });
21166
21295
  }
21167
21296
  }
21168
- }, [data === null || data === void 0 ? void 0 : data.carouselSection, carouselIndex, handleCarouselImageStartEvent]);
21297
+ }, [data === null || data === void 0 ? void 0 : data.carouselSection, handleCarouselImageStartEvent]);
21298
+ // 使用 ref 保存当前的 carouselIndex,避免闭包问题
21299
+ const currentCarouselIndexRef = React.useRef(carouselIndex);
21300
+ React.useEffect(() => {
21301
+ currentCarouselIndexRef.current = carouselIndex;
21302
+ }, [carouselIndex]);
21169
21303
  // Carousel Section 离开可视区域时的回调
21170
21304
  const handleCarouselHidden = React.useCallback(() => {
21305
+ var _a;
21306
+ console.log('[Carousel onHidden] ========== 回调被触发 ==========');
21171
21307
  // Carousel Section 离开可视区域时,暂停当前正在播放的视频
21172
21308
  // 使用 ref 来获取当前正在播放的视频索引,避免闭包问题
21173
21309
  const playingIndex = currentPlayingCarouselIndex.current;
21310
+ const currentIndex = currentCarouselIndexRef.current;
21311
+ carouselIsVisible.current = false;
21174
21312
  console.log('[Carousel onHidden] 被调用');
21313
+ console.log('[Carousel onHidden] 当前索引:', currentIndex);
21175
21314
  console.log('[Carousel onHidden] 正在播放的视频索引:', playingIndex);
21176
21315
  console.log('[Carousel onHidden] carouselVideoRefs.current 长度:', carouselVideoRefs.current.length);
21177
21316
  console.log('[Carousel onHidden] carouselVideoRefs.current[playingIndex] 存在:', !!carouselVideoRefs.current[playingIndex]);
21317
+ // 如果当前显示的是图片,上报图片结束事件
21318
+ if (((_a = data === null || data === void 0 ? void 0 : data.carouselSection) === null || _a === void 0 ? void 0 : _a[currentIndex]) && !data.carouselSection[currentIndex].url) {
21319
+ console.log('[Carousel onHidden] 当前是图片,尝试上报 viewImageCarouselEnd, 索引:', currentIndex);
21320
+ console.log('[Carousel onHidden] carouselImageStartReported[' + currentIndex + ']:', carouselImageStartReported.current[currentIndex]);
21321
+ handleCarouselImageEndEvent(currentIndex);
21322
+ }
21323
+ else {
21324
+ console.log('[Carousel onHidden] 当前不是图片或数据为空,跳过上报');
21325
+ }
21178
21326
  if (playingIndex >= 0 && carouselVideoRefs.current[playingIndex]) {
21179
21327
  const currentVideo = carouselVideoRefs.current[playingIndex];
21180
21328
  if (currentVideo) {
@@ -21195,15 +21343,96 @@ const StructurePage = (_a) => {
21195
21343
  else {
21196
21344
  console.log('[Carousel onHidden] 条件不满足,不暂停视频');
21197
21345
  }
21198
- }, []);
21346
+ }, [data, handleCarouselImageEndEvent]);
21199
21347
  // Carousel Section 进入可视区域时上报第一张图片的浏览事件,并自动播放视频
21200
- useAdvancedOnScreen(carouselSectionRef, {
21201
- threshold: 0.67,
21202
- delay: 1000,
21203
- onVisible: handleCarouselVisible,
21204
- onHidden: handleCarouselHidden,
21205
- name: 'Carousel'
21206
- });
21348
+ // 注意:只有当 carouselSection 数据存在且 ref 已挂载时才启用监听
21349
+ React.useEffect(() => {
21350
+ if (!(data === null || data === void 0 ? void 0 : data.carouselSection) || !carouselSectionRef.current) {
21351
+ return;
21352
+ }
21353
+ let visibleTimer = null;
21354
+ // 手动创建 IntersectionObserver
21355
+ const observer = new IntersectionObserver(([entry]) => {
21356
+ const isIntersecting = entry.isIntersecting;
21357
+ const intersectionRatio = entry.intersectionRatio;
21358
+ console.log('[Carousel Observer] 状态变化:', {
21359
+ isIntersecting,
21360
+ intersectionRatio,
21361
+ threshold: 0.67
21362
+ });
21363
+ if (isIntersecting && intersectionRatio >= 0.67) {
21364
+ // 进入可视区域且达到阈值
21365
+ console.log('[Carousel Observer] 进入可视区域,设置延迟定时器 1000ms');
21366
+ // 清除旧的定时器
21367
+ if (visibleTimer) {
21368
+ clearTimeout(visibleTimer);
21369
+ }
21370
+ visibleTimer = setTimeout(() => {
21371
+ console.log('[Carousel Observer] 延迟时间到,触发 handleCarouselVisible');
21372
+ handleCarouselVisible();
21373
+ visibleTimer = null;
21374
+ }, 1000);
21375
+ }
21376
+ else if (!isIntersecting || intersectionRatio < 0.67) {
21377
+ // 离开可视区域或未达到阈值
21378
+ console.log('[Carousel Observer] 离开可视区域');
21379
+ // 清除定时器
21380
+ if (visibleTimer) {
21381
+ console.log('[Carousel Observer] 清除延迟定时器(还未到时间)');
21382
+ clearTimeout(visibleTimer);
21383
+ visibleTimer = null;
21384
+ }
21385
+ else {
21386
+ console.log('[Carousel Observer] 触发 handleCarouselHidden');
21387
+ handleCarouselHidden();
21388
+ }
21389
+ }
21390
+ }, { threshold: 0.67 });
21391
+ observer.observe(carouselSectionRef.current);
21392
+ return () => {
21393
+ console.log('[Carousel] 停止监听 carousel section');
21394
+ if (visibleTimer) {
21395
+ clearTimeout(visibleTimer);
21396
+ }
21397
+ observer.disconnect();
21398
+ };
21399
+ }, [data === null || data === void 0 ? void 0 : data.carouselSection, handleCarouselVisible, handleCarouselHidden]);
21400
+ // 监听 carouselIndex 变化,当 carousel section 可见时上报图片切换事件
21401
+ const prevCarouselIndex = React.useRef(0);
21402
+ React.useEffect(() => {
21403
+ if (!(data === null || data === void 0 ? void 0 : data.carouselSection) || data.carouselSection.length === 0)
21404
+ return;
21405
+ if (!carouselIsVisible.current)
21406
+ return; // 只在 carousel 可见时处理
21407
+ const prevIndex = prevCarouselIndex.current;
21408
+ const currentIndex = carouselIndex;
21409
+ // 如果索引发生变化
21410
+ if (prevIndex !== currentIndex) {
21411
+ console.log('[Carousel Index Change] 从', prevIndex, '切换到', currentIndex);
21412
+ console.log('[Carousel Index Change] carouselIsVisible:', carouselIsVisible.current);
21413
+ // 上报旧图片的结束事件(如果是图片)
21414
+ const prevItem = data.carouselSection[prevIndex];
21415
+ if (prevItem && !prevItem.url) {
21416
+ console.log('[Carousel Index Change] 旧图片, 索引:', prevIndex, 'startReported:', carouselImageStartReported.current[prevIndex]);
21417
+ handleCarouselImageEndEvent(prevIndex);
21418
+ }
21419
+ else {
21420
+ console.log('[Carousel Index Change] 旧项不是图片,索引:', prevIndex);
21421
+ }
21422
+ // 上报新图片的开始事件(如果是图片)
21423
+ const currentItem = data.carouselSection[currentIndex];
21424
+ if (currentItem && !currentItem.url) {
21425
+ console.log('[Carousel Index Change] 新图片, 索引:', currentIndex, '即将上报 start');
21426
+ handleCarouselImageStartEvent(currentIndex, carouselImageLoadTime.current[currentIndex]);
21427
+ console.log('[Carousel Index Change] 新图片 start 已上报, startReported:', carouselImageStartReported.current[currentIndex]);
21428
+ }
21429
+ else {
21430
+ console.log('[Carousel Index Change] 新项不是图片,索引:', currentIndex);
21431
+ }
21432
+ // 更新 prevIndex
21433
+ prevCarouselIndex.current = currentIndex;
21434
+ }
21435
+ }, [carouselIndex, data === null || data === void 0 ? void 0 : data.carouselSection, handleCarouselImageStartEvent, handleCarouselImageEndEvent]);
21207
21436
  // Carousel 切换时暂停其他视频
21208
21437
  // 注意:不再在切换时立即播放当前视频,而是由 PostView 组件处理(进入2/3可视区域并停留1秒后播放)
21209
21438
  React.useEffect(() => {
@@ -21507,10 +21736,6 @@ const StructurePage = (_a) => {
21507
21736
  view_time: viewTime,
21508
21737
  content_id: (productData === null || productData === void 0 ? void 0 : productData.itemId) || ''
21509
21738
  });
21510
- console.log('[StructurePage] PAGE_DID_HIDE - 上报 ExitFeed (弹窗内)', {
21511
- viewTime,
21512
- productId: productData === null || productData === void 0 ? void 0 : productData.itemId
21513
- });
21514
21739
  }
21515
21740
  };
21516
21741
  SXP_EVENT_BUS.on(SXP_EVENT_TYPE.PAGE_DID_SHOW, onShow);
@@ -21550,7 +21775,7 @@ const StructurePage = (_a) => {
21550
21775
  return (React.createElement("div", { style: Object.assign(Object.assign({}, baseStyles.container), { height: containerHeight, width: containerWidth, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: '16px' }) }, "No data available"));
21551
21776
  }
21552
21777
  return (React.createElement("div", { className: className, style: Object.assign(Object.assign(Object.assign(Object.assign({}, baseStyles.container), { height: containerHeight, width: containerWidth }), containerStyle), { backgroundColor: 'transparent' }) },
21553
- data.heroSection && (React.createElement("div", { style: baseStyles.heroSection },
21778
+ data.heroSection && (React.createElement("div", { ref: heroSectionRef, style: baseStyles.heroSection },
21554
21779
  data.heroSection.text && (React.createElement("div", { style: {
21555
21780
  width: '100%',
21556
21781
  padding: (() => {
@@ -21673,15 +21898,12 @@ const StructurePage = (_a) => {
21673
21898
  // Cover 已加载,计算已显示的时间
21674
21899
  const coverDisplayTime = Date.now() - heroCoverLoadTime.current;
21675
21900
  const remainingDisplayTime = Math.max(0, minCoverDisplayTime - coverDisplayTime);
21676
- console.log('[Hero Video] Cover loaded, displayed for', coverDisplayTime, 'ms, waiting', remainingDisplayTime, 'ms more before showing video');
21677
21901
  if (remainingDisplayTime > 0) {
21678
21902
  // 还需要继续显示 cover
21679
21903
  setTimeout(() => {
21680
- console.log('[Hero Video] Cover displayed long enough, transitioning to video');
21681
21904
  // 检查视频当前的缓冲状态
21682
21905
  // readyState: 0=HAVE_NOTHING, 1=HAVE_METADATA, 2=HAVE_CURRENT_DATA, 3=HAVE_FUTURE_DATA, 4=HAVE_ENOUGH_DATA
21683
21906
  const isVideoBuffering = videoEl.readyState < 3; // 小于 3 说明还在缓冲
21684
- console.log('[Hero Video] Video readyState:', videoEl.readyState, 'isBuffering:', isVideoBuffering);
21685
21907
  if (isVideoBuffering) {
21686
21908
  setIsHeroVideoWaiting(true);
21687
21909
  }
@@ -21693,10 +21915,8 @@ const StructurePage = (_a) => {
21693
21915
  }
21694
21916
  else {
21695
21917
  // 已经显示足够长时间,直接显示视频
21696
- console.log('[Hero Video] Cover already displayed long enough, transitioning to video');
21697
21918
  // 检查视频当前的缓冲状态
21698
21919
  const isVideoBuffering = videoEl.readyState < 3;
21699
- console.log('[Hero Video] Video readyState:', videoEl.readyState, 'isBuffering:', isVideoBuffering);
21700
21920
  if (isVideoBuffering) {
21701
21921
  setIsHeroVideoWaiting(true);
21702
21922
  }
@@ -21743,14 +21963,9 @@ const StructurePage = (_a) => {
21743
21963
  const initTime = videoLoadTime.current[videoId] || loadTime;
21744
21964
  videoLoadTime.current[videoId] = loadTime - initTime;
21745
21965
  } }),
21746
- (!isHeroVideoLoaded || (((_g = data.heroSection) === null || _g === void 0 ? void 0 : _g.cover) && !isHeroCoverLoaded)) && (React.createElement("img", { src: (((_h = data.heroSection) === null || _h === void 0 ? void 0 : _h.cover) && !isHeroCoverError) ? data.heroSection.cover : ((sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.loading_image) || img$2), alt: 'video cover', onLoad: () => {
21747
- var _a, _b;
21966
+ (!isHeroVideoLoaded || (((_g = data.heroSection) === null || _g === void 0 ? void 0 : _g.cover) && !isHeroCoverLoaded)) && (React.createElement("img", { src: (((_h = data.heroSection) === null || _h === void 0 ? void 0 : _h.cover) && !isHeroCoverError) ? data.heroSection.cover : ((sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.placeholder_image) || img$2), alt: 'video cover', onLoad: () => {
21748
21967
  const loadTime = Date.now();
21749
21968
  heroCoverLoadTime.current = loadTime;
21750
- console.log('[Hero] Cover/Loading image loaded successfully at', loadTime);
21751
- console.log(' - cover:', (_a = data.heroSection) === null || _a === void 0 ? void 0 : _a.cover);
21752
- console.log(' - loading_image:', sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.loading_image);
21753
- console.log(' - using:', (((_b = data.heroSection) === null || _b === void 0 ? void 0 : _b.cover) && !isHeroCoverError) ? data.heroSection.cover : ((sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.loading_image) || img$2));
21754
21969
  setIsHeroCoverError(false);
21755
21970
  // 延迟一点再设置 loaded 状态,确保图片已经渲染到屏幕上
21756
21971
  setTimeout(() => {
@@ -21759,11 +21974,11 @@ const StructurePage = (_a) => {
21759
21974
  }, 100); // 给 100ms 的渲染时间
21760
21975
  }, onError: (e) => {
21761
21976
  var _a, _b;
21762
- console.error('[Hero] Cover/Loading image failed to load:', e);
21977
+ console.error('[Hero] Cover/Placeholder image failed to load:', e);
21763
21978
  console.log(' - cover:', (_a = data.heroSection) === null || _a === void 0 ? void 0 : _a.cover);
21764
- console.log(' - loading_image:', sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.loading_image);
21765
- console.log(' - Switching to loading animation');
21766
- // 如果 cover 加载失败,标记错误并切换到加载动画
21979
+ console.log(' - placeholder_image:', sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.placeholder_image);
21980
+ console.log(' - Switching to fallback image');
21981
+ // 如果 cover 加载失败,标记错误并切换到占位图
21767
21982
  if ((_b = data.heroSection) === null || _b === void 0 ? void 0 : _b.cover) {
21768
21983
  setIsHeroCoverError(true);
21769
21984
  }
@@ -21856,29 +22071,30 @@ const StructurePage = (_a) => {
21856
22071
  const elapsedTime = Date.now() - startTime;
21857
22072
  const remainingTime = Math.max(0, minDisplayTime - elapsedTime);
21858
22073
  setTimeout(() => {
21859
- // 视频准备好了,隐藏 cover/placeholder
21860
- setCarouselShowCoverStates(prev => {
22074
+ // 步骤1:先让视频淡入(在 cover 下面),等待视频完全不透明
22075
+ setCarouselVideoLoadedStates(prev => {
21861
22076
  const newStates = [...prev];
21862
- newStates[index] = false;
22077
+ newStates[index] = true;
21863
22078
  return newStates;
21864
22079
  });
21865
- // 重置暂停状态(视频开始播放,不是暂停状态)
21866
- setCarouselVideoPausedStates(prev => {
22080
+ setCarouselVideoWaitingStates(prev => {
21867
22081
  const newStates = [...prev];
21868
22082
  newStates[index] = false;
21869
22083
  return newStates;
21870
22084
  });
21871
- // 标记为已加载
21872
- setCarouselVideoLoadedStates(prev => {
21873
- const newStates = [...prev];
21874
- newStates[index] = true;
21875
- return newStates;
21876
- });
21877
- setCarouselVideoWaitingStates(prev => {
22085
+ setCarouselVideoPausedStates(prev => {
21878
22086
  const newStates = [...prev];
21879
22087
  newStates[index] = false;
21880
22088
  return newStates;
21881
22089
  });
22090
+ // 步骤2:等待视频淡入完成(300ms)+ 额外缓冲(50ms),再开始淡出 cover
22091
+ setTimeout(() => {
22092
+ setCarouselShowCoverStates(prev => {
22093
+ const newStates = [...prev];
22094
+ newStates[index] = false;
22095
+ return newStates;
22096
+ });
22097
+ }, 350); // 视频淡入时间 300ms + 50ms 缓冲
21882
22098
  }, remainingTime);
21883
22099
  });
21884
22100
  el.addEventListener('pause', (e) => {
@@ -21911,7 +22127,7 @@ const StructurePage = (_a) => {
21911
22127
  });
21912
22128
  });
21913
22129
  }
21914
- }, src: item.url, style: Object.assign(Object.assign({}, baseStyles.carouselVideo), { opacity: carouselVideoLoadedStates[index] ? 1 : 0, transition: 'opacity 0.3s ease-in-out' }), preload: "auto", muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
22130
+ }, src: item.url, style: Object.assign(Object.assign({}, baseStyles.carouselVideo), { position: 'absolute', left: 0, top: 0, zIndex: 1, opacity: carouselVideoLoadedStates[index] ? 1 : 0, transition: 'opacity 0.3s ease-in-out' }), preload: "auto", muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
21915
22131
  const videoId = `${item.itemId}-carouselSection-${index}`;
21916
22132
  const loadTime = Date.now();
21917
22133
  const initTime = videoLoadTime.current[videoId] || loadTime;
@@ -21928,11 +22144,12 @@ const StructurePage = (_a) => {
21928
22144
  : (item.cover || (sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.placeholder_image)) ? 'cover' : 'contain',
21929
22145
  zIndex: 10,
21930
22146
  backgroundColor: '#000',
21931
- display: carouselShowCoverStates[index] ? 'block' : 'none',
22147
+ opacity: carouselShowCoverStates[index] ? 1 : 0,
22148
+ visibility: carouselShowCoverStates[index] ? 'visible' : 'hidden',
22149
+ transition: 'opacity 0.3s ease-out',
21932
22150
  pointerEvents: 'none',
21933
- // CSS 优化:使用 will-change 和 content-visibility
21934
- willChange: carouselShowCoverStates[index] ? 'auto' : 'contents',
21935
- contentVisibility: carouselShowCoverStates[index] ? 'visible' : 'hidden',
22151
+ // CSS 优化:使用 will-change
22152
+ willChange: 'opacity',
21936
22153
  // 硬件加速
21937
22154
  transform: 'translateZ(0)',
21938
22155
  backfaceVisibility: 'hidden',
@@ -21980,29 +22197,30 @@ const StructurePage = (_a) => {
21980
22197
  const elapsedTime = Date.now() - startTime;
21981
22198
  const remainingTime = Math.max(0, minDisplayTime - elapsedTime);
21982
22199
  setTimeout(() => {
21983
- // 视频准备好了,隐藏 cover/placeholder
21984
- setCarouselShowCoverStates(prev => {
22200
+ // 步骤1:先让视频淡入(在 cover 下面),等待视频完全不透明
22201
+ setCarouselVideoLoadedStates(prev => {
21985
22202
  const newStates = [...prev];
21986
- newStates[index] = false;
22203
+ newStates[index] = true;
21987
22204
  return newStates;
21988
22205
  });
21989
- // 重置暂停状态(视频开始播放,不是暂停状态)
21990
- setCarouselVideoPausedStates(prev => {
22206
+ setCarouselVideoWaitingStates(prev => {
21991
22207
  const newStates = [...prev];
21992
22208
  newStates[index] = false;
21993
22209
  return newStates;
21994
22210
  });
21995
- // 标记为已加载
21996
- setCarouselVideoLoadedStates(prev => {
21997
- const newStates = [...prev];
21998
- newStates[index] = true;
21999
- return newStates;
22000
- });
22001
- setCarouselVideoWaitingStates(prev => {
22211
+ setCarouselVideoPausedStates(prev => {
22002
22212
  const newStates = [...prev];
22003
22213
  newStates[index] = false;
22004
22214
  return newStates;
22005
22215
  });
22216
+ // 步骤2:等待视频淡入完成(300ms)+ 额外缓冲(50ms),再开始淡出 cover
22217
+ setTimeout(() => {
22218
+ setCarouselShowCoverStates(prev => {
22219
+ const newStates = [...prev];
22220
+ newStates[index] = false;
22221
+ return newStates;
22222
+ });
22223
+ }, 350); // 视频淡入时间 300ms + 50ms 缓冲
22006
22224
  }, remainingTime);
22007
22225
  });
22008
22226
  el.addEventListener('pause', (e) => {
@@ -22035,7 +22253,7 @@ const StructurePage = (_a) => {
22035
22253
  });
22036
22254
  });
22037
22255
  }
22038
- }, src: item.url, style: Object.assign(Object.assign({}, baseStyles.carouselVideo), { opacity: carouselVideoLoadedStates[index] ? 1 : 0, transition: 'opacity 0.3s ease-in-out' }), preload: "auto", muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
22256
+ }, src: item.url, style: Object.assign(Object.assign({}, baseStyles.carouselVideo), { position: 'absolute', left: 0, top: 0, zIndex: 1, opacity: carouselVideoLoadedStates[index] ? 1 : 0, transition: 'opacity 0.3s ease-in-out' }), preload: "auto", muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
22039
22257
  const videoId = `${item.itemId}-carouselSection-${index}`;
22040
22258
  const loadTime = Date.now();
22041
22259
  const initTime = videoLoadTime.current[videoId] || loadTime;
@@ -22052,11 +22270,12 @@ const StructurePage = (_a) => {
22052
22270
  : (item.cover || (sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.placeholder_image)) ? 'cover' : 'contain',
22053
22271
  zIndex: 10,
22054
22272
  backgroundColor: '#000',
22055
- display: carouselShowCoverStates[index] ? 'block' : 'none',
22273
+ opacity: carouselShowCoverStates[index] ? 1 : 0,
22274
+ visibility: carouselShowCoverStates[index] ? 'visible' : 'hidden',
22275
+ transition: 'opacity 0.3s ease-out',
22056
22276
  pointerEvents: 'none',
22057
- // CSS 优化:使用 will-change 和 content-visibility
22058
- willChange: carouselShowCoverStates[index] ? 'auto' : 'contents',
22059
- contentVisibility: carouselShowCoverStates[index] ? 'visible' : 'hidden',
22277
+ // CSS 优化:使用 will-change
22278
+ willChange: 'opacity',
22060
22279
  // 硬件加速
22061
22280
  transform: 'translateZ(0)',
22062
22281
  backfaceVisibility: 'hidden',
@@ -22223,56 +22442,57 @@ const StructurePage = (_a) => {
22223
22442
  React.createElement("div", { style: { width: '100%', height: 0, visibility: 'hidden' } }))));
22224
22443
  }).filter(Boolean);
22225
22444
  })())),
22226
- data.footerSection && (React.createElement("div", { style: mergeStyles(baseStyles.footerSection, 'footerSection', { excludeBorder: true }) },
22227
- React.createElement("div", { style: {
22228
- width: '100%',
22229
- padding: '20px',
22230
- backgroundColor: '#000',
22231
- color: '#fff',
22232
- textAlign: ((_z = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.footerSection) === null || _z === void 0 ? void 0 : _z.textAlign) || 'center'
22233
- } },
22234
- data.footerSection.text && (React.createElement("div", { style: { marginBottom: '15px' } },
22235
- React.createElement("span", { style: Object.assign({}, (() => {
22236
- const config = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.footerSection;
22237
- if (!config) {
22238
- return {
22239
- fontSize: '18px',
22240
- fontWeight: 'normal',
22445
+ data.footerSection && (React.createElement(ProductView, { onExposure: () => handleProductViewExposure(data.footerSection, 'footerSection', 0), onLeave: () => handleProductViewLeave(data.footerSection, 'footerSection', 0) },
22446
+ React.createElement("div", { style: mergeStyles(baseStyles.footerSection, 'footerSection', { excludeBorder: true }) },
22447
+ React.createElement("div", { style: {
22448
+ width: '100%',
22449
+ padding: '20px',
22450
+ backgroundColor: '#000',
22451
+ color: '#fff',
22452
+ textAlign: ((_z = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.footerSection) === null || _z === void 0 ? void 0 : _z.textAlign) || 'center'
22453
+ } },
22454
+ data.footerSection.text && (React.createElement("div", { style: { marginBottom: '15px' } },
22455
+ React.createElement("span", { style: Object.assign({}, (() => {
22456
+ const config = multiCTAConfig === null || multiCTAConfig === void 0 ? void 0 : multiCTAConfig.footerSection;
22457
+ if (!config) {
22458
+ return {
22459
+ fontSize: '18px',
22460
+ fontWeight: 'normal',
22461
+ lineHeight: '1.4',
22462
+ color: '#fff'
22463
+ };
22464
+ }
22465
+ const textStyle = {
22466
+ fontSize: config.fontSize ? `${config.fontSize}px` : '18px',
22467
+ fontWeight: config.fontWeight || 'normal',
22468
+ fontStyle: config.fontStyle || 'normal',
22469
+ textDecoration: config.textDecoration || 'none',
22470
+ color: config.color || '#fff',
22241
22471
  lineHeight: '1.4',
22242
- color: '#fff'
22472
+ display: 'inline-block',
22473
+ boxSizing: 'border-box'
22243
22474
  };
22244
- }
22245
- const textStyle = {
22246
- fontSize: config.fontSize ? `${config.fontSize}px` : '18px',
22247
- fontWeight: config.fontWeight || 'normal',
22248
- fontStyle: config.fontStyle || 'normal',
22249
- textDecoration: config.textDecoration || 'none',
22250
- color: config.color || '#fff',
22251
- lineHeight: '1.4',
22252
- display: 'inline-block',
22253
- boxSizing: 'border-box'
22254
- };
22255
- if (config.backgroundColor) {
22256
- textStyle.backgroundColor = config.backgroundColor;
22257
- }
22258
- if (config.showBorder && config.borderWidth) {
22259
- textStyle.border = `${config.borderWidth}px solid ${config.borderColor || '#d9d9d9'}`;
22260
- textStyle.padding = config.padding || '4px 8px';
22261
- }
22262
- else if (config.padding) {
22263
- textStyle.padding = config.padding;
22264
- }
22265
- if (config.borderRadius) {
22266
- textStyle.borderRadius = `${config.borderRadius}px`;
22267
- }
22268
- if (config.margin) {
22269
- textStyle.margin = config.margin;
22270
- }
22271
- return textStyle;
22272
- })()) }, data.footerSection.text))),
22273
- renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton, 'footerSection', 0)),
22274
- React.createElement("div", { style: baseStyles.footerImageContainer },
22275
- React.createElement("img", { src: data.footerSection.landingImageUrl || data.footerSection.cover, alt: data.footerSection.title, style: baseStyles.footerImage }))))));
22475
+ if (config.backgroundColor) {
22476
+ textStyle.backgroundColor = config.backgroundColor;
22477
+ }
22478
+ if (config.showBorder && config.borderWidth) {
22479
+ textStyle.border = `${config.borderWidth}px solid ${config.borderColor || '#d9d9d9'}`;
22480
+ textStyle.padding = config.padding || '4px 8px';
22481
+ }
22482
+ else if (config.padding) {
22483
+ textStyle.padding = config.padding;
22484
+ }
22485
+ if (config.borderRadius) {
22486
+ textStyle.borderRadius = `${config.borderRadius}px`;
22487
+ }
22488
+ if (config.margin) {
22489
+ textStyle.margin = config.margin;
22490
+ }
22491
+ return textStyle;
22492
+ })()) }, data.footerSection.text))),
22493
+ renderCTA('footerButton', data.footerSection.bindCta, data.footerSection, baseStyles.footerButton, 'footerSection', 0)),
22494
+ React.createElement("div", { style: baseStyles.footerImageContainer },
22495
+ React.createElement("img", { src: data.footerSection.landingImageUrl || data.footerSection.cover, alt: data.footerSection.title, style: baseStyles.footerImage })))))));
22276
22496
  };
22277
22497
 
22278
22498
  var index$3 = /*#__PURE__*/Object.freeze({