pb-sxp-ui 1.20.44 → 1.20.45
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 +126 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -31
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs +6 -6
- package/dist/index.min.cjs.map +1 -1
- package/dist/index.min.js +6 -6
- package/dist/index.min.js.map +1 -1
- package/dist/pb-ui.js +126 -31
- package/dist/pb-ui.js.map +1 -1
- package/dist/pb-ui.min.js +7 -7
- package/dist/pb-ui.min.js.map +1 -1
- package/es/core/components/StructurePage/index.js +100 -24
- package/lib/core/components/StructurePage/index.js +100 -24
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -19611,15 +19611,19 @@ const CTAButton = ({ ctaData, style, onClick, onExposure }) => {
|
|
|
19611
19611
|
});
|
|
19612
19612
|
return (React.createElement("button", { ref: buttonRef, style: style, onClick: onClick }, ctaData.title));
|
|
19613
19613
|
};
|
|
19614
|
-
const ProductView = ({ children, onExposure }) => {
|
|
19614
|
+
const ProductView = ({ children, onExposure, onLeave }) => {
|
|
19615
19615
|
const productRef = React.useRef(null);
|
|
19616
19616
|
// 使用高级曝光检测:2/3区域 + 1000ms延迟
|
|
19617
19617
|
useAdvancedOnScreen(productRef, {
|
|
19618
19618
|
threshold: 0.67, // 2/3 区域可见才开始计时
|
|
19619
19619
|
delay: 1000,
|
|
19620
19620
|
onVisible: () => {
|
|
19621
|
-
// 每次进入2/3可视区域并停留1
|
|
19621
|
+
// 每次进入2/3可视区域并停留1秒后记录开始浏览时间
|
|
19622
19622
|
onExposure();
|
|
19623
|
+
},
|
|
19624
|
+
onHidden: () => {
|
|
19625
|
+
// 离开可视区域时上报productView事件
|
|
19626
|
+
onLeave === null || onLeave === void 0 ? void 0 : onLeave();
|
|
19623
19627
|
}
|
|
19624
19628
|
});
|
|
19625
19629
|
return (React.createElement("div", { ref: productRef, style: { width: '100%' } }, children));
|
|
@@ -20037,6 +20041,7 @@ const StructurePage = (_a) => {
|
|
|
20037
20041
|
const isFirstPlay = React.useRef({});
|
|
20038
20042
|
const videoLoadTime = React.useRef({});
|
|
20039
20043
|
const videoAutoPlayReported = React.useRef(new Set()); // 追踪哪些视频已通过自动播放上报
|
|
20044
|
+
const videoPlayReportTime = React.useRef({}); // 追踪视频播放上报的时间戳,防止短时间内重复上报
|
|
20040
20045
|
// 自动播放触发标记(用于区分自动播放和用户手动播放)
|
|
20041
20046
|
const heroAutoPlayTrigger = React.useRef(false);
|
|
20042
20047
|
const carouselAutoPlayTriggers = React.useRef({});
|
|
@@ -20285,6 +20290,16 @@ const StructurePage = (_a) => {
|
|
|
20285
20290
|
return;
|
|
20286
20291
|
}
|
|
20287
20292
|
const videoId = `${videoData === null || videoData === void 0 ? void 0 : videoData.itemId}-${viewPosition}-${sectionIndex}`;
|
|
20293
|
+
const now = Date.now();
|
|
20294
|
+
// 防止短时间内重复上报(play 和 playing 事件可能连续触发)
|
|
20295
|
+
// 如果距离上次上报不到500ms,则跳过
|
|
20296
|
+
const lastReportTime = videoPlayReportTime.current[videoId];
|
|
20297
|
+
if (lastReportTime && (now - lastReportTime) < 500) {
|
|
20298
|
+
console.log('[handleVideoPlay] 跳过上报:距离上次上报时间过短', {
|
|
20299
|
+
timeSinceLastReport: now - lastReportTime
|
|
20300
|
+
});
|
|
20301
|
+
return;
|
|
20302
|
+
}
|
|
20288
20303
|
// 检查是否是自动播放触发的
|
|
20289
20304
|
let isAutoPlay = false;
|
|
20290
20305
|
if (viewPosition === 'heroSection' && heroAutoPlayTrigger.current) {
|
|
@@ -20314,6 +20329,8 @@ const StructurePage = (_a) => {
|
|
|
20314
20329
|
}
|
|
20315
20330
|
// 记录视频开始播放时间
|
|
20316
20331
|
videoStartTime.current[videoId] = videoRef.currentTime || 0;
|
|
20332
|
+
// 记录本次上报时间
|
|
20333
|
+
videoPlayReportTime.current[videoId] = now;
|
|
20317
20334
|
// 判断是否首次播放
|
|
20318
20335
|
const playType = isFirstPlay.current[videoId] === false ? '1' : '0';
|
|
20319
20336
|
isFirstPlay.current[videoId] = false;
|
|
@@ -20385,20 +20402,40 @@ const StructurePage = (_a) => {
|
|
|
20385
20402
|
}
|
|
20386
20403
|
});
|
|
20387
20404
|
}, [bffEventReport, data]);
|
|
20388
|
-
//
|
|
20405
|
+
// 产品进入可视区域时记录开始浏览时间
|
|
20389
20406
|
const handleProductViewExposure = React.useCallback((productData, viewPosition, sectionIndex) => {
|
|
20407
|
+
// 生成唯一标识
|
|
20408
|
+
const productId = `${productData === null || productData === void 0 ? void 0 : productData.itemId}-${viewPosition}-${sectionIndex}`;
|
|
20409
|
+
// 如果已经记录过开始时间,不再重复记录
|
|
20410
|
+
if (productViewStartTime.current[productId]) {
|
|
20411
|
+
return;
|
|
20412
|
+
}
|
|
20413
|
+
// 记录开始浏览时间
|
|
20414
|
+
productViewStartTime.current[productId] = Date.now();
|
|
20415
|
+
console.log('[handleProductViewExposure] 记录产品开始浏览时间:', {
|
|
20416
|
+
productId,
|
|
20417
|
+
startTime: productViewStartTime.current[productId]
|
|
20418
|
+
});
|
|
20419
|
+
}, []);
|
|
20420
|
+
// 产品离开可视区域时上报 productView 事件
|
|
20421
|
+
const handleProductViewLeave = React.useCallback((productData, viewPosition, sectionIndex) => {
|
|
20390
20422
|
var _a;
|
|
20391
|
-
//
|
|
20423
|
+
// 生成唯一标识
|
|
20392
20424
|
const productId = `${productData === null || productData === void 0 ? void 0 : productData.itemId}-${viewPosition}-${sectionIndex}`;
|
|
20393
|
-
//
|
|
20425
|
+
// 如果已经上报过,不再重复上报
|
|
20394
20426
|
if (exposedProductRefs.current.has(productId)) {
|
|
20395
20427
|
return;
|
|
20396
20428
|
}
|
|
20397
|
-
//
|
|
20429
|
+
// 获取开始浏览时间
|
|
20430
|
+
const startTime = productViewStartTime.current[productId];
|
|
20431
|
+
if (!startTime) {
|
|
20432
|
+
console.warn('[handleProductViewLeave] 未找到开始浏览时间,跳过上报:', productId);
|
|
20433
|
+
return;
|
|
20434
|
+
}
|
|
20435
|
+
// 计算停留时长(秒)
|
|
20436
|
+
const timeOnSite = Math.floor((Date.now() - startTime) / 1000);
|
|
20437
|
+
// 标记为已上报
|
|
20398
20438
|
exposedProductRefs.current.add(productId);
|
|
20399
|
-
productViewStartTime.current[productId] = Date.now();
|
|
20400
|
-
// 计算在页面停留时间(从产品出现到现在)
|
|
20401
|
-
const timeOnSite = 0; // 首次曝光时为0
|
|
20402
20439
|
// 根据 viewPosition 确定 fromKName
|
|
20403
20440
|
let fromKName = 'productPage';
|
|
20404
20441
|
if (viewPosition === 'heroSection') {
|
|
@@ -20413,6 +20450,11 @@ const StructurePage = (_a) => {
|
|
|
20413
20450
|
else if (viewPosition === 'footerSection') {
|
|
20414
20451
|
fromKName = 'pdpPage';
|
|
20415
20452
|
}
|
|
20453
|
+
console.log('[handleProductViewLeave] 上报 productView 事件:', {
|
|
20454
|
+
productId,
|
|
20455
|
+
timeOnSite,
|
|
20456
|
+
fromKName
|
|
20457
|
+
});
|
|
20416
20458
|
bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
|
|
20417
20459
|
eventInfo: {
|
|
20418
20460
|
eventSubject: 'productView',
|
|
@@ -21008,19 +21050,40 @@ const StructurePage = (_a) => {
|
|
|
21008
21050
|
})()) }, data.heroSection.text))),
|
|
21009
21051
|
React.createElement("div", { style: baseStyles.heroImageContainer },
|
|
21010
21052
|
React.createElement(PostView, { videoRef: heroVideoRef, isVideo: !!data.heroSection.url, autoPlayTriggerRef: heroAutoPlayTrigger }, data.heroSection.url ? (React.createElement("div", { style: { position: 'relative', width: '100%', height: '100%' }, onClick: handleHeroVideoClick },
|
|
21011
|
-
React.createElement("video", { ref:
|
|
21053
|
+
React.createElement("video", { ref: (el) => {
|
|
21054
|
+
if (heroVideoRef) {
|
|
21055
|
+
heroVideoRef.current = el;
|
|
21056
|
+
}
|
|
21057
|
+
// 添加 addEventListener 监听事件,确保网络慢时也能触发
|
|
21058
|
+
if (el && !el.dataset.playListenerAdded) {
|
|
21059
|
+
el.dataset.playListenerAdded = 'true';
|
|
21060
|
+
// play 事件:视频开始播放时触发(可能还在缓冲)
|
|
21061
|
+
el.addEventListener('play', (e) => {
|
|
21062
|
+
console.log('[Hero Video] play event triggered');
|
|
21063
|
+
handleVideoPlay(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21064
|
+
});
|
|
21065
|
+
// playing 事件:视频真正开始播放时触发(已缓冲足够数据)
|
|
21066
|
+
// 这是网络慢时更可靠的事件
|
|
21067
|
+
el.addEventListener('playing', (e) => {
|
|
21068
|
+
console.log('[Hero Video] playing event triggered (backup)');
|
|
21069
|
+
// playing 也触发一次,防止 play 事件没触发
|
|
21070
|
+
handleVideoPlay(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21071
|
+
});
|
|
21072
|
+
el.addEventListener('pause', (e) => {
|
|
21073
|
+
console.log('[Hero Video] pause event triggered');
|
|
21074
|
+
handleVideoPlayOver(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21075
|
+
});
|
|
21076
|
+
el.addEventListener('ended', (e) => {
|
|
21077
|
+
console.log('[Hero Video] ended event triggered');
|
|
21078
|
+
handleVideoPlayOver(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21079
|
+
});
|
|
21080
|
+
}
|
|
21081
|
+
}, src: data.heroSection.url, style: baseStyles.heroVideo, muted: true, loop: true, playsInline: true, controls: false, onLoadedMetadata: () => {
|
|
21012
21082
|
var _a;
|
|
21013
21083
|
const videoId = `${(_a = data.heroSection) === null || _a === void 0 ? void 0 : _a.itemId}-heroSection-0`;
|
|
21014
21084
|
const loadTime = Date.now();
|
|
21015
21085
|
const initTime = videoLoadTime.current[videoId] || loadTime;
|
|
21016
21086
|
videoLoadTime.current[videoId] = loadTime - initTime;
|
|
21017
|
-
}, onPlay: (e) => {
|
|
21018
|
-
// 视频真正开始播放时才上报(考虑网速问题)
|
|
21019
|
-
handleVideoPlay(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21020
|
-
}, onPause: (e) => {
|
|
21021
|
-
handleVideoPlayOver(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21022
|
-
}, onEnded: (e) => {
|
|
21023
|
-
handleVideoPlayOver(data.heroSection, 'heroSection', 0, e.currentTarget);
|
|
21024
21087
|
} }),
|
|
21025
21088
|
isHeroVideoPaused && (React.createElement(FormatImage$1, { className: 'clc-pb-video-pause', src: videoPlayIcon, alt: 'play' })))) : ((_f = data.heroSection.imgUrls) === null || _f === void 0 ? void 0 : _f[0]) ? (React.createElement("img", { src: data.heroSection.imgUrls[0], alt: 'Hero', style: baseStyles.heroImage })) : null),
|
|
21026
21089
|
React.createElement("div", { style: baseStyles.heroOverlay }, renderCTA('heroButton', (_h = (_g = data.heroSection.bindProducts) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.bindCta, (_j = data.heroSection.bindProducts) === null || _j === void 0 ? void 0 : _j[0], baseStyles.heroButton, 'heroSection', 0))))),
|
|
@@ -21059,34 +21122,66 @@ const StructurePage = (_a) => {
|
|
|
21059
21122
|
React.createElement("div", { style: { position: 'relative', width: '100%', height: '100%' }, onClick: () => handleCarouselVideoClick(index) },
|
|
21060
21123
|
React.createElement("video", { ref: (el) => {
|
|
21061
21124
|
carouselVideoRefs.current[index] = el;
|
|
21125
|
+
// 添加 addEventListener 监听事件,确保网络慢时也能触发
|
|
21126
|
+
if (el && !el.dataset.playListenerAdded) {
|
|
21127
|
+
el.dataset.playListenerAdded = 'true';
|
|
21128
|
+
// play 事件:视频开始播放时触发(可能还在缓冲)
|
|
21129
|
+
el.addEventListener('play', (e) => {
|
|
21130
|
+
console.log('[Carousel Video] play event triggered, index:', index);
|
|
21131
|
+
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21132
|
+
});
|
|
21133
|
+
// playing 事件:视频真正开始播放时触发(已缓冲足够数据)
|
|
21134
|
+
el.addEventListener('playing', (e) => {
|
|
21135
|
+
console.log('[Carousel Video] playing event triggered (backup), index:', index);
|
|
21136
|
+
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21137
|
+
});
|
|
21138
|
+
el.addEventListener('pause', (e) => {
|
|
21139
|
+
console.log('[Carousel Video] pause event triggered, index:', index);
|
|
21140
|
+
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21141
|
+
});
|
|
21142
|
+
el.addEventListener('ended', (e) => {
|
|
21143
|
+
console.log('[Carousel Video] ended event triggered, index:', index);
|
|
21144
|
+
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21145
|
+
});
|
|
21146
|
+
}
|
|
21062
21147
|
}, src: item.url, style: baseStyles.carouselVideo, muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
|
|
21063
21148
|
const videoId = `${item.itemId}-carouselSection-${index}`;
|
|
21064
21149
|
const loadTime = Date.now();
|
|
21065
21150
|
const initTime = videoLoadTime.current[videoId] || loadTime;
|
|
21066
21151
|
videoLoadTime.current[videoId] = loadTime - initTime;
|
|
21067
|
-
}, onPlay: (e) => {
|
|
21068
|
-
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21069
|
-
}, onPause: (e) => {
|
|
21070
|
-
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21071
|
-
}, onEnded: (e) => {
|
|
21072
|
-
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21073
21152
|
} }),
|
|
21074
21153
|
carouselVideoPausedStates[index] && (React.createElement(FormatImage$1, { className: 'clc-pb-video-pause', src: videoPlayIcon, alt: 'play' }))))) : (
|
|
21075
21154
|
// 非当前 slide,不使用 PostView
|
|
21076
21155
|
React.createElement("div", { style: { position: 'relative', width: '100%', height: '100%' }, onClick: () => handleCarouselVideoClick(index) },
|
|
21077
21156
|
React.createElement("video", { ref: (el) => {
|
|
21078
21157
|
carouselVideoRefs.current[index] = el;
|
|
21158
|
+
// 添加 addEventListener 监听事件,确保网络慢时也能触发
|
|
21159
|
+
if (el && !el.dataset.playListenerAdded) {
|
|
21160
|
+
el.dataset.playListenerAdded = 'true';
|
|
21161
|
+
// play 事件:视频开始播放时触发(可能还在缓冲)
|
|
21162
|
+
el.addEventListener('play', (e) => {
|
|
21163
|
+
console.log('[Carousel Video (non-current)] play event triggered, index:', index);
|
|
21164
|
+
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21165
|
+
});
|
|
21166
|
+
// playing 事件:视频真正开始播放时触发(已缓冲足够数据)
|
|
21167
|
+
el.addEventListener('playing', (e) => {
|
|
21168
|
+
console.log('[Carousel Video (non-current)] playing event triggered (backup), index:', index);
|
|
21169
|
+
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21170
|
+
});
|
|
21171
|
+
el.addEventListener('pause', (e) => {
|
|
21172
|
+
console.log('[Carousel Video (non-current)] pause event triggered, index:', index);
|
|
21173
|
+
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21174
|
+
});
|
|
21175
|
+
el.addEventListener('ended', (e) => {
|
|
21176
|
+
console.log('[Carousel Video (non-current)] ended event triggered, index:', index);
|
|
21177
|
+
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21178
|
+
});
|
|
21179
|
+
}
|
|
21079
21180
|
}, src: item.url, style: baseStyles.carouselVideo, muted: true, loop: true, playsInline: true, controls: false, draggable: false, onDragStart: (e) => e.preventDefault(), onLoadedMetadata: () => {
|
|
21080
21181
|
const videoId = `${item.itemId}-carouselSection-${index}`;
|
|
21081
21182
|
const loadTime = Date.now();
|
|
21082
21183
|
const initTime = videoLoadTime.current[videoId] || loadTime;
|
|
21083
21184
|
videoLoadTime.current[videoId] = loadTime - initTime;
|
|
21084
|
-
}, onPlay: (e) => {
|
|
21085
|
-
handleVideoPlay(item, 'carouselSection', index, e.currentTarget);
|
|
21086
|
-
}, onPause: (e) => {
|
|
21087
|
-
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21088
|
-
}, onEnded: (e) => {
|
|
21089
|
-
handleVideoPlayOver(item, 'carouselSection', index, e.currentTarget);
|
|
21090
21185
|
} }),
|
|
21091
21186
|
carouselVideoPausedStates[index] && (React.createElement(FormatImage$1, { className: 'clc-pb-video-pause', src: videoPlayIcon, alt: 'play' }))))) : ((_a = item.imgUrls) === null || _a === void 0 ? void 0 : _a[0]) ? (React.createElement("img", { src: item.imgUrls[0], alt: item.text || 'Carousel', style: baseStyles.carouselImage, draggable: false, onDragStart: (e) => e.preventDefault(), onLoad: () => {
|
|
21092
21187
|
// 记录图片加载完成时间
|
|
@@ -21142,7 +21237,7 @@ const StructurePage = (_a) => {
|
|
|
21142
21237
|
return textStyle;
|
|
21143
21238
|
})()) }, (_m = data.carouselSection[carouselIndex]) === null || _m === void 0 ? void 0 : _m.text))),
|
|
21144
21239
|
renderCTA('carouselButton', (_q = (_p = (_o = data.carouselSection[carouselIndex]) === null || _o === void 0 ? void 0 : _o.bindProducts) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.bindCta, (_s = (_r = data.carouselSection[carouselIndex]) === null || _r === void 0 ? void 0 : _r.bindProducts) === null || _s === void 0 ? void 0 : _s[0], baseStyles.carouselButton, 'carouselSection', carouselIndex)))),
|
|
21145
|
-
data.highlightRevealSection && (React.createElement(ProductView, { onExposure: () => handleProductViewExposure(data.highlightRevealSection, 'highlightRevealSection', 0) },
|
|
21240
|
+
data.highlightRevealSection && (React.createElement(ProductView, { onExposure: () => handleProductViewExposure(data.highlightRevealSection, 'highlightRevealSection', 0), onLeave: () => handleProductViewLeave(data.highlightRevealSection, 'highlightRevealSection', 0) },
|
|
21146
21241
|
React.createElement("div", { style: mergeStyles(baseStyles.highlightSection, 'highlightSection', { excludeBorder: true }) },
|
|
21147
21242
|
React.createElement("div", { style: baseStyles.highlightImageContainer },
|
|
21148
21243
|
React.createElement("img", { src: data.highlightRevealSection.landingImageUrl || data.highlightRevealSection.cover, alt: data.highlightRevealSection.title, style: baseStyles.highlightImage })),
|
|
@@ -21228,7 +21323,7 @@ const StructurePage = (_a) => {
|
|
|
21228
21323
|
// 使用产品在数据数组中的实际索引来确定 buttonKey
|
|
21229
21324
|
const productDataIndex = productIndexMap[gridIndex];
|
|
21230
21325
|
const buttonKey = `productButton${productDataIndex || gridIndex + 1}`;
|
|
21231
|
-
return (React.createElement("div", { key: (product === null || product === void 0 ? void 0 : product.itemId) || `empty-${gridIndex}`, style: baseStyles.productItem }, product ? (React.createElement(ProductView, { onExposure: () => handleProductViewExposure(product, 'productGridSection', gridIndex) },
|
|
21326
|
+
return (React.createElement("div", { key: (product === null || product === void 0 ? void 0 : product.itemId) || `empty-${gridIndex}`, style: baseStyles.productItem }, product ? (React.createElement(ProductView, { onExposure: () => handleProductViewExposure(product, 'productGridSection', gridIndex), onLeave: () => handleProductViewLeave(product, 'productGridSection', gridIndex) },
|
|
21232
21327
|
React.createElement(React.Fragment, null,
|
|
21233
21328
|
React.createElement("div", { style: baseStyles.productImageContainer },
|
|
21234
21329
|
React.createElement("img", { src: product.landingImageUrl || product.cover,
|