pb-sxp-ui 1.20.26 → 1.20.28

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 (46) hide show
  1. package/dist/index.cjs +466 -396
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +355 -291
  4. package/dist/index.js +466 -396
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.min.cjs +6 -6
  7. package/dist/index.min.cjs.map +1 -1
  8. package/dist/index.min.js +6 -6
  9. package/dist/index.min.js.map +1 -1
  10. package/dist/pb-ui.js +466 -396
  11. package/dist/pb-ui.js.map +1 -1
  12. package/dist/pb-ui.min.js +6 -6
  13. package/dist/pb-ui.min.js.map +1 -1
  14. package/es/core/components/StructurePage/index.d.ts +4 -0
  15. package/es/core/components/StructurePage/index.js +8 -1
  16. package/es/core/components/SxpPageRender/Modal/index.d.ts +1 -0
  17. package/es/core/components/SxpPageRender/Modal/index.js +3 -3
  18. package/es/core/components/SxpPageRender/index.d.ts +1 -0
  19. package/es/core/components/SxpPageRender/typing.d.ts +1 -0
  20. package/es/core/index.d.ts +6 -0
  21. package/es/core/index.js +10 -0
  22. package/es/materials/sxp/popup/AddToCart/index.d.ts +1 -27
  23. package/es/materials/sxp/popup/AddToCart/index.js +117 -173
  24. package/es/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
  25. package/es/materials/sxp/popup/AddToCart/index.new.js +174 -0
  26. package/es/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
  27. package/es/materials/sxp/popup/AddToCart/index.old.js +299 -0
  28. package/es/materials/sxp/popup/AddToCart/material.js +1 -54
  29. package/es/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
  30. package/lib/core/components/StructurePage/index.d.ts +4 -0
  31. package/lib/core/components/StructurePage/index.js +8 -1
  32. package/lib/core/components/SxpPageRender/Modal/index.d.ts +1 -0
  33. package/lib/core/components/SxpPageRender/Modal/index.js +3 -3
  34. package/lib/core/components/SxpPageRender/index.d.ts +1 -0
  35. package/lib/core/components/SxpPageRender/typing.d.ts +1 -0
  36. package/lib/core/index.d.ts +6 -0
  37. package/lib/core/index.js +10 -5
  38. package/lib/materials/sxp/popup/AddToCart/index.d.ts +1 -27
  39. package/lib/materials/sxp/popup/AddToCart/index.js +115 -171
  40. package/lib/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
  41. package/lib/materials/sxp/popup/AddToCart/index.new.js +176 -0
  42. package/lib/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
  43. package/lib/materials/sxp/popup/AddToCart/index.old.js +301 -0
  44. package/lib/materials/sxp/popup/AddToCart/material.js +1 -54
  45. package/lib/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
  46. 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);
@@ -322,6 +421,24 @@ function getUrlParamByKey(key) {
322
421
  return (_b = params[key]) !== null && _b !== void 0 ? _b : '';
323
422
  }
324
423
 
424
+ var tool = /*#__PURE__*/Object.freeze({
425
+ __proto__: null,
426
+ deleteCookie: deleteCookie,
427
+ generateRandomString: generateRandomString,
428
+ getBrowserInfo: getBrowserInfo,
429
+ getCookie: getCookie,
430
+ getDevice: getDevice$1,
431
+ getIndexByblockType: getIndexByblockType,
432
+ getScreenReader: getScreenReader,
433
+ getSystem: getSystem,
434
+ getUid: getUid,
435
+ getUrlParamByKey: getUrlParamByKey,
436
+ setCookie: setCookie,
437
+ setFontForText: setFontForText,
438
+ splitUrlParams: splitUrlParams,
439
+ uuid: uuid
440
+ });
441
+
325
442
  function unzip(b64Data) {
326
443
  const strData = atob(b64Data);
327
444
  const charData = strData.split('').map(function (x) {
@@ -506,6 +623,18 @@ const refreshFeSessionId = () => {
506
623
  const getFeSessionId = () => {
507
624
  return window.localStorage.getItem(feRealSessionIdKey);
508
625
  };
626
+ // 删除sessionID
627
+ const removeFeSessionId = () => {
628
+ window.localStorage.removeItem(feRealSessionIdKey);
629
+ };
630
+
631
+ var sessionStore = /*#__PURE__*/Object.freeze({
632
+ __proto__: null,
633
+ getFeSessionId: getFeSessionId,
634
+ refreshFeSessionId: refreshFeSessionId,
635
+ removeFeSessionId: removeFeSessionId,
636
+ storeAndLoadFeSessionId: storeAndLoadFeSessionId
637
+ });
509
638
 
510
639
  /*
511
640
  * @Author: binruan@chatlabs.com
@@ -529,6 +658,9 @@ const storeAndLoadFeUserId = () => {
529
658
  }
530
659
  return fakeUserId;
531
660
  };
661
+ const removeFeUserId = () => {
662
+ window.localStorage.removeItem(FAKE_USER_KEY);
663
+ };
532
664
  const getFeUserState = () => {
533
665
  const fakeUserState = window.localStorage.getItem(FAKE_USER_STATE);
534
666
  if (lodash.isEmpty(fakeUserState)) {
@@ -558,6 +690,24 @@ const setUserConsentResult = () => {
558
690
  window.localStorage.setItem(USER_CONSENT_RESULT_KEY, 'true');
559
691
  };
560
692
 
693
+ var localStore = /*#__PURE__*/Object.freeze({
694
+ __proto__: null,
695
+ AGREE_POLICY: AGREE_POLICY,
696
+ CCONTSENT_STATE: CCONTSENT_STATE,
697
+ FAKE_USER_KEY: FAKE_USER_KEY,
698
+ FAKE_USER_STATE: FAKE_USER_STATE,
699
+ SLIDE_SKIP_STATE: SLIDE_SKIP_STATE,
700
+ USER_CONSENT_RESULT_KEY: USER_CONSENT_RESULT_KEY,
701
+ getContsentState: getContsentState,
702
+ getFeUserState: getFeUserState,
703
+ getSlideSkipState: getSlideSkipState,
704
+ getUserConsentResult: getUserConsentResult,
705
+ removeFeUserId: removeFeUserId,
706
+ setSlideSkipState: setSlideSkipState,
707
+ setUserConsentResult: setUserConsentResult,
708
+ storeAndLoadFeUserId: storeAndLoadFeUserId
709
+ });
710
+
561
711
  /*
562
712
  * @Author: binruan@chatlabs.com
563
713
  * @Date: 2024-03-20 10:27:31
@@ -1766,6 +1916,23 @@ var DATA_TYPE;
1766
1916
  DATA_TYPE["ARRAY_NUMBER"] = "array-number";
1767
1917
  })(DATA_TYPE || (DATA_TYPE = {}));
1768
1918
 
1919
+ /*
1920
+ * @Author: binruan@chatlabs.com
1921
+ * @Date: 2024-03-20 10:27:31
1922
+ * @LastEditors: binruan@chatlabs.com
1923
+ * @LastEditTime: 2024-03-20 13:56:49
1924
+ * @FilePath: \pb-sxp-ui\src\core\hooks\index.ts
1925
+ *
1926
+ */
1927
+
1928
+ var index$4 = /*#__PURE__*/Object.freeze({
1929
+ __proto__: null,
1930
+ get DATA_TYPE () { return DATA_TYPE; },
1931
+ useDataSource: useDataSource,
1932
+ useEditor: useEditor,
1933
+ useSxpDataSource: useSxpDataSource
1934
+ });
1935
+
1769
1936
  const DataSourceContext = React.createContext({ $store: {}, options: [], configs: [] });
1770
1937
  const DataSourceProvider = ({ children, isSsr, enable }) => {
1771
1938
  const [options, setOptions] = React.useState([]);
@@ -10545,7 +10712,7 @@ function useVisibleHeight() {
10545
10712
  *
10546
10713
  */
10547
10714
  const closeIcon$1 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAjhJREFUWEfFlztOw0AQhmeWiJ4CCmpQ5DiRQsIJyAWg5A0lR0AIChDiCJS8ER0cADgBeRSxt4CCDgkaKiq8i+zYeWx2413HEWmiJJv9v535Z2aN8M8vFPT9z3zETD0aAUChUJjwvPFHAJhBhB3Hqd6OAsK2yyucwykAvP38eJX398Z3AJDLlVYR8ToU9Rhj25TWr9KEsKy5dULIGQCMtfZly45TvwsAstm56UwG6wA4FUFwzrdctxZBDcWSy5XWEPG8I84/GcMipdWPtgcsaz5PCHtKG0IuTiqUvjT9U/WYMG2IOPE+AP+LtCB0xKUAAyA2Xbd2o2OG0NQXvTnvhL17D7EPtH9TRCIWwkRcGYGIQgYBABuqPuHXOQBc6pw80lBGwBQiiXhsBHQhkoprA6iM6acjhDQKu5YJZW6XeOI3XJdpvfsdTu52VfXEekD8owQiXGIubpSCbhDbLu8DwKEAd+A41SOdPpE4BS0viFOtvV2iKWqUgn5x/tmS70xR01GuDSCKc86/OCcLgTyyZ0ScDGNhFAktAJV4NFJ9YyaFiAWIE+9uVkkgBgLoig8DMWAa9ro9ynkUdlW5maZDCmB6clmz0k1HH4Cs1Ezbq2p2yEpUuBOKTSZZex00RUWIrltxuuK6EOGDSbGIOPZicpMx6fny650377qNRgBgWeVFQuA+6UjVgREhGIMlSqsPUQqIbZdOOIdZQmCv2axRnU1N1+TzJYsxOEaEV8ep7frPZ7Gd0FTEdP0ft0/kMNdg0eoAAAAASUVORK5CYII=';
10548
- const Modal = ({ visible, onClose, children, modalStyle, padding, popup, schema, fullHeight, isFullScreen = false, openState }) => {
10715
+ const Modal = ({ visible, onClose, children, modalStyle, padding, popup, schema, fullHeight, isFullScreen = false, openState, showCloseButton = true }) => {
10549
10716
  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;
10550
10717
  const { visibleHeight, bottomHeight } = useVisibleHeight();
10551
10718
  const touchRef = React.useRef(null);
@@ -10725,8 +10892,8 @@ const Modal = ({ visible, onClose, children, modalStyle, padding, popup, schema,
10725
10892
  setScrollTop(15 - ((_a = e === null || e === void 0 ? void 0 : e.target) === null || _a === void 0 ? void 0 : _a.scrollTop));
10726
10893
  }
10727
10894
  })), child()),
10728
- React.createElement("button", { className: 'modal-icon-wrapper', role: 'button', "aria-label": 'close button', onClick: onClose, style: { top: scrollTop } },
10729
- React.createElement("img", { src: (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.popupCloseIcon) || closeIcon$1, alt: 'close button', className: 'modal-icon' }))))))), modalEleRef.current);
10895
+ showCloseButton && (React.createElement("button", { className: 'modal-icon-wrapper', role: 'button', "aria-label": 'close button', onClick: onClose, style: { top: scrollTop } },
10896
+ React.createElement("img", { src: (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.popupCloseIcon) || closeIcon$1, alt: 'close button', className: 'modal-icon' })))))))), modalEleRef.current);
10730
10897
  };
10731
10898
  var Modal$1 = React.memo(Modal);
10732
10899
 
@@ -11914,18 +12081,248 @@ var settingRender$d = [
11914
12081
  }
11915
12082
  ];
11916
12083
 
12084
+ const AddToCartPopup$1 = ({ isActive = true }) => {
12085
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
12086
+ const { popupDetailData, globalConfig } = useSxpDataSource();
12087
+ const [productData, setProductData] = React.useState(null);
12088
+ const [selectedOptions, setSelectedOptions] = React.useState({});
12089
+ const [selectedVariant, setSelectedVariant] = React.useState(null);
12090
+ const [quantity, setQuantity] = React.useState(1);
12091
+ const [loading, setLoading] = React.useState(true);
12092
+ const [error, setError] = React.useState(null);
12093
+ const [showImagePreview, setShowImagePreview] = React.useState(false);
12094
+ const [previewImageUrl, setPreviewImageUrl] = React.useState('');
12095
+ // 获取商品数据
12096
+ const data = popupDetailData;
12097
+ const product = (_e = (_b = (_a = data === null || data === void 0 ? void 0 : data.video) === null || _a === void 0 ? void 0 : _a.bindProduct) !== null && _b !== void 0 ? _b : (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProducts) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.product;
12098
+ // Shopify 配置
12099
+ const shopifyConfig = window.__SHOPIFY_CONFIG__;
12100
+ const shopifyDomain = ((_f = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopify) === null || _f === void 0 ? void 0 : _f.domain) || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.domain) || 'dev-store-749237498237498636.myshopify.com';
12101
+ const storefrontToken = ((_g = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopify) === null || _g === void 0 ? void 0 : _g.storefrontAccessToken) || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
12102
+ const productId = (product === null || product === void 0 ? void 0 : product.shopifyId) || (product === null || product === void 0 ? void 0 : product.itemId) || '';
12103
+ // 查询 Shopify 商品数据
12104
+ const fetchProductData = React.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
12105
+ var _k;
12106
+ if (!productId || !shopifyDomain || !storefrontToken) {
12107
+ setLoading(false);
12108
+ return;
12109
+ }
12110
+ setLoading(true);
12111
+ setError(null);
12112
+ try {
12113
+ const query = `
12114
+ query getProduct($id: ID!) {
12115
+ product(id: $id) {
12116
+ id
12117
+ title
12118
+ images(first: 10) {
12119
+ edges {
12120
+ node {
12121
+ url
12122
+ }
12123
+ }
12124
+ }
12125
+ options {
12126
+ name
12127
+ values
12128
+ }
12129
+ variants(first: 100) {
12130
+ edges {
12131
+ node {
12132
+ id
12133
+ title
12134
+ availableForSale
12135
+ quantityAvailable
12136
+ price {
12137
+ amount
12138
+ currencyCode
12139
+ }
12140
+ image {
12141
+ url
12142
+ }
12143
+ selectedOptions {
12144
+ name
12145
+ value
12146
+ }
12147
+ }
12148
+ }
12149
+ }
12150
+ }
12151
+ }
12152
+ `;
12153
+ const fullProductId = productId.startsWith('gid://') ? productId : `gid://shopify/Product/${productId}`;
12154
+ const response = yield fetch(`https://${shopifyDomain}/api/2024-01/graphql.json`, {
12155
+ method: 'POST',
12156
+ headers: {
12157
+ 'Content-Type': 'application/json',
12158
+ 'X-Shopify-Storefront-Access-Token': storefrontToken
12159
+ },
12160
+ body: JSON.stringify({
12161
+ query,
12162
+ variables: { id: fullProductId }
12163
+ })
12164
+ });
12165
+ const result = yield response.json();
12166
+ if (result.errors) {
12167
+ throw new Error(result.errors[0].message);
12168
+ }
12169
+ console.log('[AddToCart] Shopify Product Data:', result.data.product);
12170
+ console.log('[AddToCart] Options:', (_k = result.data.product) === null || _k === void 0 ? void 0 : _k.options);
12171
+ setProductData(result.data.product);
12172
+ }
12173
+ catch (err) {
12174
+ setError(err instanceof Error ? err.message : 'Failed to load product');
12175
+ console.error('[AddToCartPopup] 加载失败:', err);
12176
+ }
12177
+ finally {
12178
+ setLoading(false);
12179
+ }
12180
+ }), [productId, shopifyDomain, storefrontToken]);
12181
+ React.useEffect(() => {
12182
+ if (isActive) {
12183
+ fetchProductData();
12184
+ }
12185
+ }, [isActive, fetchProductData]);
12186
+ // 根据选中的规格匹配 variant
12187
+ React.useEffect(() => {
12188
+ if (!productData)
12189
+ return;
12190
+ const variants = productData.variants.edges.map(edge => edge.node);
12191
+ const optionsCount = productData.options.length;
12192
+ const selectedCount = Object.keys(selectedOptions).length;
12193
+ if (selectedCount === 0 || selectedCount < optionsCount) {
12194
+ setSelectedVariant(null);
12195
+ return;
12196
+ }
12197
+ const matchedVariant = variants.find(variant => {
12198
+ return variant.selectedOptions.every(option => {
12199
+ return selectedOptions[option.name] === option.value;
12200
+ });
12201
+ });
12202
+ setSelectedVariant(matchedVariant || null);
12203
+ // 当 variant 改变时,如果有库存数量限制,确保数量不超过库存
12204
+ if ((matchedVariant === null || matchedVariant === void 0 ? void 0 : matchedVariant.quantityAvailable) !== undefined && matchedVariant.quantityAvailable > 0) {
12205
+ setQuantity(prev => Math.min(prev, matchedVariant.quantityAvailable));
12206
+ }
12207
+ }, [selectedOptions, productData]);
12208
+ // 处理规格选择
12209
+ const handleOptionSelect = (optionName, value) => {
12210
+ setSelectedOptions(prev => (Object.assign(Object.assign({}, prev), { [optionName]: value })));
12211
+ };
12212
+ // 处理加购
12213
+ const handleAddToCart = () => {
12214
+ if (!selectedVariant) {
12215
+ alert('Please select all options');
12216
+ return;
12217
+ }
12218
+ // 提取 variant ID(去掉 gid://shopify/ProductVariant/ 前缀)
12219
+ const variantId = selectedVariant.id.replace('gid://shopify/ProductVariant/', '');
12220
+ console.log('[AddToCart] 添加到购物车:', {
12221
+ variantId,
12222
+ quantity,
12223
+ shopifyDomain,
12224
+ selectedVariant
12225
+ });
12226
+ // 使用 Shopify 的 /cart/add 接口,通过查询参数添加商品
12227
+ // 这种方式会跳转到购物车页面而不是结算页面
12228
+ const params = new URLSearchParams({
12229
+ id: variantId,
12230
+ quantity: quantity.toString()
12231
+ });
12232
+ const cartUrl = `https://${shopifyDomain}/cart/add?${params.toString()}`;
12233
+ console.log('[AddToCart] 跳转到购物车 URL:', cartUrl);
12234
+ window.location.href = cartUrl;
12235
+ };
12236
+ // 计算总价
12237
+ const totalPrice = selectedVariant
12238
+ ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
12239
+ : '0.00';
12240
+ if (loading) {
12241
+ return (React.createElement("div", { className: 'add-to-cart-popup' },
12242
+ React.createElement("div", { className: 'loading' }, "Loading...")));
12243
+ }
12244
+ if (error) {
12245
+ return (React.createElement("div", { className: 'add-to-cart-popup' },
12246
+ React.createElement("div", { className: 'error' }, error)));
12247
+ }
12248
+ if (!productData) {
12249
+ return null;
12250
+ }
12251
+ const mainImage = ((_h = productData.images.edges[0]) === null || _h === void 0 ? void 0 : _h.node.url) || '';
12252
+ const variantImage = ((_j = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _j === void 0 ? void 0 : _j.url) || mainImage;
12253
+ const maxQuantity = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) || 999; // 如果没有库存数据,默认允许最多 999
12254
+ return (React.createElement("div", { className: 'add-to-cart-popup' },
12255
+ React.createElement("div", { className: 'popup-content' },
12256
+ React.createElement("div", { className: 'product-header' },
12257
+ React.createElement("div", { className: 'product-title' }, productData.title)),
12258
+ React.createElement("div", { className: 'variant-detail' },
12259
+ React.createElement("div", { className: 'variant-image-container', onClick: () => {
12260
+ if (variantImage) {
12261
+ setPreviewImageUrl(variantImage);
12262
+ setShowImagePreview(true);
12263
+ }
12264
+ }, style: { cursor: 'pointer' } },
12265
+ React.createElement("img", { src: variantImage, alt: 'Selected variant', className: 'variant-image' })),
12266
+ React.createElement("div", { className: 'variant-info' },
12267
+ React.createElement("div", { className: 'variant-specs-row' },
12268
+ React.createElement("div", { className: 'variant-specs' }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (React.createElement("span", { key: key, className: 'spec-item' }, value)))) : (React.createElement("span", { className: 'spec-placeholder' }, "Please select options"))),
12269
+ selectedVariant && selectedVariant.quantityAvailable !== undefined && (React.createElement("div", { className: 'stock-info' },
12270
+ "Available: ",
12271
+ selectedVariant.quantityAvailable))),
12272
+ React.createElement("div", { className: 'variant-price-row' },
12273
+ React.createElement("div", { className: 'price' },
12274
+ "$",
12275
+ totalPrice),
12276
+ React.createElement("div", { className: 'quantity-selector' },
12277
+ React.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
12278
+ React.createElement("span", { className: 'qty-value' }, quantity),
12279
+ React.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(quantity + 1), disabled: !selectedVariant || quantity >= maxQuantity }, "+"))))),
12280
+ React.createElement("div", { className: 'variant-options' }, productData.options.map(option => (React.createElement("div", { key: option.name, className: 'option-group' },
12281
+ React.createElement("div", { className: 'option-label' }, option.name),
12282
+ React.createElement("div", { className: 'option-values' }, option.values.map(value => {
12283
+ // 检查这个选项是否可选(availableForSale = true,如果有 quantityAvailable 则还需 > 0)
12284
+ const isAvailable = productData.variants.edges.some(({ node: variant }) => {
12285
+ const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
12286
+ if (!hasThisOption || !variant.availableForSale)
12287
+ return false;
12288
+ // 如果有 quantityAvailable 字段,则需要检查库存
12289
+ if (variant.quantityAvailable !== undefined) {
12290
+ return variant.quantityAvailable > 0;
12291
+ }
12292
+ // 没有 quantityAvailable 字段时,只要 availableForSale 为 true 就可选
12293
+ return true;
12294
+ });
12295
+ const isSelected = selectedOptions[option.name] === value;
12296
+ return (React.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
12297
+ }))))))),
12298
+ React.createElement("div", { className: 'popup-footer' },
12299
+ React.createElement("button", { className: 'add-to-cart-btn', onClick: handleAddToCart, disabled: !selectedVariant }, "Add To Cart")),
12300
+ showImagePreview && previewImageUrl && (React.createElement(Modal$1, { visible: showImagePreview, padding: 0, isFullScreen: true, onClose: () => setShowImagePreview(false) },
12301
+ React.createElement("div", { style: {
12302
+ width: '100%',
12303
+ height: '100%',
12304
+ display: 'flex',
12305
+ alignItems: 'center',
12306
+ justifyContent: 'center',
12307
+ backgroundColor: 'rgba(0, 0, 0, 0.9)'
12308
+ }, onClick: () => setShowImagePreview(false) },
12309
+ React.createElement("img", { src: previewImageUrl, alt: 'Preview', style: {
12310
+ maxWidth: '100%',
12311
+ maxHeight: '100%',
12312
+ objectFit: 'contain'
12313
+ } }))))));
12314
+ };
12315
+
11917
12316
  const CommodityDetailDiroNew$1 = (_a) => {
11918
- 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;
11919
- var { style, isDefault, rec, viewTime, isPost, bottom_image, tipText, swiper, commodityStyles, buttonStyle, index, commodityGroup, popupBg, iframeIcon, commodityImgRatio, isTel, iframeBgColor, isActive = true } = _a, props = __rest(_a, ["style", "isDefault", "rec", "viewTime", "isPost", "bottom_image", "tipText", "swiper", "commodityStyles", "buttonStyle", "index", "commodityGroup", "popupBg", "iframeIcon", "commodityImgRatio", "isTel", "iframeBgColor", "isActive"]);
11920
- React.useState(true);
11921
- const { sxpParameter, popupCurTimeRef, popupDetailData, isPreview, bffFbReport, checkCommodityIndexRef, globalConfig, ctaEvent } = useSxpDataSource();
11922
- const { jumpToWeb, productView } = useEventReport();
11923
- React.useState(false);
11924
- React.useState(false);
11925
- React.useState(true);
12317
+ 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;
12318
+ 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"]);
12319
+ const { sxpParameter, popupCurTimeRef, popupDetailData, setPopupDetailData, isPreview, bffFbReport, checkCommodityIndexRef, globalConfig, ctaEvent } = useSxpDataSource();
12320
+ useEditor();
12321
+ const { productView } = useEventReport();
11926
12322
  const [showModal, setShowModal] = React.useState(false);
11927
12323
  const curTimeRef = React.useRef(null);
11928
12324
  const [show3DModal, setShow3DModal] = React.useState(false);
12325
+ const [showAddToCart, setShowAddToCart] = React.useState(false);
11929
12326
  const [checkCommodityIndex, setCheckCommodityIndex] = React.useState((_b = popupDetailData === null || popupDetailData === void 0 ? void 0 : popupDetailData.multiCheckIndex) !== null && _b !== void 0 ? _b : 0);
11930
12327
  const swiperRef = React.useRef();
11931
12328
  const [swiperActiveIndex, setSwiperActiveIndex] = React.useState(0);
@@ -11942,30 +12339,30 @@ const CommodityDetailDiroNew$1 = (_a) => {
11942
12339
  cta = p === null || p === void 0 ? void 0 : p.bindCta;
11943
12340
  }
11944
12341
  const handleLink = (e) => {
11945
- if (product === null || product === void 0 ? void 0 : product.link) {
11946
- jumpToWeb(e, data, product, cta, position);
11947
- if (!isPost) {
11948
- productView(data, product, cta, viewTime || curTimeRef.current, position);
11949
- }
11950
- else {
11951
- ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
11952
- eventSubject: 'clickCta',
11953
- eventDescription: 'User clicked the CTA'
11954
- }, data, product, position);
11955
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
11956
- eventName: 'ClickCTA',
11957
- product: product ? [product] : undefined,
11958
- contentType: 'product',
11959
- data,
11960
- position,
11961
- cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
11962
- cta_action_type: 'open_external_link',
11963
- target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
11964
- target_url: product.link
11965
- });
11966
- }
11967
- window.location.href = window.getJointUtmLink(product.link);
12342
+ e.preventDefault();
12343
+ // 上报点击事件
12344
+ ctaEvent === null || ctaEvent === void 0 ? void 0 : ctaEvent({
12345
+ eventSubject: 'clickCta',
12346
+ eventDescription: 'User clicked the CTA'
12347
+ }, data, product, position);
12348
+ bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
12349
+ eventName: 'ClickCTA',
12350
+ product: product ? [product] : undefined,
12351
+ contentType: 'product',
12352
+ data,
12353
+ position,
12354
+ cta_text: cta === null || cta === void 0 ? void 0 : cta.enTitle,
12355
+ cta_action_type: 'open_internal_popup',
12356
+ target_content_id: product === null || product === void 0 ? void 0 : product.itemId,
12357
+ target_url: product === null || product === void 0 ? void 0 : product.link
12358
+ });
12359
+ if (!isPost) {
12360
+ productView(data, product, cta, viewTime || curTimeRef.current, position);
11968
12361
  }
12362
+ // 更新 popupDetailData,确保 AddToCart 组件能获取到商品数据
12363
+ 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 }));
12364
+ // 打开 AddToCart 弹窗
12365
+ setShowAddToCart(true);
11969
12366
  };
11970
12367
  React.useEffect(() => {
11971
12368
  var _a, _b;
@@ -11986,7 +12383,7 @@ const CommodityDetailDiroNew$1 = (_a) => {
11986
12383
  rec: recData,
11987
12384
  position
11988
12385
  });
11989
- }, [isActive, bffFbReport, isPost]);
12386
+ }, [isActive, bffFbReport, isPost, data, product, position]);
11990
12387
  React.useEffect(() => {
11991
12388
  if (!isActive || isPost)
11992
12389
  return;
@@ -12114,13 +12511,13 @@ Made in Italy` })));
12114
12511
  swiperRef.current.swiper.slideTo(0);
12115
12512
  swiperRef.current.swiper.autoplay.start();
12116
12513
  }
12117
- }, []);
12514
+ }, [popupCurTimeRef, checkCommodityIndexRef]);
12118
12515
  const renderCommodityGroup = React.useCallback(() => {
12119
12516
  var _a, _b, _c;
12120
12517
  if (isPost)
12121
12518
  return;
12122
12519
  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 }));
12123
- }, [checkCommodityIndex]);
12520
+ }, [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]);
12124
12521
  const getDotsAlign = React.useMemo(() => {
12125
12522
  const dotsAlignClass = {
12126
12523
  left: 'commondityDetail-swiper-clickable-left',
@@ -12130,24 +12527,24 @@ Made in Italy` })));
12130
12527
  return dotsAlignClass === null || dotsAlignClass === void 0 ? void 0 : dotsAlignClass[swiper === null || swiper === void 0 ? void 0 : swiper.dotsAlign];
12131
12528
  }, [swiper === null || swiper === void 0 ? void 0 : swiper.dotsAlign]);
12132
12529
  const iframeUrl = product === null || product === void 0 ? void 0 : product.remark;
12530
+ const isAlly = React.useMemo(() => getScreenReader(), []);
12133
12531
  const handleMouseEnter = React.useCallback(() => {
12134
12532
  if (swiperRef.current && swiperRef.current.swiper && isAlly) {
12135
12533
  swiperRef.current.swiper.autoplay.stop();
12136
12534
  }
12137
- }, []);
12535
+ }, [isAlly]);
12138
12536
  const handleMouseLeave = React.useCallback(() => {
12139
12537
  if (swiperRef.current && swiperRef.current.swiper && isAlly) {
12140
12538
  swiperRef.current.swiper.autoplay.start();
12141
12539
  }
12142
- }, []);
12540
+ }, [isAlly]);
12143
12541
  const handleSlideChange = React.useCallback((swiper) => {
12144
12542
  setSwiperActiveIndex(swiper.activeIndex);
12145
12543
  }, []);
12146
- const isAlly = React.useMemo(() => getScreenReader(), []);
12147
12544
  return (React.createElement("div", { className: 'pb-commondityDiroNew' },
12148
12545
  React.createElement("div", Object.assign({ className: css.css(Object.assign(Object.assign({}, style), { transform: 'translate3d(0px, 0px, 0px)' })) }, props),
12149
12546
  React.createElement("div", { style: { position: 'relative' }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave },
12150
- 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: {
12547
+ 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: {
12151
12548
  clickable: true,
12152
12549
  bulletActiveClass: 'swipe-item-active-bullet',
12153
12550
  clickableClass: getDotsAlign,
@@ -12164,7 +12561,7 @@ Made in Italy` })));
12164
12561
  : {}), { loop: true, ref: swiperRef, onSlideChange: handleSlideChange, autoplay: {
12165
12562
  delay: (swiper === null || swiper === void 0 ? void 0 : swiper.delay) * 1000
12166
12563
  }, className: css.css(Object.assign(Object.assign({ '.swiper-pagination': {
12167
- bottom: (_x = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _x !== void 0 ? _x : 0,
12564
+ bottom: (_y = swiper === null || swiper === void 0 ? void 0 : swiper.dotsMarginBottom) !== null && _y !== void 0 ? _y : 0,
12168
12565
  fontSize: '14px'
12169
12566
  } }, ((swiper === null || swiper === void 0 ? void 0 : swiper.dotsBgColor) && {
12170
12567
  '.swiper-pagination-bullet': {
@@ -12176,7 +12573,7 @@ Made in Italy` })));
12176
12573
  backgroundColor: `${swiper === null || swiper === void 0 ? void 0 : swiper.dotsActiveColor}!important`,
12177
12574
  opacity: 1
12178
12575
  }
12179
- }))) }), (_y = product === null || product === void 0 ? void 0 : product.homePage) === null || _y === void 0 ? void 0 : _y.map((src, srcKey) => {
12576
+ }))) }), (_z = product === null || product === void 0 ? void 0 : product.homePage) === null || _z === void 0 ? void 0 : _z.map((src, srcKey) => {
12180
12577
  var _a;
12181
12578
  return (React.createElement(SwiperSlide, { key: srcKey, "aria-hidden": srcKey !== swiperActiveIndex },
12182
12579
  React.createElement("div", { style: {
@@ -12192,7 +12589,7 @@ Made in Italy` })));
12192
12589
  objectPosition: `50% ${(swiper === null || swiper === void 0 ? void 0 : swiper.translateY) ? (swiper === null || swiper === void 0 ? void 0 : swiper.translateY) + 50 : 50}%`
12193
12590
  }, 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 }))));
12194
12591
  }))),
12195
- !((_z = product === null || product === void 0 ? void 0 : product.homePage) === null || _z === void 0 ? void 0 : _z.length) && (React.createElement("div", { className: css.css({
12592
+ !((_0 = product === null || product === void 0 ? void 0 : product.homePage) === null || _0 === void 0 ? void 0 : _0.length) && (React.createElement("div", { className: css.css({
12196
12593
  height,
12197
12594
  width
12198
12595
  }) },
@@ -12200,7 +12597,7 @@ Made in Italy` })));
12200
12597
  objectFit: 'cover',
12201
12598
  width: '100%',
12202
12599
  height: '100%'
12203
- }), src: (_0 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _0 !== void 0 ? _0 : bottom_image, alt: 'pdp image' }))),
12600
+ }), src: (_1 = sxpParameter === null || sxpParameter === void 0 ? void 0 : sxpParameter.bottom_image) !== null && _1 !== void 0 ? _1 : bottom_image, alt: 'pdp image' }))),
12204
12601
  (iframeUrl || !product) && iframeIcon && (React.createElement("div", { style: {
12205
12602
  display: 'flex',
12206
12603
  alignItems: 'center',
@@ -12217,7 +12614,7 @@ Made in Italy` })));
12217
12614
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top' },
12218
12615
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top-left' },
12219
12616
  React.createElement("div", { className: 'pb-commondityDiroNew-content-top-left-title', style: getStyle(commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.title), dangerouslySetInnerHTML: {
12220
- __html: setFontForText((_1 = product === null || product === void 0 ? void 0 : product.title) !== null && _1 !== void 0 ? _1 : 'Large Dior Toujours BagLarge', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.title)
12617
+ __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)
12221
12618
  } }),
12222
12619
  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: {
12223
12620
  __html: setFontForText((product === null || product === void 0 ? void 0 : product.collection) || 'Black Macrocannage CalfskinLarge', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.collection)
@@ -12227,11 +12624,11 @@ Made in Italy` })));
12227
12624
  __html: priceText !== null && priceText !== void 0 ? priceText : ''
12228
12625
  } }),
12229
12626
  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: {
12230
- __html: setFontForText((_2 = product === null || product === void 0 ? void 0 : product.taxInfo) !== null && _2 !== void 0 ? _2 : '税费', commodityStyles === null || commodityStyles === void 0 ? void 0 : commodityStyles.taxInfo)
12627
+ __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)
12231
12628
  } }))),
12232
- React.createElement("a", { "aria-label": (_3 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _3 !== void 0 ? _3 : 'Shop now', role: 'button', tabIndex: 0, onClick: handleLink, className: 'pb-commondityDiroNew-btn', style: buttonStyle },
12629
+ 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 },
12233
12630
  React.createElement("span", { dangerouslySetInnerHTML: {
12234
- __html: setFontForText((_4 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _4 !== void 0 ? _4 : 'Shop now', buttonStyle)
12631
+ __html: setFontForText((_5 = cta === null || cta === void 0 ? void 0 : cta.enTitle) !== null && _5 !== void 0 ? _5 : 'Shop now', buttonStyle)
12235
12632
  } })),
12236
12633
  productInfoText({ isPost }))),
12237
12634
  React.createElement(Modal$1, { visible: showModal, onClose: () => setShowModal(false) },
@@ -12243,7 +12640,9 @@ Made in Italy` })));
12243
12640
  height: 'calc(100% - 50px)',
12244
12641
  marginTop: '50px',
12245
12642
  border: 'none'
12246
- } })))));
12643
+ } }))),
12644
+ showAddToCart && (React.createElement(Modal$1, { visible: showAddToCart, padding: 0, isFullScreen: false, onClose: () => setShowAddToCart(false) },
12645
+ React.createElement(AddToCartPopup$1, { isActive: true })))));
12247
12646
  };
12248
12647
  var CommodityDetailDiroNewComponent = React.memo(CommodityDetailDiroNew$1);
12249
12648
 
@@ -13245,296 +13644,6 @@ var interactionRender$d = () => {
13245
13644
  React.createElement("li", null, "\u65E0\u5E93\u5B58\u6216\u4E0D\u53EF\u552E\u5356\u7684\u89C4\u683C\u81EA\u52A8\u7F6E\u7070\u4E0D\u53EF\u9009"))));
13246
13645
  };
13247
13646
 
13248
- const AddToCartPopup$1 = (_a) => {
13249
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
13250
- var { style, isActive = true, index, shopifyDomain = '', storefrontAccessToken = '', variantStyles = {}, buttonStyle = {}, quantityStyle = {}, texts = {}, popupBg = {} } = _a, props = __rest(_a, ["style", "isActive", "index", "shopifyDomain", "storefrontAccessToken", "variantStyles", "buttonStyle", "quantityStyle", "texts", "popupBg"]);
13251
- const { sxpParameter, popupDetailData, isPreview, bffFbReport, globalConfig } = useSxpDataSource();
13252
- useEventReport();
13253
- const curTimeRef = React.useRef(null);
13254
- const [productData, setProductData] = React.useState(null);
13255
- const [selectedOptions, setSelectedOptions] = React.useState({});
13256
- const [selectedVariant, setSelectedVariant] = React.useState(null);
13257
- const [quantity, setQuantity] = React.useState(1);
13258
- const [loading, setLoading] = React.useState(true);
13259
- const [error, setError] = React.useState(null);
13260
- // 获取当前弹窗商品数据(自动从 popupDetailData 获取)
13261
- const data = popupDetailData;
13262
- const product = (_c = (_b = data === null || data === void 0 ? void 0 : data.video) === null || _b === void 0 ? void 0 : _b.bindProduct) !== null && _c !== void 0 ? _c : (_e = (_d = data === null || data === void 0 ? void 0 : data.video) === null || _d === void 0 ? void 0 : _d.bindProducts) === null || _e === void 0 ? void 0 : _e[0];
13263
- product === null || product === void 0 ? void 0 : product.bindCta;
13264
- const position = (_g = (_f = popupDetailData === null || popupDetailData === void 0 ? void 0 : popupDetailData.index) !== null && _f !== void 0 ? _f : index) !== null && _g !== void 0 ? _g : 0;
13265
- // Shopify配置 - 优先级:props > globalConfig > 默认值
13266
- const finalShopifyDomain = shopifyDomain || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || 'dev-store-749237498237498636.myshopify.com';
13267
- const finalStorefrontToken = storefrontAccessToken || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
13268
- // 自动从商品数据获取 Shopify Product ID
13269
- const productId = (product === null || product === void 0 ? void 0 : product.itemId) || '';
13270
- // 文案
13271
- const finalTexts = {
13272
- addToCart: texts.addToCart || 'Add to Cart',
13273
- selectOptions: texts.selectOptions || 'Please select options',
13274
- loading: texts.loading || 'Loading...',
13275
- error: texts.error || 'Failed to load product',
13276
- color: texts.color || 'Color',
13277
- size: texts.size || 'Size',
13278
- material: texts.material || 'Material',
13279
- style: texts.style || 'Style'
13280
- };
13281
- // 查询Shopify商品数据
13282
- const fetchProductData = React.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
13283
- var _m;
13284
- if (!productId || !finalShopifyDomain || !finalStorefrontToken) {
13285
- console.log('[AddToCartPopup] 缺少必要配置:', {
13286
- productId,
13287
- shopifyDomain: finalShopifyDomain,
13288
- hasToken: !!finalStorefrontToken
13289
- });
13290
- setLoading(false);
13291
- return;
13292
- }
13293
- console.log('[AddToCartPopup] 开始加载商品数据:', {
13294
- productId,
13295
- shopifyDomain: finalShopifyDomain
13296
- });
13297
- setLoading(true);
13298
- setError(null);
13299
- try {
13300
- const query = `
13301
- query getProduct($id: ID!) {
13302
- product(id: $id) {
13303
- id
13304
- title
13305
- images(first: 10) {
13306
- edges {
13307
- node {
13308
- url
13309
- }
13310
- }
13311
- }
13312
- options {
13313
- name
13314
- values
13315
- }
13316
- variants(first: 100) {
13317
- edges {
13318
- node {
13319
- id
13320
- title
13321
- availableForSale
13322
- quantityAvailable
13323
- price {
13324
- amount
13325
- currencyCode
13326
- }
13327
- image {
13328
- url
13329
- }
13330
- selectedOptions {
13331
- name
13332
- value
13333
- }
13334
- }
13335
- }
13336
- }
13337
- }
13338
- }
13339
- `;
13340
- // 确保 Product ID 格式正确
13341
- const formattedProductId = productId.startsWith('gid://')
13342
- ? productId
13343
- : `gid://shopify/Product/${productId}`;
13344
- console.log('[AddToCartPopup] 使用的 Product ID:', formattedProductId);
13345
- const response = yield fetch(`https://${finalShopifyDomain}/api/2024-01/graphql.json`, {
13346
- method: 'POST',
13347
- headers: {
13348
- 'Content-Type': 'application/json',
13349
- 'X-Shopify-Storefront-Access-Token': finalStorefrontToken
13350
- },
13351
- body: JSON.stringify({
13352
- query,
13353
- variables: { id: formattedProductId }
13354
- })
13355
- });
13356
- if (!response.ok) {
13357
- throw new Error(`HTTP ${response.status}`);
13358
- }
13359
- const result = yield response.json();
13360
- if (result.errors) {
13361
- console.error('[AddToCartPopup] GraphQL 错误:', result.errors);
13362
- throw new Error(result.errors[0].message);
13363
- }
13364
- if (!((_m = result.data) === null || _m === void 0 ? void 0 : _m.product)) {
13365
- console.error('[AddToCartPopup] 未找到商品');
13366
- throw new Error('Product not found');
13367
- }
13368
- console.log('[AddToCartPopup] 商品数据加载成功:', result.data.product.title);
13369
- setProductData(result.data.product);
13370
- }
13371
- catch (err) {
13372
- const errorMessage = err instanceof Error ? err.message : finalTexts.error;
13373
- setError(errorMessage);
13374
- console.error('[AddToCartPopup] 加载失败:', err);
13375
- }
13376
- finally {
13377
- setLoading(false);
13378
- }
13379
- }), [productId, finalShopifyDomain, finalStorefrontToken, finalTexts.error]);
13380
- React.useEffect(() => {
13381
- if (isActive) {
13382
- fetchProductData();
13383
- }
13384
- }, [isActive, fetchProductData]);
13385
- // 根据选中的规格匹配variant
13386
- React.useEffect(() => {
13387
- if (!productData)
13388
- return;
13389
- const variants = productData.variants.edges.map(edge => edge.node);
13390
- const optionsCount = productData.options.length;
13391
- const selectedCount = Object.keys(selectedOptions).length;
13392
- if (selectedCount === 0 || selectedCount < optionsCount) {
13393
- setSelectedVariant(null);
13394
- return;
13395
- }
13396
- const matchedVariant = variants.find(variant => {
13397
- return variant.selectedOptions.every(option => selectedOptions[option.name] === option.value);
13398
- });
13399
- setSelectedVariant(matchedVariant || null);
13400
- setQuantity(1);
13401
- }, [selectedOptions, productData]);
13402
- // 处理规格选择
13403
- const handleOptionSelect = React.useCallback((optionName, value) => {
13404
- setSelectedOptions(prev => {
13405
- const newOptions = Object.assign({}, prev);
13406
- if (newOptions[optionName] === value) {
13407
- delete newOptions[optionName];
13408
- }
13409
- else {
13410
- newOptions[optionName] = value;
13411
- }
13412
- return newOptions;
13413
- });
13414
- }, []);
13415
- // 处理数量变化
13416
- const handleQuantityChange = React.useCallback((delta) => {
13417
- setQuantity(prev => {
13418
- var _a;
13419
- const newQuantity = prev + delta;
13420
- const maxQuantity = (_a = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) !== null && _a !== void 0 ? _a : 999;
13421
- return Math.max(1, Math.min(newQuantity, maxQuantity));
13422
- });
13423
- }, [selectedVariant]);
13424
- // 检查某个规格值是否可用
13425
- const isOptionValueAvailable = React.useCallback((optionName, value) => {
13426
- if (!productData)
13427
- return false;
13428
- const variants = productData.variants.edges.map(edge => edge.node);
13429
- const tempOptions = Object.assign(Object.assign({}, selectedOptions), { [optionName]: value });
13430
- return variants.some(variant => {
13431
- const matches = variant.selectedOptions.every(option => !tempOptions[option.name] || tempOptions[option.name] === option.value);
13432
- const hasStock = variant.quantityAvailable === null || variant.quantityAvailable > 0;
13433
- return matches && variant.availableForSale && hasStock;
13434
- });
13435
- }, [productData, selectedOptions]);
13436
- // 处理加购
13437
- const handleAddToCart = React.useCallback(() => {
13438
- var _a;
13439
- if (!selectedVariant || quantity === 0)
13440
- return;
13441
- const variantId = selectedVariant.id.split('/').pop();
13442
- const cartUrl = `https://${finalShopifyDomain}/cart/add?id=${variantId}&quantity=${quantity}`;
13443
- console.log('[AddToCartPopup] 加购:', {
13444
- variantId,
13445
- quantity,
13446
- cartUrl
13447
- });
13448
- // 上报事件
13449
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
13450
- eventName: 'AddToCart',
13451
- product: product ? [product] : undefined,
13452
- contentType: 'product',
13453
- data,
13454
- position,
13455
- content_id: (_a = product === null || product === void 0 ? void 0 : product.itemId) !== null && _a !== void 0 ? _a : '',
13456
- value: parseFloat(selectedVariant.price.amount) * quantity,
13457
- currency: selectedVariant.price.currencyCode,
13458
- contents: [{
13459
- id: variantId,
13460
- quantity
13461
- }]
13462
- });
13463
- // 跳转到Shopify购物车页面
13464
- window.location.href = cartUrl;
13465
- }, [selectedVariant, quantity, finalShopifyDomain, bffFbReport, product, data, position]);
13466
- // 计算总价
13467
- const totalPrice = React.useMemo(() => {
13468
- if (!selectedVariant)
13469
- return null;
13470
- const price = parseFloat(selectedVariant.price.amount);
13471
- const total = price * quantity;
13472
- return total.toFixed(2);
13473
- }, [selectedVariant, quantity]);
13474
- // 初始化时间
13475
- React.useEffect(() => {
13476
- const initTime = () => {
13477
- curTimeRef.current = new Date();
13478
- };
13479
- initTime();
13480
- window.addEventListener('pageshow', initTime);
13481
- return () => {
13482
- window.removeEventListener('pageshow', initTime);
13483
- };
13484
- }, []);
13485
- // 加载中
13486
- if (loading) {
13487
- return (React.createElement("div", { className: "add-to-cart-popup-loading", style: style },
13488
- React.createElement("div", null, finalTexts.loading)));
13489
- }
13490
- // 错误状态
13491
- if (error || !productData) {
13492
- return (React.createElement("div", { className: "add-to-cart-popup-error", style: style },
13493
- React.createElement("div", null,
13494
- finalTexts.error,
13495
- ": ",
13496
- error || 'Product not found')));
13497
- }
13498
- const mainImage = ((_h = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _h === void 0 ? void 0 : _h.url) || ((_j = productData.images.edges[0]) === null || _j === void 0 ? void 0 : _j.node.url) || ((_k = product === null || product === void 0 ? void 0 : product.homePage) === null || _k === void 0 ? void 0 : _k[0]) || '';
13499
- const hasAllOptionsSelected = productData.options.length === Object.keys(selectedOptions).length;
13500
- const isAddToCartDisabled = !selectedVariant || quantity === 0;
13501
- return (React.createElement("div", Object.assign({ className: "add-to-cart-popup-container", style: style }, props),
13502
- React.createElement("div", { className: "variant-detail-section" },
13503
- React.createElement("div", { className: "variant-image-wrapper" },
13504
- React.createElement("img", { src: mainImage, alt: productData.title, className: "variant-image" })),
13505
- React.createElement("div", { className: "variant-info-wrapper" },
13506
- React.createElement("h2", { className: "product-title-text", style: variantStyles.title, dangerouslySetInnerHTML: {
13507
- __html: setFontForText(productData.title, variantStyles.title)
13508
- } }),
13509
- selectedVariant && (React.createElement(React.Fragment, null,
13510
- React.createElement("div", { className: "selected-options-tags" }, selectedVariant.selectedOptions.map(option => (React.createElement("span", { key: option.name, className: "option-tag", style: variantStyles.selectedOption },
13511
- option.name,
13512
- ": ",
13513
- option.value)))),
13514
- React.createElement("div", { className: "price-display" },
13515
- React.createElement("span", { className: "price-value", style: variantStyles.price, dangerouslySetInnerHTML: {
13516
- __html: setFontForText(`${selectedVariant.price.currencyCode} $${totalPrice}`, variantStyles.price)
13517
- } })),
13518
- React.createElement("div", { className: "quantity-selector-wrapper", style: quantityStyle },
13519
- React.createElement("button", { className: "quantity-btn quantity-decrease", onClick: () => handleQuantityChange(-1), disabled: quantity <= 1, "aria-label": "Decrease quantity" }, "-"),
13520
- React.createElement("input", { type: "number", value: quantity, readOnly: true, className: "quantity-input-field", "aria-label": "Quantity" }),
13521
- React.createElement("button", { className: "quantity-btn quantity-increase", onClick: () => handleQuantityChange(1), disabled: quantity >= ((_l = selectedVariant.quantityAvailable) !== null && _l !== void 0 ? _l : 999), "aria-label": "Increase quantity" }, "+")))),
13522
- !hasAllOptionsSelected && (React.createElement("div", { className: "no-selection-hint", style: variantStyles.option }, finalTexts.selectOptions)))),
13523
- React.createElement("div", { className: "variant-options-section" }, productData.options.map(option => (React.createElement("div", { key: option.name, className: "option-group-wrapper" },
13524
- React.createElement("h3", { className: "option-group-name", style: variantStyles.option, dangerouslySetInnerHTML: {
13525
- __html: setFontForText(option.name, variantStyles.option)
13526
- } }),
13527
- React.createElement("div", { className: "option-values-grid" }, option.values.map(value => {
13528
- const isSelected = selectedOptions[option.name] === value;
13529
- const isAvailable = isOptionValueAvailable(option.name, value);
13530
- return (React.createElement("button", { key: value, className: `option-value-button ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable, "aria-label": `${option.name}: ${value}`, "aria-pressed": isSelected }, value));
13531
- })))))),
13532
- React.createElement("button", { className: `add-to-cart-button ${isAddToCartDisabled ? 'disabled' : ''}`, style: buttonStyle, onClick: handleAddToCart, disabled: isAddToCartDisabled, "aria-label": finalTexts.addToCart },
13533
- React.createElement("span", { dangerouslySetInnerHTML: {
13534
- __html: setFontForText(finalTexts.addToCart, buttonStyle)
13535
- } }))));
13536
- };
13537
-
13538
13647
  /*
13539
13648
  * @Author: tao
13540
13649
  * @Date: 2026-01-12
@@ -13550,60 +13659,7 @@ const AddToCartPopup = createMaterial(AddToCartPopup$1, {
13550
13659
  interactionRender: interactionRender$d
13551
13660
  },
13552
13661
  defaulSetting: {
13553
- props: {
13554
- shopifyDomain: '',
13555
- storefrontAccessToken: '',
13556
- variantStyles: {
13557
- title: {
13558
- color: '#000',
13559
- fontSize: 20,
13560
- fontWeight: 600,
13561
- marginBottom: 12
13562
- },
13563
- price: {
13564
- color: '#000',
13565
- fontSize: 24,
13566
- fontWeight: 700,
13567
- marginBottom: 16
13568
- },
13569
- option: {
13570
- color: '#111',
13571
- fontSize: 16,
13572
- fontWeight: 600,
13573
- marginBottom: 12
13574
- },
13575
- selectedOption: {
13576
- fontSize: 14,
13577
- color: '#374151'
13578
- }
13579
- },
13580
- buttonStyle: {
13581
- backgroundColor: '#000',
13582
- color: '#fff',
13583
- fontSize: 16,
13584
- height: 52,
13585
- fontWeight: 600,
13586
- textAlign: 'center',
13587
- textTransform: 'uppercase'
13588
- },
13589
- quantityStyle: {
13590
- gap: 12
13591
- },
13592
- texts: {
13593
- addToCart: 'Add to Cart',
13594
- selectOptions: 'Please select options',
13595
- loading: 'Loading...',
13596
- error: 'Failed to load product',
13597
- color: 'Color',
13598
- size: 'Size',
13599
- material: 'Material',
13600
- style: 'Style'
13601
- },
13602
- popupBg: {
13603
- horizontalMargin: 0,
13604
- bottomMargin: 0
13605
- }
13606
- },
13662
+ props: {},
13607
13663
  style: {}
13608
13664
  },
13609
13665
  w: 100,
@@ -19699,7 +19755,7 @@ const StructurePage = (_a) => {
19699
19755
  return res.json();
19700
19756
  })
19701
19757
  .then((result) => {
19702
- var _a, _b, _c, _d;
19758
+ var _a, _b, _c, _d, _e;
19703
19759
  if (result.code === '0' || result.code === '00000') {
19704
19760
  // 判断数据结构:CMS 模式和普通模式可能不同
19705
19761
  let multiCtaData = null;
@@ -19718,6 +19774,14 @@ const StructurePage = (_a) => {
19718
19774
  console.error('[StructurePage] No multiCta data found in response:', result);
19719
19775
  setError(result.message || 'No multiCta data found');
19720
19776
  }
19777
+ // 存储 Shopify 配置信息到 window 对象,供 AddToCart 弹窗使用
19778
+ if ((_e = result.data) === null || _e === void 0 ? void 0 : _e.shopify) {
19779
+ window.__SHOPIFY_CONFIG__ = {
19780
+ domain: result.data.shopify.domain,
19781
+ storefrontAccessToken: result.data.shopify.storefrontAccessToken
19782
+ };
19783
+ console.log('[StructurePage] Shopify config stored:', window.__SHOPIFY_CONFIG__);
19784
+ }
19721
19785
  }
19722
19786
  else {
19723
19787
  setError(result.message || 'Failed to load data');
@@ -19899,7 +19963,13 @@ const StructurePage = (_a) => {
19899
19963
  var index$3 = /*#__PURE__*/Object.freeze({
19900
19964
  __proto__: null,
19901
19965
  EditorCore: EditorCore,
19902
- StructurePage: StructurePage
19966
+ Pagebuilder: Pagebuilder,
19967
+ StructurePage: StructurePage,
19968
+ createMaterial: createMaterial,
19969
+ hooks: index$4,
19970
+ localStore: localStore,
19971
+ sessionStore: sessionStore,
19972
+ tool: tool
19903
19973
  });
19904
19974
 
19905
19975
  /*