pb-sxp-ui 1.20.60 → 1.20.62

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
@@ -2318,7 +2318,7 @@ function useEventReport() {
2318
2318
  * @returns { isVisible, reset } isVisible 是否可见;reset 重置可见状态(用于页面从后台恢复时重新触发检测)
2319
2319
  */
2320
2320
  function useAdvancedOnScreen(ref, options = {}) {
2321
- const { threshold = 0, delay = 0, onVisible, onHidden, name = 'unknown' } = options;
2321
+ const { threshold = 0, delay = 0, onVisible, onHidden, name = 'unknown', deps = [] } = options;
2322
2322
  const observerRef = React.useRef(null);
2323
2323
  const [isVisible, setIsVisible] = React.useState(false);
2324
2324
  const timerRef = React.useRef(null);
@@ -2408,7 +2408,8 @@ function useAdvancedOnScreen(ref, options = {}) {
2408
2408
  observerRef.current.disconnect();
2409
2409
  }
2410
2410
  };
2411
- }, [ref, threshold, delay, name]);
2411
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2412
+ }, [ref, threshold, delay, name, ...deps]);
2412
2413
  /**
2413
2414
  * 仅重置内部可见状态为"不可见",不重新触发 Observer 回调。
2414
2415
  * 用于页面从后台恢复时,让 Observer 能重新感知后续的离开事件。
@@ -11299,7 +11300,7 @@ const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHiddenDef
11299
11300
  };
11300
11301
 
11301
11302
  const CommodityDetail$1 = (_a) => {
11302
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
11303
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
11303
11304
  var { content, style, bgImg, onClick, schema, isDefault, bottom_image, tipText, isPost, viewTime, rec, swiper, commodityStyles, buttonStyle, index, commodityGroup, popupBg, iframeIcon, commodityImgRatio, isTel, iframeBgColor, isActive = true, enableAddToCart = false, addToCartPopupId = '' } = _a, props = __rest(_a, ["content", "style", "bgImg", "onClick", "schema", "isDefault", "bottom_image", "tipText", "isPost", "viewTime", "rec", "swiper", "commodityStyles", "buttonStyle", "index", "commodityGroup", "popupBg", "iframeIcon", "commodityImgRatio", "isTel", "iframeBgColor", "isActive", "enableAddToCart", "addToCartPopupId"]);
11304
11305
  console.log('[CommodityDetail] 组件已加载 - 版本 v1.20.23', { enableAddToCart, addToCartPopupId, isPost });
11305
11306
  const { sxpParameter, popupDetailData, isPreview, bffFbReport, popupCurTimeRef, checkCommodityIndexRef, globalConfig, ctaEvent, setPopupDetailData } = useSxpDataSource();
@@ -11311,14 +11312,14 @@ const CommodityDetail$1 = (_a) => {
11311
11312
  const swiperRef = React.useRef();
11312
11313
  const [swiperActiveIndex, setSwiperActiveIndex] = React.useState(0);
11313
11314
  const data = isPost ? rec : popupDetailData;
11314
- let product = isPost ? data === null || data === void 0 ? void 0 : data.product : (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProduct) !== null && _d !== void 0 ? _d : (_f = (_e = data === null || data === void 0 ? void 0 : data.video) === null || _e === void 0 ? void 0 : _e.bindProducts) === null || _f === void 0 ? void 0 : _f[0];
11315
+ let product = isPost ? data === null || data === void 0 ? void 0 : data.product : (_g = (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProduct) !== null && _d !== void 0 ? _d : (_f = (_e = data === null || data === void 0 ? void 0 : data.video) === null || _e === void 0 ? void 0 : _e.bindProducts) === null || _f === void 0 ? void 0 : _f[0]) !== null && _g !== void 0 ? _g : data === null || data === void 0 ? void 0 : data.product;
11315
11316
  let cta = isPost
11316
- ? (_g = data === null || data === void 0 ? void 0 : data.product) === null || _g === void 0 ? void 0 : _g.bindCta
11317
- : (_k = (_j = (_h = data === null || data === void 0 ? void 0 : data.video) === null || _h === void 0 ? void 0 : _h.bindProduct) === null || _j === void 0 ? void 0 : _j.bindCta) !== null && _k !== void 0 ? _k : (_o = (_m = (_l = data === null || data === void 0 ? void 0 : data.video) === null || _l === void 0 ? void 0 : _l.bindProducts) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.bindCta;
11317
+ ? (_h = data === null || data === void 0 ? void 0 : data.product) === null || _h === void 0 ? void 0 : _h.bindCta
11318
+ : (_q = (_l = (_k = (_j = data === null || data === void 0 ? void 0 : data.video) === null || _j === void 0 ? void 0 : _j.bindProduct) === null || _k === void 0 ? void 0 : _k.bindCta) !== null && _l !== void 0 ? _l : (_p = (_o = (_m = data === null || data === void 0 ? void 0 : data.video) === null || _m === void 0 ? void 0 : _m.bindProducts) === null || _o === void 0 ? void 0 : _o[0]) === null || _p === void 0 ? void 0 : _p.bindCta) !== null && _q !== void 0 ? _q : (_r = data === null || data === void 0 ? void 0 : data.product) === null || _r === void 0 ? void 0 : _r.bindCta;
11318
11319
  const position = isPost ? index : 0;
11319
- if (!isPost && (commodityGroup === null || commodityGroup === void 0 ? void 0 : commodityGroup.open) && ((_p = data === null || data === void 0 ? void 0 : data.video) === null || _p === void 0 ? void 0 : _p.bindProducts) && ((_r = (_q = data === null || data === void 0 ? void 0 : data.video) === null || _q === void 0 ? void 0 : _q.bindProducts) === null || _r === void 0 ? void 0 : _r.length) > 0) {
11320
+ if (!isPost && (commodityGroup === null || commodityGroup === void 0 ? void 0 : commodityGroup.open) && ((_s = data === null || data === void 0 ? void 0 : data.video) === null || _s === void 0 ? void 0 : _s.bindProducts) && ((_u = (_t = data === null || data === void 0 ? void 0 : data.video) === null || _t === void 0 ? void 0 : _t.bindProducts) === null || _u === void 0 ? void 0 : _u.length) > 0) {
11320
11321
  checkCommodityIndexRef.current = checkCommodityIndex;
11321
- const p = (_s = data === null || data === void 0 ? void 0 : data.video) === null || _s === void 0 ? void 0 : _s.bindProducts[checkCommodityIndex];
11322
+ const p = (_v = data === null || data === void 0 ? void 0 : data.video) === null || _v === void 0 ? void 0 : _v.bindProducts[checkCommodityIndex];
11322
11323
  product = p;
11323
11324
  cta = p === null || p === void 0 ? void 0 : p.bindCta;
11324
11325
  }
@@ -11382,6 +11383,8 @@ const CommodityDetail$1 = (_a) => {
11382
11383
  target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
11383
11384
  target_url: product.link
11384
11385
  });
11386
+ // feed 流 post 跳转外链时也需要上报 productView
11387
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
11385
11388
  }
11386
11389
  window.location.href = window.getJointUtmLink(product.link);
11387
11390
  }
@@ -11463,11 +11466,11 @@ const CommodityDetail$1 = (_a) => {
11463
11466
  }, []);
11464
11467
  const priceText = getPriceText({
11465
11468
  product,
11466
- enableFormattedPrice: (_t = commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price) === null || _t === void 0 ? void 0 : _t.enableFormattedPrice,
11469
+ enableFormattedPrice: (_w = commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price) === null || _w === void 0 ? void 0 : _w.enableFormattedPrice,
11467
11470
  globalConfig,
11468
11471
  style: commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price
11469
11472
  });
11470
- const width = (isPreview ? 375 : (_u = style === null || style === void 0 ? void 0 : style.width) !== null && _u !== void 0 ? _u : window.innerWidth) - ((_v = popupBg === null || popupBg === void 0 ? void 0 : popupBg.horizontalMargin) !== null && _v !== void 0 ? _v : 0) * 2;
11473
+ const width = (isPreview ? 375 : (_x = style === null || style === void 0 ? void 0 : style.width) !== null && _x !== void 0 ? _x : window.innerWidth) - ((_y = popupBg === null || popupBg === void 0 ? void 0 : popupBg.horizontalMargin) !== null && _y !== void 0 ? _y : 0) * 2;
11471
11474
  const height = commodityImgRatio ? width * (commodityImgRatio.h / commodityImgRatio.w) : width;
11472
11475
  const renderContent = ({ isPost }) => {
11473
11476
  var _a, _b, _c, _d;
@@ -11567,7 +11570,7 @@ const CommodityDetail$1 = (_a) => {
11567
11570
  return (React.createElement(React.Fragment, null,
11568
11571
  React.createElement("div", Object.assign({ className: css.css(Object.assign(Object.assign({}, style), { position: 'relative' })) }, props),
11569
11572
  React.createElement("div", { style: { position: 'relative' }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
11570
- product && ((_w = product === null || product === void 0 ? void 0 : product.homePage) === null || _w === void 0 ? void 0 : _w.length) > 0 && (React.createElement(Swiper, Object.assign({ height: height, modules: [Pagination, Autoplay, ...(isAlly ? [Navigation, A11y, Mousewheel, Keyboard] : [])], pagination: {
11573
+ product && ((_z = product === null || product === void 0 ? void 0 : product.homePage) === null || _z === void 0 ? void 0 : _z.length) > 0 && (React.createElement(Swiper, Object.assign({ height: height, modules: [Pagination, Autoplay, ...(isAlly ? [Navigation, A11y, Mousewheel, Keyboard] : [])], pagination: {
11571
11574
  clickable: true,
11572
11575
  bulletActiveClass: 'swipe-item-active-bullet',
11573
11576
  clickableClass: getDotsAlign,
@@ -11583,7 +11586,7 @@ const CommodityDetail$1 = (_a) => {
11583
11586
  }
11584
11587
  : {}), { loop: true, autoplay: {
11585
11588
  delay: (swiper === null || swiper === void 0 ? void 0 : swiper.delay) * 1000
11586
- }, ref: swiperRef, onSlideChange: handleSlideChange, className: css.css(Object.assign(Object.assign({ '.swiper-pagination': { bottom: (_x = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _x !== void 0 ? _x : 0, fontSize: '14px' } }, ((swiper === null || swiper === void 0 ? void 0 : swiper.dotsBgColor) && {
11589
+ }, ref: swiperRef, onSlideChange: handleSlideChange, className: css.css(Object.assign(Object.assign({ '.swiper-pagination': { bottom: (_0 = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _0 !== void 0 ? _0 : 0, fontSize: '14px' } }, ((swiper === null || swiper === void 0 ? void 0 : swiper.dotsBgColor) && {
11587
11590
  '.swiper-pagination-bullet': {
11588
11591
  backgroundColor: swiper === null || swiper === void 0 ? void 0 : swiper.dotsBgColor,
11589
11592
  opacity: 1
@@ -11594,7 +11597,7 @@ const CommodityDetail$1 = (_a) => {
11594
11597
  opacity: 1
11595
11598
  }
11596
11599
  }))) }),
11597
- React.createElement(React.Fragment, null, (_y = product === null || product === void 0 ? void 0 : product.homePage) === null || _y === void 0 ? void 0 : _y.map((src, srcKey) => {
11600
+ React.createElement(React.Fragment, null, (_1 = product === null || product === void 0 ? void 0 : product.homePage) === null || _1 === void 0 ? void 0 : _1.map((src, srcKey) => {
11598
11601
  var _a;
11599
11602
  return (React.createElement(SwiperSlide, { key: srcKey, "aria-hidden": srcKey !== swiperActiveIndex },
11600
11603
  React.createElement("div", { style: {
@@ -11610,7 +11613,7 @@ const CommodityDetail$1 = (_a) => {
11610
11613
  objectPosition: `50% ${(swiper === null || swiper === void 0 ? void 0 : swiper.translateY) ? (swiper === null || swiper === void 0 ? void 0 : swiper.translateY) + 50 : 50}%`
11611
11614
  }, src: (_a = src !== null && src !== void 0 ? src : sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _a !== void 0 ? _a : bottom_image }))));
11612
11615
  })))),
11613
- !((_z = product === null || product === void 0 ? void 0 : product.homePage) === null || _z === void 0 ? void 0 : _z.length) && (React.createElement("div", { className: css.css({
11616
+ !((_2 = product === null || product === void 0 ? void 0 : product.homePage) === null || _2 === void 0 ? void 0 : _2.length) && (React.createElement("div", { className: css.css({
11614
11617
  height,
11615
11618
  width
11616
11619
  }) },
@@ -11618,7 +11621,7 @@ const CommodityDetail$1 = (_a) => {
11618
11621
  objectFit: 'cover',
11619
11622
  width: '100%',
11620
11623
  height: '100%'
11621
- }), src: (_0 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _0 !== void 0 ? _0 : bottom_image, alt: 'pdp image' }))),
11624
+ }), src: (_3 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _3 !== void 0 ? _3 : bottom_image, alt: 'pdp image' }))),
11622
11625
  (iframeUrl || !product) && iframeIcon && (React.createElement("div", { style: {
11623
11626
  display: 'flex',
11624
11627
  alignItems: 'center',
@@ -12685,7 +12688,7 @@ const AddToCartPopup$1 = ({ isActive = true }) => {
12685
12688
  };
12686
12689
 
12687
12690
  const CommodityDetailDiroNew$1 = (_a) => {
12688
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
12691
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
12689
12692
  var { style, rec, viewTime, isPost, bottom_image, tipText, swiper, commodityStyles, buttonStyle, index, commodityGroup, popupBg, iframeIcon, commodityImgRatio, iframeBgColor, isActive = true } = _a, props = __rest(_a, ["style", "rec", "viewTime", "isPost", "bottom_image", "tipText", "swiper", "commodityStyles", "buttonStyle", "index", "commodityGroup", "popupBg", "iframeIcon", "commodityImgRatio", "iframeBgColor", "isActive"]);
12690
12693
  const { sxpParameter, popupCurTimeRef, popupDetailData, setPopupDetailData, isPreview, bffFbReport, checkCommodityIndexRef, globalConfig, ctaEvent } = useSxpDataSource();
12691
12694
  useEditor();
@@ -12698,14 +12701,14 @@ const CommodityDetailDiroNew$1 = (_a) => {
12698
12701
  const swiperRef = React.useRef();
12699
12702
  const [swiperActiveIndex, setSwiperActiveIndex] = React.useState(0);
12700
12703
  const data = isPost ? rec : popupDetailData;
12701
- let product = isPost ? data === null || data === void 0 ? void 0 : data.product : (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProduct) !== null && _d !== void 0 ? _d : (_f = (_e = data === null || data === void 0 ? void 0 : data.video) === null || _e === void 0 ? void 0 : _e.bindProducts) === null || _f === void 0 ? void 0 : _f[0];
12704
+ let product = isPost ? data === null || data === void 0 ? void 0 : data.product : (_g = (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProduct) !== null && _d !== void 0 ? _d : (_f = (_e = data === null || data === void 0 ? void 0 : data.video) === null || _e === void 0 ? void 0 : _e.bindProducts) === null || _f === void 0 ? void 0 : _f[0]) !== null && _g !== void 0 ? _g : data === null || data === void 0 ? void 0 : data.product;
12702
12705
  let cta = isPost
12703
- ? (_g = data === null || data === void 0 ? void 0 : data.product) === null || _g === void 0 ? void 0 : _g.bindCta
12704
- : (_k = (_j = (_h = data === null || data === void 0 ? void 0 : data.video) === null || _h === void 0 ? void 0 : _h.bindProduct) === null || _j === void 0 ? void 0 : _j.bindCta) !== null && _k !== void 0 ? _k : (_o = (_m = (_l = data === null || data === void 0 ? void 0 : data.video) === null || _l === void 0 ? void 0 : _l.bindProducts) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.bindCta;
12706
+ ? (_h = data === null || data === void 0 ? void 0 : data.product) === null || _h === void 0 ? void 0 : _h.bindCta
12707
+ : (_q = (_l = (_k = (_j = data === null || data === void 0 ? void 0 : data.video) === null || _j === void 0 ? void 0 : _j.bindProduct) === null || _k === void 0 ? void 0 : _k.bindCta) !== null && _l !== void 0 ? _l : (_p = (_o = (_m = data === null || data === void 0 ? void 0 : data.video) === null || _m === void 0 ? void 0 : _m.bindProducts) === null || _o === void 0 ? void 0 : _o[0]) === null || _p === void 0 ? void 0 : _p.bindCta) !== null && _q !== void 0 ? _q : (_r = data === null || data === void 0 ? void 0 : data.product) === null || _r === void 0 ? void 0 : _r.bindCta;
12705
12708
  const position = isPost ? index : 0;
12706
- if (!isPost && (commodityGroup === null || commodityGroup === void 0 ? void 0 : commodityGroup.open) && ((_p = data === null || data === void 0 ? void 0 : data.video) === null || _p === void 0 ? void 0 : _p.bindProducts) && ((_r = (_q = data === null || data === void 0 ? void 0 : data.video) === null || _q === void 0 ? void 0 : _q.bindProducts) === null || _r === void 0 ? void 0 : _r.length) > 0) {
12709
+ if (!isPost && (commodityGroup === null || commodityGroup === void 0 ? void 0 : commodityGroup.open) && ((_s = data === null || data === void 0 ? void 0 : data.video) === null || _s === void 0 ? void 0 : _s.bindProducts) && ((_u = (_t = data === null || data === void 0 ? void 0 : data.video) === null || _t === void 0 ? void 0 : _t.bindProducts) === null || _u === void 0 ? void 0 : _u.length) > 0) {
12707
12710
  checkCommodityIndexRef.current = checkCommodityIndex;
12708
- const p = (_s = data === null || data === void 0 ? void 0 : data.video) === null || _s === void 0 ? void 0 : _s.bindProducts[checkCommodityIndex];
12711
+ const p = (_v = data === null || data === void 0 ? void 0 : data.video) === null || _v === void 0 ? void 0 : _v.bindProducts[checkCommodityIndex];
12709
12712
  product = p;
12710
12713
  cta = p === null || p === void 0 ? void 0 : p.bindCta;
12711
12714
  }
@@ -12730,9 +12733,8 @@ const CommodityDetailDiroNew$1 = (_a) => {
12730
12733
  target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
12731
12734
  target_url: product === null || product === void 0 ? void 0 : product.link
12732
12735
  });
12733
- if (!isPost) {
12734
- productView(data, product, cta, viewTime || curTimeRef.current, position);
12735
- }
12736
+ // 跳转外链时无论 isPost 是否为 true 都需要上报 productView
12737
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
12736
12738
  // 跳转到商品链接
12737
12739
  if (product === null || product === void 0 ? void 0 : product.link) {
12738
12740
  window.location.href = window.getJointUtmLink(product.link);
@@ -12755,9 +12757,13 @@ const CommodityDetailDiroNew$1 = (_a) => {
12755
12757
  target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
12756
12758
  target_url: product === null || product === void 0 ? void 0 : product.link
12757
12759
  });
12758
- if (!isPost) {
12759
- productView(data, product, cta, viewTime || curTimeRef.current, position);
12760
- }
12760
+ // 打开加购弹窗时无论 isPost 是否为 true 都需要上报 productView
12761
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
12762
+ // 上报 clickShopifyPopup 事件(打开购物车弹窗时上报,无论是 feed 流 post 还是商品详情弹窗内)
12763
+ ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
12764
+ eventSubject: 'clickShopifyPopup',
12765
+ eventDescription: 'User clicked to open Shopify popup'
12766
+ }, data, product, position);
12761
12767
  // 更新 popupDetailData,确保 AddToCart 组件能获取到商品数据
12762
12768
  setPopupDetailData === null || setPopupDetailData === void 0 ? void 0 : setPopupDetailData(Object.assign(Object.assign({}, data), { video: Object.assign(Object.assign({}, data === null || data === void 0 ? void 0 : data.video), { bindProduct: product }), index: position }));
12763
12769
  // 打开 AddToCart 弹窗
@@ -12834,11 +12840,11 @@ const CommodityDetailDiroNew$1 = (_a) => {
12834
12840
  }, [isActive]);
12835
12841
  const priceText = getPriceText({
12836
12842
  product,
12837
- enableFormattedPrice: (_t = commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price) === null || _t === void 0 ? void 0 : _t.enableFormattedPrice,
12843
+ enableFormattedPrice: (_w = commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price) === null || _w === void 0 ? void 0 : _w.enableFormattedPrice,
12838
12844
  globalConfig,
12839
12845
  style: commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.price
12840
12846
  });
12841
- const width = (isPreview ? 375 : (_u = style === null || style === void 0 ? void 0 : style.width) !== null && _u !== void 0 ? _u : window.innerWidth) - ((_v = popupBg === null || popupBg === void 0 ? void 0 : popupBg.horizontalMargin) !== null && _v !== void 0 ? _v : 0) * 2;
12847
+ const width = (isPreview ? 375 : (_x = style === null || style === void 0 ? void 0 : style.width) !== null && _x !== void 0 ? _x : window.innerWidth) - ((_y = popupBg === null || popupBg === void 0 ? void 0 : popupBg.horizontalMargin) !== null && _y !== void 0 ? _y : 0) * 2;
12842
12848
  const height = commodityImgRatio ? width * (commodityImgRatio.h / commodityImgRatio.w) : width;
12843
12849
  // useEffect(() => {
12844
12850
  // console.log(scrollRef?.current?.scrollHeight, window.innerHeight);
@@ -12916,7 +12922,7 @@ Made in Italy` })));
12916
12922
  if (isPost)
12917
12923
  return;
12918
12924
  return (React.createElement(CommodityGroup$1, { products: (_a = data === null || data === void 0 ? void 0 : data.video) === null || _a === void 0 ? void 0 : _a.bindProducts, data: commodityGroup, defImg: (_c = (_b = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _b !== void 0 ? _b : bottom_image) !== null && _c !== void 0 ? _c : '', style: { padding: '0 19px' }, onCLick: handleClick, popupDetailData: popupDetailData, check: checkCommodityIndex }));
12919
- }, [checkCommodityIndex, isPost, (_w = data === null || data === void 0 ? void 0 : data.video) === null || _w === void 0 ? void 0 : _w.bindProducts, commodityGroup, sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image, bottom_image, handleClick, popupDetailData]);
12925
+ }, [checkCommodityIndex, isPost, (_z = data === null || data === void 0 ? void 0 : data.video) === null || _z === void 0 ? void 0 : _z.bindProducts, commodityGroup, sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image, bottom_image, handleClick, popupDetailData]);
12920
12926
  const getDotsAlign = React.useMemo(() => {
12921
12927
  const dotsAlignClass = {
12922
12928
  left: 'commondityDetail-swiper-clickable-left',
@@ -12943,7 +12949,7 @@ Made in Italy` })));
12943
12949
  return (React.createElement("div", { className: 'pb-commondityDiroNew' },
12944
12950
  React.createElement("div", Object.assign({ className: css.css(Object.assign(Object.assign({}, style), { transform: 'translate3d(0px, 0px, 0px)' })) }, props),
12945
12951
  React.createElement("div", { style: { position: 'relative' }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
12946
- product && ((_x = product === null || product === void 0 ? void 0 : product.homePage) === null || _x === void 0 ? void 0 : _x.length) > 0 && (React.createElement(Swiper, Object.assign({ height: height, modules: [Pagination, Autoplay, ...(isAlly ? [Navigation, A11y, Mousewheel, Keyboard] : [])], pagination: {
12952
+ product && ((_0 = product === null || product === void 0 ? void 0 : product.homePage) === null || _0 === void 0 ? void 0 : _0.length) > 0 && (React.createElement(Swiper, Object.assign({ height: height, modules: [Pagination, Autoplay, ...(isAlly ? [Navigation, A11y, Mousewheel, Keyboard] : [])], pagination: {
12947
12953
  clickable: true,
12948
12954
  bulletActiveClass: 'swipe-item-active-bullet',
12949
12955
  clickableClass: getDotsAlign,
@@ -12960,7 +12966,7 @@ Made in Italy` })));
12960
12966
  : {}), { loop: true, ref: swiperRef, onSlideChange: handleSlideChange, autoplay: {
12961
12967
  delay: (swiper === null || swiper === void 0 ? void 0 : swiper.delay) * 1000
12962
12968
  }, className: css.css(Object.assign(Object.assign({ '.swiper-pagination': {
12963
- bottom: (_y = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _y !== void 0 ? _y : 0,
12969
+ bottom: (_1 = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _1 !== void 0 ? _1 : 0,
12964
12970
  fontSize: '14px'
12965
12971
  } }, ((swiper === null || swiper === void 0 ? void 0 : swiper.dotsBgColor) && {
12966
12972
  '.swiper-pagination-bullet': {
@@ -12972,7 +12978,7 @@ Made in Italy` })));
12972
12978
  backgroundColor: `${swiper === null || swiper === void 0 ? void 0 : swiper.dotsActiveColor}!important`,
12973
12979
  opacity: 1
12974
12980
  }
12975
- }))) }), (_z = product === null || product === void 0 ? void 0 : product.homePage) === null || _z === void 0 ? void 0 : _z.map((src, srcKey) => {
12981
+ }))) }), (_2 = product === null || product === void 0 ? void 0 : product.homePage) === null || _2 === void 0 ? void 0 : _2.map((src, srcKey) => {
12976
12982
  var _a;
12977
12983
  return (React.createElement(SwiperSlide, { key: srcKey, "aria-hidden": srcKey !== swiperActiveIndex },
12978
12984
  React.createElement("div", { style: {
@@ -12988,7 +12994,7 @@ Made in Italy` })));
12988
12994
  objectPosition: `50% ${(swiper === null || swiper === void 0 ? void 0 : swiper.translateY) ? (swiper === null || swiper === void 0 ? void 0 : swiper.translateY) + 50 : 50}%`
12989
12995
  }, src: (_a = src !== null && src !== void 0 ? src : sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _a !== void 0 ? _a : bottom_image }))));
12990
12996
  }))),
12991
- !((_0 = product === null || product === void 0 ? void 0 : product.homePage) === null || _0 === void 0 ? void 0 : _0.length) && (React.createElement("div", { className: css.css({
12997
+ !((_3 = product === null || product === void 0 ? void 0 : product.homePage) === null || _3 === void 0 ? void 0 : _3.length) && (React.createElement("div", { className: css.css({
12992
12998
  height,
12993
12999
  width
12994
13000
  }) },
@@ -12996,7 +13002,7 @@ Made in Italy` })));
12996
13002
  objectFit: 'cover',
12997
13003
  width: '100%',
12998
13004
  height: '100%'
12999
- }), src: (_1 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _1 !== void 0 ? _1 : bottom_image, alt: 'pdp image' }))),
13005
+ }), src: (_4 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _4 !== void 0 ? _4 : bottom_image, alt: 'pdp image' }))),
13000
13006
  (iframeUrl || !product) && iframeIcon && (React.createElement("div", { style: {
13001
13007
  display: 'flex',
13002
13008
  alignItems: 'center',
@@ -13013,7 +13019,7 @@ Made in Italy` })));
13013
13019
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top' },
13014
13020
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top-left' },
13015
13021
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top-left-title', style: getStyle(commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.title), dangerouslySetInnerHTML: {
13016
- __html: setFontForText((_2 = product === null || product === void 0 ? void 0 : product.title) !== null && _2 !== void 0 ? _2 : 'Large Dior Toujours BagLarge', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.title)
13022
+ __html: setFontForText((_5 = product === null || product === void 0 ? void 0 : product.title) !== null && _5 !== void 0 ? _5 : 'Large Dior Toujours BagLarge', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.title)
13017
13023
  } }),
13018
13024
  React.createElement("div", { className: 'pb-commondityDiroNew-content-collection', hidden: !!product && (!(product === null || product === void 0 ? void 0 : product.collection) || (product === null || product === void 0 ? void 0 : product.collection) === ''), style: getStyle(commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.collection), dangerouslySetInnerHTML: {
13019
13025
  __html: setFontForText((product === null || product === void 0 ? void 0 : product.collection) || 'Black Macrocannage CalfskinLarge', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.collection)
@@ -13023,11 +13029,11 @@ Made in Italy` })));
13023
13029
  __html: priceText !== null && priceText !== void 0 ? priceText : ''
13024
13030
  } }),
13025
13031
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top-right-price', hidden: !!product && !(product === null || product === void 0 ? void 0 : product.taxInfo), style: getStyle(commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.taxInfo), dangerouslySetInnerHTML: {
13026
- __html: setFontForText((_3 = product === null || product === void 0 ? void 0 : product.taxInfo) !== null && _3 !== void 0 ? _3 : '税费', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.taxInfo)
13032
+ __html: setFontForText((_6 = product === null || product === void 0 ? void 0 : product.taxInfo) !== null && _6 !== void 0 ? _6 : '税费', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.taxInfo)
13027
13033
  } }))),
13028
- React.createElement("a", { "aria-label": (_4 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _4 !== void 0 ? _4 : 'Shop now', role: 'button', tabIndex: 0, onClick: handleLink, className: 'pb-commondityDiroNew-btn', style: buttonStyle },
13034
+ React.createElement("a", { "aria-label": (_7 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _7 !== void 0 ? _7 : 'Shop now', role: 'button', tabIndex: 0, onClick: handleLink, className: 'pb-commondityDiroNew-btn', style: buttonStyle },
13029
13035
  React.createElement("span", { dangerouslySetInnerHTML: {
13030
- __html: setFontForText((_5 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _5 !== void 0 ? _5 : 'Shop now', buttonStyle)
13036
+ __html: setFontForText((_8 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _8 !== void 0 ? _8 : 'Shop now', buttonStyle)
13031
13037
  } })),
13032
13038
  productInfoText({ isPost }))),
13033
13039
  React.createElement(Modal$1, { visible: showModal, onClose: () => setShowModal(false) },
@@ -20063,6 +20069,8 @@ const StructurePage = (_a) => {
20063
20069
  var { containerStyle, containerHeight = 664, containerWidth = 375, className = '', apiUrl = 'https://bff-be-dev.chatlabs.net/api/v1/recommend/list', requestBody, editorMode = false, multiCTAConfig: propMultiCTAConfig, videoPlayIcon: propVideoPlayIcon, isCmsMode = false, storyId, customHeaders, preloadImages = true } = _a, // 默认预加载图片,提升用户体验
20064
20070
  rest = __rest(_a, ["containerStyle", "containerHeight", "containerWidth", "className", "apiUrl", "requestBody", "editorMode", "multiCTAConfig", "videoPlayIcon", "isCmsMode", "storyId", "customHeaders", "preloadImages"]);
20065
20071
  const [data, setData] = React.useState(null);
20072
+ // 用 ref 保存最新的 data,供 visibilitychange 等事件回调中访问,避免闭包陈旧值
20073
+ const dataRef = React.useRef(null);
20066
20074
  const [loading, setLoading] = React.useState(true);
20067
20075
  const [error, setError] = React.useState(null);
20068
20076
  const [carouselIndex, setCarouselIndex] = React.useState(0);
@@ -20160,9 +20168,16 @@ const StructurePage = (_a) => {
20160
20168
  }, [data, isHeroVideoLoaded, sxpParameter]);
20161
20169
  // 防止重复上报的标记
20162
20170
  const h5EnterReportedRef = React.useRef(false);
20171
+ // 标记页面是否已完成首次加载(挂载时的 h5LinkEnterFeed 已上报)
20172
+ // visibilitychange 的 visible 分支只在此标记为 true 后才响应,避免刷新时重复上报
20173
+ const pageInitializedRef = React.useRef(false);
20174
+ // 标记页面是否正在卸载(刷新/关闭),防止刷新时旧页面的 visibilitychange visible 误触发
20175
+ const pageUnloadingRef = React.useRef(false);
20163
20176
  // 全局事件管理:用于追踪弹窗打开时间和状态
20164
20177
  const popupCurTimeRef = React.useRef(new Date());
20165
20178
  const currentPopupDataRef = React.useRef(null);
20179
+ // 标记弹窗是否处于打开状态,用于防止弹窗导致的 window blur/focus 误触发 h5LinkEnterFeed
20180
+ const isPopupOpenRef = React.useRef(false);
20166
20181
  // CTA 曝光时间追踪(允许重复上报)
20167
20182
  const ctaExposureTime = React.useRef({});
20168
20183
  // Carousel 图片浏览时间追踪
@@ -20233,6 +20248,7 @@ const StructurePage = (_a) => {
20233
20248
  }, [propVideoPlayIcon, (_e = (_d = schema === null || schema === void 0 ? void 0 : schema.sxpPageConf) === null || _d === void 0 ? void 0 : _d.globalConfig) === null || _e === void 0 ? void 0 : _e.videoPlayIcon]);
20234
20249
  // 处理 CTA 点击
20235
20250
  const handleCtaClick = React.useCallback((link, interaction, productData, ctaData, viewPosition, sectionIndex) => {
20251
+ var _a, _b, _c;
20236
20252
  // 判断是否为外部链接
20237
20253
  const isExternalLink = (interaction === null || interaction === void 0 ? void 0 : interaction.linkType) !== 'popup' && !!link;
20238
20254
  // ========== 1. BFF 事件上报 (ctaEvent) ==========
@@ -20263,7 +20279,9 @@ const StructurePage = (_a) => {
20263
20279
  product: null
20264
20280
  };
20265
20281
  // 上报 clickCta 事件
20266
- ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent(Object.assign({ eventSubject: 'clickCta', eventDescription: 'User clicked the CTA' }, (viewPosition && { viewPosition })), rec, productData, sectionIndex !== undefined ? sectionIndex : 0);
20282
+ // productGridSection 区域的商品 position 统一传 0(与 ctaExposure 保持一致)
20283
+ const clickCtaPosition = viewPosition === 'productGridSection' ? 0 : (sectionIndex !== undefined ? sectionIndex : 0);
20284
+ ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent(Object.assign({ eventSubject: 'clickCta', eventDescription: 'User clicked the CTA' }, (viewPosition && { viewPosition })), rec, productData, clickCtaPosition);
20267
20285
  // ========== 2. Facebook Pixel 和第三方像素上报 ==========
20268
20286
  bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
20269
20287
  eventName: 'ClickCTA',
@@ -20281,13 +20299,19 @@ const StructurePage = (_a) => {
20281
20299
  if (interaction) {
20282
20300
  const { linkType, popupType, popupAni } = interaction;
20283
20301
  if (linkType === 'popup' && popupType) {
20284
- // 上报 clickShopifyPopup 事件(如果是 Shopify 商品)
20285
- if (productData === null || productData === void 0 ? void 0 : productData.shopifyId) {
20302
+ // 上报 clickShopifyPopup 事件(仅当弹窗是 AddToCart 购物车弹窗时才上报)
20303
+ // 商品详情弹窗(CommodityDetail 等)打开时不应上报此事件
20304
+ const popupList = ((_b = (_a = schema === null || schema === void 0 ? void 0 : schema.sxpPageConf) === null || _a === void 0 ? void 0 : _a.globalConfig) === null || _b === void 0 ? void 0 : _b.popupList) || [];
20305
+ const popupNode = popupList.find((p) => p.id === popupType);
20306
+ const isAddToCartPopup = ((_c = popupNode === null || popupNode === void 0 ? void 0 : popupNode.item) === null || _c === void 0 ? void 0 : _c.type) === 'AddToCartPopup';
20307
+ if ((productData === null || productData === void 0 ? void 0 : productData.shopifyId) && isAddToCartPopup) {
20286
20308
  ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent(Object.assign({ eventSubject: 'clickShopifyPopup', eventDescription: 'User clicked to open Shopify popup' }, (viewPosition && { viewPosition })), rec, productData, sectionIndex !== undefined ? sectionIndex : 0);
20287
20309
  }
20288
20310
  // ========== 打开弹窗前:记录弹窗数据和时间 ==========
20289
20311
  // 重置弹窗时间
20290
20312
  popupCurTimeRef.current = new Date();
20313
+ // 标记弹窗已打开,防止弹窗导致的 window blur 被误判为用户离开页面
20314
+ isPopupOpenRef.current = true;
20291
20315
  // 保存当前弹窗的产品数据,用于全局事件监听
20292
20316
  currentPopupDataRef.current = {
20293
20317
  productData,
@@ -20354,7 +20378,7 @@ const StructurePage = (_a) => {
20354
20378
  if (link) {
20355
20379
  window.open(link, '_blank');
20356
20380
  }
20357
- }, [ctaEvent, bffFbReport, data]);
20381
+ }, [ctaEvent, bffFbReport, data, schema]);
20358
20382
  // 合并基础样式和编辑器配置的样式
20359
20383
  const mergeStyles = React.useCallback((baseStyle, configKey, options) => {
20360
20384
  if (!multiCTAConfig || !multiCTAConfig[configKey]) {
@@ -20757,6 +20781,18 @@ const StructurePage = (_a) => {
20757
20781
  // 默认渲染按钮(包装一个 CTAButton 组件来处理曝光事件)
20758
20782
  return (React.createElement(CTAButton, { ctaData: ctaData, style: mergeStyles(fallbackStyle || baseStyles.heroButton, buttonKey), onClick: () => handleCtaClick(ctaData === null || ctaData === void 0 ? void 0 : ctaData.link, interaction, productData, ctaData, viewPosition, sectionIndex), onExposure: () => handleCtaExposure(ctaData, productData, viewPosition, sectionIndex, buttonKey) }));
20759
20783
  }, [multiCTAConfig, handleCtaClick, handleCtaExposure, mergeStyles, rest, carouselIndex]);
20784
+ // 监听页面卸载事件(刷新/关闭),设置 pageUnloadingRef 防止旧页面监听器误触发
20785
+ React.useEffect(() => {
20786
+ const handlePageUnload = () => {
20787
+ pageUnloadingRef.current = true;
20788
+ };
20789
+ window.addEventListener('beforeunload', handlePageUnload);
20790
+ window.addEventListener('pagehide', handlePageUnload);
20791
+ return () => {
20792
+ window.removeEventListener('beforeunload', handlePageUnload);
20793
+ window.removeEventListener('pagehide', handlePageUnload);
20794
+ };
20795
+ }, []);
20760
20796
  // 页面进入事件上报 - 只在组件挂载时执行一次
20761
20797
  React.useEffect(() => {
20762
20798
  var _a, _b;
@@ -20793,6 +20829,11 @@ const StructurePage = (_a) => {
20793
20829
  clSource: getUrlParam('cl_source')
20794
20830
  }
20795
20831
  });
20832
+ // 标记首次加载已完成,后续 visibilitychange 的 visible 分支才可以响应
20833
+ // 使用 setTimeout 确保在当前 tick 结束后再设置,避免与 visibilitychange 竞争
20834
+ setTimeout(() => {
20835
+ pageInitializedRef.current = true;
20836
+ }, 0);
20796
20837
  }, [bffEventReport]); // 只在挂载时执行一次
20797
20838
  // 获取数据 - 只在组件挂载时执行一次
20798
20839
  React.useEffect(() => {
@@ -20808,6 +20849,7 @@ const StructurePage = (_a) => {
20808
20849
  setLoading(false);
20809
20850
  setError(null);
20810
20851
  // 使用传入的 multiCTAConfig 作为数据源
20852
+ dataRef.current = propMultiCTAConfig;
20811
20853
  setData(propMultiCTAConfig);
20812
20854
  return;
20813
20855
  }
@@ -20893,6 +20935,7 @@ const StructurePage = (_a) => {
20893
20935
  multiCtaData = (_d = (_c = (_b = result.data) === null || _b === void 0 ? void 0 : _b.recList) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.multiCta;
20894
20936
  }
20895
20937
  if (multiCtaData) {
20938
+ dataRef.current = multiCtaData;
20896
20939
  setData(multiCtaData);
20897
20940
  // 保存 requestId 到 curReqInfo(用于后续事件上报)
20898
20941
  if (((_e = result.data) === null || _e === void 0 ? void 0 : _e.requestId) && setCurReqInfo) {
@@ -21518,12 +21561,14 @@ const StructurePage = (_a) => {
21518
21561
  delay: 1000, // 停留 1 秒后触发
21519
21562
  onVisible: handleHeroVisible,
21520
21563
  onHidden: handleHeroHidden,
21521
- name: 'Hero'
21564
+ name: 'Hero',
21565
+ // data 加载完成后 heroSectionRef 才会挂载到 DOM,需要将 data 作为额外依赖
21566
+ // 确保 data 就绪后 IntersectionObserver 能重新初始化并正确观察元素
21567
+ deps: [data]
21522
21568
  });
21523
21569
  // Carousel Section 的曝光检测 ref
21524
21570
  const carouselSectionRef = React.useRef(null);
21525
21571
  const carouselIsVisible = React.useRef(false); // 记录 carousel section 是否在可视区域内
21526
- const windowWasBlurred = React.useRef(false); // 记录 window 是否曾经 blur,防止 focus 误触发
21527
21572
  // 调试:检查 carouselSectionRef 是否正确挂载
21528
21573
  React.useEffect(() => {
21529
21574
  var _a;
@@ -21818,36 +21863,76 @@ const StructurePage = (_a) => {
21818
21863
  // ==================== 全局事件监听:visibilitychange ====================
21819
21864
  // 监听页面可见性变化(最小化、切换标签页、返回等)
21820
21865
  // 参考 SxpPageRender 的实现
21866
+ //
21867
+ // ⚠️ 重要:以下 ref 用于在事件回调中访问最新值,避免 useEffect 依赖变化导致监听器重新注册
21868
+ // 每次渲染时同步更新,确保回调中始终拿到最新值
21869
+ const bffEventReportRef = React.useRef(bffEventReport);
21870
+ const bffFbReportRef = React.useRef(bffFbReport);
21871
+ const refreshFeSessionRef = React.useRef(refreshFeSession);
21872
+ const globalConfigRef = React.useRef(globalConfig);
21873
+ const selectTagRef = React.useRef(selectTag);
21874
+ const backMainFeedRef = React.useRef(backMainFeed);
21875
+ const handleHeroImageStartEventRef = React.useRef(handleHeroImageStartEvent);
21876
+ const handleHeroImageEndEventRef = React.useRef(handleHeroImageEndEvent);
21877
+ const handleCarouselImageStartEventRef = React.useRef(handleCarouselImageStartEvent);
21878
+ const handleCarouselImageEndEventRef = React.useRef(handleCarouselImageEndEvent);
21879
+ const handleVideoPlayOverRef = React.useRef(handleVideoPlayOver);
21880
+ bffEventReportRef.current = bffEventReport;
21881
+ bffFbReportRef.current = bffFbReport;
21882
+ refreshFeSessionRef.current = refreshFeSession;
21883
+ globalConfigRef.current = globalConfig;
21884
+ selectTagRef.current = selectTag;
21885
+ backMainFeedRef.current = backMainFeed;
21886
+ handleHeroImageStartEventRef.current = handleHeroImageStartEvent;
21887
+ handleHeroImageEndEventRef.current = handleHeroImageEndEvent;
21888
+ handleCarouselImageStartEventRef.current = handleCarouselImageStartEvent;
21889
+ handleCarouselImageEndEventRef.current = handleCarouselImageEndEvent;
21890
+ handleVideoPlayOverRef.current = handleVideoPlayOver;
21821
21891
  React.useEffect(() => {
21822
21892
  const handleVisibilityChange = () => {
21823
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
21893
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
21824
21894
  if (document.visibilityState === 'hidden') {
21825
21895
  // ========== 页面隐藏时 ==========
21826
21896
  console.log('[StructurePage] Page hidden - 触发全局事件');
21827
21897
  // 0. 上报当前可见的 post/product 的离开事件
21828
21898
  console.log('[visibilitychange] 页面隐藏,当前可见的 products 数量:', visibleProducts.current.length);
21829
- // Hero Section: 如果是图片且可见,上报图片离开事件
21830
- if (heroIsVisible.current && (data === null || data === void 0 ? void 0 : data.heroSection)) {
21831
- const item = data.heroSection;
21832
- if (!item.url && ((_a = item.imgUrls) === null || _a === void 0 ? void 0 : _a[0])) {
21899
+ // Hero Section: 上报离开事件
21900
+ if (heroIsVisible.current && ((_a = dataRef.current) === null || _a === void 0 ? void 0 : _a.heroSection)) {
21901
+ const item = dataRef.current.heroSection;
21902
+ if (!item.url && ((_b = item.imgUrls) === null || _b === void 0 ? void 0 : _b[0])) {
21903
+ // 图片:上报图片离开事件
21833
21904
  console.log('[visibilitychange] 页面隐藏,上报 Hero 图片离开事件');
21834
- handleHeroImageEndEvent();
21905
+ handleHeroImageEndEventRef.current();
21906
+ }
21907
+ else if (item.url && heroVideoRef.current && !heroVideoRef.current.paused) {
21908
+ // 视频:正在播放时上报视频结束事件
21909
+ console.log('[visibilitychange] 页面隐藏,上报 Hero 视频结束事件');
21910
+ handleVideoPlayOverRef.current(item, 'heroSection', 0, heroVideoRef.current);
21835
21911
  }
21836
21912
  }
21837
- // Carousel Section: 如果是图片且可见,上报图片离开事件
21838
- if (carouselIsVisible.current && (data === null || data === void 0 ? void 0 : data.carouselSection)) {
21913
+ // Carousel Section: 上报离开事件
21914
+ if (carouselIsVisible.current && ((_c = dataRef.current) === null || _c === void 0 ? void 0 : _c.carouselSection)) {
21839
21915
  const currentIndex = currentCarouselIndexRef.current;
21840
- const currentItem = data.carouselSection[currentIndex];
21916
+ const currentItem = dataRef.current.carouselSection[currentIndex];
21841
21917
  if (currentItem && !currentItem.url) {
21918
+ // 图片:上报图片离开事件
21842
21919
  console.log('[visibilitychange] 页面隐藏,上报 Carousel 图片离开事件, index:', currentIndex);
21843
- handleCarouselImageEndEvent(currentIndex);
21920
+ handleCarouselImageEndEventRef.current(currentIndex);
21921
+ }
21922
+ else if (currentItem === null || currentItem === void 0 ? void 0 : currentItem.url) {
21923
+ // 视频:正在播放时上报视频结束事件
21924
+ const carouselVideoRef = carouselVideoRefs.current[currentIndex];
21925
+ if (carouselVideoRef && !carouselVideoRef.paused) {
21926
+ console.log('[visibilitychange] 页面隐藏,上报 Carousel 视频结束事件, index:', currentIndex);
21927
+ handleVideoPlayOverRef.current(currentItem, 'carouselSection', currentIndex, carouselVideoRef);
21928
+ }
21844
21929
  }
21845
21930
  }
21846
21931
  // Products: 上报所有当前可见的 product 的离开事件
21847
21932
  // 注意:只上报,不从 visibleProducts 移除,也不删除 productViewStartTime
21848
21933
  // 这样页面恢复可见时可以重新记录 startTime,后续滑走/再次最小化能正常上报
21849
21934
  visibleProducts.current.forEach((product) => {
21850
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
21935
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
21851
21936
  const productId = `${(_a = product.productData) === null || _a === void 0 ? void 0 : _a.itemId}-${product.viewPosition}-${product.sectionIndex}`;
21852
21937
  const startTime = productViewStartTime.current[productId];
21853
21938
  if (!startTime)
@@ -21860,71 +21945,38 @@ const StructurePage = (_a) => {
21860
21945
  fromKName = 'recommend page';
21861
21946
  else if (product.viewPosition === 'footerSection')
21862
21947
  fromKName = 'pdpPage';
21863
- bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21948
+ (_b = bffEventReportRef.current) === null || _b === void 0 ? void 0 : _b.call(bffEventReportRef, {
21864
21949
  eventInfo: {
21865
21950
  eventSubject: 'productView',
21866
21951
  eventDescription: 'User browsed the product',
21867
- productId: ((_b = product.productData) === null || _b === void 0 ? void 0 : _b.itemId) || '',
21868
- productName: ((_c = product.productData) === null || _c === void 0 ? void 0 : _c.title) || '',
21869
- price: ((_d = product.productData) === null || _d === void 0 ? void 0 : _d.price) ? `${product.productData.price}` : '0',
21870
- productCollection: ((_e = product.productData) === null || _e === void 0 ? void 0 : _e.collection) || '',
21952
+ productId: ((_c = product.productData) === null || _c === void 0 ? void 0 : _c.itemId) || '',
21953
+ productName: ((_d = product.productData) === null || _d === void 0 ? void 0 : _d.title) || '',
21954
+ price: ((_e = product.productData) === null || _e === void 0 ? void 0 : _e.price) ? `${product.productData.price}` : '0',
21955
+ productCollection: ((_f = product.productData) === null || _f === void 0 ? void 0 : _f.collection) || '',
21871
21956
  fromKName,
21872
21957
  fromKPage: (location === null || location === void 0 ? void 0 : location.href) || '',
21873
- contentTags: ((_f = product.productData) === null || _f === void 0 ? void 0 : _f.tags) ? JSON.stringify(product.productData.tags) : '[]',
21958
+ contentTags: ((_g = product.productData) === null || _g === void 0 ? void 0 : _g.tags) ? JSON.stringify(product.productData.tags) : '[]',
21874
21959
  position: `${product.sectionIndex || 0}`,
21875
21960
  contentId: '',
21876
21961
  sceneId: '',
21877
- ctatId: ((_h = (_g = product.productData) === null || _g === void 0 ? void 0 : _g.bindCta) === null || _h === void 0 ? void 0 : _h.itemId) || '',
21878
- traceInfo: ((_j = product.productData) === null || _j === void 0 ? void 0 : _j.traceInfo) || '',
21962
+ ctatId: ((_j = (_h = product.productData) === null || _h === void 0 ? void 0 : _h.bindCta) === null || _j === void 0 ? void 0 : _j.itemId) || '',
21963
+ traceInfo: ((_k = product.productData) === null || _k === void 0 ? void 0 : _k.traceInfo) || '',
21879
21964
  timeOnSite: `${timeOnSite}`
21880
21965
  }
21881
21966
  });
21882
21967
  });
21883
21968
  // 不清空 visibleProducts,保留快照供页面恢复时使用
21884
21969
  // 1. 发送 PAGE_DID_HIDE 事件到事件总线
21885
- SXP_EVENT_BUS.emit(SXP_EVENT_TYPE.PAGE_DID_HIDE, data);
21886
- // 2. 如果有打开的弹窗,上报 ExitFeed 事件
21887
- if (currentPopupDataRef.current) {
21888
- const { productData, viewPosition, sectionIndex } = currentPopupDataRef.current;
21889
- // 获取当前 section 的 post 数据
21890
- let postData = null;
21891
- if (viewPosition === 'heroSection' && (data === null || data === void 0 ? void 0 : data.heroSection)) {
21892
- postData = data.heroSection;
21893
- }
21894
- else if (viewPosition === 'carouselSection' && (data === null || data === void 0 ? void 0 : data.carouselSection) && sectionIndex !== undefined) {
21895
- postData = data.carouselSection[sectionIndex];
21896
- }
21897
- const rec = {
21898
- video: {
21899
- bindProduct: productData,
21900
- bindProducts: productData ? [productData] : [],
21901
- itemId: (postData === null || postData === void 0 ? void 0 : postData.itemId) || '',
21902
- tags: (postData === null || postData === void 0 ? void 0 : postData.tags) || null,
21903
- scene: (postData === null || postData === void 0 ? void 0 : postData.scene) || null,
21904
- url: (postData === null || postData === void 0 ? void 0 : postData.url) || null,
21905
- imgUrls: (postData === null || postData === void 0 ? void 0 : postData.imgUrls) || null
21906
- },
21907
- product: null
21908
- };
21909
- const viewTime = new Date() - popupCurTimeRef.current;
21910
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
21911
- eventName: 'ExitFeed',
21912
- product: productData ? [productData] : undefined,
21913
- contentType: 'product',
21914
- rec: rec,
21915
- position: sectionIndex !== undefined ? sectionIndex : 0,
21916
- view_time: viewTime,
21917
- content_id: (productData === null || productData === void 0 ? void 0 : productData.itemId) || ''
21918
- });
21919
- console.log('[StructurePage] 上报 ExitFeed 事件', { viewTime, productId: productData === null || productData === void 0 ? void 0 : productData.itemId });
21920
- }
21921
- // 3. 刷新 Session(处理 Session 过期)
21970
+ // ExitFeed 事件由 PAGE_DID_HIDE 监听器统一处理,避免重复上报
21971
+ SXP_EVENT_BUS.emit(SXP_EVENT_TYPE.PAGE_DID_HIDE, dataRef.current);
21972
+ // 2. 刷新 Session(处理 Session 过期)
21922
21973
  // 只有在非 organic menu 模式下才刷新 Session(与非 multi-cta 逻辑一致)
21923
- if ((globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.playbook) !== 'organic menu' && refreshFeSession && curTime) {
21924
- const isExpire = refreshFeSession(false, (reason) => {
21974
+ if (((_d = globalConfigRef.current) === null || _d === void 0 ? void 0 : _d.playbook) !== 'organic menu' && refreshFeSessionRef.current && curTime) {
21975
+ const isExpire = refreshFeSessionRef.current(false, (reason) => {
21976
+ var _a;
21925
21977
  console.log('[StructurePage] Session expired:', reason);
21926
21978
  // Session 过期时的回调处理
21927
- bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
21979
+ (_a = bffEventReportRef.current) === null || _a === void 0 ? void 0 : _a.call(bffEventReportRef, {
21928
21980
  eventInfo: {
21929
21981
  eventSubject: 'sessionExpired',
21930
21982
  eventDescription: 'User session has expired',
@@ -21945,16 +21997,16 @@ const StructurePage = (_a) => {
21945
21997
  if (currentPopupDataRef.current) {
21946
21998
  const { productData, viewPosition, sectionIndex } = currentPopupDataRef.current;
21947
21999
  // 获取 contentId 和 sceneId(从对应的 post/image 数据中)
21948
- if (viewPosition === 'heroSection' && (data === null || data === void 0 ? void 0 : data.heroSection)) {
21949
- contentId = data.heroSection.itemId || '';
21950
- sceneId = ((_b = data.heroSection.scene) === null || _b === void 0 ? void 0 : _b.sceneId) || '';
21951
- ctatId = ((_c = data.heroSection.bindCta) === null || _c === void 0 ? void 0 : _c.itemId) || '';
22000
+ if (viewPosition === 'heroSection' && ((_e = dataRef.current) === null || _e === void 0 ? void 0 : _e.heroSection)) {
22001
+ contentId = dataRef.current.heroSection.itemId || '';
22002
+ sceneId = ((_f = dataRef.current.heroSection.scene) === null || _f === void 0 ? void 0 : _f.sceneId) || '';
22003
+ ctatId = ((_g = dataRef.current.heroSection.bindCta) === null || _g === void 0 ? void 0 : _g.itemId) || '';
21952
22004
  }
21953
- else if (viewPosition === 'carouselSection' && (data === null || data === void 0 ? void 0 : data.carouselSection) && sectionIndex !== undefined) {
21954
- const carouselItem = data.carouselSection[sectionIndex];
22005
+ else if (viewPosition === 'carouselSection' && ((_h = dataRef.current) === null || _h === void 0 ? void 0 : _h.carouselSection) && sectionIndex !== undefined) {
22006
+ const carouselItem = dataRef.current.carouselSection[sectionIndex];
21955
22007
  contentId = (carouselItem === null || carouselItem === void 0 ? void 0 : carouselItem.itemId) || '';
21956
- sceneId = ((_d = carouselItem === null || carouselItem === void 0 ? void 0 : carouselItem.scene) === null || _d === void 0 ? void 0 : _d.sceneId) || '';
21957
- ctatId = ((_e = carouselItem === null || carouselItem === void 0 ? void 0 : carouselItem.bindCta) === null || _e === void 0 ? void 0 : _e.itemId) || '';
22008
+ sceneId = ((_j = carouselItem === null || carouselItem === void 0 ? void 0 : carouselItem.scene) === null || _j === void 0 ? void 0 : _j.sceneId) || '';
22009
+ ctatId = ((_k = carouselItem === null || carouselItem === void 0 ? void 0 : carouselItem.bindCta) === null || _k === void 0 ? void 0 : _k.itemId) || '';
21958
22010
  }
21959
22011
  // 获取 productId
21960
22012
  productId = (productData === null || productData === void 0 ? void 0 : productData.itemId) || '';
@@ -21962,23 +22014,23 @@ const StructurePage = (_a) => {
21962
22014
  // 2. 如果没有弹窗,从当前可见的内容中获取
21963
22015
  else {
21964
22016
  // 优先从 Hero Section 获取(如果可见)
21965
- if (heroIsVisible.current && (data === null || data === void 0 ? void 0 : data.heroSection)) {
21966
- contentId = data.heroSection.itemId || '';
21967
- sceneId = ((_f = data.heroSection.scene) === null || _f === void 0 ? void 0 : _f.sceneId) || '';
21968
- ctatId = ((_g = data.heroSection.bindCta) === null || _g === void 0 ? void 0 : _g.itemId) || '';
22017
+ if (heroIsVisible.current && ((_l = dataRef.current) === null || _l === void 0 ? void 0 : _l.heroSection)) {
22018
+ contentId = dataRef.current.heroSection.itemId || '';
22019
+ sceneId = ((_m = dataRef.current.heroSection.scene) === null || _m === void 0 ? void 0 : _m.sceneId) || '';
22020
+ ctatId = ((_o = dataRef.current.heroSection.bindCta) === null || _o === void 0 ? void 0 : _o.itemId) || '';
21969
22021
  // 如果 Hero 有绑定的产品,获取第一个产品的 ID
21970
- if (data.heroSection.bindProducts && data.heroSection.bindProducts.length > 0) {
21971
- productId = data.heroSection.bindProducts[0].itemId || '';
22022
+ if (dataRef.current.heroSection.bindProducts && dataRef.current.heroSection.bindProducts.length > 0) {
22023
+ productId = dataRef.current.heroSection.bindProducts[0].itemId || '';
21972
22024
  }
21973
22025
  }
21974
22026
  // 其次从 Carousel Section 获取(如果可见)
21975
- else if (carouselIsVisible.current && (data === null || data === void 0 ? void 0 : data.carouselSection)) {
22027
+ else if (carouselIsVisible.current && ((_p = dataRef.current) === null || _p === void 0 ? void 0 : _p.carouselSection)) {
21976
22028
  const currentIndex = currentCarouselIndexRef.current;
21977
- const currentItem = data.carouselSection[currentIndex];
22029
+ const currentItem = dataRef.current.carouselSection[currentIndex];
21978
22030
  if (currentItem) {
21979
22031
  contentId = currentItem.itemId || '';
21980
- sceneId = ((_h = currentItem.scene) === null || _h === void 0 ? void 0 : _h.sceneId) || '';
21981
- ctatId = ((_j = currentItem.bindCta) === null || _j === void 0 ? void 0 : _j.itemId) || '';
22032
+ sceneId = ((_q = currentItem.scene) === null || _q === void 0 ? void 0 : _q.sceneId) || '';
22033
+ ctatId = ((_r = currentItem.bindCta) === null || _r === void 0 ? void 0 : _r.itemId) || '';
21982
22034
  // 如果 Carousel 有绑定的产品,获取第一个产品的 ID
21983
22035
  if (currentItem.bindProducts && currentItem.bindProducts.length > 0) {
21984
22036
  productId = currentItem.bindProducts[0].itemId || '';
@@ -21988,24 +22040,24 @@ const StructurePage = (_a) => {
21988
22040
  // 最后从当前可见的产品列表中获取(如果有)
21989
22041
  else if (visibleProducts.current.length > 0) {
21990
22042
  const firstVisibleProduct = visibleProducts.current[0];
21991
- productId = ((_k = firstVisibleProduct.productData) === null || _k === void 0 ? void 0 : _k.itemId) || '';
22043
+ productId = ((_s = firstVisibleProduct.productData) === null || _s === void 0 ? void 0 : _s.itemId) || '';
21992
22044
  // 尝试获取对应的 contentId
21993
- if (firstVisibleProduct.viewPosition === 'heroSection' && (data === null || data === void 0 ? void 0 : data.heroSection)) {
21994
- contentId = data.heroSection.itemId || '';
21995
- sceneId = ((_l = data.heroSection.scene) === null || _l === void 0 ? void 0 : _l.sceneId) || '';
21996
- ctatId = ((_m = data.heroSection.bindCta) === null || _m === void 0 ? void 0 : _m.itemId) || '';
22045
+ if (firstVisibleProduct.viewPosition === 'heroSection' && ((_t = dataRef.current) === null || _t === void 0 ? void 0 : _t.heroSection)) {
22046
+ contentId = dataRef.current.heroSection.itemId || '';
22047
+ sceneId = ((_u = dataRef.current.heroSection.scene) === null || _u === void 0 ? void 0 : _u.sceneId) || '';
22048
+ ctatId = ((_v = dataRef.current.heroSection.bindCta) === null || _v === void 0 ? void 0 : _v.itemId) || '';
21997
22049
  }
21998
- else if (firstVisibleProduct.viewPosition === 'carouselSection' && (data === null || data === void 0 ? void 0 : data.carouselSection)) {
21999
- const carouselItem = data.carouselSection[firstVisibleProduct.sectionIndex];
22050
+ else if (firstVisibleProduct.viewPosition === 'carouselSection' && ((_w = dataRef.current) === null || _w === void 0 ? void 0 : _w.carouselSection)) {
22051
+ const carouselItem = dataRef.current.carouselSection[firstVisibleProduct.sectionIndex];
22000
22052
  if (carouselItem) {
22001
22053
  contentId = carouselItem.itemId || '';
22002
- sceneId = ((_o = carouselItem.scene) === null || _o === void 0 ? void 0 : _o.sceneId) || '';
22003
- ctatId = ((_p = carouselItem.bindCta) === null || _p === void 0 ? void 0 : _p.itemId) || '';
22054
+ sceneId = ((_x = carouselItem.scene) === null || _x === void 0 ? void 0 : _x.sceneId) || '';
22055
+ ctatId = ((_y = carouselItem.bindCta) === null || _y === void 0 ? void 0 : _y.itemId) || '';
22004
22056
  }
22005
22057
  }
22006
22058
  }
22007
22059
  }
22008
- bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
22060
+ (_z = bffEventReportRef.current) === null || _z === void 0 ? void 0 : _z.call(bffEventReportRef, {
22009
22061
  eventInfo: {
22010
22062
  eventSubject: 'sessionCompleted',
22011
22063
  eventDescription: 'User session completed normally',
@@ -22022,14 +22074,25 @@ const StructurePage = (_a) => {
22022
22074
  else if (document.visibilityState === 'visible') {
22023
22075
  // ========== 页面可见时 ==========
22024
22076
  console.log('[StructurePage] Page visible - 触发全局事件');
22077
+ // 如果页面尚未完成首次加载(挂载时的 h5LinkEnterFeed 还未上报),
22078
+ // 则跳过此次 visibilitychange visible 处理,避免刷新时重复上报
22079
+ if (!pageInitializedRef.current) {
22080
+ console.log('[StructurePage] 页面尚未初始化完成,跳过 visibilitychange visible 处理');
22081
+ return;
22082
+ }
22083
+ // 如果页面正在卸载(刷新/关闭),跳过处理,避免旧页面监听器误触发
22084
+ if (pageUnloadingRef.current) {
22085
+ console.log('[StructurePage] 页面正在卸载,跳过 visibilitychange visible 处理');
22086
+ return;
22087
+ }
22025
22088
  // ⚠️ 重要:必须先刷新 Session,再上报事件(使用最新的 Session ID)
22026
22089
  // 1. 刷新 Session(根据配置决定)
22027
- if (refreshFeSession) {
22028
- refreshFeSession(false);
22090
+ if (refreshFeSessionRef.current) {
22091
+ refreshFeSessionRef.current(false);
22029
22092
  }
22030
22093
  // 2. 如果未启用 session 配置,则强制刷新 Session ID(与非 multi-cta 逻辑一致)
22031
22094
  // ⚠️ 必须在上报事件之前刷新,这样上报的事件才会使用最新的 Session ID
22032
- if (!((_q = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.session) === null || _q === void 0 ? void 0 : _q.enable)) {
22095
+ if (!((_1 = (_0 = globalConfigRef.current) === null || _0 === void 0 ? void 0 : _0.session) === null || _1 === void 0 ? void 0 : _1.enable)) {
22033
22096
  refreshFeSessionId();
22034
22097
  console.log('[StructurePage] Session 未启用,强制刷新 Session ID');
22035
22098
  }
@@ -22042,7 +22105,7 @@ const StructurePage = (_a) => {
22042
22105
  if (curTime) {
22043
22106
  curTime.current = time;
22044
22107
  }
22045
- bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
22108
+ (_2 = bffEventReportRef.current) === null || _2 === void 0 ? void 0 : _2.call(bffEventReportRef, {
22046
22109
  eventInfo: {
22047
22110
  eventSubject: 'h5LinkEnterFeed',
22048
22111
  eventDescription: 'User enter h5 link',
@@ -22052,15 +22115,15 @@ const StructurePage = (_a) => {
22052
22115
  utmId: getUrlParamByKey('utm_id'),
22053
22116
  utmContent: getUrlParamByKey('utm_content'),
22054
22117
  enterTime: Math.floor(time / 1000) + '',
22055
- enterUrl: ((_r = window === null || window === void 0 ? void 0 : window.location) === null || _r === void 0 ? void 0 : _r.href) || ''
22118
+ enterUrl: ((_3 = window === null || window === void 0 ? void 0 : window.location) === null || _3 === void 0 ? void 0 : _3.href) || ''
22056
22119
  },
22057
22120
  reportLayId: false
22058
22121
  });
22059
22122
  // 3. 上报 backMainFeed 事件(从外部返回)
22060
- backMainFeed('external', selectTag);
22123
+ backMainFeedRef.current('external', selectTagRef.current);
22061
22124
  // 4. 发送 PAGE_DID_SHOW 事件到事件总线(延迟 100ms,与 SxpPageRender 保持一致)
22062
22125
  setTimeout(() => {
22063
- SXP_EVENT_BUS.emit(SXP_EVENT_TYPE.PAGE_DID_SHOW, data);
22126
+ SXP_EVENT_BUS.emit(SXP_EVENT_TYPE.PAGE_DID_SHOW, dataRef.current);
22064
22127
  }, 100);
22065
22128
  // 4. 如果有打开的弹窗,重置弹窗时间
22066
22129
  if (currentPopupDataRef.current) {
@@ -22070,24 +22133,24 @@ const StructurePage = (_a) => {
22070
22133
  // 5. 上报当前可见的 post/product 的进入事件
22071
22134
  console.log('[visibilitychange] 页面可见,需要重新上报的 products 数量:', visibleProducts.current.length);
22072
22135
  // Hero Section: 如果是图片且可见,上报图片进入事件
22073
- if (heroIsVisible.current && (data === null || data === void 0 ? void 0 : data.heroSection)) {
22074
- const item = data.heroSection;
22075
- if (!item.url && ((_s = item.imgUrls) === null || _s === void 0 ? void 0 : _s[0])) {
22136
+ if (heroIsVisible.current && ((_4 = dataRef.current) === null || _4 === void 0 ? void 0 : _4.heroSection)) {
22137
+ const item = dataRef.current.heroSection;
22138
+ if (!item.url && ((_5 = item.imgUrls) === null || _5 === void 0 ? void 0 : _5[0])) {
22076
22139
  console.log('[visibilitychange] 页面可见,上报 Hero 图片进入事件');
22077
- handleHeroImageStartEvent();
22140
+ handleHeroImageStartEventRef.current();
22078
22141
  }
22079
22142
  }
22080
22143
  // Carousel Section: 如果是图片且可见,上报图片进入事件
22081
- if (carouselIsVisible.current && (data === null || data === void 0 ? void 0 : data.carouselSection)) {
22144
+ if (carouselIsVisible.current && ((_6 = dataRef.current) === null || _6 === void 0 ? void 0 : _6.carouselSection)) {
22082
22145
  const currentIndex = currentCarouselIndexRef.current;
22083
- const currentItem = data.carouselSection[currentIndex];
22146
+ const currentItem = dataRef.current.carouselSection[currentIndex];
22084
22147
  if (currentItem && !currentItem.url) {
22085
22148
  console.log('[visibilitychange] 页面可见,当前是图片, index:', currentIndex);
22086
22149
  // 检查图片是否已加载
22087
22150
  if (carouselImageLoadStartTime.current[currentIndex] > 0) {
22088
22151
  // 图片已加载,立即上报
22089
22152
  console.log('[visibilitychange] 图片已加载,上报 Carousel 图片进入事件');
22090
- handleCarouselImageStartEvent(currentIndex, carouselImageLoadTime.current[currentIndex]);
22153
+ handleCarouselImageStartEventRef.current(currentIndex, carouselImageLoadTime.current[currentIndex]);
22091
22154
  }
22092
22155
  else {
22093
22156
  // 图片未加载,标记为待上报,等图片加载完成后触发
@@ -22106,80 +22169,15 @@ const StructurePage = (_a) => {
22106
22169
  });
22107
22170
  }
22108
22171
  };
22109
- // ========== window blur/focus:处理 Mac 缩小窗口到 Dock 的场景 ==========
22110
- // Mac 点击黄色缩小按钮不会触发 visibilitychange,需要额外监听 window blur/focus
22111
- // 注意:只有在经历过 blur(真正离开)之后 focus 才上报,防止页面内交互误触发
22112
- const handleWindowBlur = () => {
22113
- // 窗口失去焦点(缩小到 Dock / 切换到其他 App)
22114
- windowWasBlurred.current = true;
22115
- // 与 visibilitychange hidden 分支对齐:刷新 Session
22116
- if ((globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.playbook) !== 'organic menu' && refreshFeSession && curTime) {
22117
- refreshFeSession(false);
22118
- }
22119
- };
22120
- const handleWindowFocus = () => {
22121
- var _a, _b;
22122
- // 仅在经历过真实 blur 之后才处理(防止页面内点击/滑动误触发)
22123
- if (!windowWasBlurred.current)
22124
- return;
22125
- windowWasBlurred.current = false;
22126
- // 窗口重新获得焦点(从 Dock 还原 / 切回浏览器)
22127
- // 与 visibilitychange visible 分支对齐:刷新 Session ID 并上报进入事件
22128
- if (refreshFeSession) {
22129
- refreshFeSession(false);
22130
- }
22131
- if (!((_a = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.session) === null || _a === void 0 ? void 0 : _a.enable)) {
22132
- refreshFeSessionId();
22133
- }
22134
- const getUrlParamByKey = (key) => {
22135
- const urlParams = new URLSearchParams(window.location.search);
22136
- return urlParams.get(key) || '';
22137
- };
22138
- const time = new Date();
22139
- if (curTime)
22140
- curTime.current = time;
22141
- bffEventReport === null || bffEventReport === void 0 ? void 0 : bffEventReport({
22142
- eventInfo: {
22143
- eventSubject: 'h5LinkEnterFeed',
22144
- eventDescription: 'User enter h5 link',
22145
- utmSource: getUrlParamByKey('utm_source'),
22146
- utmMedium: getUrlParamByKey('utm_medium'),
22147
- utmCampaign: getUrlParamByKey('utm_campaign'),
22148
- utmId: getUrlParamByKey('utm_id'),
22149
- utmContent: getUrlParamByKey('utm_content'),
22150
- enterTime: Math.floor(time / 1000) + '',
22151
- enterUrl: ((_b = window === null || window === void 0 ? void 0 : window.location) === null || _b === void 0 ? void 0 : _b.href) || ''
22152
- },
22153
- reportLayId: false
22154
- });
22155
- setTimeout(() => {
22156
- SXP_EVENT_BUS.emit(SXP_EVENT_TYPE.PAGE_DID_SHOW, data);
22157
- }, 100);
22158
- };
22159
- // 添加事件监听
22172
+ // 添加事件监听(只使用 visibilitychange,不使用 window blur/focus
22173
+ // window blur/focus 会被页面内点击、刷新等操作误触发,难以可靠区分,已移除
22160
22174
  document.addEventListener('visibilitychange', handleVisibilityChange);
22161
- window.addEventListener('blur', handleWindowBlur);
22162
- window.addEventListener('focus', handleWindowFocus);
22163
22175
  // 清理函数
22164
22176
  return () => {
22165
22177
  document.removeEventListener('visibilitychange', handleVisibilityChange);
22166
- window.removeEventListener('blur', handleWindowBlur);
22167
- window.removeEventListener('focus', handleWindowFocus);
22168
22178
  };
22169
- }, [
22170
- data,
22171
- bffFbReport,
22172
- bffEventReport,
22173
- refreshFeSession,
22174
- curTime,
22175
- globalConfig,
22176
- handleHeroImageStartEvent,
22177
- handleHeroImageEndEvent,
22178
- handleCarouselImageStartEvent,
22179
- handleCarouselImageEndEvent,
22180
- backMainFeed,
22181
- selectTag
22182
- ]);
22179
+ // eslint-disable-next-line react-hooks/exhaustive-deps
22180
+ }, []); // 只在挂载时注册一次,所有依赖通过 ref 访问最新值,避免重新注册导致重复上报
22183
22181
  // ==================== 初始化弹窗时间记录 ====================
22184
22182
  React.useEffect(() => {
22185
22183
  const initTime = () => {
@@ -22243,8 +22241,9 @@ const StructurePage = (_a) => {
22243
22241
  productId: productData === null || productData === void 0 ? void 0 : productData.itemId,
22244
22242
  engagement_type: 'close_popup'
22245
22243
  });
22246
- // 清空弹窗数据引用
22244
+ // 清空弹窗数据引用,并重置弹窗打开标记
22247
22245
  currentPopupDataRef.current = null;
22246
+ isPopupOpenRef.current = false;
22248
22247
  }
22249
22248
  wasPopupOpen = isPopupOpen;
22250
22249
  };