pb-sxp-ui 1.20.26 → 1.20.27
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 +292 -395
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +355 -291
- package/dist/index.js +292 -395
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs +6 -6
- package/dist/index.min.cjs.map +1 -1
- package/dist/index.min.js +6 -6
- package/dist/index.min.js.map +1 -1
- package/dist/pb-ui.js +292 -395
- package/dist/pb-ui.js.map +1 -1
- package/dist/pb-ui.min.js +6 -6
- package/dist/pb-ui.min.js.map +1 -1
- package/es/core/components/StructurePage/index.d.ts +4 -0
- package/es/core/components/StructurePage/index.js +8 -1
- package/es/core/components/SxpPageRender/Modal/index.d.ts +1 -0
- package/es/core/components/SxpPageRender/Modal/index.js +3 -3
- package/es/core/components/SxpPageRender/index.d.ts +1 -0
- package/es/core/components/SxpPageRender/typing.d.ts +1 -0
- package/es/materials/sxp/popup/AddToCart/index.d.ts +1 -27
- package/es/materials/sxp/popup/AddToCart/index.js +117 -173
- package/es/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
- package/es/materials/sxp/popup/AddToCart/index.new.js +174 -0
- package/es/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
- package/es/materials/sxp/popup/AddToCart/index.old.js +299 -0
- package/es/materials/sxp/popup/AddToCart/material.js +1 -54
- package/es/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
- package/lib/core/components/StructurePage/index.d.ts +4 -0
- package/lib/core/components/StructurePage/index.js +8 -1
- package/lib/core/components/SxpPageRender/Modal/index.d.ts +1 -0
- package/lib/core/components/SxpPageRender/Modal/index.js +3 -3
- package/lib/core/components/SxpPageRender/index.d.ts +1 -0
- package/lib/core/components/SxpPageRender/typing.d.ts +1 -0
- package/lib/materials/sxp/popup/AddToCart/index.d.ts +1 -27
- package/lib/materials/sxp/popup/AddToCart/index.js +115 -171
- package/lib/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
- package/lib/materials/sxp/popup/AddToCart/index.new.js +176 -0
- package/lib/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
- package/lib/materials/sxp/popup/AddToCart/index.old.js +301 -0
- package/lib/materials/sxp/popup/AddToCart/material.js +1 -54
- package/lib/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
- package/package.json +1 -1
|
@@ -3,53 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
5
|
const hooks_1 = require("../../../../core/hooks");
|
|
6
|
-
const
|
|
7
|
-
const tool_1 = require("../../../../core/utils/tool");
|
|
6
|
+
const Modal_1 = tslib_1.__importDefault(require("../../../../core/components/SxpPageRender/Modal"));
|
|
8
7
|
require("./index.less");
|
|
9
|
-
const AddToCartPopup = (
|
|
10
|
-
var _b, _c, _d, _e, _f, _g, _h, _j
|
|
11
|
-
|
|
12
|
-
const { sxpParameter, popupDetailData, isPreview, bffFbReport, globalConfig } = (0, hooks_1.useSxpDataSource)();
|
|
13
|
-
const { jumpToWeb } = (0, useEventReport_1.useEventReport)();
|
|
14
|
-
const curTimeRef = (0, react_1.useRef)(null);
|
|
8
|
+
const AddToCartPopup = ({ isActive = true }) => {
|
|
9
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
10
|
+
const { popupDetailData, globalConfig } = (0, hooks_1.useSxpDataSource)();
|
|
15
11
|
const [productData, setProductData] = (0, react_1.useState)(null);
|
|
16
12
|
const [selectedOptions, setSelectedOptions] = (0, react_1.useState)({});
|
|
17
13
|
const [selectedVariant, setSelectedVariant] = (0, react_1.useState)(null);
|
|
18
14
|
const [quantity, setQuantity] = (0, react_1.useState)(1);
|
|
19
15
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
20
16
|
const [error, setError] = (0, react_1.useState)(null);
|
|
17
|
+
const [showImagePreview, setShowImagePreview] = (0, react_1.useState)(false);
|
|
18
|
+
const [previewImageUrl, setPreviewImageUrl] = (0, react_1.useState)('');
|
|
21
19
|
const data = popupDetailData;
|
|
22
|
-
const product = (
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const productId = (product === null || product === void 0 ? void 0 : product.itemId) || '';
|
|
28
|
-
const finalTexts = {
|
|
29
|
-
addToCart: texts.addToCart || 'Add to Cart',
|
|
30
|
-
selectOptions: texts.selectOptions || 'Please select options',
|
|
31
|
-
loading: texts.loading || 'Loading...',
|
|
32
|
-
error: texts.error || 'Failed to load product',
|
|
33
|
-
color: texts.color || 'Color',
|
|
34
|
-
size: texts.size || 'Size',
|
|
35
|
-
material: texts.material || 'Material',
|
|
36
|
-
style: texts.style || 'Style'
|
|
37
|
-
};
|
|
20
|
+
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;
|
|
21
|
+
const shopifyConfig = window.__SHOPIFY_CONFIG__;
|
|
22
|
+
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';
|
|
23
|
+
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';
|
|
24
|
+
const productId = (product === null || product === void 0 ? void 0 : product.shopifyId) || (product === null || product === void 0 ? void 0 : product.itemId) || '';
|
|
38
25
|
const fetchProductData = (0, react_1.useCallback)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
39
|
-
var
|
|
40
|
-
if (!productId || !
|
|
41
|
-
console.log('[AddToCartPopup] 缺少必要配置:', {
|
|
42
|
-
productId,
|
|
43
|
-
shopifyDomain: finalShopifyDomain,
|
|
44
|
-
hasToken: !!finalStorefrontToken
|
|
45
|
-
});
|
|
26
|
+
var _k;
|
|
27
|
+
if (!productId || !shopifyDomain || !storefrontToken) {
|
|
46
28
|
setLoading(false);
|
|
47
29
|
return;
|
|
48
30
|
}
|
|
49
|
-
console.log('[AddToCartPopup] 开始加载商品数据:', {
|
|
50
|
-
productId,
|
|
51
|
-
shopifyDomain: finalShopifyDomain
|
|
52
|
-
});
|
|
53
31
|
setLoading(true);
|
|
54
32
|
setError(null);
|
|
55
33
|
try {
|
|
@@ -93,45 +71,34 @@ const AddToCartPopup = (_a) => {
|
|
|
93
71
|
}
|
|
94
72
|
}
|
|
95
73
|
`;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
: `gid://shopify/Product/${productId}`;
|
|
99
|
-
console.log('[AddToCartPopup] 使用的 Product ID:', formattedProductId);
|
|
100
|
-
const response = yield fetch(`https://${finalShopifyDomain}/api/2024-01/graphql.json`, {
|
|
74
|
+
const fullProductId = productId.startsWith('gid://') ? productId : `gid://shopify/Product/${productId}`;
|
|
75
|
+
const response = yield fetch(`https://${shopifyDomain}/api/2024-01/graphql.json`, {
|
|
101
76
|
method: 'POST',
|
|
102
77
|
headers: {
|
|
103
78
|
'Content-Type': 'application/json',
|
|
104
|
-
'X-Shopify-Storefront-Access-Token':
|
|
79
|
+
'X-Shopify-Storefront-Access-Token': storefrontToken
|
|
105
80
|
},
|
|
106
81
|
body: JSON.stringify({
|
|
107
82
|
query,
|
|
108
|
-
variables: { id:
|
|
83
|
+
variables: { id: fullProductId }
|
|
109
84
|
})
|
|
110
85
|
});
|
|
111
|
-
if (!response.ok) {
|
|
112
|
-
throw new Error(`HTTP ${response.status}`);
|
|
113
|
-
}
|
|
114
86
|
const result = yield response.json();
|
|
115
87
|
if (result.errors) {
|
|
116
|
-
console.error('[AddToCartPopup] GraphQL 错误:', result.errors);
|
|
117
88
|
throw new Error(result.errors[0].message);
|
|
118
89
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
throw new Error('Product not found');
|
|
122
|
-
}
|
|
123
|
-
console.log('[AddToCartPopup] 商品数据加载成功:', result.data.product.title);
|
|
90
|
+
console.log('[AddToCart] Shopify Product Data:', result.data.product);
|
|
91
|
+
console.log('[AddToCart] Options:', (_k = result.data.product) === null || _k === void 0 ? void 0 : _k.options);
|
|
124
92
|
setProductData(result.data.product);
|
|
125
93
|
}
|
|
126
94
|
catch (err) {
|
|
127
|
-
|
|
128
|
-
setError(errorMessage);
|
|
95
|
+
setError(err instanceof Error ? err.message : 'Failed to load product');
|
|
129
96
|
console.error('[AddToCartPopup] 加载失败:', err);
|
|
130
97
|
}
|
|
131
98
|
finally {
|
|
132
99
|
setLoading(false);
|
|
133
100
|
}
|
|
134
|
-
}), [productId,
|
|
101
|
+
}), [productId, shopifyDomain, storefrontToken]);
|
|
135
102
|
(0, react_1.useEffect)(() => {
|
|
136
103
|
if (isActive) {
|
|
137
104
|
fetchProductData();
|
|
@@ -148,134 +115,111 @@ const AddToCartPopup = (_a) => {
|
|
|
148
115
|
return;
|
|
149
116
|
}
|
|
150
117
|
const matchedVariant = variants.find(variant => {
|
|
151
|
-
return variant.selectedOptions.every(option =>
|
|
118
|
+
return variant.selectedOptions.every(option => {
|
|
119
|
+
return selectedOptions[option.name] === option.value;
|
|
120
|
+
});
|
|
152
121
|
});
|
|
153
122
|
setSelectedVariant(matchedVariant || null);
|
|
154
|
-
|
|
123
|
+
if ((matchedVariant === null || matchedVariant === void 0 ? void 0 : matchedVariant.quantityAvailable) !== undefined && matchedVariant.quantityAvailable > 0) {
|
|
124
|
+
setQuantity(prev => Math.min(prev, matchedVariant.quantityAvailable));
|
|
125
|
+
}
|
|
155
126
|
}, [selectedOptions, productData]);
|
|
156
|
-
const handleOptionSelect = (
|
|
157
|
-
setSelectedOptions(prev => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
else {
|
|
163
|
-
newOptions[optionName] = value;
|
|
164
|
-
}
|
|
165
|
-
return newOptions;
|
|
166
|
-
});
|
|
167
|
-
}, []);
|
|
168
|
-
const handleQuantityChange = (0, react_1.useCallback)((delta) => {
|
|
169
|
-
setQuantity(prev => {
|
|
170
|
-
var _a;
|
|
171
|
-
const newQuantity = prev + delta;
|
|
172
|
-
const maxQuantity = (_a = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) !== null && _a !== void 0 ? _a : 999;
|
|
173
|
-
return Math.max(1, Math.min(newQuantity, maxQuantity));
|
|
174
|
-
});
|
|
175
|
-
}, [selectedVariant]);
|
|
176
|
-
const isOptionValueAvailable = (0, react_1.useCallback)((optionName, value) => {
|
|
177
|
-
if (!productData)
|
|
178
|
-
return false;
|
|
179
|
-
const variants = productData.variants.edges.map(edge => edge.node);
|
|
180
|
-
const tempOptions = Object.assign(Object.assign({}, selectedOptions), { [optionName]: value });
|
|
181
|
-
return variants.some(variant => {
|
|
182
|
-
const matches = variant.selectedOptions.every(option => !tempOptions[option.name] || tempOptions[option.name] === option.value);
|
|
183
|
-
const hasStock = variant.quantityAvailable === null || variant.quantityAvailable > 0;
|
|
184
|
-
return matches && variant.availableForSale && hasStock;
|
|
185
|
-
});
|
|
186
|
-
}, [productData, selectedOptions]);
|
|
187
|
-
const handleAddToCart = (0, react_1.useCallback)(() => {
|
|
188
|
-
var _a;
|
|
189
|
-
if (!selectedVariant || quantity === 0)
|
|
127
|
+
const handleOptionSelect = (optionName, value) => {
|
|
128
|
+
setSelectedOptions(prev => (Object.assign(Object.assign({}, prev), { [optionName]: value })));
|
|
129
|
+
};
|
|
130
|
+
const handleAddToCart = () => {
|
|
131
|
+
if (!selectedVariant) {
|
|
132
|
+
alert('Please select all options');
|
|
190
133
|
return;
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
console.log('[
|
|
134
|
+
}
|
|
135
|
+
const variantId = selectedVariant.id.replace('gid://shopify/ProductVariant/', '');
|
|
136
|
+
console.log('[AddToCart] 添加到购物车:', {
|
|
194
137
|
variantId,
|
|
195
138
|
quantity,
|
|
196
|
-
|
|
139
|
+
shopifyDomain,
|
|
140
|
+
selectedVariant
|
|
197
141
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
contentType: 'product',
|
|
202
|
-
data,
|
|
203
|
-
position,
|
|
204
|
-
content_id: (_a = product === null || product === void 0 ? void 0 : product.itemId) !== null && _a !== void 0 ? _a : '',
|
|
205
|
-
value: parseFloat(selectedVariant.price.amount) * quantity,
|
|
206
|
-
currency: selectedVariant.price.currencyCode,
|
|
207
|
-
contents: [{
|
|
208
|
-
id: variantId,
|
|
209
|
-
quantity
|
|
210
|
-
}]
|
|
142
|
+
const params = new URLSearchParams({
|
|
143
|
+
id: variantId,
|
|
144
|
+
quantity: quantity.toString()
|
|
211
145
|
});
|
|
146
|
+
const cartUrl = `https://${shopifyDomain}/cart/add?${params.toString()}`;
|
|
147
|
+
console.log('[AddToCart] 跳转到购物车 URL:', cartUrl);
|
|
212
148
|
window.location.href = cartUrl;
|
|
213
|
-
}
|
|
214
|
-
const totalPrice =
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const price = parseFloat(selectedVariant.price.amount);
|
|
218
|
-
const total = price * quantity;
|
|
219
|
-
return total.toFixed(2);
|
|
220
|
-
}, [selectedVariant, quantity]);
|
|
221
|
-
(0, react_1.useEffect)(() => {
|
|
222
|
-
const initTime = () => {
|
|
223
|
-
curTimeRef.current = new Date();
|
|
224
|
-
};
|
|
225
|
-
initTime();
|
|
226
|
-
window.addEventListener('pageshow', initTime);
|
|
227
|
-
return () => {
|
|
228
|
-
window.removeEventListener('pageshow', initTime);
|
|
229
|
-
};
|
|
230
|
-
}, []);
|
|
149
|
+
};
|
|
150
|
+
const totalPrice = selectedVariant
|
|
151
|
+
? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
|
|
152
|
+
: '0.00';
|
|
231
153
|
if (loading) {
|
|
232
|
-
return (react_1.default.createElement("div", { className:
|
|
233
|
-
react_1.default.createElement("div",
|
|
154
|
+
return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
|
|
155
|
+
react_1.default.createElement("div", { className: 'loading' }, "Loading...")));
|
|
156
|
+
}
|
|
157
|
+
if (error) {
|
|
158
|
+
return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
|
|
159
|
+
react_1.default.createElement("div", { className: 'error' }, error)));
|
|
234
160
|
}
|
|
235
|
-
if (
|
|
236
|
-
return
|
|
237
|
-
react_1.default.createElement("div", null,
|
|
238
|
-
finalTexts.error,
|
|
239
|
-
": ",
|
|
240
|
-
error || 'Product not found')));
|
|
161
|
+
if (!productData) {
|
|
162
|
+
return null;
|
|
241
163
|
}
|
|
242
|
-
const mainImage = ((_h =
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
return (react_1.default.createElement("div",
|
|
246
|
-
react_1.default.createElement("div", { className:
|
|
247
|
-
react_1.default.createElement("div", { className:
|
|
248
|
-
react_1.default.createElement("
|
|
249
|
-
react_1.default.createElement("div", { className:
|
|
250
|
-
react_1.default.createElement("
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
react_1.default.createElement("div", { className:
|
|
259
|
-
react_1.default.createElement("
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
react_1.default.createElement("
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
react_1.default.createElement("div", { className:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
164
|
+
const mainImage = ((_h = productData.images.edges[0]) === null || _h === void 0 ? void 0 : _h.node.url) || '';
|
|
165
|
+
const variantImage = ((_j = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _j === void 0 ? void 0 : _j.url) || mainImage;
|
|
166
|
+
const maxQuantity = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) || 999;
|
|
167
|
+
return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
|
|
168
|
+
react_1.default.createElement("div", { className: 'popup-content' },
|
|
169
|
+
react_1.default.createElement("div", { className: 'product-header' },
|
|
170
|
+
react_1.default.createElement("div", { className: 'product-title' }, productData.title)),
|
|
171
|
+
react_1.default.createElement("div", { className: 'variant-detail' },
|
|
172
|
+
react_1.default.createElement("div", { className: 'variant-image-container', onClick: () => {
|
|
173
|
+
if (variantImage) {
|
|
174
|
+
setPreviewImageUrl(variantImage);
|
|
175
|
+
setShowImagePreview(true);
|
|
176
|
+
}
|
|
177
|
+
}, style: { cursor: 'pointer' } },
|
|
178
|
+
react_1.default.createElement("img", { src: variantImage, alt: 'Selected variant', className: 'variant-image' })),
|
|
179
|
+
react_1.default.createElement("div", { className: 'variant-info' },
|
|
180
|
+
react_1.default.createElement("div", { className: 'variant-specs-row' },
|
|
181
|
+
react_1.default.createElement("div", { className: 'variant-specs' }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (react_1.default.createElement("span", { key: key, className: 'spec-item' }, value)))) : (react_1.default.createElement("span", { className: 'spec-placeholder' }, "Please select options"))),
|
|
182
|
+
selectedVariant && selectedVariant.quantityAvailable !== undefined && (react_1.default.createElement("div", { className: 'stock-info' },
|
|
183
|
+
"Available: ",
|
|
184
|
+
selectedVariant.quantityAvailable))),
|
|
185
|
+
react_1.default.createElement("div", { className: 'variant-price-row' },
|
|
186
|
+
react_1.default.createElement("div", { className: 'price' },
|
|
187
|
+
"$",
|
|
188
|
+
totalPrice),
|
|
189
|
+
react_1.default.createElement("div", { className: 'quantity-selector' },
|
|
190
|
+
react_1.default.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
|
|
191
|
+
react_1.default.createElement("span", { className: 'qty-value' }, quantity),
|
|
192
|
+
react_1.default.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(quantity + 1), disabled: !selectedVariant || quantity >= maxQuantity }, "+"))))),
|
|
193
|
+
react_1.default.createElement("div", { className: 'variant-options' }, productData.options.map(option => (react_1.default.createElement("div", { key: option.name, className: 'option-group' },
|
|
194
|
+
react_1.default.createElement("div", { className: 'option-label' }, option.name),
|
|
195
|
+
react_1.default.createElement("div", { className: 'option-values' }, option.values.map(value => {
|
|
196
|
+
const isAvailable = productData.variants.edges.some(({ node: variant }) => {
|
|
197
|
+
const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
|
|
198
|
+
if (!hasThisOption || !variant.availableForSale)
|
|
199
|
+
return false;
|
|
200
|
+
if (variant.quantityAvailable !== undefined) {
|
|
201
|
+
return variant.quantityAvailable > 0;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
});
|
|
205
|
+
const isSelected = selectedOptions[option.name] === value;
|
|
206
|
+
return (react_1.default.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
|
|
207
|
+
}))))))),
|
|
208
|
+
react_1.default.createElement("div", { className: 'popup-footer' },
|
|
209
|
+
react_1.default.createElement("button", { className: 'add-to-cart-btn', onClick: handleAddToCart, disabled: !selectedVariant }, "Add To Cart")),
|
|
210
|
+
showImagePreview && previewImageUrl && (react_1.default.createElement(Modal_1.default, { visible: showImagePreview, padding: 0, isFullScreen: true, onClose: () => setShowImagePreview(false) },
|
|
211
|
+
react_1.default.createElement("div", { style: {
|
|
212
|
+
width: '100%',
|
|
213
|
+
height: '100%',
|
|
214
|
+
display: 'flex',
|
|
215
|
+
alignItems: 'center',
|
|
216
|
+
justifyContent: 'center',
|
|
217
|
+
backgroundColor: 'rgba(0, 0, 0, 0.9)'
|
|
218
|
+
}, onClick: () => setShowImagePreview(false) },
|
|
219
|
+
react_1.default.createElement("img", { src: previewImageUrl, alt: 'Preview', style: {
|
|
220
|
+
maxWidth: '100%',
|
|
221
|
+
maxHeight: '100%',
|
|
222
|
+
objectFit: 'contain'
|
|
223
|
+
} }))))));
|
|
280
224
|
};
|
|
281
225
|
exports.default = AddToCartPopup;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const react_1 = tslib_1.__importStar(require("react"));
|
|
5
|
+
const hooks_1 = require("../../../../core/hooks");
|
|
6
|
+
require("./index.less");
|
|
7
|
+
const AddToCartPopup = ({ isActive = true, onClose }) => {
|
|
8
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
9
|
+
const { popupDetailData, globalConfig } = (0, hooks_1.useSxpDataSource)();
|
|
10
|
+
const [productData, setProductData] = (0, react_1.useState)(null);
|
|
11
|
+
const [selectedOptions, setSelectedOptions] = (0, react_1.useState)({});
|
|
12
|
+
const [selectedVariant, setSelectedVariant] = (0, react_1.useState)(null);
|
|
13
|
+
const [quantity, setQuantity] = (0, react_1.useState)(1);
|
|
14
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
15
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
16
|
+
const data = popupDetailData;
|
|
17
|
+
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;
|
|
18
|
+
const shopifyConfig = window.__SHOPIFY_CONFIG__;
|
|
19
|
+
const shopifyDomain = (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.domain) || 'dev-store-749237498237498636.myshopify.com';
|
|
20
|
+
const storefrontToken = (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
|
|
21
|
+
const productId = (product === null || product === void 0 ? void 0 : product.shopifyId) || (product === null || product === void 0 ? void 0 : product.itemId) || '';
|
|
22
|
+
const fetchProductData = (0, react_1.useCallback)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
if (!productId || !shopifyDomain || !storefrontToken) {
|
|
24
|
+
setLoading(false);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
setLoading(true);
|
|
28
|
+
setError(null);
|
|
29
|
+
try {
|
|
30
|
+
const query = `
|
|
31
|
+
query getProduct($id: ID!) {
|
|
32
|
+
product(id: $id) {
|
|
33
|
+
id
|
|
34
|
+
title
|
|
35
|
+
images(first: 10) {
|
|
36
|
+
edges {
|
|
37
|
+
node {
|
|
38
|
+
url
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
options {
|
|
43
|
+
name
|
|
44
|
+
values
|
|
45
|
+
}
|
|
46
|
+
variants(first: 100) {
|
|
47
|
+
edges {
|
|
48
|
+
node {
|
|
49
|
+
id
|
|
50
|
+
title
|
|
51
|
+
availableForSale
|
|
52
|
+
price {
|
|
53
|
+
amount
|
|
54
|
+
currencyCode
|
|
55
|
+
}
|
|
56
|
+
image {
|
|
57
|
+
url
|
|
58
|
+
}
|
|
59
|
+
selectedOptions {
|
|
60
|
+
name
|
|
61
|
+
value
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
const fullProductId = productId.startsWith('gid://') ? productId : `gid://shopify/Product/${productId}`;
|
|
70
|
+
const response = yield fetch(`https://${shopifyDomain}/api/2024-01/graphql.json`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
'X-Shopify-Storefront-Access-Token': storefrontToken
|
|
75
|
+
},
|
|
76
|
+
body: JSON.stringify({
|
|
77
|
+
query,
|
|
78
|
+
variables: { id: fullProductId }
|
|
79
|
+
})
|
|
80
|
+
});
|
|
81
|
+
const result = yield response.json();
|
|
82
|
+
if (result.errors) {
|
|
83
|
+
throw new Error(result.errors[0].message);
|
|
84
|
+
}
|
|
85
|
+
setProductData(result.data.product);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
setError(err instanceof Error ? err.message : 'Failed to load product');
|
|
89
|
+
console.error('[AddToCartPopup] 加载失败:', err);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
setLoading(false);
|
|
93
|
+
}
|
|
94
|
+
}), [productId, shopifyDomain, storefrontToken]);
|
|
95
|
+
(0, react_1.useEffect)(() => {
|
|
96
|
+
if (isActive) {
|
|
97
|
+
fetchProductData();
|
|
98
|
+
}
|
|
99
|
+
}, [isActive, fetchProductData]);
|
|
100
|
+
(0, react_1.useEffect)(() => {
|
|
101
|
+
if (!productData)
|
|
102
|
+
return;
|
|
103
|
+
const variants = productData.variants.edges.map(edge => edge.node);
|
|
104
|
+
const optionsCount = productData.options.length;
|
|
105
|
+
const selectedCount = Object.keys(selectedOptions).length;
|
|
106
|
+
if (selectedCount === 0 || selectedCount < optionsCount) {
|
|
107
|
+
setSelectedVariant(null);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const matchedVariant = variants.find(variant => {
|
|
111
|
+
return variant.selectedOptions.every(option => {
|
|
112
|
+
return selectedOptions[option.name] === option.value;
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
setSelectedVariant(matchedVariant || null);
|
|
116
|
+
}, [selectedOptions, productData]);
|
|
117
|
+
const handleOptionSelect = (optionName, value) => {
|
|
118
|
+
setSelectedOptions(prev => (Object.assign(Object.assign({}, prev), { [optionName]: value })));
|
|
119
|
+
};
|
|
120
|
+
const handleAddToCart = () => {
|
|
121
|
+
if (!selectedVariant) {
|
|
122
|
+
alert('Please select all options');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const variantId = selectedVariant.id.split('/').pop();
|
|
126
|
+
const checkoutUrl = `https://${shopifyDomain}/cart/${variantId}:${quantity}`;
|
|
127
|
+
window.location.href = checkoutUrl;
|
|
128
|
+
};
|
|
129
|
+
const totalPrice = selectedVariant
|
|
130
|
+
? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
|
|
131
|
+
: '0.00';
|
|
132
|
+
const currency = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.price.currencyCode) || 'USD';
|
|
133
|
+
if (loading) {
|
|
134
|
+
return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
|
|
135
|
+
react_1.default.createElement("div", { className: "loading" }, "Loading...")));
|
|
136
|
+
}
|
|
137
|
+
if (error) {
|
|
138
|
+
return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
|
|
139
|
+
react_1.default.createElement("div", { className: "error" }, error)));
|
|
140
|
+
}
|
|
141
|
+
if (!productData) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const mainImage = ((_f = productData.images.edges[0]) === null || _f === void 0 ? void 0 : _f.node.url) || '';
|
|
145
|
+
const variantImage = ((_g = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _g === void 0 ? void 0 : _g.url) || mainImage;
|
|
146
|
+
return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
|
|
147
|
+
react_1.default.createElement("div", { className: "product-header" },
|
|
148
|
+
react_1.default.createElement("img", { src: mainImage, alt: productData.title, className: "product-thumb" }),
|
|
149
|
+
react_1.default.createElement("div", { className: "product-title" }, productData.title),
|
|
150
|
+
onClose && (react_1.default.createElement("button", { className: "close-btn", onClick: onClose }, "\u00D7"))),
|
|
151
|
+
react_1.default.createElement("div", { className: "variant-detail" },
|
|
152
|
+
react_1.default.createElement("div", { className: "variant-image-container" },
|
|
153
|
+
react_1.default.createElement("img", { src: variantImage, alt: "Selected variant", className: "variant-image" })),
|
|
154
|
+
react_1.default.createElement("div", { className: "variant-info" },
|
|
155
|
+
react_1.default.createElement("div", { className: "variant-specs" }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (react_1.default.createElement("span", { key: key, className: "spec-item" }, value)))) : (react_1.default.createElement("span", { className: "spec-placeholder" }, "Please select options"))),
|
|
156
|
+
react_1.default.createElement("div", { className: "variant-price-row" },
|
|
157
|
+
react_1.default.createElement("div", { className: "price" },
|
|
158
|
+
"$",
|
|
159
|
+
totalPrice),
|
|
160
|
+
react_1.default.createElement("div", { className: "quantity-selector" },
|
|
161
|
+
react_1.default.createElement("button", { className: "qty-btn", onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
|
|
162
|
+
react_1.default.createElement("span", { className: "qty-value" }, quantity),
|
|
163
|
+
react_1.default.createElement("button", { className: "qty-btn", onClick: () => setQuantity(quantity + 1) }, "+"))))),
|
|
164
|
+
react_1.default.createElement("div", { className: "variant-options" }, productData.options.map(option => (react_1.default.createElement("div", { key: option.name, className: "option-group" },
|
|
165
|
+
react_1.default.createElement("div", { className: "option-label" }, option.name),
|
|
166
|
+
react_1.default.createElement("div", { className: "option-values" }, option.values.map(value => {
|
|
167
|
+
const isAvailable = productData.variants.edges.some(({ node: variant }) => {
|
|
168
|
+
const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
|
|
169
|
+
return hasThisOption && variant.availableForSale;
|
|
170
|
+
});
|
|
171
|
+
const isSelected = selectedOptions[option.name] === value;
|
|
172
|
+
return (react_1.default.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
|
|
173
|
+
})))))),
|
|
174
|
+
react_1.default.createElement("button", { className: "add-to-cart-btn", onClick: handleAddToCart, disabled: !selectedVariant }, "Add To Cart")));
|
|
175
|
+
};
|
|
176
|
+
exports.default = AddToCartPopup;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FC, CSSProperties } from 'react';
|
|
2
|
+
import './index.less';
|
|
3
|
+
export interface IAddToCartPopupProps {
|
|
4
|
+
style?: CSSProperties;
|
|
5
|
+
isActive?: boolean;
|
|
6
|
+
index?: number;
|
|
7
|
+
shopifyDomain?: string;
|
|
8
|
+
storefrontAccessToken?: string;
|
|
9
|
+
variantStyles?: {
|
|
10
|
+
title?: CSSProperties;
|
|
11
|
+
price?: CSSProperties;
|
|
12
|
+
option?: CSSProperties;
|
|
13
|
+
selectedOption?: CSSProperties;
|
|
14
|
+
};
|
|
15
|
+
buttonStyle?: CSSProperties;
|
|
16
|
+
quantityStyle?: CSSProperties;
|
|
17
|
+
texts?: {
|
|
18
|
+
addToCart?: string;
|
|
19
|
+
selectOptions?: string;
|
|
20
|
+
loading?: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
color?: string;
|
|
23
|
+
size?: string;
|
|
24
|
+
material?: string;
|
|
25
|
+
style?: string;
|
|
26
|
+
};
|
|
27
|
+
popupBg?: {
|
|
28
|
+
horizontalMargin?: number;
|
|
29
|
+
bottomMargin?: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
declare const AddToCartPopup: FC<IAddToCartPopupProps>;
|
|
33
|
+
export default AddToCartPopup;
|