MD005 Robot Toy Model Action Figure G1 Optimus Prime OP Transformers SS86 Upgraded Version Collectible Convertible Robot Classic G1 OP Prime SS86 Enhancement Transforming Figure Toy SS86 Optimus Prime Upgrade Edition Action Doll
${function() {
const variantData = data.variant || {"id":"c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","product_id":"00f293d1-e8c9-4254-b5d4-5635d4d44848","title":"","weight_unit":"g","inventory_quantity":10,"sku":"MD005A","barcode":"","position":1,"option1":"","option2":"","option3":"","note":"","image":null,"wholesale_price":[{"price":57.99,"min_quantity":1}],"weight":"1109","compare_at_price":"0","price":"56.25","retail_price":"0","available":true,"url":"\/products\/md005-robot-toy-model-action-figure-g1-optimus-prime-op-transformers-ss86-upgraded-version-collectible-convertible-robot-classic-g1-op-prime-ss86-enhancement-transforming-figure-toy-ss86-optimus-prime-upgrade-edition-action-doll?variant=c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","available_quantity":999999999,"options":[],"off_ratio":"0","flashsale_info":{"variant_id":"c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","product_id":"","quantity":0,"discount_id":"","limit_time":0,"limit_buy":0,"user_limit_buy":0,"discount_sales":0,"discount_sales_rate":"","discount_stock":0,"ends_at":0,"starts_at":0,"allow_oversold":"","allocation_method":"","price":"56.25","compare_at_price":"","discount_price":"56.25","customary_saved_price":"","customary_off_ratio":"","discount_saved_price":"","discount_off_ratio":"0","use_before_price":false,"before_price":"","title":"","properties":"","color_setting_promotional_copy":"","discount_quantity":0,"is_need_split":false},"sales":5};
const saveType = "amount";
const productLabelDiscountOn = true;
return `
-
${saveType == 'percentage'
? `-${variantData.off_ratio}%`
: `-`
}
`;
}()}
${function(){
// 获取基础配置数据
const isRTL = originData.isRTL; // 是否从右到左显示
const isMobile = originData.isMobile; // 是否移动端
const inProductDetail = originData.inProductDetail; // 是否在商品详情页
const image_domain = originData.image_domain; // 图片域名
const copyBtnClass = originData.copyBtnClass; // 复制按钮类名
const copyClass = originData.copyClass; // 复制内容类名
// 优惠码列表
const list = originData.discountCodeData.list || [];
if (list.length === 0) {
return ``;
}
// 显示模式: 平铺或折叠
const isTiled = originData.discountCodeData.display_type === "DTE_TILE";
// 获取第一个配置作为默认配置
const productSetting = list[0].product_setting;
const { template_type, template_config } = productSetting;
const { text, tag, banner, coupon } = template_config.color;
// 优惠券样式配置
const {
coupon_border_color, // 边框颜色
coupon_background_color, // 背景色起始色
coupon_background_color_end, // 背景色结束色
coupon_color, // 文字颜色
coupon_button_background_color_start, // 按钮背景起始色
coupon_button_background_color_end, // 按钮背景结束色
coupon_button_color // 按钮文字颜色
} = coupon;
// 颜色透明度转换函数
const colorToRGBA = (color, alpha) => {
if (color.startsWith('#')) {
const hex = color.slice(1);
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
else if (color.includes('rgb')) {
const rgb = color.slice(color.indexOf('(') + 1, color.indexOf(')')).split(',');
const r = parseInt(rgb[0]);
const g = parseInt(rgb[1]);
const b = parseInt(rgb[2]);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
else {
return color
}
}
// 渲染平铺样式的优惠券列表
const renderTiledList = (list, isFold = false) => {
const rate = isRTL ? '34%' : '66%'; // RTL布局时调整分割线位置
let curDom = `
`;
// 循环渲染优惠券列表
for(const item of list) {
curDom += `
${item.discount_text}
${(isFold || item.product_setting.show_effective_date) && +item.ends_at !== -1 ? `${item.starts_at} - ${item.ends_at}` : ""}
`
}
return curDom;
}
let Dom = ``;
// 根据显示模式选择渲染方式
if (isTiled || list.length === 1) {
// 根据模板类型渲染不同样式
switch(template_type) {
// 文本样式
case 'text': {
Dom = `
${function(){
let textDom = '';
for(const item of list) {
textDom += `
${ item.discount_text }
Code:
${ item.code }
`
}
return textDom;
}()}
`;
break;
}
// 标签样式
case 'tag': {
const { text_color, background_color } = tag;
Dom = `
${function(){
let textDom = '';
for(const item of list) {
textDom += `
${ item.discount_text }
Code:
${ item.code }
`;
}
return textDom;
}()}
`
break;
}
// 横幅样式
case 'banner': {
Dom = `
${function(){
let textDom = '';
for(const item of list) {
textDom += `
${ item.discount_text }
Code:
${ item.code }
`;
}
return textDom;
}()}
`;
break;
}
// 优惠券样式
case 'coupon': {
Dom = `
${renderTiledList(list)}
`
break;
}
}
} else {
// 折叠模式样式
const foldStyle = `
`;
// 只显示前3个优惠券
const showList = list.slice(0, 3);
let couponDom = "";
for(const item of showList) {
couponDom += `
${item.discount_text}
`
}
// PC端弹窗
const webModal = `
${function(){
return renderTiledList(list, true);
}()}
`
// 移动端侧边栏
const mobileModal = `
`
couponDom += `
Click to claim
${isMobile ? mobileModal : webModal}
`;
Dom = `
${foldStyle}
${couponDom}
`
}
return `
${Dom}
`;
}()}
/**
* 优惠码组件模型类
* 处理优惠码的显示和交互逻辑
*/
class SpzCustomDiscountCodeModel extends SPZ.BaseElement {
constructor(element) {
super(element);
// 复制按钮和内容的类名
this.copyBtnClass = "discount_code_btn"
this.copyClass = "discount_code_value"
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
// 初始化服务
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
}
/**
* 渲染优惠码组件
* @param {Object} data - 渲染数据
*/
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element,
Object.assign(this.getDefaultData(), data)
)
.then((el) => {
this.clearDom();
this.element.appendChild(el);
// 绑定复制代码功能
this.copyCode(el, data);
});
}
/**
* 获取渲染模板
* @param {Object} data - 渲染数据
*/
getRenderTemplate(data) {
const renderData = Object.assign(this.getDefaultData(), data);
return this.templates_
.findAndRenderTemplate(this.element, renderData)
.then((el) => {
this.clearDom();
return el;
});
}
/**
* 清除DOM内容
*/
clearDom() {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
}
/**
* 获取默认数据
* @returns {Object} 默认数据对象
*/
getDefaultData() {
return {
isMobile: appDiscountUtils.judgeMobile(),
isRTL: appDiscountUtils.judgeRTL(),
image_domain: this.win.SHOPLAZZA.image_domain,
copyBtnClass: this.copyBtnClass,
copyClass: this.copyClass
}
}
/**
* 复制优惠码功能
* @param {Element} el - 当前元素
*/
copyCode(el) {
const copyBtnList = el.querySelectorAll(`.${this.copyBtnClass}`);
if (copyBtnList.length > 0) {
copyBtnList.forEach(item => {
item.onclick = async () => {
// 确保获取正确的元素和内容
const codeElement = item.querySelector(`.${this.copyClass}`);
if (!codeElement) return;
// 获取纯文本内容
const textToCopy = codeElement.innerText.trim();
// 尝试使用现代API,如果失败则使用备用方案
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
await navigator.clipboard.writeText(textToCopy);
} else {
throw new Error('Clipboard API not available');
}
// 显示复制成功提示
this.showCopySuccessToast(textToCopy, el);
} catch (err) {
console.error('Modern clipboard API failed, trying fallback...', err);
// 使用备用复制方案
this.fallbackCopy(textToCopy, el);
}
const discountId = item.dataset["discountId"];
// 是否跳转落地页配置
const redirection = item.dataset["redirection"] === "true";
// 跳转到落地页
if (redirection && appDiscountUtils.inProductBody(this.element)) {
this.win.open(`/promotions/discount-default/${discountId}`);
}
}
})
}
}
/**
* 使用 execCommand 的复制方案
* @param {string} codeText - 要复制的文本
* @param {Element} el - 当前元素
*/
fallbackCopy(codeText, el) {
const textarea = this.win.document.createElement('textarea');
textarea.value = codeText;
// 设置样式使文本框不可见
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '0';
// 添加 readonly 属性防止移动端虚拟键盘弹出
textarea.setAttribute('readonly', 'readonly');
this.win.document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
this.win.document.execCommand('copy');
// 显示复制成功提示
this.showCopySuccessToast(codeText, el);
} catch (err) {
console.error('Copy failed:', err);
}
this.win.document.body.removeChild(textarea);
}
/**
* 创建 Toast 元素
* @returns {Element} 创建的 Toast 元素
*/
createToastEl_() {
const toast = document.createElement('ljs-toast');
toast.setAttribute('layout', 'nodisplay');
toast.setAttribute('hidden', '');
toast.setAttribute('id', 'discount-code-toast');
toast.style.zIndex = '1051';
return toast;
}
/**
* 挂载 Toast 元素到 body
* @returns {Element} 挂载的 Toast 元素
*/
mountToastToBody_() {
const existingToast = this.win.document.getElementById('discount-code-toast');
if (existingToast) {
return existingToast;
}
const toast = this.createToastEl_();
this.win.document.body.appendChild(toast);
return toast;
}
/**
* 复制成功的提醒
* @param {string} codeText - 要复制的文本
* @param {Element} el - 当前元素
*/
showCopySuccessToast(codeText, el) {
const $toast = this.mountToastToBody_();
SPZ.whenApiDefined($toast).then(toast => {
toast.showToast("Discount code copied !");
this.codeCopyInSessionStorage(codeText);
});
}
/**
* 复制优惠码成功后要存一份到本地存储中,购物车使用
* @param {string} codeText - 要复制的文本
*/
codeCopyInSessionStorage(codeText) {
try {
sessionStorage.setItem('other-copied-coupon', codeText);
} catch (error) {
console.error(error)
}
}
}
// 注册自定义元素
SPZ.defineElement('spz-custom-discount-code-model', SpzCustomDiscountCodeModel);
${function(){
return `
Discount code cannot be displayed here. Please move it to the product detail area.
(This prompt will not be displayed on the client-side)
`;
}()}
/**
* Custom discount code component that handles displaying and managing discount codes
* @extends {SPZ.BaseElement}
*/
class SpzCustomDiscountCode extends SPZ.BaseElement {
constructor(element) {
super(element);
// API endpoint for fetching discount codes
this.getDiscountCodeApi = "\/api\/storefront\/promotion\/code\/list";
// Debounce timer for resize events
this.timer = null;
// Current variant ID
this.variantId = "c3dbea2e-ac5f-49e0-88b6-30af50ee0e45";
// Store discount code data
this.discountCodeData = {}
}
/**
* Check if layout is supported
* @param {string} layout - Layout type
* @return {boolean}
*/
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
/**
* Initialize component after build
*/
buildCallback() {
this.templates_ = SPZServices.templatesForDoc();
this.viewport_ = this.getViewport();
// Bind methods to maintain context
this.render = this.render.bind(this);
this.resize = this.resize.bind(this);
this.switchVariant = this.switchVariant.bind(this);
}
/**
* Setup component when mounted
*/
mountCallback() {
this.getData();
// Add event listeners
this.viewport_.onResize(this.resize);
this.win.document.addEventListener('dj.variantChange', this.switchVariant);
}
/**
* Cleanup when component is unmounted
*/
unmountCallback() {
this.viewport_.removeResize(this.resize);
this.win.document.removeEventListener('dj.variantChange', this.switchVariant);
// 清除定时器
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
/**
* Handle resize events with debouncing
*/
resize() {
if (this.timer) {
clearTimeout(this.timer)
this.timer = null;
}
this.timer = setTimeout(() => {
if (appDiscountUtils.inProductBody(this.element)) {
this.render();
} else {
this.renderSkeleton();
}
}, 200);
}
/**
* Handle variant changes
* @param {Event} event - Variant change event
*/
switchVariant(event) {
const variant = event.detail.selected;
if (variant.product_id == '00f293d1-e8c9-4254-b5d4-5635d4d44848' && variant.id != this.variantId) {
this.variantId = variant.id;
this.getData();
}
}
/**
* Fetch discount code data from API
*/
getData() {
if (appDiscountUtils.inProductBody(this.element)) {
const reqBody = {
product_id: "00f293d1-e8c9-4254-b5d4-5635d4d44848",
variant_id: this.variantId,
product_type: "",
}
if (!reqBody.product_id || !reqBody.variant_id) return;
this.discountCodeData = {};
this.win.fetch(this.getDiscountCodeApi, {
method: "POST",
body: JSON.stringify(reqBody),
headers: {
"Content-Type": "application/json"
}
}).then(async (response) => {
if (response.ok) {
let data = await response.json();
if (data.list && data.list.length > 0) {
data.list[0].product_setting.template_config = JSON.parse(data.list[0].product_setting.template_config);
// Format timestamps to local timezone
const zone = this.win.SHOPLAZZA.shop.time_zone;
data.list = data.list.map(item => {
if(+item.ends_at !== -1) {
item.ends_at = appDiscountUtils.convertTimestampToFormat(+item.ends_at, zone);
}
item.starts_at = appDiscountUtils.convertTimestampToFormat(+item.starts_at, zone);
return item;
});
}
this.discountCodeData = data;
this.render();
} else {
this.clearDom();
}
}).catch(err => {
console.error("discount_code", err)
this.clearDom();
});
} else {
this.renderSkeleton();
}
}
/**
* Clear component DOM except template
*/
clearDom() {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
}
/**
* Render discount codes with formatted dates
*/
render() {
// Render using discount code model
SPZ.whenApiDefined(document.querySelector('#spz_custom_discount_code_model')).then(renderApi => {
renderApi.doRender_({
discountCodeData: this.discountCodeData
})
}).catch(err => {
this.clearDom();
})
}
renderSkeleton() {
// Render template for non-product pages
this.templates_
.findAndRenderTemplate(this.element, {
isMobile: appDiscountUtils.judgeMobile()
})
.then((el) => {
this.clearDom();
this.element.appendChild(el);
})
.catch(err => {
this.clearDom();
});
}
}
// Register custom element
SPZ.defineElement('spz-custom-discount-code', SpzCustomDiscountCode);
${function(){
const isRTL = originData.isRTL;
const isMobile = originData.isMobile;
const inProductDetail = originData.inProductDetail;
if (!inProductDetail) {
return `
Flash sale cannot be displayed here. Please move it to the product detail area.
(This prompt will not be displayed on the client-side)
`;
}
const flashsaleData = originData.flashsaleData;
const image_domain = originData.image_domain;
const discount_info = flashsaleData.discount_info;
const setting = flashsaleData.product_setting;
if (!discount_info || !setting) return ``;
const displayType = setting.display_type;
const banner = setting.banner;
const productDisplay = setting.product_display;
const bannerConfig = JSON.parse(banner.config);
const productDisplayConfig = JSON.parse(productDisplay.config);
const colorConfig = productDisplayConfig.color;
const countdownConfig = productDisplayConfig.countdown;
let titleIcon = ``;
let bgImg = ``;
const deg = `${isRTL ? -90 : 90}deg`;
const { banner_bg_start, banner_bg_end, banner_text } = colorConfig;
let bgStyle = `background: linear-gradient(${deg}, ${banner_bg_start} 0%, ${banner_bg_end} 100%);`;
let bannerColor = `color: ${banner_text};`
if (banner.type === "TYPE_CUSTOM") {
const { desktop, mobile, image_render } = bannerConfig;
const bgSize = image_render === "fill" ? "background-size: cover;background-position: center;" : "background-position: center;background-size: auto 100%;";
if (isMobile && mobile) {
bgStyle = `background: URL(${image_domain + mobile});${bgSize}`;
} else if (!isMobile && desktop) {
bgStyle = `background: URL(${image_domain + desktop});${bgSize}`;
}
} else {
if (banner.type === "TYPE_ONE") {
titleIcon = productDisplay.text ? `
` : "";
bgImg = ``
}
if (banner.type === "TYPE_TWO") {
titleIcon = productDisplay.text ? `
` : ""
}
}
const { sale_bar_background_color, progress_sale_bar_background_color_start, progress_sale_bar_background_color_end } = colorConfig;
const rate = discount_info.discount_sales_rate;
const progressBarStyle = `background: linear-gradient(${deg}, ${progress_sale_bar_background_color_start} 0%, ${progress_sale_bar_background_color_end} 100%);`;
const saleCount = productDisplay.sales_progress.format === "FORMAT_NUMBER" ? discount_info.discount_sales : `${rate}%`;
const progressBarDom = productDisplay.sales_progress.enabled && rate > 0 ? `
` : ``;
const { countdown_bg, countdown_text } = colorConfig;
const { end_opened, end_format } = countdownConfig;
const tempMap = {
"DD_SSS": "DD:HH:mm:ss:SSS",
"HH_SSS": "HH:mm:ss:SSS",
"DD_ss": "DD:HH:mm:ss",
"HH_ss": "HH:mm:ss"
}
const isShowDD = [tempMap.DD_SSS, tempMap.DD_ss].includes(end_format);
const isShowSSS = [tempMap.DD_SSS, tempMap.HH_SSS].includes(end_format);
const countdown = discount_info.ends_remaining_seconds;
const countdownDom = end_opened ? `
${function() {
if (banner.type === "TYPE_TWO") return `
`
return ``;
}()}
` : ``;
const flashSaleDesc = discount_info.limit_user_product_discount > -1 ? `
Promo products limited to ${discount_info.limit_user_product_discount} item per person
` : "";
return `
${bgImg}
${titleIcon}
${productDisplay.text}
${progressBarDom}
${countdownDom}
${flashSaleDesc}
`}()}
${function(){
const dd = data.dd; const hh = data.hh; const mm = data.mm; const ss = data.ss; const sss = data.SSS;
const hours = data.d * 24 + data.h;
return `
${dd}D
·
${hours}
${hh}
:
${mm}
:
${ss}
.
${sss}
`
}()}
class SpzCustomDiscountFlashsale extends SPZ.BaseElement {
constructor(element) {
super(element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.getFlashSaleApi = "\/api\/storefront\/promotion\/flashsale\/display_setting\/product_setting";
this.timer = null;
this.variantId = "c3dbea2e-ac5f-49e0-88b6-30af50ee0e45";
// 促销活动数据
this.flashsaleData = {}
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.templates_ = SPZServices.templatesForDoc();
this.viewport_ = this.getViewport();
// 挂载bind函数 解决this指向问题
this.render = this.render.bind(this);
this.resize = this.resize.bind(this);
this.switchVariant = this.switchVariant.bind(this);
}
mountCallback() {
// 获取数据
this.getData();
this.element.onclick = (e) => {
const cur = this.win.document.querySelector(".app_discount_flashsale_desc");
if (this.flashsaleData.product_setting.is_redirection && appDiscountUtils.inProductBody(this.element) && e.target !== cur) {
this.win.open(`/promotions/discount-default/${this.flashsaleData.discount_info.id}`);
}
}
// 绑定
this.viewport_.onResize(this.resize);
// 监听子款式切换,重新渲染
this.win.document.addEventListener('dj.variantChange', this.switchVariant);
}
unmountCallback() {
// 解绑
this.viewport_.removeResize(this.resize);
this.win.document.removeEventListener('dj.variantChange', this.switchVariant);
// 清除定时器
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
resize() {
if (this.timer) {
clearTimeout(this.timer)
this.timer = null;
}
this.timer = setTimeout(() => {
this.render();
}, 200)
}
switchVariant(event) {
const variant = event.detail.selected;
if (variant.product_id == '00f293d1-e8c9-4254-b5d4-5635d4d44848' && variant.id != this.variantId) {
this.variantId = variant.id;
this.getData();
}
}
getData() {
const reqBody = {
product_id: "00f293d1-e8c9-4254-b5d4-5635d4d44848",
product_type: "",
variant_id: this.variantId
}
this.flashsaleData = {};
this.win.fetch(this.getFlashSaleApi, {
method: "POST",
body: JSON.stringify(reqBody),
headers: {
"Content-Type": "application/json"
}
}).then(async (response) => {
if (response.ok) {
this.flashsaleData = await response.json();
this.render();
} else {
this.clearDom();
}
}).catch(err => {
this.clearDom();
});
}
clearDom() {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
}
render() {
this.templates_
.findAndRenderTemplate(this.element, {
isMobile: appDiscountUtils.judgeMobile(),
isRTL: appDiscountUtils.judgeRTL(),
inProductDetail: appDiscountUtils.inProductBody(this.element),
flashsaleData: this.flashsaleData,
image_domain: this.win.SHOPLAZZA.image_domain,
})
.then((el) => {
this.clearDom();
this.element.appendChild(el);
})
}
}
SPZ.defineElement('spz-custom-discount-flashsale', SpzCustomDiscountFlashsale);
${(function () {
const automatic_discount_list = originData.automatic_discount_list;
// 显示类型
const DISPLAY_TYPE = {
DTE_FOLD: 'DTE_FOLD', // 折叠
DTE_TILE: 'DTE_TILE' // 平铺
}
const DEFAULT_CONFIG = {
BG: 'rgba(235, 57, 27, 0.04)',
TEXT_COLOR: '#EB391B',
BORDER_COLOR: 'rgb(235, 57, 27)'
};
const isExist = automatic_discount_list?.length > 0 && automatic_discount_list.some(item => item.discount[0].product_enabled);
// 如果没有任何自动折扣,则隐藏,防止gap占位
if (!isExist) {
return `
`;
} else {
return `
${(function () {
return automatic_discount_list.map((item) => {
// 模版类型
const template_type = item.discount[0].template_type;
// 是否显示自动折扣
const is_show_automatic_discount = item.discount[0].product_enabled;
// 是否跳转落地页
const is_redirection = item.discount[0].is_redirection;
// 折扣图标
const discount_icon = item.discount_icon;
// 第一个自动折扣
const first_automatic_discount = item.discount[0];
// 显示折叠展示
const isFold = (item.discount[0].display_type || DISPLAY_TYPE.DTE_FOLD) === DISPLAY_TYPE.DTE_FOLD;
// 文本数组
const text_arr = item.discount[0].config.texts;
// 落地页链接
const first_landing_url = `/promotions/discount-default/${first_automatic_discount.discount_id}`;
// 自动折扣总数
const automatic_discount_total = item.discount.length;
// 是否显示折扣图标
const isHasDiscountIcon = discount_icon ? true : false;
// 是否显示折扣图标且模版类型不为tag
const isHasDiscountIconWithNoTag = (template_type != 'tag' && isHasDiscountIcon)? true : false;
// 文本颜色
let text_color = DEFAULT_CONFIG.TEXT_COLOR;
// 背景颜色
const bgFn = (curBg) => template_type === "text" ? "transparent" : curBg;
let bg_color = bgFn(DEFAULT_CONFIG.BG);
// 边框颜色
const borderFn = (curBorder) => template_type == "tag" ? curBorder : "initial";
let border_color = borderFn(DEFAULT_CONFIG.BORDER_COLOR);
// 模版配置
let template_config = first_automatic_discount.template_config;
// 兜底方案
try {
if(template_config.length !== 0){
template_config = JSON.parse(template_config);
text_color= isHasDiscountIconWithNoTag ? template_config.color[template_type].icon_text_color : template_config.color[template_type].text_color;
bg_color = bgFn(template_config.color[template_type].background_color);
const arrayRgba = bg_color.split(",");
arrayRgba.splice(3, 1, " 1)");
border_color = borderFn(`${arrayRgba.join(",")}`);
}
} catch (error) {
console.error('template_config_error', error);
template_config = {
color: {
[template_type]: {
icon_text_color: DEFAULT_CONFIG.TEXT_COLOR,
text_color: DEFAULT_CONFIG.TEXT_COLOR,
background_color: DEFAULT_CONFIG.BG
}
}
};
}
// 标签
const isTag = template_type == 'tag';
// 文字和横幅
const isTextAndBanner = template_type == 'text' || template_type == 'banner';
// 文字样式
const textStyle = `color: ${text_color}; background-color: transparent; border: none;`;
// 标签样式
const labelStyle = `color: ${text_color};border: 1px solid ${border_color};background-color:${bg_color};padding: 4px;`;
// 横幅样式
const bannerStyle = `color: ${text_color};border: none; background-color:${bg_color}`;
// 外层样式在标签样式下不展示颜色配置,除开标签类型,颜色都可以在外层覆盖
let outerStyle = '';
if (template_type == 'text') {
outerStyle = textStyle;
} else if (template_type == 'tag') {
outerStyle = "border: none;";
} else if (template_type == 'banner') {
outerStyle = bannerStyle;
}
/**
* 1. 标签一定是单独样式展示的
* 2. 折叠:横向布局,文字和横幅,合并成一行文本; 标签:单独样式处理
* 3. 平铺:纵向布局,文字、横幅和标签; 标签:单独样式处理
*/
let txtHtml = ``;
if (isFold) {
if(isTag) {
// 标签
const spanText = text_arr.map((text) => {
return `
${text}`;
}).join('');
txtHtml = `
${spanText}
`;
} else {
// 文字和横幅
txtHtml = `
${first_automatic_discount.config.text}
`;
}
} else {
// 文字和横幅, 但标签有自己的样式
const spanText = text_arr.map((text) => {
return `
${text}`;
}).join('');
// 都是纵向布局,标签有间距
txtHtml = `
${spanText}
`;
}
/**
* 显示图标的判断
*/
const discount_type = item.discount_type;
const isShowRebateIcon = ["DT_REBATE_CTQ_OTP", "DT_REBATE_CTQ_OTR", "DT_REBATE_CTA_OTP", "DT_REBATE_CTA_OTR", "DT_M_N_DISCOUNT"].includes(discount_type) && isTextAndBanner
const isShowBxgyIcon = ["DT_BUY_ONE_GET_ONE", "DT_BUY_X_GET_Y"].includes(discount_type)
const isShowBundleIcon = ["DT_CLASSIC_BUNDLE","DT_MIX_MATCH_BUNDLE"].includes(discount_type);
/**
* 渲染下拉框或抽屉的折扣列表
*/
const discount_list_html = (curItem) => {
return `
${function() {
return curItem.discount.map(childItem => {
return `
`}).join('');
}()}
`;
}
return `
${discount_list_html(item)}
${function() {
return text_arr.map((text) => {
return `
${text}
`;
}).join('');
}()}
`;
}).join('');
})()}
`
}
})()}
const TAG = "spz-custom-product-automatic";
class SpzCustomProductAutomatic extends SPZ.BaseElement {
constructor(element) {
super(element);
this.variant_id = 'c3dbea2e-ac5f-49e0-88b6-30af50ee0e45';
this.isRTL = SPZ.win.document.dir === 'rtl';
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.init();
// 监听事件
this.bindEvent_();
}
async init() {
this.handleFitTheme();
const data = await this.getDiscountList();
this.renderApiData_(data);
}
async getDiscountList() {
const productId = '00f293d1-e8c9-4254-b5d4-5635d4d44848';
const variantId = this.variant_id;
const productType = '';
const reqBody = {
product_id: productId,
variant_id: variantId,
discount_method: "DM_AUTOMATIC",
customer: {
customer_id: window.C_SETTINGS.customer.customer_id,
email: window.C_SETTINGS.customer.customer_email
},
product_type: productType
}
const url = `/api/storefront/promotion/display_setting/text/list`;
const data = await this.xhr_.fetchJson(url, {
method: "post",
body: reqBody
}).then(res => {
return res;
}).catch(err => {
this.setContainerDisabled(false);
})
return data;
}
async renderDiscountList() {
this.setContainerDisabled(true);
const data = await this.getDiscountList();
this.setContainerDisabled(false);
// 重新渲染 抖动问题处理
this.renderApiData_(data);
}
clearDom() {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
}
async renderApiData_(data) {
const parentDiv = document.querySelector('.automatic_discount_container');
const newTplDom = await this.getRenderTemplate(data);
if (parentDiv) {
parentDiv.innerHTML = '';
parentDiv.appendChild(newTplDom);
} else {
console.log('automatic_discount_container is null');
}
}
doRender_(data) {
const renderData = data || {};
return this.templates_
.findAndRenderTemplate(this.element, renderData)
.then((el) => {
this.clearDom();
this.element.appendChild(el);
});
}
async getRenderTemplate(data) {
const renderData = data || {};
return this.templates_
.findAndRenderTemplate(this.element, { ...renderData, isRTL: this.isRTL })
.then((el) => {
this.clearDom();
return el;
});
}
setContainerDisabled(isDisable) {
const automaticDiscountEl = document.querySelector('.automatic_discount_container_outer');
if(isDisable) {
automaticDiscountEl.setAttribute('disabled', '');
} else {
automaticDiscountEl.removeAttribute('disabled');
}
}
// 绑定事件
bindEvent_() {
window.addEventListener('click', (e) => {
let containerNodes = document.querySelectorAll(".automatic-container .panel");
let bool;
Array.from(containerNodes).forEach((node) => {
if(node.contains(e.target)){
bool = true;
}
})
// 是否popover面板点击范围
if (bool) {
return;
}
if(e.target.classList.contains('drowdown-icon') || e.target.parentNode.classList.contains('drowdown-icon')){
return;
}
const nodes = document.querySelectorAll('.automatic-container');
Array.from(nodes).forEach((node) => {
node.classList.remove('open-dropdown');
})
// 兼容主题
this.toggleProductSticky(true);
})
// 监听变体变化
document.addEventListener('dj.variantChange', async(event) => {
// 重新渲染
const variant = event.detail.selected;
if (variant.product_id == '00f293d1-e8c9-4254-b5d4-5635d4d44848' && variant.id != this.variant_id) {
this.variant_id = variant.id;
this.renderDiscountList();
}
});
}
// 兼容主题
handleFitTheme() {
// top 属性影响抖动
let productInfoEl = null;
if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') {
productInfoEl = document.querySelector('.product-info-body .product-sticky-container');
} else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') {
productInfoEl = document.querySelector('.product__info-wrapper .properties-content');
}
if(productInfoEl){
productInfoEl.classList.add('force-top-auto');
}
}
// 兼容 wind/flash /hero 主题 (sticky属性影响 popover 层级展示, 会被其他元素覆盖)
toggleProductSticky(isSticky) {
let productInfoEl = null;
if (window.SHOPLAZZA.theme.merchant_theme_name === 'Wind' || window.SHOPLAZZA.theme.merchant_theme_name === 'Flash') {
productInfoEl = document.querySelector('.product-info-body .product-sticky-container');
} else if (window.SHOPLAZZA.theme.merchant_theme_name === 'Hero') {
productInfoEl = document.querySelector('.product__info-wrapper .properties-content');
}
if(productInfoEl){
if(isSticky) {
// 还原该主题原有的sticky属性值
productInfoEl.classList.remove('force-position-static');
return;
}
productInfoEl.classList.toggle('force-position-static');
}
}
setupAction_() {
this.registerAction('handleDropdown', (invocation) => {
const discount_id = invocation.args.discount_id;
const nodes = document.querySelectorAll('.automatic-container');
Array.from(nodes).forEach((node) => {
if(node.getAttribute('id') != `automatic-${discount_id}`) {
node.classList.remove('open-dropdown');
}
})
const $discount_item = document.querySelector(`#automatic-${discount_id}`);
$discount_item && $discount_item.classList.toggle('open-dropdown');
// 兼容主题
this.toggleProductSticky();
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomProductAutomatic);
/** @private {string} */
class SpzCustomAnchorScroll extends SPZ.BaseElement {
static deferredMount() {
return false;
}
constructor(element) {
super(element);
/** @private {Element} */
this.scrollableContainer_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.LOGIC;
}
buildCallback() {
this.viewport_ = this.getViewport();
this.initActions_();
}
setTarget(containerId, targetId) {
this.containerId = '#' + containerId;
this.targetId = '#' + targetId;
}
scrollToTarget() {
const container = document.querySelector(this.containerId);
const target = container.querySelector(this.targetId);
const {scrollTop} = container;
const eleOffsetTop = this.getOffsetTop_(target, container);
this.viewport_
.interpolateScrollIntoView_(
container,
scrollTop,
scrollTop + eleOffsetTop
);
}
initActions_() {
this.registerAction(
'scrollToTarget',
(invocation) => this.scrollToTarget(invocation?.caller)
);
this.registerAction(
'setTarget',
(invocation) => this.setTarget(invocation?.args?.containerId, invocation?.args?.targetId)
);
}
/**
* @param {Element} element
* @param {Element} container
* @return {number}
* @private
*/
getOffsetTop_(element, container) {
if (!element./*OK*/ getClientRects().length) {
return 0;
}
const rect = element./*OK*/ getBoundingClientRect();
if (rect.width || rect.height) {
return rect.top - container./*OK*/ getBoundingClientRect().top;
}
return rect.top;
}
}
SPZ.defineElement('spz-custom-anchor-scroll', SpzCustomAnchorScroll);
const STRENGTHEN_TRUST_URL = "/api/strengthen_trust/settings";
class SpzCustomStrengthenTrust extends SPZ.BaseElement {
constructor(element) {
super(element);
this.renderElement_ = null;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.xhr_ = SPZServices.xhrFor(this.win);
const renderId = this.element.getAttribute('render-id');
SPZCore.Dom.waitForChild(
document.body,
() => !!document.getElementById(renderId),
() => {
this.renderElement_ = SPZCore.Dom.scopedQuerySelector(
document.body,
`#${renderId}`
);
if (this.renderElement_) {
this.render_();
}
this.registerAction('track', (invocation) => {
this.track_(invocation.args);
});
}
);
}
render_() {
this.fetchData_().then((data) => {
if (!data) {
return;
}
SPZ.whenApiDefined(this.renderElement_).then((apis) => {
apis?.render(data);
document.querySelector('#strengthen-trust-render-1539149753700').addEventListener('click',(event)=>{
if(event.target.nodeName == 'A'){
this.track_({type: 'trust_content_click'});
}
})
});
});
}
track_(data = {}) {
const track = window.sa && window.sa.track;
if (!track) {
return;
}
track('trust_enhancement_event', data);
}
parseJSON_(string) {
let result = {};
try {
result = JSON.parse(string);
} catch (e) {}
return result;
}
fetchData_() {
return this.xhr_
.fetchJson(STRENGTHEN_TRUST_URL)
.then((responseData) => {
if (!responseData || !responseData.data) {
return null;
}
const data = responseData.data;
const moduleSettings = (data.module_settings || []).reduce((result, moduleSetting) => {
return result.concat(Object.assign(moduleSetting, {
logos: (moduleSetting.logos || []).map((item) => {
return moduleSetting.logos_type == 'custom' ? this.parseJSON_(item) : item;
})
}));
}, []);
return Object.assign(data, {
module_settings: moduleSettings,
isEditor: window.self !== window.top,
});
});
}
}
SPZ.defineElement('spz-custom-strengthen-trust', SpzCustomStrengthenTrust);
${data.module_title}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.
${item.content.replaceAll("{store_name}","1982store")}
To display this card to customers, you need to go to "Booster & Store Conversion" to turn on the trust enhancement feature.
${item.content.replaceAll("{store_name}","1982store")}
Product was out of stock.
Product is unavailable.
Sku : MD005A
Weight : 1109g
${function(){
const variantData = data.variant || {"id":"c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","product_id":"00f293d1-e8c9-4254-b5d4-5635d4d44848","title":"","weight_unit":"g","inventory_quantity":10,"sku":"MD005A","barcode":"","position":1,"option1":"","option2":"","option3":"","note":"","image":null,"wholesale_price":[{"price":57.99,"min_quantity":1}],"weight":"1109","compare_at_price":"0","price":"56.25","retail_price":"0","available":true,"url":"\/products\/md005-robot-toy-model-action-figure-g1-optimus-prime-op-transformers-ss86-upgraded-version-collectible-convertible-robot-classic-g1-op-prime-ss86-enhancement-transforming-figure-toy-ss86-optimus-prime-upgrade-edition-action-doll?variant=c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","available_quantity":999999999,"options":[],"off_ratio":"0","flashsale_info":{"variant_id":"c3dbea2e-ac5f-49e0-88b6-30af50ee0e45","product_id":"","quantity":0,"discount_id":"","limit_time":0,"limit_buy":0,"user_limit_buy":0,"discount_sales":0,"discount_sales_rate":"","discount_stock":0,"ends_at":0,"starts_at":0,"allow_oversold":"","allocation_method":"","price":"56.25","compare_at_price":"","discount_price":"56.25","customary_saved_price":"","customary_off_ratio":"","discount_saved_price":"","discount_off_ratio":"0","use_before_price":false,"before_price":"","title":"","properties":"","color_setting_promotional_copy":"","discount_quantity":0,"is_need_split":false},"sales":5};
return `
Sku : ${variantData && variantData.sku}
Weight : ${variantData && variantData.weight}${variantData && variantData.weight_unit}
Barcode : ${variantData && variantData.barcode}
`
}()}
${function() {
const minDays = parseInt('3');
const maxDays = parseInt('7');
const customText = "Estimated Delivery\uff1a{min_date} - {max_date}";
const minDate = new Date(Date.now() + (minDays * 86400000));
const maxDate = new Date(Date.now() + (maxDays * 86400000));
const formatDate = (minDate.getFullYear() == maxDate.getFullYear() && minDate.getFullYear() == new Date().getFullYear())
? new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit' })
: new Intl.DateTimeFormat('en', { month: 'short', day: '2-digit', year: 'numeric' });
const tipText = customText.replace(/\{min_date\}/g, '' + formatDate.format(minDate) + '')
.replace(/\{max_date\}/g, '' + formatDate.format(maxDate) + '');
return `
${tipText}
`;
}()}
const TAG = 'spz-custom-revue-util';
const DEFAULT_DELAY_TIME = 100;
class SpzCustomRevueUtil 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() {
}
debounceRender(el, thisEl, containerStr) {
return this.smoothRender_(el, thisEl, containerStr).then(() => this.attemptToFit_(thisEl));
}
smoothRender_(newEl, thisEl, containerStr) {
const that = this;
that.appendAsUnvisibleContainer_(newEl, thisEl);
const components = newEl.querySelectorAll('[layout]');
return Promise.race([
Promise.all(
Array.prototype.map.call(components, (e) =>
SPZ.whenDefined(e).then(() => e.whenBuilt())
)
),
SPZServices.timerFor(that.win).promise(DEFAULT_DELAY_TIME),
]).then(() => {
return containerStr !== 'form_' ? thisEl.mutateElement(() => that.quickReplace(thisEl, newEl)) : thisEl.mutateElement(() => that.quickReplaceForm(thisEl, newEl));
});
}
quickReplace(thisEl, newEl) {
thisEl.container_ && this.toggleVisible_(thisEl.container_);
this.toggleVisible_(newEl, true);
thisEl.container_ && SPZCore.Dom.removeElement(thisEl.container_);
thisEl.container_ = newEl;
};
quickReplaceForm(thisEl, newEl) {
thisEl.form_ && this.toggleVisible_(thisEl.form_);
this.toggleVisible_(newEl, true);
const children = thisEl.form_.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.toggleVisible_(thisEl.form_, true);
thisEl.form_.appendChild(newEl);
};
appendAsUnvisibleContainer_(el, thisEl) {
this.toggleVisible_(el);
thisEl.element.appendChild(el);
}
attemptToFit_(thisEl) {
const fitFunc = () => {
thisEl.mutateElement(this.setElementHeight_.bind(thisEl));
};
const container = thisEl.container_ || thisEl.form_;
if (container) {
const children = container.querySelectorAll('*:not(template)');
const spzChildren = Array.prototype.filter
.call(children, SPZUtils.isSpzElement)
.filter((e) => !(e.isMount && e.isMount()));
spzChildren
.map((e) => SPZ.whenDefined(e).then(() => e.whenMounted()))
.forEach((p) => p.then(() => fitFunc()));
}
return fitFunc();
}
setElementHeight_() {
const targetHeight = (this.container_ || this.form_)?./*OK*/ scrollHeight;
const height = this.element./*OK*/ offsetHeight;
if (height !== targetHeight) {
SPZCore.Dom.setStyles(this.element, {
height: `${targetHeight}px`,
});
}
}
toggleVisible_(el, visible = false) {
if (!visible) {
el.classList.add('i-spzhtml-layout-fill');
SPZCore.Dom.setStyles(el, {
'z-index': -100000,
'opacity': 0,
});
} else {
el.classList.remove('i-spzhtml-layout-fill');
SPZCore.Dom.setStyles(el, {
'z-index': 'auto',
'opacity': 1,
});
}
}
setMinWidth_() {
const targetWidth = this.container_?./*OK*/ scrollWidth;
const width = this.element./*OK*/ offsetWidth;
if (width !== targetWidth) {
SPZCore.Dom.setStyles(this.element, {
'min-width': `${targetWidth}px`,
});
}
}
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, SpzCustomRevueUtil);
const TAG = 'spz-custom-revue-render';
class SPZCustomRevueRender extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
}
mountCallback = () => {}
render = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
if (this.element.children.length > 0) {
this.element.children[0].style.display = 'none';
}
this.element.appendChild(el);
// const utilsEl = document.getElementById('spz_custom_revue_util');
// utilsEl && SPZ.whenApiDefined(utilsEl).then((api) => {
// api.debounceRender(el, this);
// });
});
}
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, SPZCustomRevueRender)
${function(){
return `
${data.starNum}/${data.starTotal}
`;
}()}
${function(){
return `
${data.showStarText === 'true' ? `
${data.starNum}/${data.starTotal}
` : ''}
`;
}()}
const TAG = 'spz-custom-revue-star';
class SPZCustomRevueStar extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.starNum = this.element.getAttribute('starNum');
this.starTotal = this.element.getAttribute('starTotal');
this.showStarText = this.element.getAttribute('showStarText');
this.starColor = this.element.getAttribute('color');
this.interact = this.element.getAttribute('interact');
this.starSize = this.element.getAttribute('starSize') || 14;
}
mountCallback = () => {
this.doRender_({
starTotal: this.starTotal,
totalArray: Array.from({ length: Number(this.starTotal) }, (v, k) => k + 1),
starNum: this.starNum,
showStarText: this.showStarText,
starColor: this.starColor,
starSize: this.starSize
}).then(() => {
if (this.interact) {
this.addEventListeners_();
}
});
}
addEventListeners_ = () => {
const stars = document.querySelectorAll('.revue-star__star');
stars.forEach(star => {
star.addEventListener('click', event => {
const starEl = star.closest('.revue-star__star');
const starIndex = Number(starEl.dataset.index);
let isHalf = event.offsetX < star.offsetWidth / 2;
// rtl
if (document.documentElement.getAttribute('dir') === 'rtl') {
isHalf = event.offsetX > star.offsetWidth / 2;
}
const starValue = isHalf ? starIndex - 0.5 : starIndex;
this.starClickHandler_({ value: starValue });
});
});
}
renderStar = () => {
const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
const stars = this.element.querySelectorAll('.revue-star__star');
stars.forEach((star, i) => {
const starIndex = i + 1;
const starEl = star.querySelector('svg:nth-child(2)');
const isHalf = this.starNum % 1 > 0 && Math.ceil(this.starNum) === starIndex;
const isSolid = starIndex <= Math.ceil(this.starNum);
starEl.style.display = isSolid ? 'block' : 'none';
if (isHalf) {
if (isRtl) {
// RTL布局下,如果是半星,显示星星的右半边
starEl.style.clipPath = `polygon(50% 0, 100% 0, 100% 100%, 50% 100%)`;
} else {
// LTR布局下,如果是半星,显示星星的左半边
starEl.style.clipPath = `polygon(0 0, 50% 0, 50% 100%, 0 100%)`;
}
} else {
starEl.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%)`
}
});
const showCountEle = this.element.querySelector('#revue-star-show-count');
showCountEle && SPZ.whenApiDefined(showCountEle).then((api) => {
api.render({ starNum: this.starNum, starTotal: this.starTotal });
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, { starSize: this.starSize, ...data }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.starNum = data.starNum;
this.renderStar();
});
}
starClickHandler_ = (event) => {
this.starNum = event.value;
this.renderStar();
this.triggerEvent_('change', { value: event.value });
}
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, SPZCustomRevueStar)
${function() {
const isPercentage = data.show_percentage === 'true' && data.total <= data.show_percentage_num;
return `
${!isPercentage ? `${data.count}` : `${data.count / data.total * 100}%`}
`
}()}
const TAG = 'spz-custom-revue-progress';
class SPZCustomRevueProgress extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.height = '6px';
this.color = this.element.getAttribute('color') || '#000000';
this.show_percentage = 'false';
this.show_percentage_num = 100;
this.count = this.element.getAttribute('count');
this.total = this.element.getAttribute('total');
}
mountCallback = () => {
this.doRender_({
count: Number(this.count),
total: Number(this.total),
height: this.height,
color: this.color,
show_percentage: this.show_percentage,
show_percentage_num: this.show_percentage_num
}).then(() => {
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
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, SPZCustomRevueProgress)
${function() {
return `
${data.count > 99 ? '99+' : data.count < 1 ? '' : data.count}
`;
}()}
const TAG = 'spz-custom-revue-like';
class SPZCustomRevueLike extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.grayColor = this.element.getAttribute('gray_color') || "#BDBDBD";
this.likedColor = this.element.getAttribute('like_color') || "#FFCB44";
this.color = this.grayColor;
this.count = this.element.getAttribute('count');
this.revueId = this.element.getAttribute('revue-id');
this.location = this.element.getAttribute('location');
}
mountCallback = () => {
const likes = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : [];
const like = likes.find(item => item.id === this.revueId);
if (like) {
this.color = like.like_status === 1 ? this.likedColor : this.grayColor;
}
// 如果location是modal,则找到相同revue-id的list的元素,拿到其count,存在list count变了,但是modal的count没变的情况
if (this.location === 'modal') {
const listElement = document.querySelector(`spz-custom-revue-like[revue-id="${this.revueId}"] .revue-like-count`);
if (listElement) {
this.count = listElement.getAttribute('data-real-count');
}
}
this.doRender_({
color: this.color,
count: this.count
}).then(() => {
this.addEventListeners_();
if(this.location === 'list') { // modal数量变更,list同步变更
document.addEventListener('like-clicked', (e) => {
if (e.detail.location !== this.location && e.detail.id === this.revueId) {
this.color = e.detail.like_status === 1 ? this.likedColor : this.grayColor;
this.count = e.detail.count;
this.element.querySelector('.revue-like__icon').querySelector('svg').setAttribute('fill', this.color);
this.element.querySelector('.revue-like__icon').querySelector('svg').querySelector('path').setAttribute('fill', this.color);
this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count;
this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count);
if(this.count > 0){
this.element.querySelector('.revue-like-count').classList.remove('hidden');
}else{
this.element.querySelector('.revue-like-count').classList.add('hidden');
}
}
});
}
});
}
addEventListeners_ = () => {
const icon = this.element.querySelector('.revue-like__icon');
icon.addEventListener('click', (e) => {
e.stopPropagation();
const likeStatus = this.color === this.likedColor ? 0 : 1;
this.color = this.color === this.likedColor ? this.grayColor : this.likedColor;
this.count = likeStatus === 1 ? parseInt(this.count) + 1 : parseInt(this.count) - 1;
icon.querySelector('svg').setAttribute('fill', this.color);
icon.querySelector('svg').querySelector('path').setAttribute('fill', this.color);
this.element.querySelector('.revue-like-count').innerText = this.count > 99 ? '99+' : this.count < 1 ? '' : this.count;
this.element.querySelector('.revue-like-count').setAttribute('data-real-count', this.count);
if(this.count > 0){
this.element.querySelector('.revue-like-count').classList.remove('hidden');
}else{
this.element.querySelector('.revue-like-count').classList.add('hidden');
}
this.postLike(likeStatus);
if (this.location === 'modal') {
const clickedEvent = new CustomEvent('like-clicked', {
detail: {
id: this.revueId,
like_status: likeStatus,
count: this.count,
location: this.location
}
});
document.dispatchEvent(clickedEvent);
}
});
}
setLikeToStorage = (likeToStore) => {
if (typeof (Storage) !== 'function') return;
const likesInStore = sessionStorage.getItem('likes') ? JSON.parse(sessionStorage.getItem('likes')) : [];
const reviewIndex = likesInStore.findIndex(item => item.id === likeToStore.id);
if (reviewIndex !== -1) {
likesInStore[reviewIndex].like_status = likeToStore.like_status;
likesInStore[reviewIndex].count = likeToStore.count;
} else {
likesInStore.push(likeToStore);
}
sessionStorage.setItem('likes', JSON.stringify(likesInStore));
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
postLike = (likeStatus) => {
fetch('/api/comment/like', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: this.revueId,
status: likeStatus
})
}).then((res) => {
if (res.status === 200) {
this.setLikeToStorage({
id: this.revueId,
like_status: likeStatus,
count: this.count
});
}
});
}
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, SPZCustomRevueLike)
${function() {
return `
${function() {
if(data.imgCover) {
if(media.videosrc) {
let src = '';
if (media.videosrc) {
src = media.videosrc + '.' + media.ext;
}
const videoDom = `
`;
if(!isPC){
return `
${videoDom}
`
}
return `
${videoDom}
`
} else if(media.mp4 || media.hls) {
const videoDom = `
`;
if(!isPC){
return `
${videoDom}
`
}
return `
${videoDom}
`
} else {
if(!isPC){
return `
`
}else{
return `
`
}
}
} else {
if (media.videosrc) {
let src = '';
if (media.videosrc) {
src = media.videosrc + '.' + media.ext;
}
return `
`
} else if(media.mp4 || media.hls) {
return `
`
} else {
return `
`
}
}
}()}
`;
}()}
const TAG = 'spz-custom-revue-media';
class SPZCustomRevueMedia extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.imgCover = this.element.getAttribute('img-cover') ?? false;
this.pc_layout = this.element.getAttribute('pc-layout') ?? '';
// data-images 格式为 xxxx.png?width=1&height=1,xxxx.png?width=1&height=1
const images = this.element.getAttribute('data-images').split(',') || [];
const parsedImages = images.map(image => {
return this.mediaParse_(image);
});
this.images = parsedImages;
this.isPC = window.innerWidth > 960;
}
mountCallback = () => {
this.doRender_({
images: this.images,
isPC: this.isPC,
imgCover: this.imgCover,
pc_layout: this.pc_layout
}).then(() => {
this.addEventListeners_();
});
}
addEventListeners_ = () => {
const images = this.element.querySelectorAll('.revue-image-item');
images.forEach((image, index) => {
image.addEventListener('click', () => {
const carousel = document.querySelector('#revue-image-carousel-render');
carousel && SPZ.whenApiDefined(carousel).then((api) => {
const width = this.isPC ? 460 : window.innerWidth * 0.9;
const height = this.isPC ? 630 : 500;
api.render({ images: this.images, index: index, width: width, height: height });
});
});
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.preview_image = url.split('?')[0];
} catch (e) {};
return result;
}
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, SPZCustomRevueMedia)
${function() {
return `
`
}()}
${function() {
return `
Most liked
Highest ratings
Lowest ratings
`
}()}
${function() {
return `
Most liked
Highest ratings
Lowest ratings
`
}()}
const TAG = 'spz-custom-revue-sort';
class SPZCustomRevueSort extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > 960;
this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%';
this.randomStr = Math.random().toString(36).substr(2);
this.sectionId = this.element.getAttribute('section-id') || '1539149753700';
this.prefix = this.element.getAttribute('prefix');
}
mountCallback = () => {
const data = {
width: this.width,
randomStr: this.randomStr
};
this.doRender_(data).then(() => {
let revueSortListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-sort-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`);
revueSortListRender && SPZ.whenApiDefined(revueSortListRender).then((api) => {
api.render(data).then(() => {
if (this.isPC) {
this.addEventListenersForPC_();
} else {
this.addEventListenersForMobile_();
}
});
});
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
addEventListenersForPC_ = () => {
const revueSelectList = this.element.querySelector('.revue_select_list');
const revueSelectItem = this.element.querySelectorAll('.revue_select_item');
const revueSelectSortIcon = this.element.querySelector(`#${this.prefix}-revue_select_sort_icon-${this.sectionId}`);
revueSelectItem.forEach(item => {
item.addEventListener('click', () => {
const sort = item.getAttribute('data-sort');
const direction = item.getAttribute('data-direction');
this.triggerEvent_('sort', { sort, direction });
this.element.querySelector('.revue_select_label').innerText = item.innerText;
revueSelectList.classList.remove('revue_select_list_active');
const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const pcDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-pc-dropdown-${this.sectionId}`);
if (!revueSelectSortIcon.classList.contains('up_icon')) {
return;
}
revueSelectSortIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
});
window.addEventListener('scroll', (e) => {
if (!revueSelectSortIcon || !revueSelectSortIcon.classList.contains('up_icon')) {
return;
}
revueSelectSortIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
}
addEventListenersForMobile_ = () => {
const revueSortDropdownRender = document.querySelector(`#${this.prefix}-revue-sort-dropdown-render-${this.sectionId}`);
revueSortDropdownRender && SPZ.whenApiDefined(revueSortDropdownRender).then(async (api) => {
await api.render();
const revueSortDropdownItem = document.querySelectorAll(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} .revue_sort_dropdown_item`);
revueSortDropdownItem.forEach(item => {
item.addEventListener('click', () => {
const sort = item.getAttribute('data-sort');
const direction = item.getAttribute('data-direction');
revueSortDropdownItem.forEach((_item)=>{_item.classList.remove('selected')})
item.classList.add('selected');
// 抛出事件
this.triggerEvent_('sort', { sort, direction });
// 移除revue_checked元素,复制一个新的到当前选中的元素
const revueChecked = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId} #${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const mDropdownEle = document.querySelector(`#${this.prefix}-revue-sort-dropdown-${this.sectionId}`);
SPZ.whenApiDefined(mDropdownEle).then((api) => {
api.close();
});
});
});
})
}
}
SPZ.defineElement(TAG, SPZCustomRevueSort)
${function() {
return `
`
}()}
${function() {
const list = data.listData;
return `
With Photos(${list.image_count})
`
}()}
${function() {
const list = data.listData;
return `
With Photos(${list.image_count})
`
}()}
const TAG = 'spz-custom-revue-type';
class SPZCustomRevueType extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > 960;
this.width = this.isPC ? `${this.element.getAttribute('width') || 150}px` : '100%';
this.randomStr = Math.random().toString(36).substr(2);
this.sectionId = this.element.getAttribute('section-id') || '1539149753700';
this.prefix = this.element.getAttribute('prefix');
}
mountCallback = () => {
}
render = (data) => {
const renderData = {
...data,
width: this.width,
randomStr: this.randomStr
};
return this.templates_
.findAndRenderTemplate(this.element, renderData, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
let revueTypeListRender = this.isPC ? this.element.querySelector(`#${this.prefix}-revue-type-list-render-${this.sectionId}`) : this.element.querySelector(`#${this.prefix}-revue-type-dropdown-render-${this.sectionId}`);
revueTypeListRender && SPZ.whenApiDefined(revueTypeListRender).then((api) => {
api.render(renderData).then(() => {
if (this.isPC) {
this.addEventListenersForPC_();
} else {
this.addEventListenersForMobile_();
}
});
});
});
}
addEventListenersForPC_ = () => {
const revueSelectList = this.element.querySelector('.revue_select_list');
const revueSelectItem = this.element.querySelectorAll('.revue_select_item');
const revueSelectTypeIcon = this.element.querySelector(`#${this.prefix}-revue_select_type_icon-${this.sectionId}`);
revueSelectItem.forEach(item => {
item.addEventListener('click', () => {
const type = item.getAttribute('data-type');
const direction = item.getAttribute('data-direction');
this.triggerEvent_('type', { type, direction });
this.element.querySelector('.revue_select_label').innerText = item.innerText;
revueSelectList.classList.remove('revue_select_list_active');
const revueChecked = this.element.querySelector(`#${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
if (!revueSelectTypeIcon.classList.contains('up_icon')) {
return;
}
const pcDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-pc-dropdown-${this.sectionId}`);
revueSelectTypeIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
});
window.addEventListener('scroll', (e) => {
if (!revueSelectTypeIcon.classList.contains('up_icon')) {
return;
}
revueSelectTypeIcon.classList.remove('up_icon');
SPZ.whenApiDefined(pcDropdownEle).then((api) => {
api.close();
});
});
}
addEventListenersForMobile_ = () => {
const revueTypeDropdownItem = this.element.querySelectorAll(`#${this.prefix}-revue-type-dropdown-${this.sectionId} .revue_type_dropdown_item`);
revueTypeDropdownItem.forEach(item => {
item.addEventListener('click', () => {
const type = item.getAttribute('data-type');
const direction = item.getAttribute('data-direction');
revueTypeDropdownItem.forEach((_item)=>{_item.classList.remove('selected')})
item.classList.add('selected');
// 抛出事件
this.triggerEvent_('type', { type, direction });
// 移除revue_checked元素,复制一个新的到当前选中的元素
const revueChecked = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId} #${this.prefix}-revue_checked`);
revueChecked && SPZCore.Dom.removeElement(revueChecked);
const revueCheckedClone = revueChecked.cloneNode(true);
item.appendChild(revueCheckedClone);
const mDropdownEle = this.element.querySelector(`#${this.prefix}-revue-type-dropdown-${this.sectionId}`);
SPZ.whenApiDefined(mDropdownEle).then((api) => {
api.close();
});
});
});
}
}
SPZ.defineElement(TAG, SPZCustomRevueType)
const TAG = 'spz-custom-revue-pagination';
class SPZCustomRevuePagination extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.numItems = this.numItems();
this.pageSize = this.pageSize();
}
mountCallback = () => {
this.doRender_({
numPages: this.numPages(),
pageNum: this.currentPageNumber(),
useCallback: true
}).then(() => {
});
}
currentPageNumber() {
let pageNum = this.element.getAttribute('page-num');
if (pageNum) return parseInt(pageNum);
}
numPages() {
return Math.ceil(this.numItems / this.pageSize);
}
numItems() {
return parseInt(this.element.getAttribute('num-items'));
}
pageSize() {
return parseInt(this.element.getAttribute('page-size')) || 10;
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, data, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
});
}
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, SPZCustomRevuePagination)
const TAG = 'spz-custom-revue-product';
class SpzCustomRevueProduct extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.section_id = this.element.getAttribute('section-id');
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
const url = new URL(window.location.href);
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.nodata = false;
this.firstRender = true;
this.commentConfig = {};
this.commentSummary = {};
this.commentList = {};
this.panelId = 'all';
this.sort = 'created_at';
this.direction = 'desc';
this.pageNum = 1;
this.pageSize = +window.reviewProductSettings[this.section_id].page_limit;
this.pc_layout = window.reviewProductSettings[this.section_id].pc_layout;
this.star_least = +window.reviewProductSettings[this.section_id].star_least;
this.only_media = window.reviewProductSettings[this.section_id].only_media;
this.product_id = window.SHOPLAZZA.meta.page.resource_id;
this.isProductPage = '1' == 1;
this.isCollectionPage = '1' == 2;
this.isCartPage = '1' == 13;
this.review_insufficient = window.reviewProductSettings[this.section_id].review_insufficient; // 评论不足类型
this.mini_quantity = window.reviewProductSettings[this.section_id].mini_quantity; // 评论少于一定数量
this.actions = window.reviewProductSettings[this.section_id].actions; // 评论处理方式
this.only_media = window.reviewProductSettings[this.section_id].only_media; // 只显示有图片的评论
this.only_featured = window.reviewProductSettings[this.section_id].only_featured ?? false; // 只显示精选评论
this.display_product_link = window.reviewProductSettings[this.section_id].display_product_link ?? false; // 是否显示商品链接
this.m_loading_type = window.reviewProductSettings[this.section_id].m_loading_type; // 移动端加载方式
this.m_modal_page_limit = window.reviewProductSettings[this.section_id].m_modal_page_limit; // 移动端弹窗加载限制
this.hide_review_section = window.reviewProductSettings[this.section_id].hide_review_section; // 无数据是否隐藏评论组件
this.accent_color = window.reviewProductSettings[this.section_id].accent_color; // 主题色
}
mountCallback = () => {
this.templates_
.findAndRenderTemplate(this.element, { isPC: this.isPC }, null)
.then((el) => {
this.element.appendChild(el);
this.renderPage();
})
}
/* fetch api/comment-config */
fetchCommentConfig_ = async () => {
const response = await fetch('/api/comment-config');
return response.json();
}
/* api/comment/count-star?product_id=` + `${product.id}` + `&star_least=${block.settings.star_least}*/
fetchCommentSummary_ = async(data) => {
const response = await fetch(`/api/v1/comments/summary`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
return response.json();
}
/* api/comment/list?star_least=5&onlyimg=0&limit=10&offset=0&sort_by=created_at&product_id=6e9e3113-87fe-49ad-8764-a2333463adea&status=1&sort_direction=desc&show_reply=1 */
fetchCommentList_ = async(data) => {
// const response = await fetch(`/api/comment/list?show_product=1&star_least=${data.star_least}&onlyimg=${data.onlyimg}&limit=${data.limit}&offset=${data.offset}&sort_by=${data.sort_by || 'created_at'}&product_id=${data.productId}&status=1&sort_direction=${data.sort_direction || 'desc'}&show_reply=${data.show_reply}`);
const response = await fetch('/api/v1/comments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
return response.json();
}
/* fetch api/comment/theme-config?theme_id= */
fetchThemeConfig_ = async(themeId) => {
const response = await fetch(`/api/comment/theme-config?theme_id=${themeId}`);
return response.json();
}
getCommentConfig = () => {
return this.fetchCommentConfig_()
}
getCommentSummary = (data = {}) => {
const fetchData = {
star_least: this.star_least,
product_ids: this.isProductPage ? '00f293d1-e8c9-4254-b5d4-5635d4d44848' : this.isCartPage ? '' : '',
collection_id: this.isCollectionPage ? '' : '',
filter_type: this.isProductPage ? 'product' : this.isCollectionPage ? 'collection' : 'store',
fill_min_threshold: this.review_insufficient === 'less_than' ? this.mini_quantity : undefined,
fill_strategy: this.actions === 'all_product' ? 'store' : '',
only_media: this.only_media ? this.only_media : this.panelId !== 'all',
only_featured: this.only_featured,
...data,
}
return this.fetchCommentSummary_(fetchData)
}
getCommentList = (data = {}) => {
const fetchData = {
show_product: true,
filter_type: (this.isProductPage || this.isCartPage)
? 'product'
: this.isCollectionPage ? 'collection' : 'store',
star_least: this.star_least,
show_reply: true,
limit: this.pageSize,
offset: (this.pageNum - 1) * this.pageSize,
only_media: this.only_media ? this.only_media : this.panelId !== 'all',
sort_by: this.sort,
sort_direction: this.direction,
product_ids: this.isProductPage ? '00f293d1-e8c9-4254-b5d4-5635d4d44848' : this.isCartPage ? '' : '',
collection_id: this.isCollectionPage ? '' : '',
only_featured: this.only_featured,
fill_strategy: this.actions === 'all_product' ? 'store' : '',
fill_min_threshold: this.review_insufficient === 'less_than' ? this.mini_quantity : undefined,
...data,
}
return this.fetchCommentList_(fetchData)
}
getPageData = () => {
return Promise.all([
this.getCommentConfig(),
this.getCommentSummary(),
this.getCommentList()
])
}
renderPage = async () => {
const [commentConfigRes, commentSummaryRes, commentListRes] = await this.getPageData();
let commentConfigData = commentConfigRes.data || {};
let commentSummaryData = commentSummaryRes.data || {};
let commentListData = commentListRes.data || [];
this.commentConfig = commentConfigData;
this.commentSummary = commentSummaryData;
this.commentList = commentListData;
this.accent_color = this.accent_color || this.commentConfig.star_color;
// 评论不足逻辑:计算最小评论数量阈值
const lessThanCount = (this.actions === "hide" || this.actions === "empty") &&
this.review_insufficient === 'less_than'
? this.mini_quantity
: 1;
// 如果评论数量不足,处理空状态
if (commentListData.count < lessThanCount) {
this.renderHideSkeleton();
if (this.hide_review_section || this.actions === "hide") {
this.renderNoData();
} else if (this.actions === "empty") {
// 商品详情页显示空评论状态,其他页面隐藏评论区域
if (this.isProductPage) {
this.renderEmptyComment();
} else {
this.renderNoData();
}
}
this.nodata = true;
return;
}
window.addEventListener('resize', SPZCore.Types.throttle(window, this.onResize, 300));
this.renderPageData([this.commentConfig, this.commentSummary, this.commentList]);
}
onResize = () => {
if(this.nodata) {
return;
}
// 判断是否需要重新渲染
if((this.isPC && window.innerWidth > (window.breakpoint || 960)) || (!this.isPC && window.innerWidth < (window.breakpoint || 960))) {
return;
}
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.panelId = 'all';
this.sort = 'created_at';
this.direction = 'desc';
this.pageNum = 1;
this.templates_
.findAndRenderTemplate(this.element, { isPC: this.isPC }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
this.renderPageData([this.commentConfig, this.commentSummary, this.commentList]);
})
}
renderPageData = (data) => {
const [commentConfigData, commentSummaryData, commentListData] = data;
// 渲染头部
this.renderHeader_({
starData: commentSummaryData,
listData: commentListData,
comment_avg_star: commentSummaryData.comment_avg_star,
comment_count: commentSummaryData.comment_count,
});
// 有评论逻辑
this.renderStarCounts(commentSummaryData);
if(this.isPC && this.pc_layout === 'single_column') {
this.renderCommentTab({
listData: commentListData,
isPC: this.isPC,
}, `revue-tab-${this.section_id}`);
} else {
this.renderList_({
listData: commentListData,
config: this.commentConfig,
shop_name: window.SHOPLAZZA.shop.shop_name,
isPC: this.isPC,
star_color: this.accent_color,
});
}
}
renderNoData = () => {
const sectionEle = document.querySelector(`#revue-product-compo`);
if (sectionEle) {
sectionEle.setAttribute('hidden', 'true');
}
if(window.top === window.self) { // c端不渲染
return;
}
// b端渲染
const noDataPlaceholder = document.querySelector(`#revue_no_data_placeholder_${this.section_id}`);
if(noDataPlaceholder) {
SPZ.whenApiDefined(noDataPlaceholder).then(async (api) => {
await api.render();
});
}
}
renderHideSkeleton = () => {
const skeletonEle = document.querySelector('#revue_skeleton');
if (skeletonEle) {
skeletonEle.classList.add('hidden');
}
}
renderEmptyComment = () => {
const emptyEle = document.querySelector(`#revue-empty-1539149753700`);
if(emptyEle) {
emptyEle.classList.remove('hidden');
}
}
renderHeader_ = (data) => {
const headerEle = document.querySelector(`#app-review-revue-header-${this.section_id}`);
if (headerEle) {
SPZ.whenApiDefined(headerEle).then(async (api) => {
api.render({
...data,
star_color: this.accent_color,
isPC: this.isPC,
});
});
}
}
renderStarCounts = (data, eleId = `revue-summary-${this.section_id}`) => {
const ndata = {
...this.commentSummary,
star_color: this.accent_color,
isPC: this.isPC,
...data,
}
const summaryEle = document.querySelector(`#${eleId}`);
if (summaryEle) {
SPZ.whenApiDefined(summaryEle).then((api) => {
api.render({
...ndata,
});
});
}
}
/* 渲染单列布局 (有 tab 和 list) */
renderCommentTab = (data, eleId) => {
const elementId = eleId || `revue-tab-${this.section_id}`;
const ndata = { listData: this.commentList, isPC: this.isPC, ...data }
const tabEle = document.querySelector(`#${elementId}`);
let listId;
if (tabEle) {
SPZ.whenApiDefined(tabEle).then(async (api) => {
await api.render({
...ndata,
// suffix: "list",
});
if(eleId) {
listId = `revue-comment-list-${this.section_id}_tab`;
}
this.renderList_({
...ndata,
// suffix: "list",
}, listId);
});
}
}
/* 只渲染 list */
renderList_ = (data, eleId) => {
const listEle = document.querySelector(`#revue-comment-list`);
if (listEle && !eleId) {
SPZ.whenApiDefined(listEle).then(async (api) => {
await api.render({
...data,
// suffix: "list",
pageSize: this.pageSize,
hasmore: data.listData.has_more,
})
let nlist = data.listData.list.map(item => {
return {
...item,
config: this.commentConfig,
star_color: this.accent_color,
shop_name: window.SHOPLAZZA.shop.shop_name,
current_panel: this.panelId,
pageNum: this.pageNum,
suffix: data.suffix,
show_link: this.display_product_link,
}
})
let hasmore = data.listData.has_more;
if(!this.isPC && this.m_loading_type === 'modal') {
nlist = nlist.slice(0, this.m_modal_page_limit);
hasmore = true;
}
api.renderList({
...data,
list: nlist,
count: this.panelId === 'all' ? data.listData.count : data.listData.image_count,
// suffix: "list",
hasmore: hasmore,
pageSize: this.pageSize
})
})
return;
}
const viewallListEle = document.querySelector(`#${eleId}`);
if (viewallListEle) {
SPZ.whenApiDefined(viewallListEle).then(async (api) => {
await api.render({
...data,
pageSize: this.pageSize,
hasmore: data.listData.has_more,
});
let nlist = data.listData.list.map(item => {
return {
...item,
config: this.commentConfig,
star_color: this.accent_color,
shop_name: window.SHOPLAZZA.shop.shop_name,
current_panel: this.panelId,
pageNum: this.pageNum,
suffix: data.suffix,
show_link: this.display_product_link,
}
})
api.renderList({
...data,
list: nlist,
count: this.panelId === 'all' ? data.listData.count : data.listData.image_count,
hasmore: data.listData.has_more,
pageSize: this.pageSize,
})
});
}
}
renderCommentList = (data, eleId = 'revue-comment-list', renderType = 'list', redo = false) => {
const listEle = document.querySelector(`#${eleId}`);
if (listEle) {
SPZ.whenApiDefined(listEle).then((api) => {
let nlist = data.listData.list.map(item => {
return {
...item,
config: this.commentConfig,
star_color: this.accent_color,
shop_name: window.SHOPLAZZA.shop.shop_name,
current_panel: this.panelId,
pageNum: this.pageNum,
hasmore: data.listData.has_more,
show_link: this.display_product_link,
// suffix: data.suffix,
}
})
if(!this.isPC && this.m_loading_type === 'modal' && renderType === 'list') {
nlist = nlist.slice(0, this.m_modal_page_limit);
}
api.renderList({
count: this.panelId === 'all' ? data.listData.count : data.listData.image_count,
list: nlist,
// suffix: "list",
hasmore: data.listData.has_more,
pageSize: this.pageSize
}, redo);
});
return;
}
}
renderByScrollPagination = async (eleId, renderType) => {
this.pageNum = this.pageNum + 1;
const params = {}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, eleId, renderType, false);
}
setupAction_ = () => {
this.registerAction('renderTabChangeList', async (invocation) => {
// 兼容 ljs-tab 首次加载会触发 tabchange 事件
if(this.firstRender) {
this.firstRender = false;
return;
}
const panelId = invocation.args.data.panelId;
const { eleId, renderType } = invocation.args;
this.panelId = panelId;
this.pageNum = 1;
this.modalHasMore = true;
const params = {
// only_media: panelId !== 'all',
}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, eleId, renderType, true);
});
this.registerAction('renderTypeChangeList', async (invocation) => {
const { type } = invocation.args.data;
const { eleId, renderType } = invocation.args;
this.panelId = type;
this.pageNum = 1;
this.modalHasMore = true;
const params = {
// only_media: type !== 'all',
}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, eleId, renderType, true);
});
this.registerAction('renderSortedList', async(invocation) => {
const { sort, direction } = invocation.args.data;
const eleId = invocation.args.eleId;
const renderType = invocation.args.renderType;
this.sort = sort;
this.direction = direction;
this.pageNum = 1;
this.modalHasMore = true;
const params = {
sort_by: sort,
sort_direction: direction,
}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, eleId, renderType, true);
});
this.registerAction('renderByPagination', async(invocation) => {
const { pageNum, eleId, renderType } = invocation.args;
this.pageNum = pageNum;
const params = {}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, `revue-comment-list-${this.section_id}_tab`, 'tab', true);
const tabsEle = document.querySelector('#revue-product-compo');
if (tabsEle) {
tabsEle.scrollIntoView({ behavior: 'smooth' });
}
});
this.registerAction('renderByViewMore', async(invocation) => {
const { eleId, renderType } = invocation.args;
this.pageNum = this.pageNum + 1;
const params = {}
const res = await this.getCommentList(params);
this.renderCommentList({
listData: res.data,
}, eleId, renderType, false);
});
this.registerAction('refresh', async(invocation) => {
this.panelId = 'all';
this.sort = 'created_at';
this.direction = 'desc';
this.pageNum = 1;
this.templates_
.findAndRenderTemplate(this.element, { isPC: this.isPC }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
this.renderPage();
});
const productEle = document.querySelector(`#revue-viewall-modal-comp`);
if (productEle) {
SPZ.whenApiDefined(productEle).then(async (api) => {
api.refresh();
});
}
});
}
}
SPZ.defineElement(TAG, SpzCustomRevueProduct)
(function() {
const TAG = 'spz-custom-new-revue';
class SpzCustomNewRevue extends SPZ.BaseElement {
constructor(element) {
super(element);
this.config_ = null;
this.loading_ = false;
this.accent_color = this.element.getAttribute('accent-color');
this.sectionId = this.element.getAttribute('section-id');
this.prefix = this.element.getAttribute('prefix');
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.form_ = SPZCore.Dom.scopedQuerySelector(
this.element,
'form'
);
this.hasShowLengthInputs_ = SPZCore.Dom.scopedQuerySelectorAll(
this.form_,
'[showlength]'
);
[...this.hasShowLengthInputs_].forEach(item => {
const countRecordDom = SPZCore.Dom.scopedQuerySelector(
this.form_,
`#${item.id} ~ div[type="count-record"]`
);
if (!countRecordDom) {
console.error(`Cannot find count record DOM element for input ${item.id}`);
return;
}
item.addEventListener('input', (e) => {
countRecordDom.innerText = `${e.target.value.length}/3000`;
});
});
this.setupAction_();
this.getRevueConfigData_();
}
setupAction_() {
this.registerAction('submitForm', async(invocation) => {
if (this.loading_) {
return;
}
this.loading_ = true;
const formData = Object.entries(invocation.args.data).reduce((acc, [key, value]) => {
if (key === 'star' || key === 'type') {
acc[key] = Number(value[0]);
} else {
acc[key] = value[0];
}
return acc;
}, {});
try {
const data = await fetch('/api/comment', {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(formData)
}).then(res => res.json());
if (data.state === 0) {
this.triggerEvent_('submitSuccess', {
panelId: 'with_photo',
message: ''
});
return;
}
throw new Error(data.msg);
} catch(e) {
e = await e;
this.triggerEvent_('submitError', {data: e.message});
} finally {
this.loading_ = false;
}
});
this.registerAction('renderFormStar', async(invocation) => {
this.triggerEvent_('rerenderFormStar', { star_color: this.starColor_ });
})
}
mountCallback() {
}
getRevueConfigData_ = () => {
fetch('/api/comment-config')
.then(res => res.json())
.then(data => {
this.config_ = data.data;
// anonymous_permission 是否支持匿名
if (!this.config_.anonymous_permission) {
const anonymousInput = this.form_.querySelector(`#${this.prefix}-revue-anonymous-${this.sectionId}`);
anonymousInput.value = 'false';
anonymousInput.parentNode.classList.add('hidden', 'anonymous-permission-hidden');
}
this.starColor_ = this.config_.star_color;
if(this.accent_color && this.accent_color != 'null'){
this.starColor_ = this.accent_color;
}
// render star
// star_color 星星颜色
const starEl = this.form_.querySelector(`#${this.prefix}-revue_write_modal_star-${this.sectionId}`);
if (starEl) {
SPZ.whenApiDefined(starEl).then((api) => {
api.render({ star_color: this.starColor_ });
});
}
});
}
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, SpzCustomNewRevue);
})()
(function() {
const TAG = 'spz-custom-revue-product-info-script';
class SpzCustomRevueProductInfoScript extends SPZ.BaseElement {
constructor(element) {
super(element);
/** @private {!Element} */
this.product_id = null;
}
async buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.product_id = this.getProductId_();
this.triggerEvent_('init', { product_id: this.product_id });
try {
const data = await this.getProductInfo_();
if (data?.data?.product) {
this.triggerEvent_('finish', data.data.product);
}
} catch (error) {
console.error('Failed to fetch product info:', error);
// Handle the error appropriately
}
}
getProductId_ = () => {
return window.SHOPLAZZA.meta.page.resource_id;
}
async getProductInfo_() {
if (!this.product_id) {
console.error('Product ID is undefined or null');
return null;
}
try {
const response = await fetch(`/api/products/${this.product_id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching product info:', error);
throw error; // Rethrow to be caught by the caller
}
}
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.LOGIC;
}
}
SPZ.defineElement(TAG, SpzCustomRevueProductInfoScript);
})()
${function(){
return `
${data.starNum}/${data.starTotal}
`;
}()}
${function(){
return `
${data.showStarText === 'true' ? `
${data.starNum}/${data.starTotal}
` : ''}
`;
}()}
const TAG = 'spz-custom-revue-star';
class SPZCustomRevueStar extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.starNum = this.element.getAttribute('starNum');
this.starTotal = this.element.getAttribute('starTotal');
this.showStarText = this.element.getAttribute('showStarText');
this.starColor = this.element.getAttribute('color');
this.interact = this.element.getAttribute('interact');
this.starSize = this.element.getAttribute('starSize') || 14;
}
mountCallback = () => {
this.doRender_({
starTotal: this.starTotal,
totalArray: Array.from({ length: Number(this.starTotal) }, (v, k) => k + 1),
starNum: this.starNum,
showStarText: this.showStarText,
starColor: this.starColor,
starSize: this.starSize
}).then(() => {
if (this.interact) {
this.addEventListeners_();
}
});
}
addEventListeners_ = () => {
const stars = document.querySelectorAll('.revue-star__star');
stars.forEach(star => {
star.addEventListener('click', event => {
const starEl = star.closest('.revue-star__star');
const starIndex = Number(starEl.dataset.index);
let isHalf = event.offsetX < star.offsetWidth / 2;
// rtl
if (document.documentElement.getAttribute('dir') === 'rtl') {
isHalf = event.offsetX > star.offsetWidth / 2;
}
const starValue = isHalf ? starIndex - 0.5 : starIndex;
this.starClickHandler_({ value: starValue });
});
});
}
renderStar = () => {
const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
const stars = this.element.querySelectorAll('.revue-star__star');
stars.forEach((star, i) => {
const starIndex = i + 1;
const starEl = star.querySelector('svg:nth-child(2)');
const isHalf = this.starNum % 1 > 0 && Math.ceil(this.starNum) === starIndex;
const isSolid = starIndex <= Math.ceil(this.starNum);
starEl.style.display = isSolid ? 'block' : 'none';
if (isHalf) {
if (isRtl) {
// RTL布局下,如果是半星,显示星星的右半边
starEl.style.clipPath = `polygon(50% 0, 100% 0, 100% 100%, 50% 100%)`;
} else {
// LTR布局下,如果是半星,显示星星的左半边
starEl.style.clipPath = `polygon(0 0, 50% 0, 50% 100%, 0 100%)`;
}
} else {
starEl.style.clipPath = `polygon(0 0, 100% 0, 100% 100%, 0 100%)`
}
});
const showCountEle = this.element.querySelector('#revue-star-show-count');
showCountEle && SPZ.whenApiDefined(showCountEle).then((api) => {
api.render({ starNum: this.starNum, starTotal: this.starTotal });
});
}
doRender_ = (data) => {
return this.templates_
.findAndRenderTemplate(this.element, { starSize: this.starSize, ...data }, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.starNum = data.starNum;
this.renderStar();
});
}
starClickHandler_ = (event) => {
this.starNum = event.value;
this.renderStar();
this.triggerEvent_('change', { value: event.value });
}
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, SPZCustomRevueStar)
(function() {
const TAG = 'spz-custom-new-revue-files-show';
class SpzCustomNewRevueFilesShow extends SPZ.BaseElement {
constructor(element) {
super(element);
/** @private {!Element} */
this.files_ = []
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.setupAction_();
this.element.setAttribute('nums', this.files_.length);
}
mountCallback() {
}
setupAction_() {
this.registerAction('upload', async(invocation) => {
const uploadFileList = invocation.args?.data || [];
uploadFileList.forEach(file => {
if(this.files_.some(item => item.url === file.url)) return
this.files_.push(file);
})
this.doRender_();
});
this.registerAction('delete', async(invocation) => {
this.files_ = this.files_.filter((_, index) => index !== invocation.args.index);
this.doRender_();
this.triggerEvent_('delete', { count: this.files_.length, files: this.files_ });
});
this.registerAction('preview', async(invocation) => {
let previewFileData = this.files_[invocation.args.index];
if (previewFileData.type === 'video') {
previewFileData = {...this.parseVideoSrc_(previewFileData.url), ...previewFileData};
}
this.triggerEvent_('preview', previewFileData);
});
this.registerAction('clear', async(invocation) => {
this.files_ = [];
this.doRender_();
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
parseVideoSrc_(src) {
const url = new URL(src);
const params = new URLSearchParams(url.search);
return {
videoUrl: url.origin + url.pathname,
mediaType: params.get('media_type'),
vID: params.get('vID'),
mp4: params.get('mp4'),
hls: params.get('hls')
};
}
doRender_ = () => {
this.triggerEvent_('setInputValue', {
data: this.files_
.map(file => {
const url = file.type === 'video' ? file.poster : file.url;
return `${url}?width=${file.width}&height=${file.height}`;
})
.join(',')
});
this.element.setAttribute('nums', this.files_.length);
return this.templates_
.findAndRenderTemplate(this.element, {
files: this.files_
})
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
}
isLayoutSupported = (layout) => {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomNewRevueFilesShow);
})()
${function() {
if (!data) {
return '';
}
const {url, type, height, width, poster, mp4} = data;
if (type === 'image') {
return `
`
}
if (type === 'video') {
return `
`
}
return ``
}()}
const TAG = 'spz-custom-revue-header';
class SPZCustomRevueHeader extends SPZ.BaseElement {
constructor(element) {
super(element);
this.showCount = this.element.getAttribute('show-count');
}
static deferredMount() {
return false;
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.showCount = this.element.getAttribute('show-count');
this.showSummary = this.element.getAttribute('show-summary');
this.showWriteReview = this.element.getAttribute('show-write-review');
this.showType = this.element.getAttribute('show-type') ;
this.showSort = this.element.getAttribute('show-sort') ;
this.sectionId = this.element.getAttribute('section-id');
this.viewall = this.element.getAttribute('viewall') ?? false;
this.prefix = this.element.getAttribute('prefix');
}
mountCallback() {
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
render(data) {
const ndata = {
...data,
showCount: this.showCount,
showSummary: this.showSummary,
showWriteReview: this.showWriteReview,
showType: this.showType,
showSort: this.showSort,
}
if(this.viewall == 'review'){
ndata.viewall = false
}
return this.templates_
.findAndRenderTemplate(this.element, ndata, null, true)
.then(({el}) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
if(data && Object.keys(data).length > 0) {
this.updateRender(data);
this.setupSummaryContainerEffects_(data);
}
});
}
updateRender(data) {
this.renderStarCounts_(data);
this.renderTypeSelect(data);
this.renderSortSelect(data);
}
renderStarCounts_(data) {
const renderData = {
...data.starData,
star_color: data.star_color,
isPC: data.isPC,
}
const summaryEle = data.isPC ? this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`) : this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`);
if(summaryEle) {
SPZ.whenApiDefined(summaryEle).then((api) => {
api.render(renderData);
});
}
}
renderTypeSelect(data) {
const typeSelect = this.element.querySelector(`#${this.prefix}-revue-header-type-${this.sectionId}`);
if(typeSelect) {
SPZ.whenApiDefined(typeSelect).then((api) => {
api.render(data);
api.registerAction('headerType_', (invocation) => {
this.triggerEvent_('headerType', invocation.args.data);
});
});
}
}
renderSortSelect(data) {
const suffix = data.suffix || this.sectionId;
const sortSelect = this.element.querySelector(`#${this.prefix}-revue-header-sort-${suffix}`);
if(sortSelect) {
SPZ.whenApiDefined(sortSelect).then((api) => {
api.registerAction('headerSort_', (invocation) => {
this.triggerEvent_('headerSort', invocation.args.data);
});
});
}
}
setupSummaryContainerEffects_(data) {
if(data.isPC) {
this.setupSummaryContainerHover_();
} else {
this.setupSummaryContainerTap_();
}
}
setupSummaryContainerHover_() {
const summaryContainer = this.element.querySelector(`#revue-header-summary-container-${this.sectionId}`);
const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header_pc`);
if (!summaryContainer || !summaryEle) return;
let isHovering = false;
// 鼠标移入容器时显示summary
SPZUtils.Event.listen(summaryContainer, 'mouseenter', () => {
isHovering = true;
summaryEle.removeAttribute('hidden');
const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.add('up-icon');
}
});
// 鼠标移入summary时也保持显示
SPZUtils.Event.listen(summaryEle, 'mouseenter', () => {
isHovering = true;
});
// 鼠标移出容器时,检查是否还在summary上
SPZUtils.Event.listen(summaryContainer, 'mouseleave', () => {
isHovering = false;
setTimeout(() => {
if (!isHovering) {
summaryEle.setAttribute('hidden', 'true');
const selectIcon = summaryContainer.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.remove('up-icon');
}
}
}, 50);
});
// 鼠标移出summary时,检查是否还在容器上
SPZUtils.Event.listen(summaryEle, 'mouseleave', () => {
isHovering = false;
setTimeout(() => {
if (!isHovering) {
summaryEle.setAttribute('hidden', 'true');
const selectIcon = summaryEle.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
if(selectIcon) {
selectIcon.classList.remove('up-icon');
}
}
}, 50);
});
}
setupSummaryContainerTap_() {
const selectIcon = this.element.querySelector(`#revue-header-summary-icon-${this.sectionId}`);
const summaryEle = this.element.querySelector(`#${this.prefix}-revue-summary-${this.sectionId}_header`);
if(!summaryEle) return;
let isTapped = false; // 是否显示summary
SPZ.whenApiDefined(summaryEle).then((api) => {
api.registerAction('display', () => {
if(isTapped) {
isTapped = false;
summaryEle.removeAttribute('hidden');
selectIcon.classList.add('up-icon');
} else {
isTapped = true;
summaryEle.setAttribute('hidden', 'true');
selectIcon.classList.remove('up-icon');
}
});
});
}
}
SPZ.defineElement(TAG, SPZCustomRevueHeader);
${function() {
const pc_layout = 'single_column';
const isProductPage = '1' == 1;
const product_id = '00f293d1-e8c9-4254-b5d4-5635d4d44848';
const accent_color = '';
const randomStr = Math.random().toString(36).substring(7);
const item = data;
const config = data.config;
const formatDate = value => {
let date = new Date(value * 1000);
const day = date.toLocaleString('en-US', { day: '2-digit' });
const month = date.toLocaleString('en-US', { month: 'short' });
const year = date.toLocaleString('en-US', { year: 'numeric' });
return month + '/' + day + '/' + year;
};
return `
${formatDate(item.created_at)}
`;
}()}
${function() {
const isPC = data.isPC;
const pc_layout = data.pc_layout;
const is_pagination = isPC && pc_layout == 'single_column';
const column_type = (isPC && pc_layout == 'double_column') ? 2 : 1;
const is_view_more = data.hasmore && ((isPC && pc_layout == 'double_column') || (!isPC && data.m_loading_type === 'curr_page'));
const is_view_all = (data.viewall ?? true) && !isPC && data.m_loading_type === 'modal';
const is_write_review = (data.write_review ?? true) && !isPC;
const scroll_loading = data.scroll_loading ?? false;
const is_reach_bottom = (isPC && pc_layout == 'double_column') || !isPC;
return `
Wow you reached the bottom
View all
Write a Review
`;
}()}
const TAG = 'spz-custom-revue-list';
class SPZCustomRevueList extends SPZ.BaseElement {
constructor(element) {
super(element);
}
static deferredMount() {
return false;
}
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;
}
buildCallback = () => {
this.element_id = this.element.getAttribute('id');
this.section_id = this.element.getAttribute('section-id');
this.suffix = this.element.getAttribute('suffix');
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.isPC = window.innerWidth > (window.breakpoint || 960);
}
mountCallback = () => {
// this.render({});
this.setAction()
}
render = (data) => {
const ndata = {
...data,
pc_layout: window.reviewProductSettings[this.section_id].pc_layout,
m_loading_type: window.reviewProductSettings[this.section_id].m_loading_type,
container_id: this.element_id,
suffix: this.suffix,
isProductPage: this.isProductPage,
}
return this.templates_
.findAndRenderTemplate(this.element, ndata, null)
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
this.triggerEvent_('finish', {});
this.setupIntersectionObserver();
});
}
renderList = (data, redo = false) => {
const listEle = document.querySelector(`#revue-list-${this.suffix}`);
const viewMoreEle = document.querySelector(`#revue-list-view-more`);
const loadingEle = document.querySelector(`#revue-list-scroll-loading`);
const viewMoreModal = document.querySelector(`#revue-viewall-modal-comp`);
const reachBottomEle = document.querySelector(`#revue-list-reach-bottom-${this.suffix}`);
if(viewMoreModal) {
SPZ.whenApiDefined(viewMoreModal).then((api) => {
api.setMarkScrollTop()
})
}
if (listEle) {
SPZ.whenApiDefined(listEle).then((api) => {
api.listRender(data, redo);
});
}
if(viewMoreEle) {
if(data.hasmore) {
viewMoreEle.removeAttribute('hidden');
} else {
viewMoreEle.setAttribute('hidden', true);
}
}
if (loadingEle) {
if(data.hasmore) {
loadingEle.removeAttribute('hidden');
} else {
loadingEle.setAttribute('hidden', true);
}
}
if (reachBottomEle) {
if(data.hasmore) {
reachBottomEle.setAttribute('hidden', true);
} else {
reachBottomEle.removeAttribute('hidden');
}
}
}
setupIntersectionObserver = () => {
// 创建 Intersection Observer 实例
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const viewallModal = document.querySelector(`#revue-viewall-modal-comp`);
if (viewallModal) {
SPZ.whenApiDefined(viewallModal).then((api) => {
api.loadMore();
});
}
}
});
}, {
threshold: 0.1 // 当目标元素 10% 进入视区时触发
});
const loadingElement = document.querySelector('.revue-list-scroll-loading');
if (loadingElement) {
observer.observe(loadingElement);
}
}
setAction = () => {
this.registerAction('checkOverFlow', () => {
// 检查普通评论
this.element.querySelectorAll('.revue_text_line_4').forEach(elem => {
if (elem.scrollHeight > elem.clientHeight + 10) {
elem.classList.add('overflow-text');
} else {
elem.classList.remove('overflow-text');
}
});
// 检查回复内容
this.element.querySelectorAll('.revue_reply').forEach(elem => {
const contentElem = elem.querySelector('.revue_reply_content');
if (contentElem.scrollHeight > contentElem.clientHeight + 10) {
elem.classList.add('overflow-text');
} else {
elem.classList.remove('overflow-text');
}
});
});
}
}
SPZ.defineElement(TAG, SPZCustomRevueList);
${function(){
const starOrder = ['one_star', 'two_star', 'three_star', 'four_star', 'five_star'];
function sortStarRatings(ratings) {
const sortedRatingsArr = [];
starOrder.map((star,index) => {
sortedRatingsArr.push(index+1);
return star;
});
return sortedRatingsArr;
};
const star_levels = sortStarRatings(data.star_detail).reverse();
return `
${data.comment_avg_star}
Total reviews: ${data.comment_count > 999 ? '999+' : data.comment_count}
`;
}()}
${function() {
return `
`
}()}
const TAG = 'spz-custom-revue-viewall-modal';
class SPZCustomRevueViewallModal extends SPZ.BaseElement {
constructor(element) {
super(element);
}
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;
}
buildCallback = () => {
this.section_id = this.element.getAttribute('section-id');
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.firstRender = true;
this.markScrollTop = 0;
this.scrollTop = 0;
}
mountCallback = () => {
this.doRender_();
this.setupAction_();
}
doRender_() {
this.templates_
.findAndRenderTemplate(this.element, {})
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
}).then(() => {
const viewallModalContentEle = document.querySelector(`#revue-viewall-modal-content-${this.section_id}`);
viewallModalContentEle.addEventListener('scroll', () => {
this.scrollTop = viewallModalContentEle.scrollTop;
});
})
}
setupAction_() {
this.registerAction('renderTab', async (invocation) => {
if(this.firstRender) {
this.firstRender = false;
const productEle = document.querySelector(`#revue-product-compo`);
const summaryEle = document.querySelector(`#revue-summary-${this.section_id}_viewall`);
if (productEle) {
SPZ.whenApiDefined(productEle).then(async (api) => {
api.renderStarCounts({}, `revue-summary-${this.section_id}_viewall`);
api.renderCommentTab({
viewall: false,
write_review: false,
scroll_loading: true
}, `revue-tab-${this.section_id}_viewall`);
});
}
}
});
this.registerAction('scrollToLast', async (invocation) => {
const viewallModalContentEle = document.querySelector(`#revue-viewall-modal-content-${this.section_id}`);
if(viewallModalContentEle) {
requestAnimationFrame(() => {
viewallModalContentEle.scrollTop = this.markScrollTop;
});
}
});
}
setMarkScrollTop() {
this.markScrollTop = this.scrollTop;
}
refresh() {
this.firstRender = true;
this.scrollTop = 0;
const productEle = document.querySelector(`#revue-viewall-modal-${this.section_id}`);
if (productEle) {
SPZ.whenApiDefined(productEle).then(async (api) => {
api.close();
});
}
}
loadMore() {
const productEle = document.querySelector(`#revue-product-compo`);
if (productEle) {
SPZ.whenApiDefined(productEle).then(async (api) => {
await api.renderByScrollPagination(`revue-comment-list-${this.section_id}_tab`, 'tab');
});
}
}
}
SPZ.defineElement(TAG, SPZCustomRevueViewallModal);
let section_id = '1539149753700';
window.reviewProductSettings = {};
const default_settings = {
"star_least": "5",
"only_featured": false,
"only_media": false,
"review_insufficient": "no_reviews",
"mini_quantity": 5,
"actions": "empty",
"pc_layout": "single_column",
"m_loading_type": "modal",
"m_modal_page_limit": "3",
"page_limit": 10,
"display_product_link": false,
"hide_review_section": true,
"title": "Reviews",
"title_color": "rgba(51, 51, 51, 1)",
"primary_color": "rgba(48, 53, 77, 1)",
"section_bg_color": "rgba(255, 255, 255, 1)",
"background_color_new": "rgba(255, 255, 255, 1)"
};
// 兼容旧数据,去除html标签
const user_settings = {
"description_text": "Here are what our customers say.",
"star_least": "5",
"only_featured": false,
"only_media": false,
"review_insufficient": null,
"mini_quantity": 5,
"actions": null,
"pc_layout": "single_column",
"m_loading_type": null,
"m_modal_page_limit": null,
"comment_page_limit": 10,
"page_limit": 10,
"display_product_link": false,
"hide_review_section": true,
"title": "Customer Reviews",
"accent_color": null,
"title_color": "rgba(51, 51, 51, 1)",
"text_color": "rgba(48, 53, 77, 1)",
"section_bg_color": null,
"background_color_new": null
};
window.reviewProductSettings[section_id] = Object.assign({}, default_settings, user_settings, {
page_limit: user_settings.comment_page_limit || user_settings.page_limit || default_settings.page_limit
});
${function() {
const randomStr = Math.random().toString(36).substring(7);
const list = data.listData;
const isPC = data.isPC;
const pc_layout = 'single_column';
return `
- All(${list.count})
- With Photos(${list.image_count})
`;
}()}
${function(){
return `
${function(){ if (media.videosrc) { let src = ''; if (media.videosrc) { src = media.videosrc + '.' + media.ext; } return ` ` } else if(media.mp4 || media.hls) { return ` ` } else { return ` ` } }()}
`;
}()}
${function(){
const isPC = data.isPC;
const pc_layout = 'single_column';
return `
Customer Reviews
No reviews yet, why don't you leave the first review?
Write a Review
No reviews yet, why don't you leave the first review?
Write a Review
`;
}()}
${function(){
return `
No reviews available. The product reviews component has been hidden
Product Detail Reviews
`
}()}