pb-sxp-ui 1.20.27 → 1.20.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/index.cjs +353 -39
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +353 -39
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.min.cjs +9 -9
  6. package/dist/index.min.cjs.map +1 -1
  7. package/dist/index.min.js +9 -9
  8. package/dist/index.min.js.map +1 -1
  9. package/dist/pb-ui.js +353 -39
  10. package/dist/pb-ui.js.map +1 -1
  11. package/dist/pb-ui.min.js +9 -9
  12. package/dist/pb-ui.min.js.map +1 -1
  13. package/es/core/context/SxpDataSourceProvider.d.ts +1 -1
  14. package/es/core/index.d.ts +6 -0
  15. package/es/core/index.js +10 -0
  16. package/es/core/utils/materials.js +30 -7
  17. package/es/core/utils/tool.d.ts +1 -0
  18. package/es/core/utils/tool.js +43 -0
  19. package/es/materials/sxp/popup/AddToCart/index.js +27 -8
  20. package/es/materials/sxp/popup/AddToCart/index.new.js +27 -6
  21. package/es/materials/sxp/popup/AddToCart/index.old.js +13 -2
  22. package/es/materials/sxp/popup/CommodityDetail/index.js +21 -16
  23. package/es/materials/sxp/popup/CommodityDetailDiroNew/index.js +25 -0
  24. package/lib/core/context/SxpDataSourceProvider.d.ts +1 -1
  25. package/lib/core/index.d.ts +6 -0
  26. package/lib/core/index.js +10 -5
  27. package/lib/core/utils/materials.js +29 -6
  28. package/lib/core/utils/tool.d.ts +1 -0
  29. package/lib/core/utils/tool.js +45 -1
  30. package/lib/materials/sxp/popup/AddToCart/index.js +27 -8
  31. package/lib/materials/sxp/popup/AddToCart/index.new.js +27 -6
  32. package/lib/materials/sxp/popup/AddToCart/index.old.js +12 -1
  33. package/lib/materials/sxp/popup/CommodityDetail/index.js +21 -16
  34. package/lib/materials/sxp/popup/CommodityDetailDiroNew/index.js +25 -0
  35. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -187,6 +187,14 @@ function uuid(len, radix) {
187
187
  }
188
188
  return uuid.join('');
189
189
  }
190
+ const getIndexByblockType = (type, index) => {
191
+ if (type === 'CommodityCarouselBlock' || type === 'CopyBlock') {
192
+ return 'initial';
193
+ }
194
+ else {
195
+ return index;
196
+ }
197
+ };
190
198
  const generateRandomString = (length) => {
191
199
  let result = '';
192
200
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -246,6 +254,70 @@ const setFontForText = (textContent, style) => {
246
254
  }
247
255
  return content;
248
256
  };
257
+ function getBrowserInfo() {
258
+ var _a, _b, _c, _d, _e, _f, _g;
259
+ const userAgent = self.navigator.userAgent;
260
+ if (!userAgent)
261
+ return null;
262
+ if (/edge\/([\d\.]+)/i.exec(userAgent))
263
+ return `Edge ${(_a = /edge\/([\d\.]+)/i.exec(userAgent)) === null || _a === void 0 ? void 0 : _a[1]}`;
264
+ if (/edg\/([\d\.]+)/i.exec(userAgent))
265
+ return `Edge(Chromium) ${(_b = /edge\/([\d\.]+)/i.exec(userAgent)) === null || _b === void 0 ? void 0 : _b[1]}`;
266
+ if (/msie/i.test(userAgent))
267
+ return `Internet Explorer ${(_c = /msie ([\d\.]+)/i.exec(userAgent)) === null || _c === void 0 ? void 0 : _c[1]}`;
268
+ if (/Trident/i.test(userAgent))
269
+ return `Internet Explorer ${(_d = /rv:([\d\.]+)/i.exec(userAgent)) === null || _d === void 0 ? void 0 : _d[1]}`;
270
+ if (/chrome/i.test(userAgent))
271
+ return `Chrome ${(_e = /chrome\/([\d\.]+)/i.exec(userAgent)) === null || _e === void 0 ? void 0 : _e[1]}`;
272
+ if (/firefox/i.test(userAgent))
273
+ return `Firefox ${(_f = /firefox\/([\d\.]+)/i.exec(userAgent)) === null || _f === void 0 ? void 0 : _f[1]}`;
274
+ if (/safari/i.test(userAgent))
275
+ return `Safari ${(_g = /version\/([\d\.]+)/i.exec(userAgent)) === null || _g === void 0 ? void 0 : _g[1]}`;
276
+ return null;
277
+ }
278
+ function getSystem() {
279
+ var _a, _b, _c;
280
+ const userAgent = self.navigator.userAgent;
281
+ if (!userAgent)
282
+ return null;
283
+ if (/iphone/i.test(userAgent))
284
+ return `IOS ${(_a = userAgent.match(/OS\s(.*?)\slike/)) === null || _a === void 0 ? void 0 : _a[1]}`;
285
+ if (/android/i.test(userAgent))
286
+ return `Android ${(_b = userAgent.match(/Android\s(.*?)\;/)) === null || _b === void 0 ? void 0 : _b[1]}`;
287
+ if (/windows/i.test(userAgent))
288
+ return `Windows ${(_c = userAgent.match(/Windows\s(.*?)\;/)) === null || _c === void 0 ? void 0 : _c[1]}`;
289
+ if (/mac/i.test(userAgent))
290
+ return 'Mac OS';
291
+ return null;
292
+ }
293
+ function getDevice$1() {
294
+ const userAgent = self.navigator.userAgent;
295
+ if (!userAgent)
296
+ return null;
297
+ if (/iphone/i.test(userAgent))
298
+ return 'iPhone';
299
+ if (/android/i.test(userAgent)) {
300
+ // var index1 = userAgent.indexOf(';');
301
+ // var index2 = userAgent.indexOf(';', index1 + 1);
302
+ // var index3 = userAgent.indexOf(';', index2 + 1);
303
+ // var index4 = userAgent.indexOf(';', index3 + 1);
304
+ // if (index2 !== -1 && index3 !== -1) {
305
+ // var value1 = userAgent.substring(index3 + 1, index4);
306
+ // return `${value1}`;
307
+ // }
308
+ const index1 = userAgent.indexOf('(');
309
+ const index2 = userAgent.indexOf(')');
310
+ if (index1 !== -1 && index2 !== -1) {
311
+ const value = userAgent.substring(index1 + 1, index2);
312
+ return `${value}`;
313
+ }
314
+ }
315
+ if (/windows/i.test(userAgent))
316
+ return 'Windows';
317
+ if (/mac/i.test(userAgent))
318
+ return 'Mac';
319
+ return null;
320
+ }
249
321
  function getCookie(val) {
250
322
  // const expirationDate = new Date();
251
323
  // expirationDate.setDate(expirationDate.getDate() + 100);
@@ -307,6 +379,33 @@ function deleteCookie(name, path = '/', domain = '') {
307
379
  document.cookie = `${name}=; ${expiration}${pathPart}${domainPart}`;
308
380
  console.log(`已尝试删除Cookie: ${name}`);
309
381
  }
382
+ function setCookie(name, value, days = 0, path = '/', domain = '', secure = false, sameSite = 'Lax') {
383
+ let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
384
+ // 设置过期时间
385
+ if (days) {
386
+ const date = new Date();
387
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
388
+ cookieString += `; expires=${date.toUTCString()}`;
389
+ }
390
+ // 设置路径
391
+ if (path) {
392
+ cookieString += `; path=${path}`;
393
+ }
394
+ // 设置域名
395
+ if (domain) {
396
+ cookieString += `; domain=${domain}`;
397
+ }
398
+ // 设置Secure标志
399
+ if (secure) {
400
+ cookieString += '; secure';
401
+ }
402
+ // 设置SameSite属性
403
+ if (sameSite) {
404
+ cookieString += `; samesite=${sameSite}`;
405
+ }
406
+ // 设置Cookie
407
+ document.cookie = cookieString;
408
+ }
310
409
  function getUrlParamByKey(key) {
311
410
  var _a, _b;
312
411
  const queryString = location.search.slice(1);
@@ -321,6 +420,73 @@ function getUrlParamByKey(key) {
321
420
  }
322
421
  return (_b = params[key]) !== null && _b !== void 0 ? _b : '';
323
422
  }
423
+ /**
424
+ * 货币代码到货币符号的映射
425
+ * 根据 Shopify 返回的 currencyCode 获取对应的货币符号
426
+ */
427
+ function getCurrencySymbol(currencyCode) {
428
+ const currencySymbolMap = {
429
+ // 主要货币
430
+ USD: '$', // 美元
431
+ EUR: '€', // 欧元
432
+ GBP: '£', // 英镑
433
+ JPY: '¥', // 日元
434
+ CNY: '¥', // 人民币
435
+ KRW: '₩', // 韩元
436
+ INR: '₹', // 印度卢比
437
+ RUB: '₽', // 俄罗斯卢布
438
+ BRL: 'R$', // 巴西雷亚尔
439
+ CAD: 'CA$', // 加拿大元
440
+ AUD: 'A$', // 澳元
441
+ CHF: 'CHF', // 瑞士法郎
442
+ SEK: 'kr', // 瑞典克朗
443
+ NOK: 'kr', // 挪威克朗
444
+ DKK: 'kr', // 丹麦克朗
445
+ PLN: 'zł', // 波兰兹罗提
446
+ THB: '฿', // 泰铢
447
+ IDR: 'Rp', // 印尼盾
448
+ MYR: 'RM', // 马来西亚林吉特
449
+ PHP: '₱', // 菲律宾比索
450
+ SGD: 'S$', // 新加坡元
451
+ HKD: 'HK$', // 港币
452
+ TWD: 'NT$', // 新台币
453
+ NZD: 'NZ$', // 新西兰元
454
+ MXN: 'MX$', // 墨西哥比索
455
+ ZAR: 'R', // 南非兰特
456
+ TRY: '₺', // 土耳其里拉
457
+ AED: 'AED', // 阿联酋迪拉姆
458
+ SAR: 'SAR', // 沙特里亚尔
459
+ ILS: '₪', // 以色列新谢克尔
460
+ ARS: 'AR$', // 阿根廷比索
461
+ CLP: 'CL$', // 智利比索
462
+ COP: 'CO$', // 哥伦比亚比索
463
+ VND: '₫', // 越南盾
464
+ EGP: 'E£', // 埃及镑
465
+ NGN: '₦', // 尼日利亚奈拉
466
+ PKR: '₨', // 巴基斯坦卢比
467
+ BDT: '৳', // 孟加拉塔卡
468
+ };
469
+ return currencySymbolMap[currencyCode.toUpperCase()] || currencyCode;
470
+ }
471
+
472
+ var tool = /*#__PURE__*/Object.freeze({
473
+ __proto__: null,
474
+ deleteCookie: deleteCookie,
475
+ generateRandomString: generateRandomString,
476
+ getBrowserInfo: getBrowserInfo,
477
+ getCookie: getCookie,
478
+ getCurrencySymbol: getCurrencySymbol,
479
+ getDevice: getDevice$1,
480
+ getIndexByblockType: getIndexByblockType,
481
+ getScreenReader: getScreenReader,
482
+ getSystem: getSystem,
483
+ getUid: getUid,
484
+ getUrlParamByKey: getUrlParamByKey,
485
+ setCookie: setCookie,
486
+ setFontForText: setFontForText,
487
+ splitUrlParams: splitUrlParams,
488
+ uuid: uuid
489
+ });
324
490
 
325
491
  function unzip(b64Data) {
326
492
  const strData = atob(b64Data);
@@ -506,6 +672,18 @@ const refreshFeSessionId = () => {
506
672
  const getFeSessionId = () => {
507
673
  return window.localStorage.getItem(feRealSessionIdKey);
508
674
  };
675
+ // 删除sessionID
676
+ const removeFeSessionId = () => {
677
+ window.localStorage.removeItem(feRealSessionIdKey);
678
+ };
679
+
680
+ var sessionStore = /*#__PURE__*/Object.freeze({
681
+ __proto__: null,
682
+ getFeSessionId: getFeSessionId,
683
+ refreshFeSessionId: refreshFeSessionId,
684
+ removeFeSessionId: removeFeSessionId,
685
+ storeAndLoadFeSessionId: storeAndLoadFeSessionId
686
+ });
509
687
 
510
688
  /*
511
689
  * @Author: binruan@chatlabs.com
@@ -529,6 +707,9 @@ const storeAndLoadFeUserId = () => {
529
707
  }
530
708
  return fakeUserId;
531
709
  };
710
+ const removeFeUserId = () => {
711
+ window.localStorage.removeItem(FAKE_USER_KEY);
712
+ };
532
713
  const getFeUserState = () => {
533
714
  const fakeUserState = window.localStorage.getItem(FAKE_USER_STATE);
534
715
  if (lodash.isEmpty(fakeUserState)) {
@@ -558,6 +739,24 @@ const setUserConsentResult = () => {
558
739
  window.localStorage.setItem(USER_CONSENT_RESULT_KEY, 'true');
559
740
  };
560
741
 
742
+ var localStore = /*#__PURE__*/Object.freeze({
743
+ __proto__: null,
744
+ AGREE_POLICY: AGREE_POLICY,
745
+ CCONTSENT_STATE: CCONTSENT_STATE,
746
+ FAKE_USER_KEY: FAKE_USER_KEY,
747
+ FAKE_USER_STATE: FAKE_USER_STATE,
748
+ SLIDE_SKIP_STATE: SLIDE_SKIP_STATE,
749
+ USER_CONSENT_RESULT_KEY: USER_CONSENT_RESULT_KEY,
750
+ getContsentState: getContsentState,
751
+ getFeUserState: getFeUserState,
752
+ getSlideSkipState: getSlideSkipState,
753
+ getUserConsentResult: getUserConsentResult,
754
+ removeFeUserId: removeFeUserId,
755
+ setSlideSkipState: setSlideSkipState,
756
+ setUserConsentResult: setUserConsentResult,
757
+ storeAndLoadFeUserId: storeAndLoadFeUserId
758
+ });
759
+
561
760
  /*
562
761
  * @Author: binruan@chatlabs.com
563
762
  * @Date: 2024-03-20 10:27:31
@@ -1766,6 +1965,23 @@ var DATA_TYPE;
1766
1965
  DATA_TYPE["ARRAY_NUMBER"] = "array-number";
1767
1966
  })(DATA_TYPE || (DATA_TYPE = {}));
1768
1967
 
1968
+ /*
1969
+ * @Author: binruan@chatlabs.com
1970
+ * @Date: 2024-03-20 10:27:31
1971
+ * @LastEditors: binruan@chatlabs.com
1972
+ * @LastEditTime: 2024-03-20 13:56:49
1973
+ * @FilePath: \pb-sxp-ui\src\core\hooks\index.ts
1974
+ *
1975
+ */
1976
+
1977
+ var index$4 = /*#__PURE__*/Object.freeze({
1978
+ __proto__: null,
1979
+ get DATA_TYPE () { return DATA_TYPE; },
1980
+ useDataSource: useDataSource,
1981
+ useEditor: useEditor,
1982
+ useSxpDataSource: useSxpDataSource
1983
+ });
1984
+
1769
1985
  const DataSourceContext = React.createContext({ $store: {}, options: [], configs: [] });
1770
1986
  const DataSourceProvider = ({ children, isSsr, enable }) => {
1771
1987
  const [options, setOptions] = React.useState([]);
@@ -10837,7 +11053,7 @@ const getBgStyle = (imgSrc) => {
10837
11053
  : '';
10838
11054
  };
10839
11055
  const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHiddenDef, style }) => {
10840
- var _a, _b, _c, _d, _e, _f, _g, _h;
11056
+ var _a, _b, _c, _d, _e;
10841
11057
  let text = '';
10842
11058
  if ((!(product === null || product === void 0 ? void 0 : product.currency) || !(product === null || product === void 0 ? void 0 : product.price)) && isHiddenDef)
10843
11059
  return null;
@@ -10845,18 +11061,51 @@ const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHiddenDef
10845
11061
  if (typeof price !== 'number')
10846
11062
  return text;
10847
11063
  let priceSymbol = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol;
10848
- let currency = (product === null || product === void 0 ? void 0 : product.currency) ? (_c = (_b = (_a = product === null || product === void 0 ? void 0 : product.currency) === null || _a === void 0 ? void 0 : _a.split('-')[1]) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== null && _c !== void 0 ? _c : '' : '$';
11064
+ // product.currency 中提取货币代码
11065
+ // 支持多种格式:
11066
+ // 1. "EUR" - 直接是货币代码
11067
+ // 2. "USD-$" - 货币代码-货币符号
11068
+ // 3. "$-USD" - 货币符号-货币代码 (不常见)
11069
+ let currencyCode = '';
11070
+ if (product === null || product === void 0 ? void 0 : product.currency) {
11071
+ const parts = product.currency.split('-');
11072
+ if (parts.length > 1) {
11073
+ // 判断哪一部分是货币代码(通常是3个大写字母)
11074
+ // 货币代码通常是3个字母,如 USD, EUR, GBP
11075
+ const firstPart = parts[0].toUpperCase();
11076
+ const secondPart = parts[1].toUpperCase();
11077
+ // 如果第一部分是3个字母,则认为是货币代码
11078
+ if (/^[A-Z]{3}$/.test(firstPart)) {
11079
+ currencyCode = firstPart;
11080
+ }
11081
+ else if (/^[A-Z]{3}$/.test(secondPart)) {
11082
+ currencyCode = secondPart;
11083
+ }
11084
+ else {
11085
+ // 都不是标准格式,取第一部分
11086
+ currencyCode = firstPart;
11087
+ }
11088
+ }
11089
+ else {
11090
+ currencyCode = parts[0].toUpperCase();
11091
+ }
11092
+ }
11093
+ else {
11094
+ currencyCode = 'USD'; // 默认美元
11095
+ }
11096
+ // 使用货币代码映射获取货币符号
11097
+ let currency = getCurrencySymbol(currencyCode);
10849
11098
  const isToLocStr = enableFormattedPrice === undefined || enableFormattedPrice;
10850
11099
  let decPic = price === null || price === void 0 ? void 0 : price.toString();
10851
11100
  if (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.showTwoDecimalPoint) {
10852
11101
  decPic = price === null || price === void 0 ? void 0 : price.toFixed(2);
10853
11102
  }
10854
- let decInd = (_d = decPic === null || decPic === void 0 ? void 0 : decPic.indexOf('.')) !== null && _d !== void 0 ? _d : -1;
11103
+ let decInd = (_a = decPic === null || decPic === void 0 ? void 0 : decPic.indexOf('.')) !== null && _a !== void 0 ? _a : -1;
10855
11104
  if (isToLocStr) {
10856
11105
  text =
10857
- (_e = price === null || price === void 0 ? void 0 : price.toLocaleString('zh', {
11106
+ (_b = price === null || price === void 0 ? void 0 : price.toLocaleString('zh', {
10858
11107
  minimumFractionDigits: (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.showTwoDecimalPoint) ? 2 : 0
10859
- })) !== null && _e !== void 0 ? _e : '';
11108
+ })) !== null && _b !== void 0 ? _b : '';
10860
11109
  let startIndex = 0;
10861
11110
  let endIndex = decInd !== null && decInd !== void 0 ? decInd : text === null || text === void 0 ? void 0 : text.length;
10862
11111
  if ((priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.millesimalSymbol) === '.') {
@@ -10887,7 +11136,7 @@ const getPriceText = ({ product, enableFormattedPrice, globalConfig, isHiddenDef
10887
11136
  }
10888
11137
  });
10889
11138
  }
10890
- currency = `<span style="font-family:${(_h = (_g = (_f = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol) === null || _f === void 0 ? void 0 : _f.fontFamily) !== null && _g !== void 0 ? _g : style === null || style === void 0 ? void 0 : style['fontFamily-en']) !== null && _h !== void 0 ? _h : 'inherit'}">${currency}</span>`;
11139
+ currency = `<span style="font-family:${(_e = (_d = (_c = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.priceSymbol) === null || _c === void 0 ? void 0 : _c.fontFamily) !== null && _d !== void 0 ? _d : style === null || style === void 0 ? void 0 : style['fontFamily-en']) !== null && _e !== void 0 ? _e : 'inherit'}">${currency}</span>`;
10891
11140
  text = setFontForText(text, style);
10892
11141
  if ((priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) && (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) !== 'none') {
10893
11142
  text = (priceSymbol === null || priceSymbol === void 0 ? void 0 : priceSymbol.currencyPosition) === 'left' ? currency + text : text + currency;
@@ -10923,30 +11172,37 @@ const CommodityDetail$1 = (_a) => {
10923
11172
  cta = p === null || p === void 0 ? void 0 : p.bindCta;
10924
11173
  }
10925
11174
  const handleLink = (e) => {
10926
- // 如果启用了加购功能且配置了加购弹窗ID,则打开加购弹窗
11175
+ // 如果启用了加购功能且配置了加购弹窗ID,检查是否是 Shopify 商品
10927
11176
  if (enableAddToCart && addToCartPopupId) {
10928
- // 设置弹窗数据
10929
- setPopupDetailData === null || setPopupDetailData === void 0 ? void 0 : setPopupDetailData(Object.assign(Object.assign({}, data), { index: position }));
10930
- // 上报点击事件
10931
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
10932
- eventName: 'ClickCTA',
10933
- product: product ? [product] : undefined,
10934
- contentType: 'product',
10935
- data,
10936
- position,
10937
- cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
10938
- cta_action_type: 'open_internal_popup',
10939
- target_content_id: product === null || product === void 0 ? void 0 : product.itemId
10940
- });
10941
- console.log('[CommodityDetail] 打开加购弹窗:', addToCartPopupId);
10942
- // 打开加购弹窗
10943
- if (typeof window !== 'undefined' && window.sxpPopup) {
10944
- window.sxpPopup(addToCartPopupId);
11177
+ // 问题3:只有 Shopify 商品(有 shopifyId)才打开加购弹窗
11178
+ if (!(product === null || product === void 0 ? void 0 : product.shopifyId)) {
11179
+ console.warn('[CommodityDetail] 非 Shopify 商品,跳过加购弹窗');
11180
+ // 继续执行默认行为(跳转链接)
10945
11181
  }
10946
11182
  else {
10947
- console.warn('[CommodityDetail] sxpPopup 方法不存在');
11183
+ // 设置弹窗数据
11184
+ setPopupDetailData === null || setPopupDetailData === void 0 ? void 0 : setPopupDetailData(Object.assign(Object.assign({}, data), { index: position }));
11185
+ // 上报点击事件
11186
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
11187
+ eventName: 'ClickCTA',
11188
+ product: product ? [product] : undefined,
11189
+ contentType: 'product',
11190
+ data,
11191
+ position,
11192
+ cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
11193
+ cta_action_type: 'open_internal_popup',
11194
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId
11195
+ });
11196
+ console.log('[CommodityDetail] 打开加购弹窗:', addToCartPopupId);
11197
+ // 打开加购弹窗
11198
+ if (typeof window !== 'undefined' && window.sxpPopup) {
11199
+ window.sxpPopup(addToCartPopupId);
11200
+ }
11201
+ else {
11202
+ console.warn('[CommodityDetail] sxpPopup 方法不存在');
11203
+ }
11204
+ return;
10948
11205
  }
10949
- return;
10950
11206
  }
10951
11207
  // 默认行为:跳转到商品链接
10952
11208
  if (product === null || product === void 0 ? void 0 : product.link) {
@@ -11916,7 +12172,7 @@ var settingRender$d = [
11916
12172
 
11917
12173
  const AddToCartPopup$1 = ({ isActive = true }) => {
11918
12174
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
11919
- const { popupDetailData, globalConfig } = useSxpDataSource();
12175
+ const { popupDetailData, globalConfig, bffFbReport } = useSxpDataSource();
11920
12176
  const [productData, setProductData] = React.useState(null);
11921
12177
  const [selectedOptions, setSelectedOptions] = React.useState({});
11922
12178
  const [selectedVariant, setSelectedVariant] = React.useState(null);
@@ -12056,6 +12312,18 @@ const AddToCartPopup$1 = ({ isActive = true }) => {
12056
12312
  shopifyDomain,
12057
12313
  selectedVariant
12058
12314
  });
12315
+ // 上报 sessionCompleted 事件(问题4)
12316
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
12317
+ eventName: 'sessionCompleted',
12318
+ product: product ? [product] : undefined,
12319
+ contentType: 'product',
12320
+ rec: data,
12321
+ position: data === null || data === void 0 ? void 0 : data.index,
12322
+ cta_text: 'Add To Cart',
12323
+ cta_action_type: 'add_to_cart',
12324
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
12325
+ target_url: `https://${shopifyDomain}/cart/add`
12326
+ });
12059
12327
  // 使用 Shopify 的 /cart/add 接口,通过查询参数添加商品
12060
12328
  // 这种方式会跳转到购物车页面而不是结算页面
12061
12329
  const params = new URLSearchParams({
@@ -12070,6 +12338,10 @@ const AddToCartPopup$1 = ({ isActive = true }) => {
12070
12338
  const totalPrice = selectedVariant
12071
12339
  ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
12072
12340
  : '0.00';
12341
+ // 只有选择了 variant 后才显示货币符号
12342
+ const displayPrice = selectedVariant
12343
+ ? `${getCurrencySymbol(selectedVariant.price.currencyCode)}${totalPrice}`
12344
+ : totalPrice;
12073
12345
  if (loading) {
12074
12346
  return (React.createElement("div", { className: 'add-to-cart-popup' },
12075
12347
  React.createElement("div", { className: 'loading' }, "Loading...")));
@@ -12103,9 +12375,7 @@ const AddToCartPopup$1 = ({ isActive = true }) => {
12103
12375
  "Available: ",
12104
12376
  selectedVariant.quantityAvailable))),
12105
12377
  React.createElement("div", { className: 'variant-price-row' },
12106
- React.createElement("div", { className: 'price' },
12107
- "$",
12108
- totalPrice),
12378
+ React.createElement("div", { className: 'price' }, displayPrice),
12109
12379
  React.createElement("div", { className: 'quantity-selector' },
12110
12380
  React.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
12111
12381
  React.createElement("span", { className: 'qty-value' }, quantity),
@@ -12113,16 +12383,26 @@ const AddToCartPopup$1 = ({ isActive = true }) => {
12113
12383
  React.createElement("div", { className: 'variant-options' }, productData.options.map(option => (React.createElement("div", { key: option.name, className: 'option-group' },
12114
12384
  React.createElement("div", { className: 'option-label' }, option.name),
12115
12385
  React.createElement("div", { className: 'option-values' }, option.values.map(value => {
12116
- // 检查这个选项是否可选(availableForSale = true,如果有 quantityAvailable 则还需 > 0)
12386
+ // 检查这个选项值与当前已选择的其他选项组合后是否可用
12117
12387
  const isAvailable = productData.variants.edges.some(({ node: variant }) => {
12388
+ // 检查这个variant是否匹配当前选项值
12118
12389
  const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
12119
- if (!hasThisOption || !variant.availableForSale)
12390
+ if (!hasThisOption)
12120
12391
  return false;
12121
- // 如果有 quantityAvailable 字段,则需要检查库存
12122
- if (variant.quantityAvailable !== undefined) {
12123
- return variant.quantityAvailable > 0;
12124
- }
12125
- // 没有 quantityAvailable 字段时,只要 availableForSale 为 true 就可选
12392
+ // 检查这个variant是否匹配其他已选择的选项
12393
+ const matchesOtherSelections = Object.entries(selectedOptions).every(([key, val]) => {
12394
+ // 跳过当前正在检查的选项
12395
+ if (key === option.name)
12396
+ return true;
12397
+ return variant.selectedOptions.some(opt => opt.name === key && opt.value === val);
12398
+ });
12399
+ if (!matchesOtherSelections)
12400
+ return false;
12401
+ // 检查是否可售
12402
+ if (!variant.availableForSale)
12403
+ return false;
12404
+ // 如果有 quantityAvailable 字段且为0,但 availableForSale 为 true,仍然可选(问题2)
12405
+ // 如果 quantityAvailable 不存在,只要 availableForSale 为 true 就可选
12126
12406
  return true;
12127
12407
  });
12128
12408
  const isSelected = selectedOptions[option.name] === value;
@@ -12173,7 +12453,35 @@ const CommodityDetailDiroNew$1 = (_a) => {
12173
12453
  }
12174
12454
  const handleLink = (e) => {
12175
12455
  e.preventDefault();
12176
- // 上报点击事件
12456
+ // 问题3:只有 Shopify 商品(有 shopifyId)才打开加购弹窗
12457
+ if (!(product === null || product === void 0 ? void 0 : product.shopifyId)) {
12458
+ console.warn('[CommodityDetailDiroNew] 非 Shopify 商品,跳转到外链');
12459
+ // 上报点击事件
12460
+ ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
12461
+ eventSubject: 'clickCta',
12462
+ eventDescription: 'User clicked the CTA'
12463
+ }, data, product, position);
12464
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
12465
+ eventName: 'ClickCTA',
12466
+ product: product ? [product] : undefined,
12467
+ contentType: 'product',
12468
+ data,
12469
+ position,
12470
+ cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
12471
+ cta_action_type: 'open_external_link',
12472
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
12473
+ target_url: product === null || product === void 0 ? void 0 : product.link
12474
+ });
12475
+ if (!isPost) {
12476
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
12477
+ }
12478
+ // 跳转到商品链接
12479
+ if (product === null || product === void 0 ? void 0 : product.link) {
12480
+ window.location.href = window.getJointUtmLink(product.link);
12481
+ }
12482
+ return;
12483
+ }
12484
+ // Shopify 商品:上报点击事件
12177
12485
  ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
12178
12486
  eventSubject: 'clickCta',
12179
12487
  eventDescription: 'User clicked the CTA'
@@ -19796,7 +20104,13 @@ const StructurePage = (_a) => {
19796
20104
  var index$3 = /*#__PURE__*/Object.freeze({
19797
20105
  __proto__: null,
19798
20106
  EditorCore: EditorCore,
19799
- StructurePage: StructurePage
20107
+ Pagebuilder: Pagebuilder,
20108
+ StructurePage: StructurePage,
20109
+ createMaterial: createMaterial,
20110
+ hooks: index$4,
20111
+ localStore: localStore,
20112
+ sessionStore: sessionStore,
20113
+ tool: tool
19800
20114
  });
19801
20115
 
19802
20116
  /*