Tactical MICH Kevlar 2000 Level IV Ballistic Real Bulletproof Helmet
${function() {
const variantData = data.variant || {"id":"d0d56253-6e5c-4655-a912-526aa4c4a548","product_id":"20a2925e-d7fa-4211-9fae-6700f19be3c6","title":"Black-S","weight_unit":"g","inventory_quantity":9999,"sku":"TK0002-MICH-Black-1","barcode":"749753003707","position":1,"option1":"Black","option2":"S","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/834714f149265779cd1e318d302e63c8.jpeg","path":"834714f149265779cd1e318d302e63c8.jpeg","width":750,"height":750,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":469.99,"min_quantity":1}],"weight":"1600","compare_at_price":"599.99","price":"469.99","retail_price":"599.99","available":true,"url":"\/products\/tactical-mich-kevlar-2000-level-iv-ballistic-real-bulletproof-helmet?variant=d0d56253-6e5c-4655-a912-526aa4c4a548","available_quantity":999999999,"options":[{"name":"Color","value":"Black"},{"name":"Size","value":"S"}],"off_ratio":22,"flashsale_info":[],"sales":1};
const saveType = "amount";
const productLabelDiscountOn = true;
return `
-
${saveType == 'percentage'
? `-${variantData.off_ratio}% `
: `- `
}
`;
}()}
Color:
Black
${function(){
return `${data.value} `;
}()}
${function(){
const tipText = "Please select a {{ name }}".replace(/\{\{\s+name\s+\}\}/g, data);
return `${tipText}
`
}()}
Size:
S
${function(){
return `${data.value} `;
}()}
${function(){
const tipText = "Please select a {{ name }}".replace(/\{\{\s+name\s+\}\}/g, data);
return `${tipText}
`
}()}
Add to cart
$469.99
${function(){
const wholesale_enabled = false;
const qty = data.quantity || 1;
const currentSelectVariant = data.variant;
const defaultVariant = (data.product && data.product.variants && data.product.variants[0]);
const productVariant = {"id":"d0d56253-6e5c-4655-a912-526aa4c4a548","product_id":"20a2925e-d7fa-4211-9fae-6700f19be3c6","title":"Black-S","weight_unit":"g","inventory_quantity":9999,"sku":"TK0002-MICH-Black-1","barcode":"749753003707","position":1,"option1":"Black","option2":"S","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/834714f149265779cd1e318d302e63c8.jpeg","path":"834714f149265779cd1e318d302e63c8.jpeg","width":750,"height":750,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":469.99,"min_quantity":1}],"weight":"1600","compare_at_price":"599.99","price":"469.99","retail_price":"599.99","available":true,"url":"\/products\/tactical-mich-kevlar-2000-level-iv-ballistic-real-bulletproof-helmet?variant=d0d56253-6e5c-4655-a912-526aa4c4a548","available_quantity":999999999,"options":[{"name":"Color","value":"Black"},{"name":"Size","value":"S"}],"off_ratio":22,"flashsale_info":[],"sales":1};
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
Buy now
Product was out of stock.
Product is unavailable.
Sku : TK0002-MICH-Black-1
Weight : 1600g
Barcode : 749753003707
${function(){
const variantData = data.variant || {"id":"d0d56253-6e5c-4655-a912-526aa4c4a548","product_id":"20a2925e-d7fa-4211-9fae-6700f19be3c6","title":"Black-S","weight_unit":"g","inventory_quantity":9999,"sku":"TK0002-MICH-Black-1","barcode":"749753003707","position":1,"option1":"Black","option2":"S","option3":"","note":"","image":{"src":"\/\/img.staticdj.com\/834714f149265779cd1e318d302e63c8.jpeg","path":"834714f149265779cd1e318d302e63c8.jpeg","width":750,"height":750,"alt":"","aspect_ratio":1},"wholesale_price":[{"price":469.99,"min_quantity":1}],"weight":"1600","compare_at_price":"599.99","price":"469.99","retail_price":"599.99","available":true,"url":"\/products\/tactical-mich-kevlar-2000-level-iv-ballistic-real-bulletproof-helmet?variant=d0d56253-6e5c-4655-a912-526aa4c4a548","available_quantity":999999999,"options":[{"name":"Color","value":"Black"},{"name":"Size","value":"S"}],"off_ratio":22,"flashsale_info":[],"sales":1};
return `
Sku : ${variantData && variantData.sku}
Weight : ${variantData && variantData.weight}${variantData && variantData.weight_unit}
Barcode : ${variantData && variantData.barcode}
`
}()}
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);
${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() {
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() {
const media = data?.images[0];
const count = data?.images?.length || 0;
const isPC = data?.isPC;
return `
+${count}
${function(){
if (media.videosrc) {
const src = media.videosrc + '.' + media.ext;
return `
`
} else if(media.mp4 || media.hls) {
return `
` } else {
return `
`
}
}()}
`;
}()}
const TAG = 'spz-custom-review-media';
class SPZCustomReviewMedia 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);
// 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
}).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);
});
}
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, SPZCustomReviewMedia)
const TAG = 'spz-custom-revue-carousel';
class SpzCustomRevueCarourel extends SPZ.BaseElement {
constructor(element) {
super(element);
this.debouncedCartChangeHandler = null;
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.setupAction_();
this.reviewsList = []
this.isPC = window.innerWidth > (window.breakpoint || 960);
this.blockIndex = this.element.getAttribute('data-block-index');
this.blockSectionId = this.element.getAttribute('data-section-id');
this.commentConfig = window.globaCarouselSettings[this.blockIndex]
const { star_least, only_complex, only_show_selected } = this.commentConfig;
this.params = {
star_least: Number(star_least) || 1,
only_media: !!only_complex,
show_reply: true,
limit: 20,
offset: 0,
filter_type: 'product',
show_product: true,
only_featured: !!only_show_selected,
}
this.debouncedCartChangeHandler = this.debounce_(this.renderReviewsByCartProducts_.bind(this), 500);
}
mountCallback = () => {
const { isNeedFill, min } = this.getIfFillReviews_();
if (this.blockSectionId == 'cart_drawer') {
this.product_ids = this.commentConfig.cart_products_id;
} else {
this.product_ids = window.SHOPLAZZA.meta.page.resource_id;
};
this.params = {
...this.params,
product_ids: this.product_ids || '',
...isNeedFill ? { fill_strategy: 'store', fill_min_threshold: min } : {}
};
this.fetchConfigReviewsCarousel_();
if (this.blockSectionId == 'cart_drawer') {
document.removeEventListener('dj.cartChange', this.debouncedCartChangeHandler);
document.addEventListener('dj.cartChange', this.debouncedCartChangeHandler);
}
}
unmountCallback() {
document.removeEventListener('dj.cartChange', this.debouncedCartChangeHandler);
}
setupAction_ = () => {
this.registerAction('renderProductCommentModal', async(invocation) => {
const { current } = invocation.args;
const currentReview = this.reviewsList.find(_data => _data.id == current);
const imgArr = currentReview.img.map(image => {
const width = this.getUrlKey_('width', image);
const height = this.getUrlKey_('height', image);
return {
width,
height,
rate: (height/width).toFixed(2)*100,
url: image
};
});
const modalEle = document.querySelector(`#revueDetailModal-${this.blockSectionId}`);
if (modalEle) {
SPZ.whenApiDefined(modalEle).then((api) => {
api.renderModalFn({
data: {
...currentReview,
img: imgArr
},
...this.blockSectionId == 'cart_drawer' ? { mimic_mobile_style: true } : {},
closeCB: () => {
const carouselEl = document.querySelector(`#reviews-carousel-${this.blockSectionId}-${this.blockIndex}`);
if(carouselEl){
carouselEl.removeAttribute('pause');
}
},
commentConfig: this.commentConfig, // 评论配置
layout: '', // 布局
level_type: this.commentConfig.star_least, // 最低星级
show_number: 1 // 显示数量
});
})
const carouselEl = document.querySelector(`#reviews-carousel-${this.blockSectionId}-${this.blockIndex}`);
if(carouselEl){
carouselEl.setAttribute('pause', '');
}
}
});
}
getUrlKey_ = (name, url) => {
return (
decodeURIComponent(
(new RegExp("[?|&]" + name + "=" + "([^&;]+?)(&|#|;|$)").exec(
url
) || [, ""])[1].replace(/\+/g, "%20")
) || null
);
}
getIfFillReviews_ = () => {
const { comment_handle_type, review_type, min_comments_count } = this.commentConfig;
const min = Number(min_comments_count);
const isExistFillType = comment_handle_type !== 'no_carousel_card';
return { isNeedFill: isExistFillType, min: review_type == 'less than' ? min : 1 };
}
debounce_ = (func, delay) => {
let timeoutId;
return (...args) => { // 使用箭头函数保留实例上下文
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func(...args);
}, delay);
};
}
fetchCommentConfig_ = async () => {
const response = await fetch('/api/comment-config');
return response.json();
}
fetchCommentList_ = async(data) => {
const response = await fetch('/api/v1/comments', {
method: 'POST',
body: JSON.stringify(data)
});
return response.json();
}
fetchCartList_ = async() => {
const response = await fetch(`/api/cart`);
return response.json();
}
renderReviewsByCartProducts_ = async () => {
try {
const data = await this.fetchCartList_();
this.product_ids = data?.cart?.line_items.map(item => item.product_id).join(',');
if (this.product_ids) {
this.params = {
...this.params,
product_ids: this.product_ids,
};
const commentsRes = await this.fetchCommentList_(this.params);
const { isNeedFill, min } = this.getIfFillReviews_();
const isBlank = !isNeedFill && Number(commentsRes.data.count) < min;
this.renderReviewsList_({ list: isBlank ? { list: [] } : commentsRes.data, config: this.commentConfig });
}
} catch (err) {
this.renderEmptyReviewCarousel_();
}
}
fetchConfigReviewsCarousel_ = ()=> {
Promise.all([this.fetchCommentConfig_(), this.fetchCommentList_(this.params)])
.then(([configRes, commentsRes]) => {
const rawColor = this.commentConfig.carousel_accent_color
const star_color = !rawColor ? configRes.data.star_color : rawColor;
this.commentConfig = {
...this.commentConfig,
...configRes.data,
star_color,
};
const { isNeedFill, min } = this.getIfFillReviews_();
const isBlank = !isNeedFill && Number(commentsRes.data.count) < min;
this.renderReviewsList_({ list: isBlank ? { list: [] } : commentsRes.data, config: this.commentConfig });
})
.catch(error => {
this.renderEmptyReviewCarousel_();
});
}
renderEmptyReviewCarousel_ = () => {
const emptyEle = document.querySelector(`#revue_empty-${this.blockSectionId}-${this.blockIndex}`);
const isInB = window.top != window.self;
if (emptyEle && isInB) {
emptyEle.classList.remove('hidden');
}
const carouselEle = document.querySelector(`#revue-carousel-box-${this.blockSectionId}-${this.blockIndex}`);
if (carouselEle) {
carouselEle.classList.add('hidden');
}
}
renderReviewsList_ = (data) => {
const listEle = document.querySelector(`#revue-carousel-box-${this.blockSectionId}-${this.blockIndex}`);
const emptyEle = document.querySelector(`#revue_empty-${this.blockSectionId}-${this.blockIndex}`);
if (listEle) {
if (data.list.list.length == 0) {
this.renderEmptyReviewCarousel_();
} else {
listEle.classList.remove('hidden');
if (emptyEle) {
emptyEle.classList.add('hidden');
}
SPZ.whenApiDefined(listEle).then((api) => {
api.render({
...data,
list: data.list.list,
star_color: this.commentConfig.star_color,
isPC: this.isPC,
}, true);
})
.catch((error) => {
console.log(error);
});
};
}
this.reviewsList = data.list.list
}
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, SpzCustomRevueCarourel)
const TAG = 'spz-custom-revue-modal';
class SPZCustomRevueModal extends SPZ.BaseElement {
constructor(element) {
super(element);
this.renderedId = '';
this.closeCB = null;
this.sectionId = this.element.getAttribute('section-id');
}
static deferredMount() {
return false;
}
buildCallback = () => {
this.setupAction_();
}
mountCallback = () => {
}
setupAction_ = () => {
this.registerAction('renderModal', this.renderModalFn)
this.registerAction('closeFn',() => {
this?.closeCB?.()
})
}
mediaParse_ = function (url) {
var result = {};
try {
url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (str, key, value) {
try {
result[key] = decodeURIComponent(value);
} catch (e) {
result[key] = value;
}
});
result.src = url.split('?')[0];
} catch (e) {};
return result;
}
impFunc = function (selector, cb) {
// 添加自动曝光
const el = document.querySelector(selector);
const onImpress = () => {
cb();
};
// 元素未曝光时添加曝光事件监听,已曝光则可以立刻触发处理器
if (el && !el.getAttribute('imprsd')) {
el.addEventListener('impress', onImpress);
} else if (el) {
onImpress();
}
};
addModalImpression = function (selector, params) {
this.impFunc(selector, () => {
window.sa && window.sa.track('plugin_reviews_modal_pv', {
...params,
plugin_timestamp: new Date().valueOf().toString(),
});
});
};
renderModalFn(receivedData){
if(!receivedData) return;
const {
data:current,
commentConfig,
layout,
level_type,
show_number,
closeCB,
mimic_mobile_style,
props
} = receivedData;
try{
if(closeCB){
this.closeCB = () => {
closeCB()
};
}
}catch(e){
console.log(e);
};
const commentModalEl = document.querySelector(`#revue-product-comment-modal-${this.sectionId}`);
const modalRenderEl = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`);
if (!!mimic_mobile_style) {
if (commentModalEl) {
commentModalEl.classList.add('mobile-wrap');
}
if (modalRenderEl) {
modalRenderEl.classList.add('w-h-full-h5');
}
};
const parsedImages = current?.img?.map(image => {
return this.mediaParse_(`${image.url}?width=${image.width}&height=${image.height}`);
});
const modalEle = document.querySelector(`#revue_flow_modal_render-${this.sectionId}`);
if (modalEle) {
SPZ.whenApiDefined(modalEle).then((api) => {
api.render({ ...current, img: parsedImages, config: commentConfig, shop_name: window.SHOPLAZZA.shop.shop_name, mimic_mobile_style }, true).then(() => {
this.addModalImpression('.revue_modal_container', {
id: current.id,
username: current.username,
content: current.content,
star: current.star,
is_verified: current.is_verified,
is_featured: current.is_featured,
anonymous: current.anonymous,
iso_code_3: current.iso_code_3,
like_count: current.like,
layout_type: layout,
level_type: level_type,
show_number: show_number,
});
}).then(()=>{
this.renderedId = current.id
});
});
}
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement('spz-custom-revue-modal', SPZCustomRevueModal)
${function(){
const num = Number(data.total || 0);
const list = [...Array(num).keys()];
const blockIndex = data.blockIndex;
return `
`;
}()}
const TAG = 'spz-custom-revue-selector';
class SpzCustomRevueSelector extends SPZ.BaseElement {
constructor(element) {
super(element);
}
buildCallback = () => {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.total = this.element.getAttribute('total');
this.blockIndex = this.element.getAttribute('blockIndex');
this.setupAction_();
}
setupAction_ = () => {
this.registerAction('toggleChangeSelector', async (invocation) => {
const { option } = invocation.args;
const total = Number(this.total);
this.updateDots_(option, total);
});
}
updateDots_ = (currentIndex, totalDots) => {
const dots = this.element.querySelectorAll('.dot');
if (totalDots < 2 || dots?.length < 1) return;
const maxVisibleDots = 5;
// 重置所有点
dots?.forEach(dot => {
dot.classList.remove('active', 'scaled');
});
// 设置当前激活点
if (dots[currentIndex]) {
dots[currentIndex].classList.add('active');
}
// 计算显示的点和缩放效果
let startIndex = 0;
let endIndex = totalDots - 1;
let visibleRange = [0, totalDots - 1];
let translateX = 0;
if (totalDots > maxVisibleDots) {
// 计算可见范围
if (currentIndex < maxVisibleDots - 1) {
// 前 maxVisibleDots-1 个点
startIndex = 0;
endIndex = maxVisibleDots - 1;
translateX = 0;
} else if (currentIndex >= totalDots - (maxVisibleDots - 1)) {
// 最后 maxVisibleDots-1 个点
startIndex = totalDots - maxVisibleDots;
endIndex = totalDots - 1;
translateX = -(totalDots - maxVisibleDots) * 8;
} else {
// 中间点
startIndex = currentIndex - Math.floor(maxVisibleDots / 2);
endIndex = currentIndex + Math.floor(maxVisibleDots / 2);
// 调整边界情况
if (startIndex < 0) {
endIndex -= startIndex;
startIndex = 0;
}
if (endIndex >= totalDots) {
startIndex -= (endIndex - totalDots + 1);
endIndex = totalDots - 1;
}
translateX = -startIndex * 8;
}
// 设置可见范围
visibleRange = [startIndex, endIndex];
// 设置点的缩放效果
// 最左边的点(除了第一个)
if (startIndex > 0) {
dots[startIndex].classList.add('scaled');
}
// 最右边的点(除了最后一个)
if (endIndex < totalDots - 1) {
dots[endIndex].classList.add('scaled');
}
// 特殊处理第一个和最后一个点
if (currentIndex === 0) {
dots[0].classList.remove('scaled');
}
if (currentIndex === totalDots - 1) {
dots[totalDots - 1].classList.remove('scaled');
}
}
// 设置dotsWrap的位置
const dotsWrap = this.element.querySelector('#dotsWrap');
if (dotsWrap) {
dotsWrap.style.transform = `translateX(${translateX}px)`;
}
}
mountCallback() {
this.doRender_({
total: this.total,
blockIndex: this.blockIndex
});
}
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, SpzCustomRevueSelector);
${function() {
const randomStr = Math.random().toString(36).substring(7);
const reviewsList = data.list;
const config = data.config;
const isPC = data.isPC;
const rawColor = '';
const star_color_value = !rawColor ? config?.star_color : rawColor;
return `
`;
}()}
Reviews carousel block
No reviews available. The product reviews component has been hidden