Women’s Neck-Hanging Printed Two-Piece Set — Boho Irregular Ruffle Top & Wide-Leg Pants
${function(){
const variant = data.variant;
const price = "59.99";
const priceMax = "59.99";
const saveType = "amount";
const saveText = "Save {off_ratio}";
return `
${saveText.replace('{off_ratio}', saveType == 'percent' ? variant.off_ratio + '%' : ` ` )}
` }()}
Color:
brown
Size:
S(US 6-8/UK 10-12/EU 38-40)
${data.value}
Add to cart
- $59.99
-
Buy now
SKU :
-brown-S(US 6-8/UK 10-12/EU 38-40)-A0022243
SKU :
${data.variant && data.variant.sku}
Weight :
${data.variant && data.variant.weight}${data.variant && data.variant.weight_unit}
Barcode :
${data.variant && data.variant.barcode}
Vendor :
${data.product && data.product.vendor}
${function() {
const minDays = parseInt('3');
const maxDays = parseInt('7');
const customText = "Estimated Delivery\uff1a{min_date} - {max_date}";
const minDate = new Date(Date.now() + (minDays * 86400000));
const maxDate = new Date(Date.now() + (maxDays * 86400000));
const formatDate = (minDate.getFullYear() == maxDate.getFullYear() && minDate.getFullYear() == new Date().getFullYear())
? new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit' })
: new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit', year: 'numeric' });
const tipText = customText.replace(/\{min_date\}/g, '' + formatDate.format(minDate) + ' ')
.replace(/\{max_date\}/g, '' + formatDate.format(maxDate) + ' ');
return `
`;
}()}
${function() {
const freeAmount = 160;
const totalPrice = +data.total_price;
const diffPrice = freeAmount - totalPrice;
if (diffPrice > 0) {
const text = "Buy {amount} more to enjoy \u003Cspan\u003EFREE\u003C\/span\u003E Shipping";
return text.replace('{amount}', ` `);
} else {
return "Your order is free delivery";
}
}()}
/** @private {string} */
class SpzCustomAnchorScroll extends SPZ.BaseElement {
static deferredMount() {
return false;
}
constructor(element) {
super(element);
/** @private {Element} */
this.scrollableContainer_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
this.viewport_ = this.getViewport();
this.initActions_();
}
setTarget(containerId, targetId) {
this.containerId = '#' + containerId;
this.targetId = '#' + targetId;
}
scrollToTarget() {
const container = document.querySelector(this.containerId);
const target = container.querySelector(this.targetId);
const {scrollTop} = container;
const eleOffsetTop = this.getOffsetTop_(target, container);
this.viewport_
.interpolateScrollIntoView_(
container,
scrollTop,
scrollTop + eleOffsetTop
);
}
initActions_() {
this.registerAction(
'scrollToTarget',
(invocation) => this.scrollToTarget(invocation?.caller)
);
this.registerAction(
'setTarget',
(invocation) => this.setTarget(invocation?.args?.containerId, invocation?.args?.targetId)
);
}
/**
* @param {Element} element
* @param {Element} container
* @return {number}
* @private
*/
getOffsetTop_(element, container) {
if (!element./*OK*/ getClientRects().length) {
return 0;
}
const rect = element./*OK*/ getBoundingClientRect();
if (rect.width || rect.height) {
return rect.top - container./*OK*/ getBoundingClientRect().top;
}
return rect.top;
}
}
SPZ.defineElement('spz-custom-anchor-scroll', SpzCustomAnchorScroll);
const STRENGTHEN_TRUST_URL = "/api/strengthen_trust/settings";
class SpzCustomStrengthenTrust extends SPZ.BaseElement {
constructor(element) {
super(element);
this.renderElement_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.xhr_ = SPZServices.xhrFor(this.win);
const renderId = this.element.getAttribute('render-id');
SPZCore.Dom.waitForChild(
document.body,
() => !!document.getElementById(renderId),
() => {
this.renderElement_ = SPZCore.Dom.scopedQuerySelector(
document.body,
`#${renderId}`
);
if (this.renderElement_) {
this.render_();
}
this.registerAction('track', (invocation) => {
this.track_(invocation.args);
});
}
);
}
render_() {
this.fetchData_().then((data) => {
if (!data) {
return;
}
SPZ.whenApiDefined(this.renderElement_).then((apis) => {
apis?.render(data);
document.querySelector('#strengthen-trust-render-1651799308132').addEventListener('click',(event)=>{
if(event.target.nodeName == 'A'){
this.track_({type: 'trust_content_click'});
}
})
});
});
}
track_(data = {}) {
const track = window.sa && window.sa.track;
if (!track) {
return;
}
track('trust_enhancement_event', data);
}
parseJSON_(string) {
let result = {};
try {
result = JSON.parse(string);
} catch (e) {}
return result;
}
fetchData_() {
return this.xhr_
.fetchJson(STRENGTHEN_TRUST_URL)
.then((responseData) => {
if (!responseData || !responseData.data) {
return null;
}
const data = responseData.data;
const moduleSettings = (data.module_settings || []).reduce((result, moduleSetting) => {
return result.concat(Object.assign(moduleSetting, {
logos: (moduleSetting.logos || []).map((item) => {
return moduleSetting.logos_type == 'custom' ? this.parseJSON_(item) : item;
})
}));
}, []);
return Object.assign(data, {
module_settings: moduleSettings,
isEditor: window.self !== window.top,
});
});
}
}
SPZ.defineElement('spz-custom-strengthen-trust', SpzCustomStrengthenTrust);
${data.module_title}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.
${item.content.replaceAll("{store_name}","thestylerebels")}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.
${function(){
return `
`
} ()}
${function(){
const { original_price, received_discounts, picked_qty } = data;
const button_text = originData.setting.button.text;
const button_config = JSON.parse(originData.setting.button.config);
const button_action = originData.setting.button.redirect_page;
const text = originData.setting.price_text_config.text;
const price_config = JSON.parse(originData.setting.price_text_config.config);
const price_text = text.replace(/\{picked_qty\}/g, `${picked_qty} `).replace(/\{original_price\}/g, ` `).replace(/\{received_discounts\}/g, ` `);
return `
${price_text}
${button_text}
`
}()}
${function(){
return `
`
} ()}
x ${data.discount_min_purchase_qty}
${function() {
const images = data.images || [];
const selectedVariant = data.variants.find(v => (v.available && v.is_hit_discount == true)) || data.variants[0];
const selectedIndex = !!selectedVariant ? images.findIndex(img => img.src === (selectedVariant.image && selectedVariant.image.src)) : 0;
const initialSlide = selectedIndex === -1 ? 0 : selectedIndex;
return `
${images.map((image, index) => `
`).join('')}
${images.map(image => { const imageWidth = image.width || 450; const imageHeight = image.height || 450; return ` ` }).join('')}
`;
}()}
${function() {
const productData = data;
const limit_purchase = data.limit_purchase || 0;
let product_change_event = '';
let mouse_over_event = '';
let mouse_out_event = '';
const product_options = productData.options.filter(Boolean) || [];
for (let opt of product_options) {
const nameEscape = opt.name.replace(/\/|\\|\s|\'|\"|`|\<|\>/g, '');
product_change_event = product_change_event + `apps-discount-quick-shop-selected-variant-${opt.id}.rerender(data=event.selectedValues.${opt.name});`;
mouse_out_event = mouse_out_event + `apps-discount-quick-shop-selected-variant-${opt.id}.rerender(data=event.selectData.${opt.name});`;
mouse_over_event = mouse_over_event + `@${nameEscape}Mouseover="apps-discount-quick-shop-selected-variant-${opt.id}.rerender(data=event);"`;
}
const selectedVariant = productData.variants.find(v => (v.available && v.is_hit_discount == true)) || productData.variants[0];
const statusLan = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available)) ?
"" :
"";
const status = (
(selectedVariant && !selectedVariant.available) ||
(!selectedVariant && !data.available)) ? 'soldout' : 'available';
return `
${data.title || data.brief}
Add ${productData.discount_min_purchase_qty} items for discount
`;
}()}
${function() {
const selectedVariant = data.variant || data.variants[0];
const image = selectedVariant.image || data.product.image;
const imageWidth = image?.width || 120;
const imageHeight = image?.height || 120;
return `
`
}()}
${function() {
const defaultVariant = data.variants?.find(v => (v.available && v.is_hit_discount == true)) || data.variants?.[0];
const selectedVariant = data.variant || defaultVariant;
const isHasRrice = (selectedVariant.price || selectedVariant.price == 0) ? true : false;
if(selectedVariant.flash_sale_info && selectedVariant.flash_sale_info.discount_price) {
selectedVariant.price = selectedVariant.flash_sale_info.discount_price;
}
if(data.flash_sale_info && data.flash_sale_info.discount_price) {
data.price = data.flash_sale_info.discount_price;
}
return !!selectedVariant ? `
` : `
-
`;
}()}
${function(){
const defaultVariant = data.variants?.find(v => (v.available && v.is_hit_discount == true)) || data.variants?.[0];
const selectedVariant = data.variant || defaultVariant;
let show_flashsale_limit_tip = false;
let limit_user_product_discount;
if(selectedVariant.flash_sale_info && selectedVariant.flash_sale_info?.limit_user_product_type != "LUPT_NO_LIMIT" && selectedVariant.flash_sale_info?.limit_user_product_discount > 0) {
show_flashsale_limit_tip = true;
limit_user_product_discount = selectedVariant.flash_sale_info?.limit_user_product_discount;
}
if(data.flash_sale_info && data.flash_sale_info?.limit_user_product_type != "LUPT_NO_LIMIT" && data.flash_sale_info?.limit_user_product_discount > 0) {
show_flashsale_limit_tip = true;
limit_user_product_discount = data.flash_sale_info?.limit_user_product_discount;
}
return `
Promo products limited to ${limit_user_product_discount} item per person
`
}()}
${function() {
const currentProduct = data.product;
return (currentProduct.options || []).map((option, index) => {
const optionName = option.name || '';
const optionId = option.id || '';
const position = `option${index + 1}`;
return `
${optionName}:
${option.values.map((value,idx) => {
const selected = data.product.defaultSelectValues?.[optionName] == value ? 'checked' : '';
return `
${value}
`;
}).join('')}
`;
}).join('')
}()}
${function() {
const value = (data.originData && data.originData.value) || data.value;
const isHasValue = value ? true : false;
return `
${value}
`
}()}
${(function() {
const productsInfo = data.product_info && data.product_info.product ? data.product_info.product : data;
return productsInfo.map(product => {
let price = product.price || 0;
let priceMin = product.price_min || 0;
let priceMax = product.price_max || 0;
let compareAtPriceMax = product.compare_at_price_max || 0;
let compareAtPrice = product.compare_at_price || 0;
let title = product.title || '';
let id = product.id || '';
let url = product.url || '';
let type = product.type || '';
let is_sold_out = false;
if (product.available == false && product.inventory_policy != 'continue') {
is_sold_out = true;
}
const soldOutLang1 = "";
const soldOutLang2 = "Sold out";
const discountType = data.discount_info ? data.discount_info.discount_type : product.discount_type;
const defaultVariantTitle = product.variants[0]?.title.replace('-', '/');
const variantDiscountInfo = product.variants[0]?.discount_info || {};
let discount_min_purchase_qty = 0;
if (discountType === "DT_CLASSIC_BUNDLE" && data.discount_info.enable_min_purchase_qty) {
discount_min_purchase_qty = data.discount_info.min_purchase_qty_type === "spu" ? product.discount_min_purchase_qty : product.variants[0].discount_info.discount_min_purchase_qty;
}
let imageWidth;
if (product.image.width) {
imageWidth = product.image.width;
} else {
imageWidth = "300px";
}
let imageHeight;
if (product.image.height) {
imageHeight= product.image.height;
} else {
imageHeight = "300px";
}
if (product.published) {
return `
${function(){
if (soldOutLang1){
return `
${soldOutLang1}
`
}else{
return `
${soldOutLang2}
`
}
}()}
x ${discount_min_purchase_qty}
`
} else {
return ``;
}
}).join('');
})()}
class SpzCustomDiscountBundleProducts extends SPZ.BaseElement {
constructor(element) {
super(element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.getDiscountPriceApi = "\/api\/storefront\/promotion\/calculate\/discounted_price";
this.buyNowApi = "\/api\/checkout\/order";
this.batchAtcApi = "\/api\/cart\/batch";
// 款式信息集合
this.productStyleInfo = [];
// 弹窗内选择款式集合
this.modalVariantInfo = [];
this.show_classic_bundle_spu_style = false;
this.bundleProducts = []; //捆绑商品
this.bundleConfig = {}; //下方按钮配置
this.discountId = "";
this.discountType = "";
this.discountInfo = "";
this.lineItems = [];
this.tempCss = {};
this.renderQuickShop_ = this.win.SPZCore.Types.debounce(this.win, this.renderQuickShopModal.bind(this), 500);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
this.setupAction_();
};
init(data = []) {
this.productStyleInfo = data;
}
handleRequestError_(data) {
this.showToast(data?.message || data?.errors?.[0] || 'Unknown error');
};
showToast(message) {
SPZ.whenApiDefined(document.getElementById("discount_toast")).then((apis) => {
apis.showToast(message);
});
}
//外部组件调用传值
setBundleData(products, config = "", id = "", type = "", info = {}) {
this.bundleProducts = products;
if(config) {
this.bundleConfig = config;
this.discountId = id;
this.discountType = type;
this.discountInfo = info;
this.show_classic_bundle_spu_style = type === 'DT_CLASSIC_BUNDLE' && info.enable_min_purchase_qty && info.min_purchase_qty_type == 'spu';
// 经典捆绑初始化商品数据
if(type == 'DT_CLASSIC_BUNDLE') {
this.productStyleInfo = products.map((item) => {
return this.getFilteredVariants_(item, 'single');
});
}
}
}
handleChangeSort() {
const result = this.productStyleInfo.reduce((map, item) => {
if (!map[item.product_id]) {
map[item.product_id] = [];
}
map[item.product_id].push(item);
return map;
}, {});
Object.values(result).forEach((item) => {
this.handleSpzVariantRender_(item, item[0].product_id);
this.handleProductOption_(item[0].product_id, true);
});
}
// 调用spz-tag组件的doRender方法
handleSpzVariantRender_(data, id) {
const spzVariantTag = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionSpzVariantTags-${id}`);
spzVariantTag && SPZ.whenApiDefined(spzVariantTag).then((api) => {
api.render(data, true);
});
}
// 执行经典捆绑最低购买数量更新
handleMinPurchaseQtyUpdate_(data, id) {
const minPruchaseQty = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionMinPurchaseQty-${id}`);
minPruchaseQty && SPZ.whenApiDefined(minPruchaseQty).then((api) => {
api.render(data, true);
});
}
// 更新价格
updateProductPrice_(data) {
const bottomBtnContainer = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionBottomContainer`);
if (data.length == 0) {
bottomBtnContainer && SPZ.whenApiDefined(bottomBtnContainer).then((api) => {
const renderInfo = {
setting: this.bundleConfig,
...{
original_price: 0,
received_discounts: 0,
picked_qty: 0
}
}
api.render({original_price: 0, received_discounts: 0}, true);
});
return;
}
const reqBody = {
discount_id: this.discountId,
customer: {
customer_id: '',
email: '',
},
sales_channel: {
sale_channel_type: "online",
sale_channel_id: '2387772'
},
line_items: data
}
// 如果已经有一个请求在等待,那么取消这个请求
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
this.debounceTimer = setTimeout(() => {
this.xhr_.fetchJson(this.getDiscountPriceApi, {
method: "post",
body: reqBody
}).then((res)=>{
// 更新商品列表价格
Object.keys(res.line_items).forEach((key) => {
const currentProductPrice = SPZCore.Dom.scopedQuerySelector(document.body, `#appDiscountProductPrice-${key}`);
currentProductPrice && SPZ.whenApiDefined(currentProductPrice).then((api) => {
api.render(res.line_items[key], true);
});
});
// 更新底部按钮总价/总折扣价
const picked_qty = data.reduce((acc, item) => {
return acc + item.quantity;
}, 0);
bottomBtnContainer && SPZ.whenApiDefined(bottomBtnContainer).then((api) => {
const data = {
setting: this.bundleConfig,
...{
...res.total_price,
picked_qty
}
}
api.render(data, true);
});
}).catch((err)=>{
this.handleRequestError_(err);
}).finally(()=>{
})
}, 100);
}
// 还原商品价格
resetProductPrice_(data) {
const {price, compare_at_price, id} = data;
const currentProductPrice = SPZCore.Dom.scopedQuerySelector(document.body, `#appDiscountProductPrice-${id}`);
currentProductPrice && SPZ.whenApiDefined(currentProductPrice).then((api) => {
api.render({total_received_discounts: price, total_price: compare_at_price}, true);
});
}
//处理与selector组件的交互
handleProductOption_(productId, show) {
const currentProductOption = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionSelectOption-${productId}`);
if (!currentProductOption) {
return;
};
currentProductOption.toggleAttribute('show', show);
const isSelected = currentProductOption.hasAttribute('selected');
// !show 取消选中
// !isSelected 选中商品
if (!show || !isSelected) {
const productSelector = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductSelector`);
productSelector && SPZ.whenApiDefined(productSelector).then((api) => {
api.toggle_({ option: productId, value: show });
});
}
}
// 混搭弹窗内的前端库存校验
handleModalInventoryCheck_(data) {
if(this.discountType == 'DT_MIX_MATCH_BUNDLE' || this.discountType == 'DT_CLASSIC_BUNDLE') {
const currentVariantAddNum = this.modalVariantInfo.find((item) => item.variant_id == data.variant_id)?.quantity || 0;
const quickShopBody = SPZCore.Dom.scopedQuerySelector(document.body, '#apps-discount-quick-shop-body');
if(!!data.variant && currentVariantAddNum == Number(data.variant.available_quantity)) {
quickShopBody && quickShopBody.setAttribute('status', 'soldout');
} else {
quickShopBody && quickShopBody.setAttribute('status', 'available');
}
} else {
return;
}
}
// 添加商品子款式
renderVariantTag() {
let variantInfo;
const quickShopBody = SPZCore.Dom.scopedQuerySelector(document.body, '#apps-discount-quick-shop-body');
quickShopBody && SPZ.whenApiDefined(quickShopBody).then((api) => {
variantInfo = api.getVariantsData();
const productId = variantInfo.product_id;
const variantId = variantInfo.variant_id;
const minPruchaseQtyRender = variantInfo.product.discount_min_purchase_qty || variantInfo.variant.discount_info.discount_min_purchase_qty;
if(this.discountType === 'DT_MIX_MATCH_BUNDLE') {
const index = this.productStyleInfo.findIndex((item) => item.variant_id == variantInfo.variant_id);
if (index != -1) {
this.productStyleInfo[index].quantity = Number(this.productStyleInfo[index].quantity) + Number(variantInfo.quantity);
this.updateProductPrice_(this.productStyleInfo);
} else {
this.productStyleInfo.push(this.getFilteredVariants_(variantInfo));
// 若当前商品已选中,更新商品价格
const currentProductOption = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionSelectOption-${productId}`);
const isSelected = currentProductOption && currentProductOption.hasAttribute('selected');
isSelected && this.updateProductPrice_(this.productStyleInfo);
}
const selectedVariantsFilter = this.productStyleInfo.filter((item) => item.product_id == productId);
this.handleSpzVariantRender_(selectedVariantsFilter, productId);
this.handleProductOption_(productId, true);
} else {
if(this.discountInfo.enable_min_purchase_qty == true && this.discountInfo.min_purchase_qty_type == 'spu' && minPruchaseQtyRender > 1) {
const index = this.modalVariantInfo.findIndex((item) => item.variant_id == variantId);
if (index != -1) {
this.modalVariantInfo[index].quantity = Number(this.modalVariantInfo[index].quantity) + 1;
} else {
this.modalVariantInfo.push(this.getFilteredVariants_(variantInfo, 'classic_spu'));
}
const modalVariantTag = SPZCore.Dom.scopedQuerySelector(document.body, '#promotionModalVariantTagRender');
modalVariantTag && SPZ.whenApiDefined(modalVariantTag).then((api) => {
api.render(this.modalVariantInfo, true);
});
this.handleModalInventoryCheck_(variantInfo);
const selectedVariantsNum = this.modalVariantInfo.reduce((acc, item) => {
return acc + item.quantity;
}, 0);
if(selectedVariantsNum == minPruchaseQtyRender) {
this.handleSpzVariantRender_([this.getFilteredVariants_(variantInfo)], productId);
this.productStyleInfo = this.productStyleInfo.filter((item) => item.product_id != productId).concat(this.modalVariantInfo);
const renderData = this.productStyleInfo.filter((item) => item.product_id == productId).map((item) => {
return {
...item,
is_classic_bundle_product_list_variant_tag: true
}
});
const classicSpuTag = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionClassicSpuTags-${productId}`);
classicSpuTag && SPZ.whenApiDefined(classicSpuTag).then((api) => {
api.render(renderData, true);
});
this.updateProductPrice_(this.productStyleInfo);
const quickView = SPZCore.Dom.scopedQuerySelector(document.body, '#apps-discount-quick-view');
quickView && SPZ.whenApiDefined(quickView).then((api)=>{
api.close();
});
this.modalVariantInfo = [];
} else {
return;
}
}
// this.productStyleInfo 中已存在与productId, variantId都相同的商品 则直接return 关闭弹窗
const isExist = this.productStyleInfo.some((item) => item.product_id == productId && item.variant_id == variantId);
if (isExist) {
const quickView = SPZCore.Dom.scopedQuerySelector(document.body, '#apps-discount-quick-view');
quickView && SPZ.whenApiDefined(quickView).then((api)=>{
api.close();
});
return;
}
// 若 this.productStyleInfo 中已存在与productId相同的商品,则不再添加 否则替换
const index = this.productStyleInfo.findIndex((item) => item.product_id == productId);
if (index != -1) {
this.productStyleInfo[index] = this.getFilteredVariants_(variantInfo);
} else {
this.productStyleInfo.push(this.getFilteredVariants_(variantInfo));
}
const selectedVariantsFilter = this.productStyleInfo.filter((item) => item.product_id == productId);
this.handleSpzVariantRender_(selectedVariantsFilter, productId);
this.handleMinPurchaseQtyUpdate_({discount_min_purchase_qty: minPruchaseQtyRender}, productId);
this.updateProductPrice_(this.productStyleInfo);
}
const quickView = SPZCore.Dom.scopedQuerySelector(document.body, '#apps-discount-quick-view');
quickView && SPZ.whenApiDefined(quickView).then((api)=>{
api.close();
});
});
}
// 单变体点击添加按钮
renderSingleVariant(data) {
const { product_id } = data;
const currentProduct = this.bundleProducts.find((product) => product.id == product_id);
// 若当前商品已存在,则不再添加 而是更新数量
const index = this.productStyleInfo.findIndex((item) => item.product_id == product_id);
if (index != -1) {
this.productStyleInfo[index].quantity = Number(this.productStyleInfo[index].quantity) + 1;
this.updateProductPrice_(this.productStyleInfo);
} else {
this.productStyleInfo.push(this.getFilteredVariants_(currentProduct, 'single'));
}
const renderProductArr = this.productStyleInfo.filter((item) => item.product_id == product_id);
this.handleSpzVariantRender_(renderProductArr, product_id);
this.handleProductOption_(product_id, true);
}
// 过滤选中商品的子款式 获取有用的信息 product_id,variant_id,price,compare_at_price,quantity,title,variant_title
getFilteredVariants_(data, type = '') {
const { id, title, variants, inventory_tracking, inventory_policy, inventory_quantity, product_type } = data;
const { product_id, variant_id, variant, quantity, product, discount_min_purchase_qty } = data;
const isSingle = type == 'single';
const variantData = isSingle ? (variants[0] || data) : variant;
const productData = isSingle ? data : product;
let item_quantity = 0;
if (this.discountType === 'DT_MIX_MATCH_BUNDLE') {
item_quantity = isSingle ? 1 : Number(quantity);
} else if (type === 'classic_spu') {
item_quantity = 1;
} else {
item_quantity = discount_min_purchase_qty || productData.discount_min_purchase_qty || variantData.discount_info.discount_min_purchase_qty || 1;
}
return {
product_id: isSingle ? id : product_id,
variant_id: variantData?.id || '',
price: variantData?.price || '0.00',
compare_at_price: variantData?.compare_at_price || '0.00',
quantity: item_quantity,
inventory_tracking: productData.inventory_tracking,
inventory_policy: productData.inventory_policy,
inventory_quantity: productData.inventory_quantity,
product_type: productData.product_type || this.bundleProducts.find((item) => item.id == product_id)?.product_type || this.bundleProducts.find((item) => item.id == id)?.product_type || '',
title: productData.title,
variant_title: variantData?.options.map((option) => option.value).join('/') || '',
is_multi_style: productData.variants.length > 1,
}
}
handleLoading_ (event) {
const { type, action } = event;
const loadingElementId = type === 'product' ? '#discount-match-drawer-products_loading' : '#apps-discount-whole-loading';
const loadingElement = document.querySelector(loadingElementId);
if (loadingElement) {
SPZ.whenApiDefined(loadingElement).then((api) => {
if (action === 'show') {
api.show_();
} else {
api.close_();
}
});
}
}
handleSelectProduct(productArr) {
// 从this.productStyleInfo 过滤出选中的商品
const selectedProducts = this.productStyleInfo.filter((item) => productArr.includes(item.product_id));
this.updateProductPrice_(selectedProducts);
}
// 渲染加购弹窗内容
async renderQuickShopModal(data){
this.handleLoading_({type: 'whole', action: 'show'});
this.xhr_.fetchJson(`/api/storefront/promotion/landing_page/product?product_id=${data.product_id}&discount_id=${this.discountId}&apply_scenario=1`, {
method: "get",
}).then(async(res)=>{
//flash主题放block有层级问题
if(/Flash/.test(window.C_SETTINGS.theme.merchant_theme_name) && document.querySelector(".productInfoSection")) {
this.tempCss.zIndex = document.querySelector(".product-info-body").style.zIndex;
document.querySelector('.product-info-body').style.zIndex="1048";
}
this.handleLoading_({type: 'whole', action: 'close'});
const $quickShop = await SPZ.whenApiDefined(document.querySelector('#apps-discount-quick-view-render'));
// 定义默认渲染的子款式
const selectedVariant = res.product.variants.find((v)=> (v.available && v.is_hit_discount)) || res.product.variants[0];
let selectedValues = {};
selectedVariant.options.length && selectedVariant.options.forEach(item => {
selectedValues[item.name] = item.value;
})
// 默认选中的 子款式、 options
res.product.defaultSelectValues = selectedValues;
let data = {...res.product, product:res.product, selectedVariant, show_classic_bundle_spu_style: this.show_classic_bundle_spu_style, discountType: this.discountType};
$quickShop.render(data);
// 打开加购弹窗
SPZ.whenApiDefined(document.querySelector(`#apps-discount-quick-view`)).then((api)=>{
api.open();
});
}).catch((err)=>{
this.handleLoading_({type: 'whole', action: 'close'});
})
}
// 获取选中的商品ids
getDefaultSelectorOptions_() {
try {
const selectedOptions = SPZCore.Dom.scopedQuerySelectorAll(document.body, '[id^="promotionSelectOption-"][selected]');
return SPZCore.Types.toArray(selectedOptions).map((item) => item.getAttribute('option'));
} catch (error) {
return [];
}
}
// 删除商品子款式
deleteVariantTag(data) {
const { product_id, variant_id } = data;
if(this.discountInfo.enable_min_purchase_qty == true && this.discountInfo.min_purchase_qty_type == 'spu') {
const modalProductVariants = this.modalVariantInfo.filter((item) => item.product_id == product_id && item.variant_id != variant_id);
const modalVariantTag = SPZCore.Dom.scopedQuerySelector(document.body, '#promotionModalVariantTagRender');
modalVariantTag && SPZ.whenApiDefined(modalVariantTag).then((api) => {
api.render(modalProductVariants, true);
});
this.handleModalInventoryCheck_(data);
this.modalVariantInfo = modalProductVariants;
return;
}
const currentProductVariants = this.productStyleInfo.filter((item) => item.product_id == product_id && item.variant_id != variant_id);
this.handleSpzVariantRender_(currentProductVariants, product_id);
// 更新selectedVariants
this.productStyleInfo = this.productStyleInfo.filter((item) => item.variant_id != variant_id);
if(currentProductVariants.length > 0) {
// currentProductVariants 中只要有一项是多款式商品,就更新价格
const isMultiStyle = currentProductVariants.some((item) => item.is_multi_style);
if (!isMultiStyle) return;
this.handleProductOption_(product_id, true);
const selected = this.getDefaultSelectorOptions_();
this.updateProductPrice_(this.productStyleInfo.filter((item) => selected.includes(item.product_id)));
} else {
this.handleProductOption_(product_id, false);
this.resetProductPrice_(this.bundleProducts.find((item) => item.id == product_id));
}
}
// 加购弹窗未参与活动 加购按钮不可点击 TODO 拆出来
handleNotHitDiscount_(data) {
const $quickShopBody = document.querySelector('#apps-discount-quick-shop-body');
//当前子框式未命中活动
if(data.variant.is_hit_discount == false) {
$quickShopBody.setAttribute('variantstatus', 'notHitDiscount')
} else {
$quickShopBody.setAttribute('variantstatus', '')
}
}
setupAction_() {
// 子款式 未参与活动
this.registerAction('handleNotHitDiscount', (invocation) => {
const data = invocation.args.data;
this.handleNotHitDiscount_(data);
});
// 渲染加购弹窗
this.registerAction('renderQuickShop', (invocation) => {
const data = invocation.args;
this.renderQuickShop_(data);
});
this.registerAction('renderSingleVariant', (invocation) => {
const data = invocation.args;
this.renderSingleVariant(data);
});
this.registerAction('getVariantInfo', (invocation) => {
this.renderVariantTag();
});
this.registerAction('deleteVariantTag', (invocation) => {
const data = invocation.args;
this.deleteVariantTag(data);
});
this.registerAction('getSelectedProduct', (invocation) => {
const data = invocation.args.data;
this.handleSelectProduct(data);
});
//TODO 加购下单逻辑单独拆组件
this.registerAction('handleClick', (data) => {
if(this.discountType == 'DT_CLASSIC_BUNDLE') {
this.lineItems = this.productStyleInfo;
} else {
const selectedOptions = SPZCore.Dom.scopedQuerySelectorAll(document.body, '[id^="promotionSelectOption-"]');
const idArr = [...selectedOptions].reduce((acc, item) => {
if (item.hasAttribute('selected')) {
const optionValue = item.getAttribute('option');
if (optionValue) {
acc.push(optionValue);
}
}
return acc;
}, []);
this.lineItems = this.productStyleInfo.filter((item) => idArr.includes(item.product_id));
}
const action = data.args.action === "cart";
if(action) {
//add to cart
this.xhr_
.fetchJson(this.batchAtcApi, {
method: 'POST',
body: {
line_items: this.lineItems.map((item) => {
return {
product_id: item.product_id,
variant_id: item.variant_id,
quantity: Number(item.quantity)
}
})
}
})
.then((data) => {
setTimeout(() => {
window.location.href = '/cart';
});
})
.catch(async (error) => {
await error.then((data) => {
this.handleRequestError_(data);
});
});
} else {
//checkout
this.xhr_
.fetchJson(this.buyNowApi, {
method: 'POST',
body: {
line_items: (this.lineItems || []).map((product) => {
return {
quantity: Number(product.quantity),
variant_id: product.variant_id,
note: product.note || '',
properties: product.properties || {}
}
}),
refer_info: { source: 'buy_now' }
}
})
.then(async (data) => {
if (data.state === 'success') {
window.location.href = data.data?.checkout_url;
}
this.handleRequestError_(data);
})
.catch(async (error) => {
await error.then((data) => {
this.handleRequestError_(data);
});
});
}
});
this.registerAction('resetModalVariantInfo', () => {
//flash主题放block有层级问题
if(/Flash/.test(window.C_SETTINGS.theme.merchant_theme_name) && document.querySelector(".productInfoSection")) {
document.querySelector('.product-info-body').style.zIndex = this.tempCss.zIndex;
}
this.modalVariantInfo = [];
});
this.registerAction('handleModalInventoryCheck', (invocation) => {
const data = invocation.args.data;
this.handleModalInventoryCheck_(data);
});
};
};
SPZ.defineElement('spz-custom-discount-bundle-products', SpzCustomDiscountBundleProducts);
${function(){
if(originData.discount_info.discount_id === "0") return;
const title = originData.product_setting.document.discount_text || '';
const title_color = JSON.parse(originData.product_setting.discount_text_config) || '';
const promotioUrl = '/promotions/discount-default/' + originData.discount_info.discount_id;
const is_redirection = originData.product_setting.is_redirection;
let dom = originData ? `
` : `
`;
return dom;
}()}
class SpzCustomDiscountBundle extends SPZ.BaseElement {
constructor(element) {
super(element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.variant_id = '9c5c3c97-db66-4015-adb6-d26ac5904037';
this.discountCardApi = "\/api\/storefront\/promotion\/product_details_page\/card";
this.productsApi = "\/api\/storefront\/promotion\/product_page\/product\/list";
this.bundleRenderElement = "appDiscountProductBundle";
this.model = {
loading: false,
page: 2,
limit: 20,
params: {
count: 0,
has_more: false,
sort: {
by: "price",
direction: "asc"
}
}
}
this.discountId = "";
this.discountType = "";
this.bundleProducts = []; //捆绑活动商品
this.buttomConfig = {};//总价及下方按钮配置
this.renderDiscount = this.win.SPZCore.Types.debounce(this.win, this.discountHandel.bind(this) , 500);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
async getDiscountCardList() {
const productId = '009acdf5-997c-4dc0-a8a1-244b727d10a2';
const productType = 'default';
const variantId = this.variant_id;
if(!productId || !variantId || !productType) {
return;
}
const reqBody = {
product_id: productId,
variant_id: variantId,
discount_types: ["DT_CLASSIC_BUNDLE","DT_MIX_MATCH_BUNDLE"],
discount_methods: ["DM_AUTOMATIC"],
customer: {
customer_id: '',
email: '',
},
product_type: productType
}
const data = await this.xhr_.fetchJson(this.discountCardApi, {
method: "post",
body: reqBody
}).then(res => {
return res;
}).catch(err => {
console.error(err);
})
return data;
};
async discountHandel() {
const $bundle = document.querySelector(".app-discount-bundle-inner");
$bundle && SPZCore.Dom.removeElement($bundle);
const data = await this.getDiscountCardList();
if(!data || !data.discount_info || data.discount_info.discount_id === "0") {
return;
}
//变量赋值
this.bundleProducts = data.product_info.product;
this.buttomConfig = data.product_setting;
this.discountId = data.discount_info.discount_id;
this.discountType = data.discount_info.discount_type;
this.model.params ={
count: data.product_info.count,
has_more: data.product_info.has_more,
sort: data.product_info.sort
}
//给捆绑组件传值
SPZ.whenApiDefined(document.getElementById("appDiscountBundleProductsFunc")).then((api) => {
api.setBundleData(this.bundleProducts, this.buttomConfig, this.discountId, this.discountType, data.discount_info);
})
document.querySelector(".app_discount_bundle").dataset.discountType = data.discount_info.discount_type;
SPZ.whenApiDefined(document.getElementById(this.bundleRenderElement))
.then(apis => {
apis.render(data,true).then(() => {
SPZ.whenApiDefined(document.getElementById("bundleProductsRender")).then((api) => {
api.render(data,true).then(() => {
this.bindEvent_();
if(this.bundleProducts.length < 5) {
document.querySelector(".app-discount-bundle-arrow-left").style.display="none";
document.querySelector(".app-discount-bundle-arrow-right").style.display="none";
}
//经典捆绑渲染按钮
if(this.discountType === "DT_CLASSIC_BUNDLE") {
SPZ.whenApiDefined(document.getElementById("promotionBottomContainer")).then((api) => {
const buttonData = {
setting: this.buttomConfig,
...data.product_info.total_price
}
api.render(buttonData, true);
})
}
})
})
})
.then(() => {
document.querySelector(".app-discount-bundle-inner").classList.add("discount_bundle_" + data.product_setting.template_type || "vertical");
});
});
//本地调试 放商详block里
const isSection = document.querySelector(
'div[data-section-type^="shoplazza://apps/publicapp/blocks/discount_bundle/"] .app_discount_bundle'
);
if(!isSection) {
document.querySelector(".app_discount_bundle").classList.add("productInfoSection");
}
};
// 获取加载的商品数据,拼接html模板
async loadData(cb) {
// 请求数据
this.model.loading = true;
//查询活动商品接口
const reqBody = {
discount_id: this.discountId,
page: this.model.page,
limit: this.model.limit,
"apply_scenario": "AS_ENTITLED_PRODUCT",
sort: this.model.params.sort,
sales_channel: {
sale_channel_type: "online",
sale_channel_id: '2387772'
},
product_id: '009acdf5-997c-4dc0-a8a1-244b727d10a2'
}
this.xhr_.fetchJson(this.productsApi, {
method: "post",
body: reqBody
}).then(async(res)=>{
const count = res.count;
this.model.params.has_more = res.has_more;
if (count > 0) {
this.model.page++;
if (res.products && res.products.length > 0) {
let products = res.products.map((product) => {
return {
...product,
url: appDiscountUtils.globalizePath(product.url),
image_padding_bottom: appDiscountUtils.image_padding_bottom(product.image.width, product.image.height,'no-limit'),
discount_type: this.discountType
}
});
// 获取商品列表渲染模板, dom挂载
const $content = document.querySelector(".app-discount-bundle-products");
this.templates_ = SPZServices.templatesForDoc();
this.templates_.renderTemplate(document.querySelector('#appDiscountBundleProductsTemplate'), products).then((el) => {
const childNodes = el.querySelectorAll('.as-render-product-item');
if (childNodes && childNodes.length > 0) {
$content.append(...childNodes);
}
}).then(() => {
//重新渲染ljs-selector
const productSelector = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductSelector`);
productSelector && SPZ.whenApiDefined(productSelector).then((api) => {
api.init();
});
});
this.bundleProducts = [...this.bundleProducts, ...res.products];
SPZ.whenApiDefined(document.getElementById("appDiscountBundleProductsFunc")).then((api) => {
api.setBundleData(this.bundleProducts);
})
// 监听load去掉灰色背景
document.dispatchEvent(new CustomEvent('fire.load.img'));
// 触发懒加载
cb && cb(products);
window.lazyLoadInstance && window.lazyLoadInstance.update();
}
}
this.model.loading = false;
}).catch((err)=>{
console.error(err);
this.model.loading = false;
})
};
setupAction_() {
this.registerAction('shiftMove', (data) => {
const $el = document.querySelector(".app-discount-bundle-products");
const action = data.args.direct === "right";
const scrollwidth = action ? $el.offsetWidth : -$el.offsetWidth;
$el.scrollBy({
left: scrollwidth,
behavior: 'smooth'
});
});
};
bindEvent_() {
// 监听子款式切换,重新渲染
document.addEventListener('dj.variantChange', async(event) => {
const variant = event.detail.selected;
if (variant.product_id == '009acdf5-997c-4dc0-a8a1-244b727d10a2') {
this.variant_id = variant.id;
}
this.renderDiscount();
});
// 监听滚动,请求数据
const $el = document.querySelector(".app-discount-bundle-products");
if($el) {
$el.addEventListener("scroll", this.win.SPZCore.Types.debounce(
this.win,
() => {
const isLeft = $el.scrollLeft === 0;
const isRightEnd = $el.scrollLeft + $el.offsetWidth + 10 >= $el.scrollWidth;
const isBottomEnd = $el.scrollTop + $el.clientHeight + 10 >= $el.scrollHeight;
const isEnd = isBottomEnd && isRightEnd;
if(isEnd && this.model.params.has_more && !this.model.loading) {
this.loadData();
}
},
50
))
};
};
buildCallback() {
this.setupAction_();
};
mountCallback() {
this.renderDiscount();
this.bindEvent_();
};
}
SPZ.defineElement('spz-custom-discount-bundle', SpzCustomDiscountBundle);
${(function () {
const automatic_discount_list = originData.automatic_discount_list;
// 显示类型
const DISPLAY_TYPE = {
DTE_FOLD: 'DTE_FOLD', // 折叠
DTE_TILE: 'DTE_TILE' // 平铺
}
const DEFAULT_CONFIG = {
BG: 'rgba(235, 57, 27, 0.04)',
TEXT_COLOR: '#EB391B',
BORDER_COLOR: 'rgb(235, 57, 27)',
ADD_TO_CART_BG: 'transparent',
ADD_TO_CART_TEXT_COLOR: 'rgb(235, 57, 27)',
ADD_TO_CART_BORDER_COLOR: 'rgb(235, 57, 27)',
};
const isExist = automatic_discount_list?.length > 0 && automatic_discount_list.some(item => item.discount[0].product_enabled);
// 如果没有任何自动折扣,则隐藏,防止gap占位
if (!isExist) {
return `
`;
} else {
return `
${(function () {
return automatic_discount_list.map((item) => {
// 模版类型
const template_type = item.discount[0].template_type;
// 是否显示自动折扣
const is_show_automatic_discount = item.discount[0].product_enabled;
// 是否跳转落地页
const is_redirection = item.discount[0].is_redirection;
// 折扣图标
const discount_icon = item.discount_icon;
// 第一个自动折扣
const first_automatic_discount = item.discount[0];
// 显示折叠展示
const isFold = (item.discount[0].display_type || DISPLAY_TYPE.DTE_FOLD) === DISPLAY_TYPE.DTE_FOLD;
// 文本数组
const text_arr = item.discount[0].config.texts;
// 条件值数组
const condition_values = item?.discount[0]?.condition_values || [];
// 落地页链接
const first_landing_url = `/promotions/discount-default/${first_automatic_discount.discount_id}`;
// 自动折扣总数
const automatic_discount_total = item.discount.length;
// 是否显示折扣图标
const isHasDiscountIcon = discount_icon ? true : false;
// 是否显示折扣图标且模版类型不为tag和add_to_cart
const isHasDiscountIconWithNoTag = (template_type != 'tag' && template_type != 'add_to_cart' && isHasDiscountIcon)? true : false;
// 文本颜色
let text_color = DEFAULT_CONFIG.TEXT_COLOR;
// 背景颜色
const bgFn = (curBg) => template_type === "text" ? "transparent" : curBg;
let bg_color = bgFn(DEFAULT_CONFIG.BG);
// 边框颜色
const borderFn = (curBorder) => template_type == "tag" ? curBorder : "initial";
let border_color = borderFn(DEFAULT_CONFIG.BORDER_COLOR);
// 模版配置
let template_config = first_automatic_discount.template_config;
// 一键加购样式
let addToCartBtnBgColor = DEFAULT_CONFIG.ADD_TO_CART_BG;
let addToCartBtnTextColor = DEFAULT_CONFIG.ADD_TO_CART_TEXT_COLOR;
let addToCartBtnBorderColor = DEFAULT_CONFIG.ADD_TO_CART_BORDER_COLOR;
// 兜底方案
try {
if(template_config.length !== 0){
template_config = JSON.parse(template_config);
text_color= isHasDiscountIconWithNoTag ? template_config.color[template_type].icon_text_color : template_config.color[template_type].text_color;
bg_color = bgFn(template_config.color[template_type].background_color);
const arrayRgba = bg_color.split(",");
arrayRgba.splice(3, 1, " 1)");
border_color = borderFn(`${arrayRgba.join(",")}`);
addToCartBtnTextColor = template_config.color[template_type].button_text_color;
addToCartBtnBorderColor = template_config.color[template_type].button_border_color;
addToCartBtnBgColor = template_config.color[template_type].button_background_color;
}
} catch (error) {
console.error('template_config_error', error);
template_config = {
color: {
[template_type]: {
icon_text_color: DEFAULT_CONFIG.TEXT_COLOR,
text_color: DEFAULT_CONFIG.TEXT_COLOR,
background_color: DEFAULT_CONFIG.BG
}
}
};
}
// 标签
const isTag = template_type == 'tag';
// 文字和横幅
const isTextAndBanner = template_type == 'text' || template_type == 'banner';
// 是否一键加购
const isAddToCart = template_type == 'add_to_cart';
// 文字样式
const textStyle = `color: ${text_color}; background-color: transparent; border: none;`;
// 标签样式
const labelStyle = `color: ${text_color};border: 1px solid ${border_color};background-color:${bg_color};padding: 4px;`;
// 横幅样式
const bannerStyle = `color: ${text_color};border: none; background-color:${bg_color}`;
// 一键加购单个容器样式(只包含动态样式)
const addToCartSingleContainerStyle = `color: ${text_color}; background-color:${bg_color};`;
// 一键加购按钮样式(只包含配置的动态样式)
const addToCartBtnStyle = `color: ${addToCartBtnTextColor}; background-color:${addToCartBtnBgColor}; border:1px solid ${addToCartBtnBorderColor};`;
// 外层样式在标签样式下不展示颜色配置,除开标签类型,颜色都可以在外层覆盖
let outerStyle = '';
if (template_type == 'text') {
outerStyle = textStyle;
} else if (template_type == 'tag') {
outerStyle = "border: none;";
} else if (template_type == 'banner') {
outerStyle = bannerStyle;
}
/**
* 1. 标签一定是单独样式展示的
* 2. 折叠:横向布局,文字和横幅,合并成一行文本; 标签:单独样式处理
* 3. 平铺:纵向布局,文字、横幅和标签; 标签:单独样式处理;一键加购默认都是平铺
*/
let txtHtml = ``;
if (isFold) {
if(isTag) {
// 标签
const spanText = text_arr.map((text) => {
return `
${text} `;
}).join('');
txtHtml = `
${spanText}
`;
} else {
// 文字和横幅
txtHtml = `
${first_automatic_discount.config.text}
`;
}
} else {
// 文字和横幅, 但标签有自己的样式,且一键加购有单独样式
const spanText = text_arr.map((text, index) => {
const condition_value = condition_values[index];
if(isAddToCart) {
return `
${text}
`
} else {
return `
${text} `;
}
}).join('');
// 都是纵向布局,标签有间距
txtHtml = `
${spanText}
`;
}
/**
* 显示图标的判断
*/
const discount_type = item.discount_type;
const isShowRebateIcon = ["DT_REBATE_CTQ_OTP", "DT_REBATE_CTQ_OTR", "DT_REBATE_CTA_OTP", "DT_REBATE_CTA_OTR", "DT_M_N_DISCOUNT"].includes(discount_type) && isTextAndBanner
const isShowBxgyIcon = ["DT_BUY_ONE_GET_ONE", "DT_BUY_X_GET_Y"].includes(discount_type)
const isShowBundleIcon = ["DT_CLASSIC_BUNDLE","DT_MIX_MATCH_BUNDLE"].includes(discount_type);
/**
* 渲染下拉框或抽屉的折扣列表
*/
const discount_list_html = (curItem) => {
return `
${function() {
return curItem.discount.map(childItem => {
return `
`}).join('');
}()}
`;
}
return `
${discount_list_html(item)}
${function() {
return text_arr.map((text) => {
return `
${text}
`;
}).join('');
}()}
`;
}).join('');
})()}
`
}
})()}
const TAG = "spz-custom-product-automatic";
class SpzCustomProductAutomatic extends SPZ.BaseElement {
constructor(element) {
super(element);
this.variant_id = '9c5c3c97-db66-4015-adb6-d26ac5904037';
this.isRTL = SPZ.win.document.dir === 'rtl';
this.isAddingToCart_ = false; // 加购中状态
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.init();
// 监听事件
this.bindEvent_();
}
async init() {
this.handleFitTheme();
const data = await this.getDiscountList();
this.renderApiData_(data);
}
async getDiscountList() {
const productId = '009acdf5-997c-4dc0-a8a1-244b727d10a2';
const variantId = this.variant_id;
const productType = 'default';
const reqBody = {
product_id: productId,
variant_id: variantId,
discount_method: "DM_AUTOMATIC",
customer: {
customer_id: window.C_SETTINGS.customer.customer_id,
email: window.C_SETTINGS.customer.customer_email
},
product_type: productType
}
const url = `/api/storefront/promotion/display_setting/text/list`;
const data = await this.xhr_.fetchJson(url, {
method: "post",
body: reqBody
}).then(res => {
return res;
}).catch(err => {
this.setContainerDisabled(false);
})
return data;
}
async renderDiscountList() {
this.setContainerDisabled(true);
const data = await this.getDiscountList();
this.setContainerDisabled(false);
// 重新渲染 抖动问题处理
this.renderApiData_(data);
}
clearDom() {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
}
async renderApiData_(data) {
const parentDiv = document.querySelector('.automatic_discount_container');
const newTplDom = await this.getRenderTemplate(data);
if (parentDiv) {
parentDiv.innerHTML = '';
parentDiv.appendChild(newTplDom);
} else {
console.log('automatic_discount_container is null');
}
}
doRender_(data) {
const renderData = data || {};
return this.templates_
.findAndRenderTemplate(this.element, renderData)
.then((el) => {
this.clearDom();
this.element.appendChild(el);
});
}
async getRenderTemplate(data) {
const renderData = data || {};
return this.templates_
.findAndRenderTemplate(this.element, { ...renderData, isRTL: this.isRTL })
.then((el) => {
this.clearDom();
return el;
});
}
setContainerDisabled(isDisable) {
const automaticDiscountEl = document.querySelector('.automatic_discount_container_outer');
if(isDisable) {
automaticDiscountEl.setAttribute('disabled', '');
} else {
automaticDiscountEl.removeAttribute('disabled');
}
}
// 绑定事件
bindEvent_() {
window.addEventListener('click', (e) => {
let containerNodes = document.querySelectorAll(".automatic-container .panel");
let bool;
Array.from(containerNodes).forEach((node) => {
if(node.contains(e.target)){
bool = true;
}
})
// 是否popover面板点击范围
if (bool) {
return;
}
if(e.target.classList.contains('drowdown-icon') || e.target.parentNode.classList.contains('drowdown-icon')){
return;
}
const nodes = document.querySelectorAll('.automatic-container');
Array.from(nodes).forEach((node) => {
node.classList.remove('open-dropdown');
})
// 兼容主题
this.toggleProductSticky(true);
})
// 监听变体变化
document.addEventListener('dj.variantChange', async(event) => {
// 重新渲染
const variant = event.detail.selected;
if (variant.product_id == '009acdf5-997c-4dc0-a8a1-244b727d10a2' && variant.id != this.variant_id) {
this.variant_id = variant.id;
this.renderDiscountList();
}
});
}
// 兼容主题
handleFitTheme() {
// top 属性影响抖动
let productInfoEl = null;
if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') {
productInfoEl = document.querySelector('.product-info-body .product-sticky-container');
} else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') {
productInfoEl = document.querySelector('.product__info-wrapper .properties-content');
}
if(productInfoEl){
productInfoEl.classList.add('force-top-auto');
}
}
// 兼容 wind/flash /hero 主题 (sticky属性影响 popover 层级展示, 会被其他元素覆盖)
toggleProductSticky(isSticky) {
let productInfoEl = null;
if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') {
productInfoEl = document.querySelector('.product-info-body .product-sticky-container');
} else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') {
productInfoEl = document.querySelector('.product__info-wrapper .properties-content');
}
if(productInfoEl){
if(isSticky) {
// 还原该主题原有的sticky属性值
productInfoEl.classList.remove('force-position-static');
return;
}
productInfoEl.classList.toggle('force-position-static');
}
}
setupAction_() {
this.registerAction('handleDropdown', (invocation) => {
const discount_id = invocation.args.discount_id;
const nodes = document.querySelectorAll('.automatic-container');
Array.from(nodes).forEach((node) => {
if(node.getAttribute('id') != `automatic-${discount_id}`) {
node.classList.remove('open-dropdown');
}
})
const $discount_item = document.querySelector(`#automatic-${discount_id}`);
$discount_item && $discount_item.classList.toggle('open-dropdown');
// 兼容主题
this.toggleProductSticky();
});
// 加购事件
this.registerAction('handleAddToCart', (invocation) => {
// 阻止事件冒泡
const event = invocation.event;
if (event) {
event.stopPropagation();
event.preventDefault();
}
// 如果正在加购中,直接返回
if (this.isAddingToCart_) {
return;
}
const quantity = invocation.args.quantity || 1;
this.addToCart(quantity);
});
}
// 加购方法
async addToCart(quantity) {
// 设置加购中状态
this.isAddingToCart_ = true;
const productId = '009acdf5-997c-4dc0-a8a1-244b727d10a2';
const variantId = this.variant_id;
const url = '/api/cart';
const reqBody = {
product_id: productId,
variant_id: variantId,
quantity: quantity
};
try {
const data = await this.xhr_.fetchJson(url, {
method: 'POST',
body: reqBody
});
// 触发加购成功提示
this.triggerAddToCartToast_();
return data;
} catch (error) {
error.then(err=>{
this.showToast_(err?.message || err?.errors?.[0] || 'Unknown error');
})
} finally {
// 无论成功失败,都重置加购状态
this.isAddingToCart_ = false;
}
}
showToast_(message) {
const toastEl = document.querySelector("#apps-match-drawer-add_to_cart_toast");
if (toastEl) {
SPZ.whenApiDefined(toastEl).then((apis) => {
apis.showToast(message);
});
}
}
// 触发加购成功提示
triggerAddToCartToast_() {
// 如果主题有自己的加购提示,则不显示
const themeAddToCartToastEl = document.querySelector('#add-cart-event-proxy');
if (themeAddToCartToastEl) return;
// 显示应用的加购成功提示
this.showToast_("Added successfully");
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomProductAutomatic);
class SpzCustomDiscountBundle extends SPZ.BaseElement {
constructor(element) {
super(element);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
mountCallback() {}
unmountCallback() {}
setupAction_() {
this.registerAction('showAddToCartToast', () => {
const themeAddToCartToastEl = document.querySelector('#add-cart-event-proxy')
if(themeAddToCartToastEl) return
const toastEl = document.querySelector('#apps-match-drawer-add_to_cart_toast')
SPZ.whenApiDefined(toastEl).then((apis) => {
apis.showToast("Added successfully");
});
});
}
buildCallback() {
this.setupAction_();
};
}
SPZ.defineElement('spz-custom-discount-toast', SpzCustomDiscountBundle);
The current produc does not participate any Rebate. Switch the participating product to check the design.
(This prompt will not be displayed on the client-side.)
${function() {
const show = rebate.format_rules.length > data.maxShowCount;
return `
${rule}
${function() {
if(!show) return "";
return `
`;
}()}
`;
}()}
${originData.rebate.format_rules.join(", ")}
var theme = window.C_SETTINGS && C_SETTINGS.theme && C_SETTINGS.theme.merchant_theme_name;
var isFlash = /Flash/gi.test(theme);
var isGeek = /Geek/gi.test(theme);
var isNova23 = /Nova 2023/gi.test(theme);
var isWind = /Wind/gi.test(theme);
var isOnePage = /OnePage/gi.test(theme);
var isHero = /Hero/gi.test(theme);
var isBoost = /Boost/gi.test(theme);
var isEva = /Eva/gi.test(theme);
var isFarida = /Farida/gi.test(theme);
var isPluto = /Pluto/gi.test(theme);
var isLifeStyle = /Life Style/gi.test(theme);
if(window.self === window.top) {
(window.disabled_exts ||=[]).push('product_detail_rebate');
}
class SpzRebateComponent extends SPZ.BaseElement {
constructor(element) {
super(element);
}
xhr_ = SPZServices.xhrFor(this.win);
viewport_ = this.getViewport();
action_ = null;
lang = document.documentElement.lang || 'en-US';
landPage = "\/promotions\/rebate\/";
pageType = 1;
cart = [];
initData = null;
rebateInfo = null;
renderData = null;
footerImage = `${this.win.SHOPLAZZA["image_domain"]}oss/operation/e8ebb03dbb710457ca3b4b6a70898ab2.svg`;
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
this.initData = this.getProduct();
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.registerAction("triggerGetRenderData", () => {
const event = SPZUtils.Event.create(this.win, "triggerGetRenderData", this.renderData);
this.action_.trigger(this.element, "getRenderData", event);
});
this.registerAction("bindPropagation", () => {
document.querySelector(".product_detail_rebate_list").addEventListener("click", e => {
e.stopPropagation();
this.win.sa &&
this.win.sa.track("plugin_rebate_promotion_click", {
plugin_timestamp: Date.now(),
plugin_location: "info",
product_id: this.initData.product.id,
discount_id: this.rebateInfo.discount_list.map((item) => item.discount_id)[0],
});
});
});
}
async mountCallback() {
document.addEventListener("dj.variantChange", e => {
const data = e.detail;
if (document.querySelector("#product-select-modal.show")) return;
this.initData = this.getProductJson(data);
if (this.initData && this.initData.product && data.product && this.initData.product.id === data.product.id) {
this.initRebate(this.initData, true);
} else {
this.getRebateInfo();
}
});
document.addEventListener("dj.addToCart", e => {
const v = e.detail;
this.rebateInfo &&
this.win.sa &&
this.win.sa.track("plugin_rebate_atc", {
variant_discount_id: this.getVariantDiscountId(v.variant_id).map(item => item.discount_id),
discount_ids: this.rebateInfo.discount_list.map(item => item.discount_id),
variant_id: v.variant_id,
product_id: v.product_id,
price: v.item_price,
number: v.number,
});
});
await this.getRebateInfo();
setTimeout(()=>{
if (document.querySelector(".plugin-container__bottom-fixed")) {
this.showDiscountPopupsInfoBar();
} else {
this.win.addEventListener("extloaded", () => {
this.showDiscountPopupsInfoBar();
});
}
},1000)
}
getProductJson = (mergeData = {}) => {
const productJson = document.querySelector("#product-json");
let productJsonData = {};
if (productJson && productJson.textContent) {
try {
productJsonData = JSON.parse(productJson.textContent);
} catch (e) {}
}
// 深度合并函数
const deepMerge = (target, source) => {
if (source === null || source === undefined) {
return target;
}
if (typeof source !== 'object' || Array.isArray(source)) {
return source;
}
const result = { ...target };
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (
typeof source[key] === 'object' &&
source[key] !== null &&
!Array.isArray(source[key]) &&
typeof target[key] === 'object' &&
target[key] !== null &&
!Array.isArray(target[key])
) {
result[key] = deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
}
return result;
};
return deepMerge(productJsonData, mergeData);
}
getProduct = (() => {
document.addEventListener("dj.variantChange", e => {
if (!e.detail || !e.detail.product) return;
let productJsonData = getProductJson(e.detail);
if (this.win.jQuery && this.win.jQuery.fn && this.win.jQuery(document).data("djproduct") && productJsonData) {
this.win.jQuery(document).data("djproduct", productJsonData);
}
});
return () => {
let productData = null;
if (this.win.jQuery && this.win.jQuery.fn) {
try {
let product = this.win.jQuery(document).data("djproduct");
if (product) {
productData = JSON.parse(JSON.stringify(product));
} else {
productData = null;
}
} catch (error) {
productData = null;
}
}
if (!productData) {
const productJson = document.querySelector("#product-json");
productData = (productJson && productJson.textContent && JSON.parse(productJson.textContent)) || null;
}
return productData;
};
})();
clearRebateInfoDom = () => {
// 1. 清除 apis.render 渲染的 DOM (app_rebate_section)
const rebateSection = document.getElementById('app_rebate_section');
if (rebateSection) {
const rebateRuleSection = rebateSection.querySelector('.rebate_rule_section');
if (rebateRuleSection) {
rebateRuleSection.remove();
}
}
// 2. 清除 templates_.renderTemplate 渲染的 DOM (app_rebate_block)
const rebateBlock = document.getElementById('app_rebate_block');
if (rebateBlock) {
const appRebateList = rebateBlock.querySelector('.app_rebate_list');
if (appRebateList) {
appRebateList.remove();
}
}
// 3. 清除 insertProductDetailRebateTag 插入的 DOM
// 清除所有 rebate-tag 元素
document.querySelectorAll('.slider-discount-tag.dj_skin_product_title.rebate-tag').forEach(tag => {
tag.remove();
});
// 移除 data-rebate-tag 属性
const productContainer = document.querySelector('.product-details, .product-details, .page_container, .product-images, [data-section-type="product"]');
if (productContainer && productContainer.hasAttribute('data-rebate-tag')) {
productContainer.removeAttribute('data-rebate-tag');
}
};
initRebate = this.win.SPZCore.Types.debounce(
this.win,
(async (data) => {
let discount_list = Object.assign([], this.rebateInfo?.discount_list);
/* 按子商品的多少对优惠信息进行排序 */
discount_list &&
discount_list.sort((a, b) => {
return b.variant_ids.length - a.variant_ids.length;
});
/* 选中子商品时 筛选子商品的优惠信息 */
if (data.selected && data.selected.id) {
discount_list = this.getVariantDiscountId(data.selected.id);
}
/* 无满减信息 */
if (!(discount_list && discount_list.length)) {
this.clearRebateInfoDom();
return;
}
const isSection = !!document.querySelector(
`div[data-section-type^="shoplazza://apps/publicapp/blocks/rebate"] #rebate_custom_component`
);
if (
(this.rebateInfo.rebate_type == "sku" && data && data.selected && data.selected.id) ||
this.rebateInfo.rebate_type == "spu"
) {
let nowLandpage = this.landPage;
if (discount_list[0]) {
nowLandpage = this.landPage + discount_list[0].discount_id || "";
}
const info = {
rebate: discount_list[0],
maxShowCount: this.win.innerWidth > 768 ? 3 : 1,
landPage: nowLandpage,
modalFooterImg: `url(${`${this.win.SHOPLAZZA["image_domain"]}oss/operation/e8ebb03dbb710457ca3b4b6a70898ab2.svg`})`,
};
this.renderData = info;
if(isSection) {
SPZ.whenApiDefined(
document.getElementById("app_rebate_section")
).then(apis => {
apis.render(info, true);
});
} else {
// 重新渲染 抖动问题处理
this.templates_ = SPZServices.templatesForDoc();
const newTplDom = await this.templates_.renderTemplate(document.querySelector('#appRebateBlockTpl'), info)
const parentDiv = document.querySelector('#app_rebate_block');
const oldDom = parentDiv.querySelector('.app_rebate_list');
if(oldDom){
parentDiv.replaceChild(newTplDom, oldDom);
} else {
parentDiv.appendChild(newTplDom);
}
}
}
this.insertProductDetailRebateTag(this.rebateInfo.tag);
var pluginCurrencyEvent = new CustomEvent("plugin_currency_update");
document.dispatchEvent(pluginCurrencyEvent);
}).bind(this),
10
);
getRebateInfo = async () => {
if (this.initData && this.initData.product && this.initData.product.id) {
var variant_ids = this.initData.product.variants.map(variant => variant.id);
const res = await this.xhr_.fetchJson(
"\/api\/discount-rebate\/product-discount",
{
method: "POST",
body: {
product_id: this.initData.product.id,
product_type: this.initData.product.product_type,
variant_ids: variant_ids,
},
}
);
if (!SPZCore.Types.isEmptyObject(res.rebate_info)) {
res.rebate_info.tag = res.tag;
res.rebate_info.rebate_type = res.rebate_type;
this.rebateInfo = res.rebate_info;
this.initRebate(this.initData);
} else {
if (this.win.top !== this.win.self) {
const noActivity = document.getElementById("no-rebate-activity");
noActivity && (noActivity["style"].display = "block");
}
}
}
};
getVariantDiscountId = (variant_id) => {
if (!variant_id || !this.rebateInfo) return [];
var rebateId = this.rebateInfo.variant_discount_map[variant_id];
return this.rebateInfo.discount_list.filter(item => item.discount_id == rebateId) || [];
};
insertProductDetailRebateTag = (tag) => {
if (!tag) return
// 旧判断逻辑
const productSelectModal = document.querySelector('#product-select-modal');
if (productSelectModal && productSelectModal.classList.contains('show')) {
return;
}
setTimeout(() => {
var $tag_container = [];
if (isNova23) {
$tag_container = document.querySelectorAll('.product-details .product-images-container');
} else if (isFlash) {
$tag_container = document.querySelectorAll('.product-detail .product-images .product-main-images-container');
} else if (isGeek) {
$tag_container = document.querySelectorAll('.product-images #product-images-inner-container spz-carousel .i-spzhtml-slide-item');
} else if (isWind) {
$tag_container = document.querySelectorAll('.product-detail .product-images-container .i-spzhtml-slides-container');
} else if (isOnePage) {
$tag_container = document.querySelectorAll('.product-details .product-main-images');
} else if (isHero) {
$tag_container = document.querySelectorAll('.product-detail #product-images-container #product-images-carousel .spz-carousel-slide');
} else if (isBoost) {
$tag_container = document.querySelectorAll('.boost-product-detail .product-image__layout-list .slides .slides-item .product-info__slide .slider-zoom');
} else if (isEva) {
$tag_container = document.querySelectorAll('.page_container [data-section-type="product"] .support-slick');
} else if (isFarida) {
$tag_container = document.querySelectorAll('.product-details .product-images-container');
} else if (isLifeStyle) {
$tag_container = document.querySelectorAll('.page_container [data-section-type="product_detail"] .sep-slider,.support-slick');
} else if (isPluto) {
$tag_container = document.querySelectorAll('.page_container [data-section-type="product_detail"] .sep-slider,.support-slick');
}
if($tag_container.length === 0) return;
// 给商祥页添加满送插件的标识属性
const $product_container = document.querySelector('.product-details, .product-details, .page_container, .product-images, [data-section-type="product"]')
if($product_container) {
$product_container.setAttribute('data-rebate-tag', 'true');
}
// 部分主题需要调整样式
if (isWind) {
Array.from($tag_container).forEach(container => {
container.style.position = 'relative';
});
}
document.querySelectorAll('.slider-discount-tag.dj_skin_product_title.rebate-tag').forEach(tag => tag.remove());
// 遍历所有容器并插入标签
Array.from($tag_container).forEach(container => {
container.insertAdjacentHTML('beforeend', `<div class="slider-discount-tag dj_skin_product_title rebate-tag">${tag}</div>`);
});
}, 1000)
};
fetchInfoBar = async () => {
let discount_ids = [];
if (this.pageType === 1) {
discount_ids = this.rebateInfo && this.rebateInfo.discount_list.map(item => item.discount_id);
} else if (this.pageType === 38) {
discount_ids = [this.win.rebateObj.rebateCollection_id] || [];
}
const productObj = this.getProduct();
const { cart } = await this.xhr_.fetchJson('/api/cart')
return this.xhr_.fetchJson("\/api\/discount-rebate\/global-text", {
method: "POST",
body: {
product_type: productObj && productObj.product && productObj.product.product_type,
line_items: (cart?.line_items || []).map(item => ({
variant_id: item.variant_id,
product_id: item.product_id,
quantity: item.quantity,
price: item.price,
selected: !item.unchecked,
})),
discount_ids: discount_ids,
},
});
};
renderBottomBanner = res => {
if (!res.tips) return;
document.querySelector(".discount__info-bar")?.remove();
var bar_style = `background:linear-gradient(90deg,${res.config.background_color_start},${res.config.background_color_end}); color:${res.config.color};`;
let data = { tips: res.tips, landPage: this.landPage + res.id, bar_style };
const html = SPZCore.Dom.htmlFor(this.element);
const banner = html([
`<a impr="1" imprevt="1" id="rebate_bottom_bar" href=${data.landPage} class="discount__info-bar text-truncate" data-activity-type="rebate" style="${data.bar_style}">${data.tips}</a>`,
]);
document.querySelector(".plugin-container__bottom-fixed").appendChild(banner);
const pluginCurrencyEvent = new CustomEvent("plugin_currency_update");
document.dispatchEvent(pluginCurrencyEvent);
if (res.id) {
var trackParams = {
page: this.pageType,
discount_id: res.id,
product_id: this.getProduct()?.product.id,
};
banner.addEventListener("click", () => {
this.win.sa &&
this.win.sa.track("plugin_rebate_promotion_click", {
plugin_timestamp: Date.now(),
plugin_location: "bottom_bar",
product_id: trackParams.product_id,
discount_id: trackParams.discount_id,
});
});
this.win.sa && this.win.sa.track("plugin_rebate_banner_pv", trackParams);
}
};
showDiscountPopupsInfoBar = () => {
if ([13, 14, 19, 30, 31].includes(this.pageType)) return;
if (document.querySelector(".plugin-container__bottom-fixed .discount__info-bar")) return;
this.fetchInfoBar().then(this.renderBottomBanner);
document.addEventListener("dj.cartChange", () => {
this.fetchInfoBar().then(this.renderBottomBanner);
});
};
}
SPZ.defineElement("spz-custom-rebate", SpzRebateComponent);