${(function(){
const get_random_six_digits = () => {
return Math.random().toString().slice(-6)
};
const wholesale_enabled = false;
const setting_product_image_display = "100%";
const product_image = data.image;
const secondary_image = data.secondImage;
const image_width = product_image.width || 600;
let image_height = product_image.height || 600;
if(setting_product_image_display == '100%'){
image_height = image_width
}else if(setting_product_image_display == '133.33%'){
image_height = image_width * 1.3333;
};
let product_image_hover_on = false;
if(secondary_image.src){
product_image_hover_on = true;
}
const has_save_label = true && ((+data.compare_at_price) > (+data.price));
const is_single_variant = data.variants.length == 1;
const min_price_variant_href = (data.min_price_variant && data.min_price_variant.available) ? data.min_price_variant.withinUrl : data.withinUrl;
const retail_price_max = data.retail_price_max || data.compare_at_price_max;
const THUMBNAILS_MAX_SIZE = 3;
const thumbnails = data.thumbVariants.slice(0, THUMBNAILS_MAX_SIZE);
const image_wrap_id = 'image_wrap_' + get_random_six_digits();
const image_carousel_id = 'image_carousel_' + get_random_six_digits();
const thumbnails_selector_id = 'thumbnails_selector_' + get_random_six_digits();
const form_id = 'form_' + get_random_six_digits() + "";
const mixed_wholesale = data.mixed_wholesale;
return `
`
})()}
class SpzCustomLabelScript extends SPZ.BaseElement {
constructor(element) {
super(element);
}
isLayoutSupported(layout) {
return true;
}
mountCallback() {
const script = this.element;
const boxEl = script.closest('.product_snippet_label_area');
const labelEl = boxEl.querySelector('.product_snippet__label');
if(!labelEl) return;
const observer = new ResizeObserver((entries) => {
const labelEls = boxEl.querySelectorAll('.product_snippet__label');
const offsetWidth = Math.max(...Array.from(labelEls).map(el => el.offsetWidth));
if(offsetWidth>0){
const padding = offsetWidth / 2 + 8 + 'px';
boxEl.style.left = padding;
boxEl.style.right = padding;
boxEl.style.visibility = 'visible';
}
});
observer.observe(labelEl);
}
}
SPZ.defineElement('spz-custom-label-script', SpzCustomLabelScript);
(function () {
class SPZCustomEventTrack extends SPZ.BaseElement {
constructor(element) {
super(element);
this.action_ = SPZServices.actionServiceForDoc(this.element);
}
isLayoutSupported(layout) {
return true;
}
buildCallback() {
this.setupAction_();
}
track(key, value) {
console.log('tracking...', key, value);
if(window.sa){
window.sa.track(key, value);
}else{
let sa = null;
Object.defineProperty(window, 'sa',{
get: function() {
return sa;
},
set(val){
sa = val;
sa.track(key, value);
}
})
}
}
setupAction_() {
const clickParams = {
business_type: 'product_theme',
event_name: 'function_click',
function_name: 'Farida',
plugin_name: 'Farida',
template_name: "discountComplex",
template_type: 51,
module: 'online_store',
module_type: 'online_store',
tab_name: '',
card_name: '',
event_developer: 'ccbGolumn',
event_type: 'click',
};
this.registerAction('trackClick', (e) => {
const event_info = e.args || {};
this.track('function_click', {
...clickParams,
event_info: JSON.stringify(event_info),
});
});
this.registerAction('trackExpose', (e) => {
const event_info = e.args || {};
this.track('function_expose', {
...clickParams,
event_name: 'function_expose',
event_type: 'expose',
event_info: JSON.stringify(event_info),
});
});
}
}
SPZ.defineElement('spz-custom-event-track', SPZCustomEventTrack);
}())
const TAG = 'spz-custom-utils';
const DEFAULT_DELAY_TIME = 100;
class SpzCustomUtils extends SPZ.BaseElement {
constructor(element) {
super(element);
this.templates_ = SPZServices.templatesForDoc();
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
}
static deferredMount() {
return false;
}
mountCallback() {
}
//判断是否为移动端
isMobile() {
/* 判断机型与处理 */
const u = navigator.userAgent
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // android终端
const isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // ios终端
return (isAndroid || isiOS);
};
/**
* url query param to object
* @param {string} url
* @returns {object} query object
*/
params(url) {
url = url || window.location.href;
let params = {};
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
params[key] = decodeURIComponent(value);
} catch (e) {
params[key] = value;
}
});
return params;
};
/**
* @param fn {Function} 实际要执行的函数
* @param delay {Number} 延迟时间,单位是毫秒(ms)
* @return {Function} 返回一个“防反跳”了的函数
*/
debounce(fn, delay) {
// 定时器,用来 setTimeout
let timer;
// 返回一个函数,这个函数会在一个时间区间结束后的 delay 毫秒时执行 fn 函数
return function () {
// 保存函数调用时的上下文和参数,传递给 fn
const context = this;
const args = arguments;
// 每次这个返回的函数被调用,就清除定时器,以保证不执行 fn
clearTimeout(timer);
// 当返回的函数被最后一次调用后(也就是用户停止了某个连续的操作),
// 再过 delay 毫秒就执行 fn
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
};
/* 节流防抖 */
throttle(func, wait, mustRun) {
var timeout,
startTime = new Date();
return function () {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);
// 如果达到了规定的触发时间间隔,触发 handler
if (mustRun && curTime - startTime >= mustRun) {
func.apply(context, args);
startTime = curTime;
// 没达到触发间隔,重新设定定时器
} else {
timeout = setTimeout(func, wait);
}
};
};
//滚动加载方法
isToPageEnd(id) {
const $el = document.querySelector(`[data-section-id='${id}']`);
const scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; //滚动条距离顶部的高度
const clientHeight = window.innerHeight; //当前可视的页面高度
const scrollHeight = document.body.scrollHeight; //当前页面的总高度
const elOffsetTop = $el.getBoundingClientRect().top + window.pageYOffset - document.documentElement.clientTop; // 元素距离文档顶部距离
// 如果改卡片下面还有卡片或者dom,计算滚动加载需要考虑这个高度
const toBottom = scrollHeight - ($el.offsetHeight + elOffsetTop); //元素到浏览器底部的高度
if (scrollTop + clientHeight + toBottom + 100 >= scrollHeight) {
return true;
}
return false;
};
/**
* url 添加前缀
* @param {string} path , 必须是前面有斜杠前缀的路径
* @returns string
*/
prefixionPath(prefix,urlPath) {
if(typeof prefix !== 'string') return ;
if(typeof urlPath !== 'string') return ;
if(urlPath.indexOf('/') !== 0){
throw new Error('prefixPath: urlPath must be start with /');
}
if(prefix.indexOf('/') !== 0){
throw new Error('prefixPath: prefix must be start with /');
}
return prefix+urlPath;
}
/**
* @param {string} urlPath
* @returns {string}
* @example globalizePath('/path_a/path_b')// => '/en/path_a/path_b'
*/
globalizePath(urlPath) {
if(typeof urlPath !== 'string') return ;
if(urlPath.indexOf('/') !== 0){
urlPath = '/'+urlPath;
}
let prefix = ((SHOPLAZZA && SHOPLAZZA.routes && SHOPLAZZA.routes.root) || '');
if(prefix.length>0){
if(prefix.indexOf('/') !== 0){
prefix = '/'+prefix;
}
return this.prefixionPath(prefix,urlPath);
}else{
return urlPath;
}
}
image_padding_bottom(width, height, origin) {
origin = origin || 'limit';
if (width && height) {
const hw_ratio = height / width;
if (origin == 'limit') {
if (hw_ratio < 0.62) {
return '62%';
} else if (hw_ratio > 1.6) {
return '160%';
}
}
return parseInt(hw_ratio * 100) + '%';
}
return '100%';
}
getNumber(str) {
str = str + '';
return str.match(/\d+(\.\d+)?/g) ? Number(str.match(/\d+(\.\d+)?/g)[0]) : str;
};
// 处理货币符号
finance_money_with_shop_symbol(price, onlyNumber) {
const symbol = onlyNumber ? '' : window.SHOPLAZZA.currency_symbol;
const position = window.SHOPLAZZA ? window.SHOPLAZZA.currency_symbol_pos : 'left';
const format = window.SHOPLAZZA ? window.SHOPLAZZA.money_format : 'amount';
if (position == 'right') {
return Number(Number(price) * 1).format(format) + symbol;
}
return symbol + Number(Number(price) * 1).format(format);
};
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, SpzCustomUtils)
${function() {
const options = data.options;
const defaultValue = data.defaultValue || options[0].value;
let defaultText = options[0].text;
const hasOption = Array.apply(null, options).find((option) => {
return option.value === defaultValue;
});
if(hasOption){
defaultText = hasOption.text;
}
return `
`
}()}
const TAG = 'spz-custom-sort';
class SpzCustomSort extends SPZ.BaseElement {
constructor(element) {
super(element);
this.spz_custom_id = '';
}
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_();
}
init() {
this.bindEvent();
}
bindEvent() {
const $selectList = SPZCore.Dom.scopedQuerySelectorAll(
this.element,
".sort_custom_content li"
);
const $customerSelect = SPZCore.Dom.scopedQuerySelector(
this.element,
".sort_custom_select"
);
// 选择下拉选项
Array.from($selectList).forEach((node) => {
SPZUtils.Event.listen(node, 'click', ()=> {
let value = node.getAttribute('value');
let text = node.getAttribute('text');
// 触发selectChange 事件
this.triggerEvent_('selectChange', {
value: value,
name: value
})
$customerSelect.innerHTML = text;
const panelChilds = this.element.querySelectorAll(".sort_custom_panel li");
// 清空其他选项的勾选状态
Array.from(panelChilds).forEach((el) => {
if(el.getAttribute('value') == value) {
el.classList.add("active")
} else {
el.classList.remove('active');
}
})
});
})
}
// 渲染界面
async doRender_(data) {
// 操作该组件的dom id
this.spz_custom_id = data.id;
return this.templates_
.findAndRenderTemplate(this.element, data)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
this.init();
});
}
setupAction_() {
this.registerAction('render', async(invocation) => {
const data = invocation.args.data;
this.doRender_(data)
});
this.registerAction('handleSelect', async(invocation) => {
const data = invocation.args.data;
});
this.registerAction('handleDropdownOpen', async(invocation) => {
const $selectDropDown = SPZCore.Dom.scopedQuerySelector(
this.element,
".select_drop_down"
);
$selectDropDown.classList.add('select_drop_down_rotate');
});
this.registerAction('handleDropdownClose', async(invocation) => {
const $selectDropDown = SPZCore.Dom.scopedQuerySelector(
this.element,
".select_drop_down"
);
$selectDropDown.classList.remove('select_drop_down_rotate');
});
}
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, SpzCustomSort)
${(function() {
const products = data.products;
const discountType = data.discount_info.discount_type;
const progress = 'PROGRESS_ONGOING';
const product_grid_image_size = null || '100%';
const product_display = originData?.product_display;
const show_atc = product_display ? product_display?.show_add_to_cart : true;
const show_discount_label = product_display?.show_discount_label || false;
const show_sales_progress = product_display?.sales_progress?.enabled || false;
const sales_progress_format = product_display?.sales_progress?.format || '';
const limit_purchase_tip = data.limit_purchase_tip || "";
const productConfig = product_display?.config && JSON.parse(product_display?.config).color;
const sale_progress_bg = productConfig.sale_bar_background_color;
const sale_progress_bg_start = productConfig.progress_sale_bar_background_color_start;
const sale_progress_bg_end = productConfig.progress_sale_bar_background_color_end;
const sale_count_text = productConfig.sale_bar_atc_color;
const discount_label_start = productConfig.discount_sign_background_color_start;
const discount_label_end = productConfig.discount_sign_background_color_end;
const discount_label_text = productConfig.discount_sign_color;
if(!products.length) return '';
return products.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 = "Sold out";
const soldOutLang2 = "Sold out";
const noNeedBtn = discountType !== "DT_MIX_MATCH_BUNDLE" && discountType !== "DT_CLASSIC_BUNDLE";
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_grid_image_size !== 'natural') {
imageHeight = (imageWidth * parseFloat(product_grid_image_size)) / 100;
}
const flash_sale_info = product?.flash_sale_info;
const is_flashsale_sold_out = flash_sale_info?.discount_sales_rate == "100";
const off_ratio = flash_sale_info?.off_ratio;
price = flash_sale_info?.discount_price || price;
const defaultVariantTitle = product.variants[0].title.replace('-', '/') || '';
const variantDiscountInfo = product.variants[0].discount_info || {};
const minPurchaseQty = product.discount_min_purchase_qty || product.variants[0].discount_info.discount_min_purchase_qty || 0;
if (product.published) {
return `
x ${minPurchaseQty}
${product.title}
Almost sold out
${flash_sale_info?.discount_sales}
${flash_sale_info?.discount_sales_rate}%
sold
${function(){
if (soldOutLang1){
return `
${soldOutLang1}
`
}else{
return `
${soldOutLang2}
`
}
}()}
`
} else {
return ``;
}
}).join('');
})()}
const TAG = "spz-custom-render-products";
class SpzCustomProducts extends SPZ.BaseElement {
constructor(element) {
super(element);
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
}
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element, data)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
getRenderTemplate(data) {
const renderData = data || {};
return this.templates_
.findAndRenderTemplate(this.element, renderData)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
return el;
});
}
setupAction_() {
this.registerAction('test', (invocation) => {
});
}
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, SpzCustomProducts)
${function() {
return `
`;
}()}
const TAG = 'spz-custom-discount-default-server';
const E_DISCOUNT_PROGRESS = {
ProgressFinished : "PROGRESS_FINISHED",
ProgressNotStarted : "PROGRESS_NOT_STARTED",
ProgressOngoing : "PROGRESS_ONGOING"
};
class SpzCustomDiscountDefault extends SPZ.BaseElement {
constructor(element) {
super(element);
this.templates_ = null;
this.section_id = 15890337540001 || 1;
this.discountObj = window.discountObj;
this.discount_info = this.discountObj.discount_info;
this.product_display = this.discountObj.landing_page_info.product_display;
this.buy_product_info = this.discountObj.buy_product_info;
this.obtain_product_info = this.discountObj.obtain_product_info;
this.discount_id = this.discount_info.id;// 活动id
this.discountI18n = {};
const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name || window.SHOP_PARAMS.theme_name;
this.isHero = /Hero/.test(THEME_NAME);
// PROGRESS_ONGOING: 进行中 PROGRESS_NOT_STARTED: 未开始 PROGRESS_FINISHED: 已结束
this.E_DISCOUNT_PROGRESS = {
ProgressFinished : "PROGRESS_FINISHED",
ProgressNotStarted : "PROGRESS_NOT_STARTED",
ProgressOngoing : "PROGRESS_ONGOING"
};
this.E_TAB_MAP = {
scenario_buy : {
value: "1",
domId: "product_list_buy_products"
},
scenario_obtain : {
value: "2",
domId: "product_list_obtain_products"
}
}
this.tabContentIdMap = {};
this.currentTab = this.E_TAB_MAP.scenario_buy.value;
this.model_buy = {
discount_id: this.discount_id, //活动id
scenario: 1, // 枚举值,1:购买商品,2:获得商品
sort: { by: 'recommend', direction: 'asc' },
page: 2, //分页码
limit: 20, // 每页数量
loading: false, // 请求数据标示
has_more: this.buy_product_info.has_more // 是否还有数据
};
this.model_get = {
discount_id: this.discount_id, //活动id
scenario: 2, // 枚举值,1:购买商品,2:获得商品
sort: { by: 'recommend', direction: 'asc' },
page: 2,
limit: 20,
loading: false,
has_more: this.obtain_product_info?.has_more
};
this.modelMap = {
[this.E_TAB_MAP.scenario_buy.value]: this.model_buy,
[this.E_TAB_MAP.scenario_obtain.value]: this.model_get,
}
this.sortDict = {
recommend_asc: { by: 'recommend', direction: 'asc' },
title_asc: { by: 'title', direction: 'asc' },
title_desc: { by: 'title', direction: 'desc' },
price_asc: { by: 'price', direction: 'asc' },
price_desc: { by: 'price', direction: 'desc' },
created_at_desc: { by: 'created_at', direction: 'desc' },
sales_desc: { by: 'sales', direction: 'desc' },
add_to_cart_count_desc: { by: 'add_to_cart_count', direction: 'desc' },
views_desc: { by: 'views', direction: 'desc' }
};
this.sortOptions = [
{
value: 'recommend_asc',
text: "Recommend"
},
{
value: 'price_asc',
text: "Price, low to high"
},
{
value: 'price_desc',
text: "Price, high to low"
},
{
value: 'title_asc',
text: "Name, A to Z"
},
{
value: 'title_desc',
text: "Name, Z to A"
},
{
value: 'created_at_desc',
text: "Newest in"
},
{
value: 'sales_desc',
text: "Total sales, high to low"
},
{
value: 'add_to_cart_count_desc',
text: "Purchases, high to low"
},
{
value: 'views_desc',
text: "Page views, high to low"
}
];
// 直出商品数据 + 异步请求商品数据
this.products = this.buy_product_info.product;
this.productUrl = "";
// 款式信息集合
this.productStyleInfo = [];
// 弹窗内选择款式集合
this.modalVariantInfo = [];
// 加购商品列表
this.lineItems = [];
this.buyNowApi = "\/api\/checkout\/order";
this.batchAtcApi = "\/api\/cart\/batch";
this.debounceTimer = null;
this.discount_type = this.discount_info.discount_type;
}
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_();
Object.entries(this.E_TAB_MAP).forEach(([key, valueObj]) => {
this.tabContentIdMap[valueObj.value] = valueObj.domId;
})
}
async mountCallback() {
this.utilsApi_ = await SPZ.whenApiDefined(document.querySelector('#spz_custom_utils'));
this.init();
this.handleRenderSort();
}
init() {
this.xhr_.fetchJson(`/api/discount-i18n`, {
method: "get",
}).then((res)=>{
this.discountI18n = res;
this.bindEvent();
})
// url 携带 sort_by参数
var queryParams = this.utilsApi_.params();
var sortValue = queryParams.sort_by;
if (sortValue) {
this.model_buy.sort = this.sortDict[sortValue];
}
// 经典捆绑初始化商品数据
if(this.discount_type == 'DT_CLASSIC_BUNDLE') {
this.productStyleInfo.push(...this.buy_product_info.product.map((item) => {
return this.getFilteredVariants_(item, 'single');
}));
}
}
handleRenderSort() {
// 渲染排序
const sort_x_id = 'promotionSortProductsX';
const $sortX = document.getElementById(sort_x_id)
$sortX && SPZ.whenApiDefined($sortX).then((api) => {
// 渲染排序列表
api.doRender_({options: this.sortOptions, defaultValue: 'recommend_asc', id: sort_x_id });
})
const sort_y_id = 'promotionSortProductsY';
const $sortY = document.getElementById(sort_y_id)
$sortY && SPZ.whenApiDefined($sortY).then((api) => {
// 渲染排序列表
api.doRender_({options: this.sortOptions, defaultValue: 'recommend_asc' , id: sort_y_id});
})
}
// 获取数据,拼接html模板
async getData() {
// 请求数据
let model = this.modelMap[this.currentTab];
if (!model.has_more || model.loading) {
return;
}
model.loading = true;
this.handleLoading_({type: 'product', action: 'show'});
let $content = document.querySelector(`#${this.tabContentIdMap[this.currentTab]} .discount-default__productlist-wrap`) || document.querySelector(`.discount-default__productlist-wrap`);
let $defaultEmpty = $content && $content.querySelector('.discount_default_empty');
//查询活动商品接口
const reqBody = {
discount_id: model.discount_id,
page: model.page,
limit: model.limit,
apply_scenario: model.scenario,
sort: model.sort,
sales_channel: {
sale_channel_type: "online",
sale_channel_id: '2015527'
}
}
this.xhr_.fetchJson(`/api/storefront/promotion/landing_page/product/list`, {
method: "post",
body: reqBody
}).then(async(res)=>{
// 更新参与活动的商品数量
const productCount = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionEventProductCount`);
productCount && SPZ.whenApiDefined(productCount).then((api) => {
api.render(res.count, true);
});
this.products.push(...res.products);
this.handleLoading_({type: 'product', action: 'close'});
const count = res.count;
model.has_more = res.has_more;
if (count > 0) {
$defaultEmpty && ($defaultEmpty.style.display = 'none');
model.page++;
if (res.products && res.products.length > 0) {
let products = res.products.map((product) => {
return {
...product,
url: this.utilsApi_.globalizePath(product.url),
image_padding_bottom: this.utilsApi_.image_padding_bottom(product.image.width, product.image.height,'no-limit')
}
});
// 获取商品列表渲染模板, dom挂载
const renderApi = await SPZ.whenApiDefined(document.querySelector('#discounts_products_render'));
const el = await renderApi.getRenderTemplate({
products: products,
discountI18n: this.discountI18n,
discount_info: this.discount_info,
product_display: this.product_display
});
const childNodes = el.querySelectorAll('.as-render-product-item');
if (childNodes && childNodes.length > 0) {
$content.append(...el.childNodes);
}
if(this.discount_type == 'DT_CLASSIC_BUNDLE') {
// 遍历$content 插入商品垂直虚线分割
const productListAsync = $content.querySelectorAll('.as-render-product-item');
if (productListAsync.length > 0) {
productListAsync.forEach((item, index) => {
const htmlStr = `<span class="promotion_dotted_line"></span>
<div class="promotion_plus_bundle">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M1 6H11M6 1L6 11" stroke="black" stroke-width="1.6" stroke-linecap="round"/>
</svg>
</div>
<span class="promotion_dotted_line"></span>`;
this.createAndInsertSeparator_('promotion_separator md:hidden', (index + 1) % 4 !== 0 && index !== productListAsync.length - 1, htmlStr, $content, item);
this.createAndInsertSeparator_('promotion_separator lg:hidden', (index + 1) % 2 !== 0 && index !== productListAsync.length - 1, htmlStr, $content, item);
});
}
}
if(this.discount_type == 'DT_MIX_MATCH_BUNDLE') {
const productSelector = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductSelector`);
productSelector && SPZ.whenApiDefined(productSelector).then((api) => {
api.init();
});
const currentPageSelectedProducts = res.products.filter(item => this.productStyleInfo.map(item => item.product_id).includes(item.id));
this.updateProductPrice_(currentPageSelectedProducts);
}
}
} else {
// 空列表
const $emptyTemplate = document.querySelector('#promotionDiscountEmpty .discount_default_empty');
const $cloneEmptyTemplate = $emptyTemplate.cloneNode(true);
$content.innerHTML = '';
$content.append($cloneEmptyTemplate);
$defaultEmpty && ($defaultEmpty.style.display = 'flex');
}
model.loading = false;
}).catch((err)=>{
this.handleRequestError_(err);
}).finally(()=>{
model.loading = false;
this.handleLoading_({type: 'product', action: 'close'});
// 经典spu纬度需要该商品信息: is_classic_bundle_product_list_variant_tag
if(this.discount_type == 'DT_CLASSIC_BUNDLE' && this.discount_info.enable_min_purchase_qty == true && this.discount_info.min_purchase_qty_type == 'spu') {
this.productStyleInfo = this.productStyleInfo.map((item) => {
return {
...item,
is_classic_bundle_product_list_variant_tag: true,
}
});
}
const result = this.productStyleInfo.reduce((map, item) => {
if (!map[item.product_id]) {
map[item.product_id] = [];
}
map[item.product_id].push(item);
return map;
}, {});
// 渲染变体tags
if(this.discount_type == 'DT_MIX_MATCH_BUNDLE' || this.discount_type == 'DT_CLASSIC_BUNDLE') {
Object.values(result).forEach((item) => {
this.handleSpzVariantRender_(item, item[0].product_id);
this.handleProductOption_(item[0].product_id, true);
});
}
// 渲染经典额外变体
if(this.discount_type == 'DT_CLASSIC_BUNDLE' && this.discount_info.enable_min_purchase_qty == true && this.discount_info.min_purchase_qty_type == 'spu') {
Object.values(result).forEach((item) => {
if(item[0].is_multi_style && item[0].discount_min_purchase_qty > 1) {
const classicSpuTag = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionClassicSpuTags-${item[0].product_id}`);
classicSpuTag && SPZ.whenApiDefined(classicSpuTag).then((api) => {
api.render(item, true);
});
}
});
}
// 渲染经典捆绑商品最低购买数量
if(this.discount_type == 'DT_CLASSIC_BUNDLE') {
Object.values(result).forEach((item) => {
this.handleMinPurchaseQtyUpdate_({discount_min_purchase_qty: item[0].discount_min_purchase_qty}, item[0].product_id);
});
}
})
}
createAndInsertSeparator_(className, condition, htmlStr, $content, item) {
if (condition) {
const separator = document.createElement('div');
separator.className = className;
separator.innerHTML = htmlStr;
$content.insertBefore(separator, item.nextSibling);
}
}
bindEvent() {
// 监听滚动,请求数据
window.addEventListener("scroll", this.utilsApi_.debounce(
() => {
// 判断是否到底
const model = this.modelMap[this.currentTab];
if (!model.loading && model.has_more && this.utilsApi_.isToPageEnd(this.section_id)) {
this.getData();
}
},
10,
50
))
}
// 商品排序
handleSort_(data) {
let sortKey = data.value;
this.modelMap[this.currentTab].sort = this.sortDict[sortKey || 'recommend_asc'];
this.modelMap[this.currentTab].page = 1;
this.modelMap[this.currentTab].has_more = true;
this.productStyleInfo = this.handleMixMatchBundleFilterSelected_(this.productStyleInfo);
// 清空商品列表dom, 重新请求排序数据渲染
let $productList = document.querySelector(`#${this.tabContentIdMap[this.currentTab]} .discount-default__productlist-wrap`) || document.querySelector(`.discount-default__productlist-wrap`);;
$productList && ($productList.innerHTML = '');
this.getData();
}
// tab 切换
tabChange_(value) {
this.currentTab = value || this.E_TAB_MAP.scenario_buy.value;
}
// 渲染界面
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element, data)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
// 捆绑商品加购/立即购买
handleBundleAddToCart_(data) {
const { action } = data;
if(this.discount_type == 'DT_CLASSIC_BUNDLE') {
this.lineItems = this.productStyleInfo;
} else {
this.lineItems = this.handleMixMatchBundleFilterSelected_(this.productStyleInfo);
}
if(action == 'cart') {
//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: 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: 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;
} else {
this.handleRequestError_(data);
}
})
.catch(async (error) => {
await error.then((data) => {
this.handleRequestError_(data);
});
});
}
}
handleRequestError_(data) {
const message = data?.message || data?.errors?.[0] || 'Unknown error';
const toast = SPZCore.Dom.scopedQuerySelector(document.body, '#discount_toast');
toast && SPZ.whenApiDefined(toast).then((api) => {
api.showToast(message);
});
};
// 渲染加购弹窗内容
async renderQuickShop(data) {
this.handleLoading_({type: 'whole', action: 'show'});
const apply_scenario = this.modelMap[this.currentTab].scenario;
this.xhr_.fetchJson(`/api/storefront/promotion/landing_page/product?product_id=${data.product_id}&discount_id=${this.discount_id}&apply_scenario=${apply_scenario}`, {
method: "get",
}).then(async(res)=>{
this.handleLoading_({type: 'whole', action: 'close'});
const $quickShop = await SPZ.whenApiDefined(document.querySelector('#promotion-quick-view-render'));
// 定义默认渲染的子款式
const selectedVariant = res.product.variants.find((v)=> (v.available && v.is_hit_discount == true)) || 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};
$quickShop.render(data);
// 打开加购弹窗
SPZ.whenApiDefined(document.querySelector(`#promotion-quick-view`)).then((api)=>{
api.open();
});
}).catch((err)=>{
this.handleLoading_({type: 'whole', action: 'close'});
})
}
// 单变体点击添加按钮
renderSingleVariant(data) {
const { product_id } = data;
const currentProduct = this.products.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, discount_min_purchase_qty } = data;
const { product_id, variant_id, variant, quantity, product } = data;
const isSingle = type == 'single';
const variantData = isSingle ? (variants[0] || data) : variant;
const productData = isSingle ? data : product;
let item_quantity = 0;
if (this.discount_type === '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.products.find((item) => item.id == product_id)?.product_type || this.products.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,
discount_min_purchase_qty: discount_min_purchase_qty || productData.discount_min_purchase_qty || variantData.discount_info.discount_min_purchase_qty || 0,
}
}
// 更新价格方法
updateProductPrice_(data) {
const bottomBtnContainer = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionBottomContainer`);
if (data.length == 0) {
bottomBtnContainer && SPZ.whenApiDefined(bottomBtnContainer).then((api) => {
api.render({original_price: 0, received_discounts: 0}, true);
});
return;
}
data = this.handleMixMatchBundleFilterSelected_(data);
const reqBody = {
discount_id: this.discount_id,
customer: {
customer_id: '',
email: '',
},
sales_channel: {
sale_channel_type: "online",
sale_channel_id: '2015527'
},
line_items: data
}
// 如果已经有一个请求在等待,那么取消这个请求
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
}
this.handleLoading_({type: 'whole', action: 'show'});
this.debounceTimer = setTimeout(() => {
this.xhr_.fetchJson(`/api/storefront/promotion/calculate/discounted_price`, {
method: "post",
body: reqBody
}).then((res)=>{
// 更新商品列表价格
Object.keys(res.line_items).forEach((key) => {
const currentProductPrice = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductPrice-${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) => {
api.render({...res.total_price, picked_qty}, true);
});
}).catch(async (err)=>{
await err.then((data) => {
this.handleRequestError_(data);
});
}).finally(()=>{
this.handleLoading_({type: 'whole', action: 'close'});
})
}, 100);
}
// 还原商品价格
resetProductPrice_(data) {
const {price, compare_at_price, id} = data;
const currentProductPrice = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductPrice-${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 productSelector = SPZCore.Dom.scopedQuerySelector(document.body, `#promotionProductSelector`);
productSelector && SPZ.whenApiDefined(productSelector).then((api) => {
api.toggle_({option: productId, value: show});
});
}
// 调用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);
});
}
// 添加商品子款式
renderVariantTag() {
let variantInfo;
const quickShopBody = SPZCore.Dom.scopedQuerySelector(document.body, '#promotion-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.discount_type == '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.discount_info.enable_min_purchase_qty == true && this.discount_info.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, '#promotion-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, '#promotion-quick-view');
quickView && SPZ.whenApiDefined(quickView).then((api)=>{
api.close();
});
return;
}
// 更新this.productStyleInfo中的商品款式信息
const index = this.productStyleInfo.findIndex((item) => item.product_id == productId);
if (index != -1) {
this.productStyleInfo[index] = 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, '#promotion-quick-view');
quickView && SPZ.whenApiDefined(quickView).then((api)=>{
api.close();
});
});
}
// 混搭弹窗内的前端库存校验
handleModalInventoryCheck_(data) {
if(this.discount_type == 'DT_CLASSIC_BUNDLE') {
const currentVariantAddNum = this.modalVariantInfo.find((item) => item.variant_id == data.variant_id)?.quantity || 0;
const quickShopBody = SPZCore.Dom.scopedQuerySelector(document.body, '#promotion-quick-shop-body');
if(!!data.variant && currentVariantAddNum == Number(data.variant.available_quantity)) {
quickShopBody && quickShopBody.setAttribute('status', 'soldout');
} else {
quickShopBody && quickShopBody.setAttribute('status', 'available');
}
}
}
// 删除商品子款式
deleteVariantTag(data) {
const { product_id, variant_id } = data;
if(this.discount_info.enable_min_purchase_qty == true && this.discount_info.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);
isMultiStyle && this.updateProductPrice_(this.productStyleInfo);
this.handleProductOption_(product_id, true);
} else {
this.handleProductOption_(product_id, false);
this.resetProductPrice_(this.products.find((item) => item.id == product_id));
}
}
// 加购弹窗未参与活动 加购按钮不可点击
handleNotHitDiscount_(data) {
const $quickShopBody = document.querySelector('#promotion-quick-shop-body');
const $limitTip = document.querySelector('.promotion_flashsale_tip');
//当前子框式未命中活动
if(data.variant.is_hit_discount == false) {
$quickShopBody.setAttribute('variantstatus', 'notHitDiscount');
$limitTip && $limitTip.setAttribute('selectedvariantstatus', 'notHitDiscount');
} else {
$quickShopBody.setAttribute('variantstatus', '')
$limitTip && $limitTip.setAttribute('selectedvariantstatus', '')
}
}
//切换快速加购弹窗内限时促销限购提示
handleFlashsaleTip_(data) {
const flashsaleEl = document.querySelector('#quick-shop-flashsale-tip');
SPZ.whenApiDefined(flashsaleEl).then((api) => {
api.render(data);
});
}
// loading
handleLoading_(event) {
const { type, action } = event;
const loadingElementId = type == 'product' ? '#promotionProductsLoading' : '#promotionWholeLoading';
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);
}
handleMixMatchBundleFilterSelected_(data) {
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;
}, []);
if(this.discount_type == 'DT_MIX_MATCH_BUNDLE') {
return data.filter((item) => idArr.includes(item.product_id));
}
return data;
}
handleCopyDiscountCode_(data) {
sessionStorage.setItem('other-copied-coupon', data);
const message = 'Discount code copied !';
const toast = SPZCore.Dom.scopedQuerySelector(document.body, '#discount_toast');
toast && SPZ.whenApiDefined(toast).then((api) => {
api.showToast(message);
});
}
setupAction_() {
this.registerAction('handleTabChange', (invocation) => {
const { panelId } = invocation.args.data;
this.tabChange_(panelId);
});
// 监听排序组件 选中选项
this.registerAction('handleSort', (invocation) => {
const data = invocation.args.data;
this.handleSort_(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('getCartCount', (invocation) => {
//一些老主题还未用spz重构,需要手动触发一次老方法以更新购物车图标的数量
window.$ && $(document).trigger('dj.common.cart.change');
});
// 捆绑商品加购/立即购买
this.registerAction('handleBundleAddToCart', (invocation) => {
const data = invocation.args;
this.handleBundleAddToCart_(data);
});
// 子款式 未参与活动
this.registerAction('handleNotHitDiscount', (invocation) => {
const data = invocation.args.data;
this.handleNotHitDiscount_(data);
});
// 限时促销限购提示
this.registerAction('handleFlashsaleTip', (invocation) => {
const data = invocation.args.data;
this.handleFlashsaleTip_(data);
});
// 加购提示
this.registerAction('handleAddToCartToast', (invocation) => {
const themeAddToCartToastEl = document.querySelector('#add-cart-event-proxy');
if(themeAddToCartToastEl) return;
const langValue = 'Added successfully';
this.triggerEvent_("addToCartToast", langValue);
});
this.registerAction('handleGoShopping', () => {
window.location.href = this.productUrl;
});
this.registerAction('getProductUrl', (invocation) => {
const data = invocation.args;
this.productUrl = data.product_url;
});
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);
});
this.registerAction('pageReload', () => {
//活动进行中 长期活动 = 虚拟倒计时
const VirtualCountdown = this.discount_info.progress === "PROGRESS_ONGOING" && this.discount_info.ends_at == -1;
if(VirtualCountdown) return;
window.location.reload();
});
this.registerAction('resetModalVariantInfo', () => {
this.modalVariantInfo = [];
});
this.registerAction('handleModalInventoryCheck', (invocation) => {
const data = invocation.args.data;
this.handleModalInventoryCheck_(data);
});
this.registerAction('copyDiscountCode', (invocation) => {
const data = invocation.args.data;
this.handleCopyDiscountCode_(data);
});
}
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, SpzCustomDiscountDefault)
The activity inventory has been sold out, flash sale discount will not be applied.
Continue Shopping
${function() {
const productData = data;
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 + `quick-shop-selected-variant-${opt.id}.rerender(data=event.selectedValues.${opt.name});`;
mouse_out_event = mouse_out_event + `quick-shop-selected-variant-${opt.id}.rerender(data=event.selectData.${opt.name});`;
mouse_over_event = mouse_over_event + `@${nameEscape}Mouseover="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];
return `
`;
}()}
${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 value = (data.originData && data.originData.value) || data.value;
const isHasValue = value ? true : false;
return `
${value}
`
}()}
function toggleExpand() {
const $discountText = document.getElementById('discount-text-list-md');
if ($discountText.hasAttribute('show')) {
$discountText.removeAttribute('show');
} else {
$discountText.setAttribute('show', '');
}
}
exportFunction('toggleExpand', toggleExpand);
function toggleExpand() {
const $discountText = document.getElementById('discount-text-list');
if ($discountText.hasAttribute('show')) {
$discountText.removeAttribute('show');
} else {
$discountText.setAttribute('show', '');
}
}
exportFunction('toggleExpand', toggleExpand);
(function() {
const STATUS = {
LOADING: 'loading',
FINISH: 'finish'
};
const RESULT = {
EMPTY: 'data-empty'
};
class SPZCustomCartSku extends SPZ.BaseElement {
renderData = [];
/**
* add to cart reselect item, and delete process:
* 1. record reselect id before show reselect modal
* 2. add to cart success, mark `needDeleteReselectRecord` to true
* 3. close reselect modal / drawer
* 4. spz-cart re-render, triggered mounted event
* 5. call refresh
*/
// mark delete reselect id
recordReselectId = null;
// mark should delete reselect record after spz-cart mounted event
needDeleteReselectRecord = false;
refreshLock = false;
addToCartSuccess = false;
// cache paused refresh data
refreshDataCache = [];
// cache similar products data, for manual add to cart
similarData = [];
constructor(element) {
super(element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.action_ = SPZServices.actionServiceForDoc(this.element);
}
setupAction_() {
this.registerAction('refresh', (invocation) => {
const data = invocation && invocation.args && invocation.args.data || {};
this.refresh(data);
});
this.registerAction('deleteReselect', SPZCore.Types.debounce(
this.win,
(invocation) => {
this.deleteReselect(invocation?.args?.id);
},
200
));
this.registerAction('deleteInvalid', SPZCore.Types.debounce(
this.win,
(invocation) => {
this.deleteInvalid(invocation?.args?.id);
},
200
));
this.registerAction('setReselectRecordId', (invocation) => {
this.setReselectRecordId(invocation?.args?.id);
});
this.registerAction('clearNeedReselectRecord', (invocation) => {
this.clearNeedReselectRecord();
});
this.registerAction('registerDeleteReselectTask', (invocation) => {
this.registerDeleteReselectTask(invocation?.args?.data);
});
this.registerAction('lockRefresh', (invocation) => {
this.lockRefresh();
});
this.registerAction('releaseRefreshLock', (invocation) => {
this.releaseRefreshLock();
});
this.registerAction('manualAddToCart', (invocation) => {
this.manualAddToCart(invocation?.args?.id);
});
this.registerAction('syncSimilarData', (invocation) => {
this.syncSimilarData(invocation?.args?.data);
});
// DEBUG
this.registerAction('log', (invocation) => {
const data = invocation && invocation.args && invocation.args.data || {};
console.log('log', invocation);
});
}
buildCallback() {
this.setupAction_();
}
isLayoutSupported(layout) {
return true;
}
/**
* 数据去重
* @param {Array} data
*/
uniq_(data) {
const result = [];
for(let i = 0; i < data.length; i++) {
if(!result.includes(data[i])) {
result.push(data[i]);
}
}
return result;
}
checkRefreshLock() {
if (this.refreshLock) {
return false;
}
return true;
}
setLoading(isLoading) {
SPZCore.Dom.toggleAttribute(this.element, STATUS.LOADING, isLoading);
SPZCore.Dom.toggleAttribute(this.element, STATUS.FINISH, !isLoading);
}
setDataResult(data) {
const isDataEmpty = !data.reselectSkus.length && !data.invalidSkus.length && !data.line_items.length;
SPZCore.Dom.toggleAttribute(this.element, RESULT.EMPTY, isDataEmpty);
}
// 存在多次请求顺序问题
async refresh(_data) {
// 接口失败时返回数据不为对象
if ((typeof _data !== 'object' || !Array.isArray(_data.line_items))) {
const newData = {
line_items: [],
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshError', newData);
this.setLoading(false);
return;
}
if (!this.checkRefreshLock()) {
this.setRefreshCache(_data);
return;
}
this.setLoading(true);
let data = _data;
if (this.needDeleteReselectRecord && this.recordReselectId) {
let hasError = false;
try {
data = await this.deleteReselectRecordBeforeRefresh(_data);
} catch (e) {
hasError = true;
console.error(e);
const newData = {
...data,
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshError', newData);
this.setLoading(false);
this.setDataResult(newData);
return;
}
this.needDeleteReselectRecord = false;
if (hasError) {
return;
}
}
// 获取失效商品
const invisibleItems = data.ineffectives;
// 获取失效商品内仅售罄商品的sku
const soldOutItems = invisibleItems.filter(item => item.reason === "line_item_sold_out");
// 通过失效 sku 获取 spu, 注意去重
const soldOutSpuIds = this.uniq_(soldOutItems.map(item => item.product_id));
const querySpuIds = soldOutSpuIds.map(spuId => `ids[]=${spuId}`).join('&');
if (!soldOutSpuIds.length) {
const newData = {
...data,
reselectSkus: [],
invalidSkus: [],
displayInvalidSkus: []
};
this.trigger_('refreshSuccess', newData);
this.renderData = newData;
this.setLoading(false);
this.setDataResult(newData);
return;
}
// 请求 spu 下其他 sku 库存
this.xhr_.fetchJson(`/api/product/list?limit=${soldOutSpuIds.length}&${querySpuIds}`)
.then(res => {
const reselectSkus = [];
const invalidSkus = [];
// spu 维度展示
const displayInvalidSkus = [];
res.data.list.forEach(product => {
// spu 匹配, 存在多 sku 情况
const soldOutSkus = soldOutItems.filter(item => item.product_id === product.id);
if (!soldOutSkus.length) {
return;
}
// 通过失效 sku 获取 spu 下其他 sku 库存, 存在其他可用库存时标记为 "Reselect" 按钮可用
//const allowReselect = product.variants
// .filter(variant => variant.option1 === soldOutProduct.variant.option1 && variant.option2 === soldOutProduct.variant.option2 && variant.option3 === soldOutProduct.variant.option3)
// .some(variant => variant.available);
// 查询售罄 sku 对应商品是否可加购, 标记为 Reselect 可用项
if (product.available) {
reselectSkus.push(...soldOutSkus);
// 若商品不可用(全部 sku 售罄), 归纳到失效商品列表
} else {
invalidSkus.push(...soldOutSkus);
// spu 维度, 仅添加一项 sku
displayInvalidSkus.push(soldOutSkus[0]);
}
});
const newData = {
...data,
reselectSkus,
invalidSkus,
displayInvalidSkus
};
this.trigger_('refreshSuccess', newData);
this.renderData = newData;
this.setLoading(false);
this.setDataResult(newData);
})
.catch(err => {
this.setLoading(false);
console.error(err);
this.trigger_('refreshError', data);
}).finally(() => {
});
}
async deleteReselect(id, ignoreEmit) {
if (!id) {
return;
}
const targetSku = this.renderData.reselectSkus.find(item => item.id === id);
try {
const res = await this.xhr_.fetchJson(`/api/cart/${targetSku.variant_id}`, {
method: 'DELETE',
body: {
id: targetSku.id,
product_id: targetSku.product_id,
variant_id: targetSku.variant_id,
}
});
const newData = {
...this.renderData,
reselectSkus: this.renderData.reselectSkus.filter(item => item.id !== id),
};
this.renderData = newData;
!ignoreEmit && this.trigger_('deleteSuccess', newData);
} catch (err) {
console.error(err);
!ignoreEmit && this.trigger_('deleteError', err);
}
}
deleteInvalid(id) {
if (!id) {
return;
}
const targetSku = this.renderData.invalidSkus.find(item => item.id === id);
this.xhr_.fetchJson(`/api/cart/${targetSku.variant_id}`, {
method: 'DELETE',
body: {
id: targetSku.id,
product_id: targetSku.product_id,
variant_id: targetSku.variant_id,
}
})
.then(res => {
const newData = {
...this.renderData,
invalidSkus: this.renderData.invalidSkus.filter(item => item.id !== id),
displayInvalidSkus: this.renderData.displayInvalidSkus.filter(item => item.id !== id),
};
this.trigger_('deleteSuccess', newData);
this.renderData = newData;
})
.catch(err => {
console.error(err);
this.trigger_('deleteError', err);
});
}
// record reselect sku id before show reselect modal
setReselectRecordId(id) {
if (!id) {
return;
}
this.recordReselectId = id;
}
async deleteReselectRecordBeforeRefresh(data) {
if (!this.recordReselectId) {
return;
}
await this.deleteReselect(this.recordReselectId, true);
return {
...data,
ineffectives: data.ineffectives.filter(item => item.id !== this.recordReselectId)
}
}
clearNeedReselectRecord() {
this.needDeleteReselectRecord = false;
}
registerDeleteReselectTask(productData) {
const targetSku = this.renderData.reselectSkus.find(item => item.id === this.recordReselectId);
if (targetSku?.variant_id && productData?.variant.id) {
if (targetSku.variant_id === productData?.variant.id) {
this.needDeleteReselectRecord = false;
return;
}
}
this.needDeleteReselectRecord = true;
}
// pause cart refresh(trigger by similar products)
lockRefresh() {
if (this.refreshLock) {
return;
}
this.refreshLock = true;
}
releaseRefreshLock() {
if (!this.refreshLock) {
return;
}
this.refreshLock = false;
this.refreshWithCache();
}
// direct add_to_cart(trigger by similar products)
async manualAddToCart(id) {
const target = this.similarData.find(item => item.id === id);
this.lockRefresh();
try {
const res = await this.xhr_.fetchJson(`/api/cart`, {
method: 'POST',
body: {
note: '',
product_id: target.id,
quantity: '1',
variant_id: target.variants[0].id,
refer_info: {
source: 'add_to_cart'
}
}
});
const newCartItems = res.data.items;
this.setRefreshCache(newCartItems, true);
} catch (err) {
console.error(err);
}
}
syncSimilarData(data) {
this.similarData = data.data;
}
setRefreshCache(data, patch) {
if (patch) {
this.refreshDataCache = {
...this.refreshDataCache,
line_items: [...this.refreshDataCache.line_items, ...data]
};
} else {
this.refreshDataCache = data;
}
}
refreshWithCache() {
this.refresh(this.refreshDataCache);
}
mountCallback() {
}
unmountCallback() {
}
/**
* trigger event
* @param {Object} data
*/
trigger_(name, data) {
const event = SPZUtils.Event.create(this.win, `spz-custom-cart-sku.${name}`, {
data,
});
this.action_.trigger(this.element, name, event);
}
}
SPZ.defineElement('spz-custom-cart-sku', SPZCustomCartSku);
}())
(function () {
class SPZCustomCartTrack extends SPZ.BaseElement {
constructor(element) {
super(element);
this.action_ = SPZServices.actionServiceForDoc(this.element);
}
isLayoutSupported(layout) {
return true;
}
buildCallback() {
this.setupAction_();
}
hash(val) {
const hashKey = Object.keys(val).sort((a, b) => a - b).reduce((acc, k) => {
acc += `{'${k}':'${val[k]}'}`;
return acc;
}, '');
return hashKey;
}
trackExtra(key, value) {
console.log('trackExtra...', key, value);
if (!window.sa) {
return;
}
const hashKey = this.hash(value);
let hasExtraInfo = false;
if (window.sa.eventInfo && window.sa.eventInfo[key] && window.sa.eventInfo[key].extra_properties) {
hasExtraInfo = window.sa.eventInfo[key].extra_properties.some(p => {
return hashKey === this.hash(p);
});
}
if (hasExtraInfo) {
return;
}
window.sa && window.sa.registerExtraInfo(key, value);
}
delExtraTrack(key, value) {
const hashKey = this.hash(value);
if (window.sa.eventInfo && window.sa.eventInfo[key] && window.sa.eventInfo[key].extra_properties) {
window.sa.eventInfo[key].extra_properties = window.sa.eventInfo[key].extra_properties.filter(p => hashKey !== this.hash(p));
}
}
track(key, value) {
console.log('tracking...', key, value);
window.sa && window.sa.track(key, value);
}
setupAction_() {
this.registerAction('registerReselectAtc', () => {
this.trackExtra('add_to_cart', {
function_name: 'Farida',
action_type: 'reselect'
});
});
this.registerAction('clearReselectAtc', () => {
this.delExtraTrack('add_to_cart', {
function_name: 'Farida',
action_type: 'reselect'
});
});
this.registerAction('registerSimilarAtc', () => {
this.trackExtra('add_to_cart', {
function_name: 'Farida',
action_type: 'similar_product'
});
});
this.registerAction('clearSimilarAtc', () => {
this.delExtraTrack('add_to_cart', {
function_name: 'Farida',
action_type: 'similar_product'
});
});
const clickParams = {
business_type: 'product_plugin',
event_name: 'function_click',
function_name: 'Farida',
plugin_name: 'Farida',
template_name: 'cart',
template_type: '13',
module: 'online_store',
module_type: 'online_store',
tab_name: '',
card_name: '',
event_developer: 'ccbken',
event_type: 'click',
};
this.registerAction('trackDelReselect', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'cart_delete',
element_type: 'sku'
})
});
});
this.registerAction('trackClickReselect', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'reselect',
element_type: 'sku'
})
});
});
this.registerAction('trackDelSimilar', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'cart_delete',
element_type: 'spu'
})
});
});
this.registerAction('trackClickSimilar', () => {
this.track('function_click', {
...clickParams,
event_info: JSON.stringify({
action_type: 'reselect',
element_type: 'spu'
})
});
});
this.registerAction('trackOpenSimilar', () => {
this.track('function_expose', {
...clickParams,
event_name: 'function_expose',
event_type: 'expose',
event_info: JSON.stringify({
popup_name: 'farida_product_popup'
})
});
});
}
}
SPZ.defineElement('spz-custom-cart-track', SPZCustomCartTrack);
}())
Invalid(${data.invalidSkus.length})
-
Sold out
${item.item_text}
-
*${item.quantity}
${item.item_text}
${discount_item.title}
(-)
-
${item.item_text}
Reselect product specialfications
Reselect product specialfications
Your shopping cart is empty
${function() {
const textArray = ("Save ").split(/\{\{\s*save_amount\}\}/);
if (textArray.length > 0 && textArray.length < 2) {
textArray.push('');
}
return textArray.map((text, index) => {
if (index == 0) {
return `${text}`;
}
return `
${text}
`;
}).join('');
}()}
${function() {
const textArray = ("Save ").split(/\{\{\s*save_amount\}\}/);
if (textArray.length > 0 && textArray.length < 2) {
textArray.push('');
}
return textArray.map((text, index) => {
if (index == 0) {
return `${text}`;
}
return `
${text}
`;
}).join('');
}()}
-
${discount_application.title}:
-
${data.invalid_msg}
Taxes and shipping cost are calculated at checkout
${data.invalid_msg}
${function() {
const textArray = ("Save ").split(/\{\{\s*save_amount\}\}/);
if (textArray.length > 0 && textArray.length < 2) {
textArray.push('');
}
return textArray.map((text, index) => {
if (index == 0) {
return `${text}`;
}
return `
${text}
`;
}).join('');
}()}
${function() {
const textArray = ("Save ").split(/\{\{\s*save_amount\}\}/);
if (textArray.length > 0 && textArray.length < 2) {
textArray.push('');
}
return textArray.map((text, index) => {
if (index == 0) {
return `${text}`;
}
return `
${text}
`;
}).join('');
}()}
${function() {
const textArray = ("Save ").split(/\{\{\s*save_amount\}\}/);
if (textArray.length > 0 && textArray.length < 2) {
textArray.push('');
}
return textArray.map((text, index) => {
if (index == 0) {
return `${text}`;
}
return `
${text}
`;
}).join('');
}()}
-
${discount_application.title}:
-
${data.invalid_msg}
Taxes and shipping cost are calculated at checkout
-
Subtotal:
-
Save:
-
-
${discount_application.title}:
-
Taxes and shipping cost are calculated at checkout
const summaryStickyRender = document.querySelector('#cart-drawer-summary-sticky-render');
if (summaryStickyRender) {
document.body.style.setProperty('--cart-drawer-summary-sticky-height', summaryStickyRender.clientHeight + 'px');
}
${function() {
const variantData = data.variant || data.product.variants.find(v => v.available) || data.product.variants[0];
const saveType = null;
const productLabelDiscountOn = null;
return `
${saveType == 'percentage'
? `-${variantData.off_ratio}%`
: `-`
}
`;
}()}
See detail
${function() {
const currentProduct = data.product;
// fix default selected variant
let selected = {};
if (!(data.selectedOptions || []).length) {
const selectedVariant = currentProduct.variants.find(v => v.available) || currentProduct.variants[0];
if (selectedVariant) {
selectedVariant.options.forEach((option, idx) => {
if (!selected[option.name]) {
selected[option.name] = [];
}
if (selected[option.name].indexOf(option.value) === -1) {
selected[option.name].push(option.value);
}
});
}
} else {
selected = selected.reduce((acc, item) => {
acc[item.name] = item.value;
return acc;
}, {});
}
return (currentProduct.options || []).map((option, index) => {
const optionName = option.name || '';
const position = `option${index + 1}`;
const isThumbImage = !!option.showThumbImage;
const variantType = "button";
const thumbStyle = "image";
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName}
`
}).join('');
}()}
${data.value}
${data.index + 1}/${data.total}
${Array(data.total).fill(0).map((num, index) => {
return ``;
}).join('')}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : '';
}
}()}
${function() {
const selectedVariant = data.variant;
const isSoldOut = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available));
const statusLan = isSoldOut ?
"Sold out" :
"Add to cart";
return `
`;
}()}
const summaryStickyRender = document.querySelector('#cart-reselect-drawer-summary-sticky-render');
if (summaryStickyRender) {
document.body.style.setProperty('--cart-reselect-drawer-summary-sticky-height', summaryStickyRender.clientHeight + 'px');
}
${function(){
const productData = data.product;
const product_options = productData.options.filter(Boolean) || [];
const selectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const statusLan = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available)) ?
"Sold out" :
"Add to cart";
const mobSelectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const mobInitialSlideIndex = productData.images.findIndex((item) => item.src === selectedVariant.image.src);
const mobImages = productData.images;
const mobImageSize = productData.images.length;
const stickyEvent = "cart-reselect-drawer-summary-sticky-render.toggleClass(class='visible', force=false);cart-reselect-drawer-summary-sticky-compensation-container.toggleClass(class='visible', force=false);cart-reselect-drawer-observer.restart();cart-reselect-drawer-summary-sticky-render.rerender(data=event);";
return `
${function() {
const force_image_size = 'natural';
const thumbnailPosition = 'below';
const selectedVariant = data.product.variants.find(v => v.available) || data.product.variants[0];
const initialSlideIndex = data.product.images.findIndex((item) => item.src === selectedVariant.image.src);
const images = data.product.images;
const imageSize = data.product.images.length;
return `
${data.product.images.map((item, index0) => {
const extra = index0 === initialSlideIndex ? 'ssr size-ratio="2"' : '';
return `
`;
}).join('')}
${imageSize > 1 ? `
${images.map((index1) => {
return ``;
}).join('')}
` : ''}
${imageSize > 1 ? `
${images.map((imageItem, index1) => {
const extra2 = initialSlideIndex == index1 ? 'selected' : '';
return`
`;
}).join('')}
` : ''}`;
}()}
`;
}()}
${function(){
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const variantData = currentSelectVariant || defaultVariant || data;
const retail_price = variantData.retail_price || 0;
return `
`
}()}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : '';
}
}()}
${(function(){
const get_random_six_digits = () => {
return Math.random().toString().slice(-6)
};
const wholesale_enabled = false;
const setting_product_image_display = "100%";
const product_image = data.image;
const secondary_image = data.secondImage;
const image_width = product_image.width;
let image_height = product_image.height;
if(setting_product_image_display == '100%'){
image_height = image_width;
}else if(setting_product_image_display == '133.33%'){
image_height = image_width * 1.3333;
};
let product_image_hover_on = false;
if(secondary_image.src){
product_image_hover_on = true;
}
const has_save_label = true && ((+data.compare_at_price) > (+data.price));
const is_single_variant = data.variants.length == 1;
const min_price_variant_href = (data.min_price_variant && data.min_price_variant.available) ? data.min_price_variant.withinUrl : data.withinUrl;
const retail_price_max = data.retail_price_max || data.compare_at_price_max;
const THUMBNAILS_MAX_SIZE = 3;
const thumbnails = data.thumbVariants.slice(0, THUMBNAILS_MAX_SIZE);
const image_wrap_id = 'image_wrap_' + get_random_six_digits();
const image_carousel_id = 'image_carousel_' + get_random_six_digits();
const thumbnails_selector_id = 'thumbnails_selector_' + get_random_six_digits();
const form_id = 'form_' + get_random_six_digits();
const mixed_wholesale = data.mixed_wholesale;
let atc_handler = '';
if (data.has_only_default_variant) {
atc_handler = `custom-cart-drawer-sku.manualAddToCart(id=${data.id});add-card-toast-success.showToast(content='Added successfully');`;
} else {
atc_handler = `cart-invalid-drawer-quick-shop-render.render(src='/api/products/${data.id}',redo=true);cart-invalid-product-detail-drawer.open();`;
}
return `
`
})()}
${function() {
const isEmpty = data && data.length === 0;
if (isEmpty) {
return `
oops! There are currently no similar products in the store
`;
}
return ``;
}()}
${Array(data.total).fill(0).map((num, index) => {
return ``;
}).join('')}
${function() {
let variantImageShowed = false;
const currentProduct = data.product;
// fix default selected variant
let selectedValues = data.selectedValues;
if (Object.keys(data.selectedValues.length === 0)) {
const selectedVariant = currentProduct.variants.find(v => v.available) || currentProduct.variants[0];
if (selectedVariant) {
selectedVariant.options.forEach((option, idx) => {
if (!selectedValues[option.name]) {
selectedValues[option.name] = [];
}
if (selectedValues[option.name].indexOf(option.value) === -1) {
selectedValues[option.name].push(option.value);
}
});
}
}
return (currentProduct.options || []).map((option, index) => {
const optionName = option.name || '';
const position = `option${index + 1}`;
let isThumbImage = false;
if (currentProduct.need_variant_image && !variantImageShowed) {
const variantNames = ["color"] || [];
for (let i = 0, len = variantNames.length; i < len; i++) {
const name = variantNames[i].toLowerCase();
if (name === optionName.toLowerCase()) {
isThumbImage = true;
variantImageShowed = true;
}
}
}
const variantType = "button";
const thumbStyle = "image";
return `
${optionName}
`
}).join('');
}()}
${data.originData && data.originData.value || data.value}
${function() {
const variantData = data.variant || data.product.variants.find(v => v.available) || data.product.variants[0];
const saveType = "percentage";
const productLabelDiscountOn = true;
return `
${saveType == 'percentage'
? `-${variantData.off_ratio}%`
: `-`
}
`;
}()}
See detail
${function(){
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const variantData = currentSelectVariant || defaultVariant || data;
const retail_price = variantData.retail_price || 0;
return `
`
}()}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : '';
}
}()}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : '';
}
}()}
${function() {
const selectedVariant = data.variant;
const isSoldOut = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available));
const statusLan = isSoldOut ?
"Sold out" :
"Add to cart";
return `
`;
}()}
const summaryStickyRender = document.querySelector('#cart-invalid-drawer-quick-shop-summary-sticky-render');
if (summaryStickyRender) {
document.body.style.setProperty('--cart-product-drawer-summary-sticky-height', summaryStickyRender.clientHeight + 'px');
}
${function() {
const variantData = data.variant || data.product.variants.find(v => v.available) || data.product.variants[0];
const saveType = "percentage";
const productLabelDiscountOn = true;
return `
${saveType == 'percentage'
? `-${variantData.off_ratio}%`
: `-`
}
`;
}()}
See detail
${function(){
const productData = data.product;
let product_change_event = '',
mouse_over_event = ' ',
mouse_out_event = '';
const product_options = productData.options.filter(Boolean) || [];
for (let opt of product_options) {
product_change_event = product_change_event + `cart-invalid-drawer-quick-shop-selected-variant-${opt.name}.rerender(data=event.selectedValues.${opt.name});`;
mouse_out_event = mouse_out_event + `cart-invalid-drawer-quick-shop-selected-variant-${opt.name}.rerender(data=event.selectData.${opt.name});`;
mouse_over_event = mouse_over_event + `@${opt.name}Mouseover="cart-invalid-drawer-quick-shop-selected-variant-${opt.name}.rerender(data=event);"`;
}
const selectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const statusLan = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available)) ?
"Sold out" :
"Add to cart";
const mobSelectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const mobInitialSlideIndex = productData.images.findIndex((item) => item.src === selectedVariant.image.src);
const mobImages = productData.images;
const mobImageSize = productData.images.length;
const stickyEvent = "cart-invalid-drawer-quick-shop-summary-sticky-render.toggleClass(class='visible', force=false);cart-invalid-drawer-quick-shop-summary-sticky-compensation-container.toggleClass(class='visible', force=false);cart-invalid-drawer-quick-shop-observer.restart();cart-invalid-drawer-quick-shop-summary-sticky-render.rerender(data=event);";
return `
${function() {
const force_image_size = 'natural';
const thumbnailPosition = 'below';
const selectedVariant = data.product.variants.find(v => v.available) || data.product.variants[0];
let initialSlideIndex = data.product.images.findIndex((item) => item.src === selectedVariant.image.src);
if (initialSlideIndex === -1) {
initialSlideIndex = 0;
}
const images = data.product.images;
const imageSize = data.product.images.length;
console.log('cart-product-drawer product images render', data, selectedVariant, initialSlideIndex, images, imageSize);
return `
${images.map((item, index0) => {
const extra = index0 === initialSlideIndex ? 'ssr size-ratio="2"' : '';
return `
`;
}).join('')}
${imageSize > 1 ? `
${images.map((index1) => {
return ``;
}).join('')}
` : ''}
${imageSize > 1 ? `
${images.map((imageItem, index1) => {
const extra2 = initialSlideIndex == index1 ? 'selected' : '';
return`
`;
}).join('')}
` : ''}`;
}()}
`;
}()}
${function(){
const productData = data.product;
const product_options = productData.options.filter(Boolean) || [];
const selectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const statusLan = ((selectedVariant && !selectedVariant.available) || (!selectedVariant && !productData.available)) ?
"Sold out" :
"Add to cart";
return `
`
}()}
${function(){
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const variantData = currentSelectVariant || defaultVariant || data;
const retail_price = variantData.retail_price || 0;
return `
`
}()}
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : '';
}
}()}
${function(){
return data.product.options.map((option, index) => {
const optionName = option.name || '';
const optionId = option.id || '';
let isThumbImage = !!option.showThumbImage;
const thumbStyle = "image";
const variantType = "button";
const isSelected = (value) => {
const selected = (data.selectedOptions || []).find(v => v.name === optionName);
return selected && selected.value.length && selected.value[0] == value;
};
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName}
`
}).join('');
}()}
${data.value}
const TAG = "spz-custom-popup";
const DISPLAY_TYPE = {
POPUP: "PTT_POPUP" // 弹窗
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取弹窗列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
class SpzCustomPopup extends SPZ.BaseElement {
constructor(element) {
super(element);
this.popupList_ = []; // 弹窗数据
this.popupZIndex = 1050; // 弹窗层级
// 节流处理 每5s内多次点击 算一次点击上报
this.throttleReport = this.win.SPZCore.Types.throttle(
this.win,
(data) => {
this.reportData(data)
},
5000
)
}
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.fetchData_();
}
// 接口请求,获取数据
fetchData_() {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.POPUP,
discount_id: id
}
}).then((res) => {
// 请求成功 执行render
this.doRender_(res.list);
}).catch((err) => {
console.error(err);
});
}
// 渲染dom
doRender_(data) {
this.popupList_ = data || [];
if (this.popupList_.length > 0) {
this.popupList_.forEach((item) => {
item.config = JSON.parse(item.config);
})
}
return this.templates_
.findAndRenderTemplate(this.element, { list: this.popupList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
// 遍历显示弹窗
this.popupList_.forEach((item) => {
this.showPopup_(item);
});
})
}
showPopup_(popup) {
// 展示弹窗 符合展示条件的弹窗
const $popup = document.querySelector(`#popup-${popup.id}`);
$popup && SPZ.whenApiDefined($popup).then((api)=> {
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = popup.device === DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = popup.device == DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
// 根据推送时间 延迟展示弹窗
setTimeout(() => {
api.open();
}, popup.delay_seconds * 1000);
}
})
}
// 上报数据
async reportData(data) {
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
setupAction_() {
this.registerAction('handleTrack', async(invocation) => {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
const data = invocation.args;
const event = data.event;
// 点击上报 节流处理
if(event === REPORT_EVENT.CLICK) {
await this.throttleReport(data);
} else {
this.reportData(data);
}
});
}
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, SpzCustomPopup);
const TAG = "spz-custom-announcement";
const DISPLAY_TYPE = {
ANNOUNCEMENT: "PTT_BANNER" // 公告栏
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取公告栏列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
const POSITION = {
TOP: "PP_TOP", // 顶部
BOTTOM: "PP_BOTTOM" // 底部
}
const MODE = {
FIXED: "PM_FIXED", // 固定
NORMAL: "PM_SCROLLING" // 滚动
}
const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name;
class SpzCustomAnnouncement extends SPZ.BaseElement {
constructor(element) {
super(element);
this.announcementList_ = []; // 公告栏数据
}
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.fetchData_();
this.createAnnouncementDom_();
this.listenCartChange_();
}
fetchData_(type = '') {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.ANNOUNCEMENT,
discount_id: id
}
}).then((res) => {
this.announcementList_ = res.list || [];
if (this.announcementList_.length > 0) {
this.announcementList_.forEach((item) => {
item.config = JSON.parse(item.config);
});
}
if(type === 'cartChange') {
this.announcementList_.forEach((item) => {
this.updateText_(item);
});
} else {
this.doRender_(this.announcementList_);
}
}).catch((error) => {
console.error(error);
})
}
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element, { list: this.announcementList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.announcementList_.forEach((item) => {
this.showAnnouncement_(item);
});
}).then(() => {
this.handleThemeCompatibility_();
});
}
// 更新文案
updateText_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementText = announcement.querySelectorAll('.announcement_text');
const textArr = item.config.text_discount.replace_texts;
const textDom = textArr.map((text) => {
return `${text}`;
}).join(',');
announcementText.forEach((text) => {
text.innerHTML = textDom;
});
}
// 创建公告栏dom
createAnnouncementDom_() {
const isHero = /Hero/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
// 创建滚动的底部公告栏
const announcementBottomContainer = document.createElement('div');
announcementBottomContainer.className = 'announcement__container_bottom bootstrap';
document.body.appendChild(announcementBottomContainer);
// 创建固定的底部公告栏
const announcementBottomSticky = document.createElement('ljs-sticky');
announcementBottomSticky.className = 'announcement__container_bottom-sticky';
announcementBottomSticky.setAttribute('layout', 'container');
announcementBottomSticky.setAttribute('position', 'bottom');
announcementBottomSticky.style.position = 'fixed';
announcementBottomSticky.style.bottom = '0';
announcementBottomSticky.style.left = '0';
announcementBottomSticky.style.right = '0';
announcementBottomSticky.style.zIndex = '1030';
document.body.appendChild(announcementBottomSticky);
const announcementTopContainer = document.createElement('div');
announcementTopContainer.classList.add('announcement__container_top');
if (isHero) {
announcementTopContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopContainer.classList.add('bootstrap');
document.body.insertBefore(announcementTopContainer, document.body.children[0]);
const announcementTopFixedContainer = document.createElement('div');
announcementTopFixedContainer.classList.add('announcement__container_top-fixed');
if (isHero) {
announcementTopFixedContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopFixedContainer.classList.add('bootstrap');
const insertBeforeElement = headerSticky ? headerEl : document.body;
insertBeforeElement.insertBefore(announcementTopFixedContainer, insertBeforeElement.children[0]);
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
let fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
if (isEvaMaskHeader) {
if (fixedBannerTopContainer) {
fixedBannerTopContainer.remove();
}
const newBanner = document.createElement('div');
newBanner.className = 'announcement__container_top-fixed bootstrap';
document.body.insertBefore(newBanner, document.body.firstChild);
fixedBannerTopContainer = newBanner;
} else {
if (!headerEl) return;
const observer = new MutationObserver(() => {
const isSticky = SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
if (!isSticky) return;
const isTopFixedAnnouncementInHeader = headerEl.querySelector('.announcement__container_top-fixed');
if (isTopFixedAnnouncementInHeader) return;
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
if (announcementTopFixedContainer) {
announcementTopFixedContainer.remove();
headerEl.insertBefore(announcementTopFixedContainer, headerEl.children[0]);
observer.disconnect();
}
});
observer.observe(headerEl, { attributes: true, attributeFilter: ['style', 'class'] });
}
if (headerSticky && !isEvaMaskHeader && fixedBannerTopContainer) {
fixedBannerTopContainer.style.position = 'relative';
fixedBannerTopContainer.style.zIndex = '29';
}
}
}
// 展示公告栏
showAnnouncement_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementBottomContainer = document.querySelector('.announcement__container_bottom');
const announcementBottomSticky = document.querySelector('.announcement__container_bottom-sticky');
const announcementTopContainer = document.querySelector('.announcement__container_top');
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = item.device === DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = item.device == DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
if (item.position === POSITION.BOTTOM) {
if(item.mode === MODE.FIXED) {
announcementBottomSticky && announcementBottomSticky.appendChild(announcement);
} else {
announcementBottomContainer && announcementBottomContainer.appendChild(announcement);
}
} else {
if (item.mode === MODE.FIXED) {
announcementTopFixedContainer && announcementTopFixedContainer.appendChild(announcement);
} else {
announcementTopContainer && announcementTopContainer.appendChild(announcement);
}
}
this.reportData({
placement_id: item.id,
event: REPORT_EVENT.IMPRESSION
});
}
}
// 处理主题兼容
handleThemeCompatibility_() {
try {
const isBoost = /Boost/.test(THEME_NAME);
const isHyde = /Hyde/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const boostHeader = document.querySelector('.boost-header');
const fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
const notFixedBannerTopContainer = document.querySelector('.announcement__container_top');
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
const header = document.querySelector('.header__fixed') || document.querySelector('.header__wrapper');
const headerFixed = header && SPZCore.Dom.computedStyle(this.win, header).position === 'fixed';
const handleScroll = SPZCore.Types.throttle(this.win, () => {
if (isHyde) {
if (header && headerSticky) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
if (!isEvaMaskHeader) return;
if (evaHeader.classList.contains('header__fixed')) {
evaHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(document.documentElement.scrollTop === 0) {
evaHeader.style.marginTop = '0';
}
}
if (headerSticky) return;
if (headerFixed) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
observer.disconnect(); // 停止观察
break;
}
}
});
// 开始观察 fixedBannerTopContainer 的子节点变化
observer.observe(fixedBannerTopContainer, { childList: true, subtree: true });
// 初始检查
if (fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(header) {
header.style.marginTop = '0';
}
}
if (isBoost) {
fixedBannerTopContainer.style.zIndex = '1031';
if (boostHeader && boostHeader.classList.contains('header__fixed')) {
boostHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
}, 16);
window.addEventListener('scroll', handleScroll);
window.dispatchEvent(new Event('scroll'));
} catch (error) {
console.error('error', error);
}
}
// 上报数据
async reportData(data) {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
// 监听购物车变化事件dj.cartChange
listenCartChange_() {
SPZUtils.Event.listen(document, 'dj.cartChange', (event) => {
this.fetchData_('cartChange');
});
}
setupAction_() {
this.registerAction('handleClose', (invocation) => {
const data = invocation.args;
const id = data.id;
const announcement = document.querySelector(`#announcement-${id}`);
announcement && SPZCore.Dom.removeElement(announcement);
window.dispatchEvent(new Event('scroll'));
});
this.registerAction('handleJumpLink', (invocation) => {
const data = invocation.args;
if(!data.show_url) return;
data.url && window.open(data.url, data.open_new_window ? '_blank' : '_self');
this.reportData({
placement_id: data.id,
event: REPORT_EVENT.CLICK
});
});
}
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, SpzCustomAnnouncement);
${function() {
return data.originData.list.map((item) => {
const background = item.config.background;
const interactive = item.config.interactive;
const textArr = item.config.text_discount.replace_texts;
const textColor = item.config.text_discount.color;
const backgroundSize = background.presentation_rule === 'fill' ? 'cover' : 'contain';
const pcImage = (background.url && background.upload) ? background.url : '';
const mobileImage = (background.mobile_url && background.upload) ? background.mobile_url : '';
const color1 = background.color;
const color2 = background.color2 || background.color;
const backgroundStyle = `background: url(//img.staticdj.com/${pcImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
const backgroundMobileStyle = `background: url(//img.staticdj.com/${mobileImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
return `
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
`
})
}()}