1/7
${data.index + 1}/${data.total}
Product Description
Quiet seduction, beautifully worn. This vintage halter crop arrives like late-summer dusk. Lightweight chiffon blend drapes with airy softness and gentle sheen, while the high halter neck gathers into delicate pleats across the bodice for subtle volume and shape. The off-shoulder design frames the collarbone elegantly, long lantern sleeves with ruched cuffs add poetic movement, and the cropped hem brings modern playfulness — sultry yet effortless.
Made for golden days and open nights: summer festivals, live concerts, beach vacations, sunset dates, casual brunches, or simply when you want to feel captivating without trying. The rich chocolate tone layers beautifully over denim, leather, or flowy skirts, carrying itself with timeless boho romance.
Knot it over high-waisted jeans and cowboy boots for edgy days. Pair with a midi skirt and strappy sandals for romantic evenings. Layer under a suede jacket or denim vest for transitional softness — one top. Every mood. Your signature piece.
Design Highlights
Lightweight chiffon blend — sheer, flowing, airy with gentle sheen
High halter neck with soft pleated bodice for subtle shape
Off-shoulder silhouette revealing elegant collarbone
Long lantern sleeves with ruched cuffs for poetic movement
Cropped length with gentle flare — modern yet timeless
Specifications
Material
Chiffon Blend
Length
Crop Top
Neckline
High Halter
Sleeve
Long Lantern Sleeves
Fit
Relaxed / Flowy
Style
Boho Vintage, Prairie Romance, Sheer Halter
Size Guide (inch)
Size
Length
Sleeve
S
17.7
22.8
M
18.1
23.2
L
18.5
23.6
XL
18.9
24.0
2XL
19.3
24.4
Tip: Relaxed cropped fit with beautiful flow. For more volume and breeziness, consider sizing up one. Measurements are taken flat; allow 0.5–1 inch difference due to manual measurement.
Chocolate flows deep, pleats whisper soft — quiet allure in every fold.
Festival & Concert Ready Styling
Style it with high-waisted denim shorts, cowboy boots, layered turquoise jewelry, and a wide-brim hat for that perfect festival or country concert look — effortless boho energy that turns heads from Coachella to local summer shows.
Care Instructions
Machine wash cold gentle cycle
Tumble dry low or hang dry
Do not bleach
Low heat iron on reverse if needed
Note: Sheer chiffon may wrinkle naturally — embrace the lived-in romance. Colors may vary slightly due to monitor settings. 1 inch = 2.54 cm.
Your cart is reserved for
${data.mm} m
${data.ss} s
!
${data.line_items.map(item => {
const renderDiscountApp = () => {
const isEmpty = item.discount_applications && item.discount_applications.length < 1;
const isNotExist = !item.discount_applications;
if (isEmpty || isNotExist) {
return ""
}
return `
${
(item.discount_applications || []).map(discount_item => {
const discount_item_amount = discount_item.discount_amount || discount_item.amount || '';
return `
${discount_item.title}
(- )
`
}).join('')
}
`
}
return `
${item.options.map(o => `
${o.name}: ${o.value}
`).join('')}
${(item.parsedProperties || []).map((propertie)=>{
if (propertie.isImage){
return `
${propertie.name}: View image `
}else{
return `
${propertie.name}: ${propertie.value}
`
}
}).join('')}
${renderDiscountApp()}
`;
}).join('')}
${function() {
return `
Add order note
Add order note
`;
}()}
${function() {
const freeAmount = 89.99;
const totalPrice = (data && data.total_price) || (data && data.cart && data.cart.total_price) || 0;
const amountDiff = freeAmount - totalPrice;
const percentDiff = (amountDiff > 0 ? (totalPrice / freeAmount * 100).toFixed(2) : 100) + '%';
const spendTip = "Buy {amount} more to enjoy FREE Shipping";
let tipText = "Your order is free delivery";
if (amountDiff > 0 && spendTip) {
tipText = spendTip.replace('{amount}', ` `);
}
return `
`;
}()}
${data.total_discount > 0 ? ( `
Save
${data.discount_applications.length > 0 && data.discount_applications.map(item => { return `
${item.title}:
` }).join('')}
Save
` ) : ''}
Check out
Taxes and shipping calculated at checkout
${function() {
const freeAmount = 89.99;
const totalPrice = (data && data.total_price) || (data && data.cart && data.cart.total_price) || 0;
const amountDiff = freeAmount - totalPrice;
const percentDiff = (amountDiff > 0 ? (totalPrice / freeAmount * 100).toFixed(2) : 100) + '%';
const spendTip = "Buy {amount} more to enjoy FREE Shipping";
let tipText = "Your order is free delivery";
if (amountDiff > 0 && spendTip) {
tipText = spendTip.replace('{amount}', ` `);
}
return `
`;
}()}
${data.total_discount > 0 ? (
`
Save
${data.discount_applications.length > 0 && data.discount_applications.map(item => {
return `
${item.title}:
`
}).join('')}
Save
`
) : ''}
Check out
Taxes and shipping calculated at checkout
${function(){
const settings_product_title = "title";
const product_grid_image_size = "natural";
const settings_product_image_hover_on = true;
let settings_product_save_label = true;
const product_sold_out_label = true;
const settings_product_swatches_name = [];
const settings_collection_color_swatches = true;
const product_price_currency = ` `
const from_on = data.price_max != data.price_min ? ''.replace(/\{\{\s*price\s*\}\}/, product_price_currency) : product_price_currency;
const variantShowLimit = 2;
const private_id = 'product-tmpl-' + Math.random().toFixed(6).slice(-6)
const product_variants = data.variants || [];
const product_id = data.id;
const images = data.images || [];
const image = data.image || {};
const imageWidth = image.width;
let imageHeight = image.height;
if (product_grid_image_size !== 'natural') {
imageHeight = (imageWidth * parseFloat(product_grid_image_size)) / 100;
}
const price = Number(data.price_min);
let compareAtPrice = Number(data.compare_at_price);
let offRatio = data.off_ratio;
const type = data.type;
const isMock = data.isMock;
let product_image_hover_on = false;
for (let i = 0; i < product_variants.length; i++) {
const item = product_variants[i];
const vcap = Number(item.compare_at_price);
if (item.price == price && vcap > compareAtPrice) {
compareAtPrice = vcap;
offRatio = item.off_ratio;
}
}
let second_image = null;
if (settings_product_image_hover_on) {
for (let i = 1; i < images.length; i++) {
const img = images[i];
if (img.src && img.src.indexOf('video=') === -1) {
second_image = img;
product_image_hover_on = true;
break;
}
}
}
let sold_label_on = false;
let sale_label_on = false;
if (settings_product_save_label == null) {
settings_product_save_label = true;
}
if (!data.available && product_sold_out_label) {
sold_label_on = true;
}
if (settings_product_save_label && compareAtPrice > price && data.available) {
sale_label_on = true;
}
const diffPrice = compareAtPrice - price;
const variantValues = [];
const showVariants = [];
if (data.need_variant_image && settings_collection_color_swatches && settings_product_swatches_name.length > 0) {
for (let i = 0; i < (data.options || []).length; i++) {
const option = data.originData.options[i];
const optionName = option.name && option.name.toLowerCase();
if (settings_product_swatches_name.includes(optionName)) {
for (let j = 0; j < product_variants.length; j++) {
const variant = product_variants[j];
const value = variant.options[i].value;
if (!variantValues.includes(value)) {
variantValues.push(value);
showVariants.push(variant);
}
}
break;
}
}
}
return `
${data.available ?
`
` : ''}
`;
}()}
${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]) || Object.keys(data).length > 1 ? data : null;
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
${function(){
const productData = data.product;
const selectedVariant = productData.variants.find(v => v.available) || productData.variants[0];
const product_options = productData.options.filter(Boolean) || [];
return `
Price
${function() {
const origin = "shop"
const product = (origin === 'shop' ? data.product : data) || {};
const selectedVariant = product.variants.find(v => v.available) || product.variants[0];
return !!selectedVariant ? `
-
` : `
-
`;
}()}
${selectedVariant.available ? "Add to cart" : "SOLD OUT"}
Buy it now
Product was out of stock.
Product is unavailable.
${function() {
const MAX_INVENTORY = 999999;
const product0 = Object.prototype.toString.call(data) == '[object Array]' ? data[0] : (data.product || data);
const inventoryQty = product0.inventory_quantity;
const inventoryPolicy = product0.inventory_policy;
const inventoryTracking = product0.inventory_tracking;
const exactInventoryStatusId = "quick-shop-exact-inventory-render";
const lowStock = 5;
const defaultVariant = product0 && product0.variants && product0.variants[0];
const selectedVariant = product0.variants.find(v => v.available) || defaultVariant;
const selectedVariantAvailableQuantity = selectedVariant && selectedVariant.available_quantity;
let actualInventory = inventoryQty;
if ((inventoryTracking && inventoryPolicy == 'continue') || !inventoryTracking) {
actualInventory = MAX_INVENTORY;
}
return `
Avaliability:
Out of stock
in stock, ready to be shipped
Low stock
`;
}()}
` }()}
${function(){
const optionName = option.name || '';
const optionId = option.id || '';
let isThumbImage = !!option.showThumbImage;
const thumbStyle = "image";
const variantType = "button";
const isSelected = (value) => {
const selected = (data.selectedOptions || []).find(v => v.name === optionName);
return selected && selected.value.length && selected.value[0] == value;
};
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName.toLowerCase()}
${optionName}:
${data.selectedOptions && data.selectedOptions.length && data.selectedOptions.find(v => v.name === optionName).value[0]}
`;
}()}
${function(){
return `${data.value} `
}()}
${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]) || Object.keys(data).length > 1 ? data : null;
const productVariant = null;
const variantData = currentSelectVariant || defaultVariant || productVariant;
const wholesale_price = variantData.wholesale_price || [];
if(wholesale_enabled && wholesale_price.length > 0) {
let wholesaleIndex = wholesale_price.findIndex(item => {
return item.min_quantity > qty;
});
if(wholesaleIndex < 0){
wholesaleIndex = wholesale_price.length - 1;
}else if(wholesaleIndex > 0){
wholesaleIndex = wholesaleIndex - 1;
}
const wholesalePrice = wholesale_price[wholesaleIndex] || '';
return `
`
}else {
const price = variantData && variantData.price;
return price != undefined ? `
` : ' ';
}
}()}
${function(){
const remove_variant_images_on = false;
let product_swatches_name = [];
product_swatches_name = product_swatches_name.map((name) => name.toLowerCase());
const variantsNamesSet = new Set(data.options.map((opt) => opt.name.toLowerCase()) || []);
const containsSwatches = product_swatches_name.some((name) => variantsNamesSet.has(name));
const variantsList = (data && data.variants) || []
const variants = variantsList.map((item) => item.image.path) || [];
const productData = data;
let images = data.images;
if(remove_variant_images_on && containsSwatches) {
images = data.images.filter((img) => !variants.includes(img.path));
}
const selectedVariant = data.variants.find(v => v.available) || data.variants[0];
const selectedIndex = !!selectedVariant ? images.findIndex(img => img.src === (selectedVariant.image && selectedVariant.image.src)) : 0;
const initialSlide = selectedIndex === -1 ? 0 : selectedIndex;
return `
${images.map((image, index) => `
`).join('')}
${images.map(image => `
`).join('')}
${images.length > 1 ? `
` : ''}
${data.title}
Price
${function() {
const origin = "view"
const product = (origin === 'shop' ? data.product : data) || {};
const selectedVariant = product.variants.find(v => v.available) || product.variants[0];
return !!selectedVariant ? `
-
` : `
-
`;
}()}
${selectedVariant.available ? "Add to cart" : "SOLD OUT"}
Buy it now
Product was out of stock.
Product is unavailable.
${function() {
const MAX_INVENTORY = 999999;
const product0 = Object.prototype.toString.call(data) == '[object Array]' ? data[0] : (data.product || data);
const inventoryQty = product0.inventory_quantity;
const inventoryPolicy = product0.inventory_policy;
const inventoryTracking = product0.inventory_tracking;
const exactInventoryStatusId = "quick-view-exact-inventory-render";
const lowStock = 5;
const defaultVariant = product0 && product0.variants && product0.variants[0];
const selectedVariant = product0.variants.find(v => v.available) || defaultVariant;
const selectedVariantAvailableQuantity = selectedVariant && selectedVariant.available_quantity;
let actualInventory = inventoryQty;
if ((inventoryTracking && inventoryPolicy == 'continue') || !inventoryTracking) {
actualInventory = MAX_INVENTORY;
}
return `
Avaliability:
Out of stock
in stock, ready to be shipped
Low stock
`;
}()}
`
}()}
${function(){
const optionName = option.name || '';
const optionId = option.id || '';
let isThumbImage = !!option.showThumbImage;
const thumbStyle = "image";
const variantType = "button";
const isSelected = (value) => {
const selected = (data.selectedOptions || []).find(v => v.name === optionName);
return selected && selected.value.length && selected.value[0] == value;
};
const getThumbImage = (value) => {
const options = data.product.options || [];
const option = options.find(o => o.name === optionName);
if (option.thumbImages) {
const thumbImage = option.thumbImages.find(t => t.value === value);
if (thumbImage && thumbImage.image) {
return {
src: thumbImage.image.src,
alt: thumbImage.image.alt
};
}
}
return {src: '', alt: ''};
};
return `
${optionName.toLowerCase()}
${optionName}:
${data.selectedOptions && data.selectedOptions.length && data.selectedOptions.find(v => v.name === optionName).value[0]}
`;
}()}
${function(){
return `${data.value} `
}()}
const TAG = "spz-custom-popup";
const DISPLAY_TYPE = {
POPUP: "PTT_POPUP" // 弹窗
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取弹窗列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
class SpzCustomPopup extends SPZ.BaseElement {
constructor(element) {
super(element);
this.popupList_ = []; // 弹窗数据
this.popupZIndex = 1050; // 弹窗层级
// 节流处理 每5s内多次点击 算一次点击上报
this.throttleReport = this.win.SPZCore.Types.throttle(
this.win,
(data) => {
this.reportData(data)
},
5000
)
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.fetchData_();
}
// 接口请求,获取数据
fetchData_() {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.POPUP,
discount_id: id
}
}).then((res) => {
// 请求成功 执行render
this.doRender_(res.list);
}).catch((err) => {
console.error(err);
});
}
// 渲染dom
doRender_(data) {
this.popupList_ = data || [];
if (this.popupList_.length > 0) {
this.popupList_.forEach((item) => {
item.config = JSON.parse(item.config);
})
}
return this.templates_
.findAndRenderTemplate(this.element, { list: this.popupList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
// 遍历显示弹窗
this.popupList_.forEach((item) => {
this.showPopup_(item);
});
})
}
showPopup_(popup) {
// 展示弹窗 符合展示条件的弹窗
const $popup = document.querySelector(`#popup-${popup.id}`);
$popup && SPZ.whenApiDefined($popup).then((api)=> {
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = popup.device === DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = popup.device == DISPLAY_DEVICE.PC_AND_MOBILE || popup.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
// 根据推送时间 延迟展示弹窗
setTimeout(() => {
api.open();
}, popup.delay_seconds * 1000);
}
})
}
// 上报数据
async reportData(data) {
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
setupAction_() {
this.registerAction('handleTrack', async(invocation) => {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
const data = invocation.args;
const event = data.event;
// 点击上报 节流处理
if(event === REPORT_EVENT.CLICK) {
await this.throttleReport(data);
} else {
this.reportData(data);
}
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomPopup);
const TAG = "spz-custom-announcement";
const DISPLAY_TYPE = {
ANNOUNCEMENT: "PTT_BANNER" // 公告栏
};
const API = {
LIST: `/api/storefront/promotion/placement/list`, // 获取公告栏列表
REPORT: `/api/storefront/promotion/placement/data/report` // 上报数据
};
const DISPLAY_DEVICE = {
PC_AND_MOBILE: "PD_PC_MOBILE", // PC和移动端
PC: "PD_PC", // PC
MOBILE: "PD_MOBILE" // 移动端
};
const REPORT_EVENT = {
CLICK: "PE_CLICK", // 点击事件
IMPRESSION: "PE_IMPRESSION" // 曝光事件
};
const POSITION = {
TOP: "PP_TOP", // 顶部
BOTTOM: "PP_BOTTOM" // 底部
}
const MODE = {
FIXED: "PM_FIXED", // 固定
NORMAL: "PM_SCROLLING" // 滚动
}
const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name;
class SpzCustomAnnouncement extends SPZ.BaseElement {
constructor(element) {
super(element);
this.announcementList_ = []; // 公告栏数据
}
static deferredMount() {
return false;
}
buildCallback() {
this.action_ = SPZServices.actionServiceForDoc(this.element);
this.templates_ = SPZServices.templatesForDoc(this.element);
this.xhr_ = SPZServices.xhrFor(this.win);
this.setupAction_();
this.viewport_ = this.getViewport();
}
mountCallback() {
this.fetchData_();
this.createAnnouncementDom_();
this.listenCartChange_();
}
fetchData_(type = '') {
const id = window.SHOPLAZZA.meta.page.template_type === 51 ? window.SHOPLAZZA.meta.page.resource_id : 0;
return this.xhr_.fetchJson(API.LIST, {
method: 'POST',
body: {
page_id: window.SHOPLAZZA.meta.page.template_type,
placement_type: DISPLAY_TYPE.ANNOUNCEMENT,
discount_id: id
}
}).then((res) => {
this.announcementList_ = res.list || [];
if (this.announcementList_.length > 0) {
this.announcementList_.forEach((item) => {
item.config = JSON.parse(item.config);
});
}
if(type === 'cartChange') {
this.announcementList_.forEach((item) => {
this.updateText_(item);
});
} else {
this.doRender_(this.announcementList_);
}
}).catch((error) => {
console.error(error);
})
}
doRender_(data) {
return this.templates_
.findAndRenderTemplate(this.element, { list: this.announcementList_ })
.then((el) => {
const children = this.element.querySelector('*:not(template)');
children && SPZCore.Dom.removeElement(children);
this.element.appendChild(el);
})
.then(() => {
this.announcementList_.forEach((item) => {
this.showAnnouncement_(item);
});
}).then(() => {
this.handleThemeCompatibility_();
});
}
// 更新文案
updateText_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementText = announcement.querySelectorAll('.announcement_text');
const textArr = item.config.text_discount.replace_texts;
const textDom = textArr.map((text) => {
return `${text} `;
}).join(',');
announcementText.forEach((text) => {
text.innerHTML = textDom;
});
}
// 创建公告栏dom
createAnnouncementDom_() {
const isHero = /Hero/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
// 创建滚动的底部公告栏
const announcementBottomContainer = document.createElement('div');
announcementBottomContainer.className = 'announcement__container_bottom bootstrap';
document.body.appendChild(announcementBottomContainer);
// 创建固定的底部公告栏
const announcementBottomSticky = document.createElement('ljs-sticky');
announcementBottomSticky.className = 'announcement__container_bottom-sticky';
announcementBottomSticky.setAttribute('layout', 'container');
announcementBottomSticky.setAttribute('position', 'bottom');
announcementBottomSticky.style.position = 'fixed';
announcementBottomSticky.style.bottom = '0';
announcementBottomSticky.style.left = '0';
announcementBottomSticky.style.right = '0';
announcementBottomSticky.style.zIndex = '1030';
document.body.appendChild(announcementBottomSticky);
const announcementTopContainer = document.createElement('div');
announcementTopContainer.classList.add('announcement__container_top');
if (isHero) {
announcementTopContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopContainer.classList.add('bootstrap');
document.body.insertBefore(announcementTopContainer, document.body.children[0]);
const announcementTopFixedContainer = document.createElement('div');
announcementTopFixedContainer.classList.add('announcement__container_top-fixed');
if (isHero) {
announcementTopFixedContainer.classList.add('announcement__container_top_zIndex_1030');
}
announcementTopFixedContainer.classList.add('bootstrap');
const insertBeforeElement = headerSticky ? headerEl : document.body;
insertBeforeElement.insertBefore(announcementTopFixedContainer, insertBeforeElement.children[0]);
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
let fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
if (isEvaMaskHeader) {
if (fixedBannerTopContainer) {
fixedBannerTopContainer.remove();
}
const newBanner = document.createElement('div');
newBanner.className = 'announcement__container_top-fixed bootstrap';
document.body.insertBefore(newBanner, document.body.firstChild);
fixedBannerTopContainer = newBanner;
} else {
if (!headerEl) return;
const observer = new MutationObserver(() => {
const isSticky = SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
if (!isSticky) return;
const isTopFixedAnnouncementInHeader = headerEl.querySelector('.announcement__container_top-fixed');
if (isTopFixedAnnouncementInHeader) return;
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
if (announcementTopFixedContainer) {
announcementTopFixedContainer.remove();
headerEl.insertBefore(announcementTopFixedContainer, headerEl.children[0]);
observer.disconnect();
}
});
observer.observe(headerEl, { attributes: true, attributeFilter: ['style', 'class'] });
}
if (headerSticky && !isEvaMaskHeader && fixedBannerTopContainer) {
fixedBannerTopContainer.style.position = 'relative';
fixedBannerTopContainer.style.zIndex = '29';
}
}
}
// 展示公告栏
showAnnouncement_(item) {
const announcement = document.querySelector(`#announcement-${item.id}`);
const announcementBottomContainer = document.querySelector('.announcement__container_bottom');
const announcementBottomSticky = document.querySelector('.announcement__container_bottom-sticky');
const announcementTopContainer = document.querySelector('.announcement__container_top');
const announcementTopFixedContainer = document.querySelector('.announcement__container_top-fixed');
const isPC = this.viewport_.getWidth() >= 960;
const isMobile = this.viewport_.getWidth() < 960;
const isMatchPCDevice = item.device === DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.PC;
const isMatchMobileDevice = item.device == DISPLAY_DEVICE.PC_AND_MOBILE || item.device === DISPLAY_DEVICE.MOBILE;
if((isPC && isMatchPCDevice) || (isMobile && isMatchMobileDevice)) {
if (item.position === POSITION.BOTTOM) {
if(item.mode === MODE.FIXED) {
announcementBottomSticky && announcementBottomSticky.appendChild(announcement);
} else {
announcementBottomContainer && announcementBottomContainer.appendChild(announcement);
}
} else {
if (item.mode === MODE.FIXED) {
announcementTopFixedContainer && announcementTopFixedContainer.appendChild(announcement);
} else {
announcementTopContainer && announcementTopContainer.appendChild(announcement);
}
}
this.reportData({
placement_id: item.id,
event: REPORT_EVENT.IMPRESSION
});
}
}
// 处理主题兼容
handleThemeCompatibility_() {
try {
const isBoost = /Boost/.test(THEME_NAME);
const isHyde = /Hyde/.test(THEME_NAME);
const isEva = /Eva/.test(THEME_NAME);
const boostHeader = document.querySelector('.boost-header');
const fixedBannerTopContainer = document.querySelector('.announcement__container_top-fixed');
const notFixedBannerTopContainer = document.querySelector('.announcement__container_top');
const headerEl = document.querySelector('[data-section-type="header"]');
const headerSticky = headerEl && SPZCore.Dom.computedStyle(this.win, headerEl).position === 'sticky';
const header = document.querySelector('.header__fixed') || document.querySelector('.header__wrapper');
const headerFixed = header && SPZCore.Dom.computedStyle(this.win, header).position === 'fixed';
const handleScroll = SPZCore.Types.throttle(this.win, () => {
if (isHyde) {
if (header && headerSticky) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
if (isEva) {
const evaHeader = document.querySelector('header.header');
const isEvaMaskHeader = evaHeader && SPZCore.Dom.computedStyle(this.win, evaHeader).position === 'absolute';
if (!isEvaMaskHeader) return;
if (evaHeader.classList.contains('header__fixed')) {
evaHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(document.documentElement.scrollTop === 0) {
evaHeader.style.marginTop = '0';
}
}
if (headerSticky) return;
if (headerFixed) {
header.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList' && fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
observer.disconnect(); // 停止观察
break;
}
}
});
// 开始观察 fixedBannerTopContainer 的子节点变化
observer.observe(fixedBannerTopContainer, { childList: true, subtree: true });
// 初始检查
if (fixedBannerTopContainer.childElementCount > 0) {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
if(header) {
header.style.marginTop = '0';
}
}
if (isBoost) {
fixedBannerTopContainer.style.zIndex = '1031';
if (boostHeader && boostHeader.classList.contains('header__fixed')) {
boostHeader.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
} else {
notFixedBannerTopContainer.style.marginTop = `${fixedBannerTopContainer.offsetHeight}px`;
}
}
}, 16);
window.addEventListener('scroll', handleScroll);
window.dispatchEvent(new Event('scroll'));
} catch (error) {
console.error('error', error);
}
}
// 上报数据
async reportData(data) {
// 如果是主题编辑器则不用处理
if(window.top !== window.self) {
return;
}
this.xhr_.fetchJson(API.REPORT, {
method: "POST",
body: {
placement_id: data.placement_id,
event: data.event
}
});
}
// 监听购物车变化事件dj.cartChange
listenCartChange_() {
SPZUtils.Event.listen(document, 'dj.cartChange', (event) => {
this.fetchData_('cartChange');
});
}
setupAction_() {
this.registerAction('handleClose', (invocation) => {
const data = invocation.args;
const id = data.id;
const announcement = document.querySelector(`#announcement-${id}`);
announcement && SPZCore.Dom.removeElement(announcement);
window.dispatchEvent(new Event('scroll'));
});
this.registerAction('handleJumpLink', (invocation) => {
const data = invocation.args;
if(!data.show_url) return;
data.url && window.open(data.url, data.open_new_window ? '_blank' : '_self');
this.reportData({
placement_id: data.id,
event: REPORT_EVENT.CLICK
});
});
}
triggerEvent_(name, data) {
const event = SPZUtils.Event.create(this.win, `${ TAG }.${ name }`, data || {});
this.action_.trigger(this.element, name, event);
}
isLayoutSupported(layout) {
return layout == SPZCore.Layout.CONTAINER;
}
}
SPZ.defineElement(TAG, SpzCustomAnnouncement);
${function() {
return data.originData.list.map((item) => {
const background = item.config.background;
const interactive = item.config.interactive;
const textArr = item.config.text_discount.replace_texts;
const textColor = item.config.text_discount.color;
const backgroundSize = background.presentation_rule === 'fill' ? 'cover' : 'contain';
const pcImage = (background.url && background.upload) ? background.url : '';
const mobileImage = (background.mobile_url && background.upload) ? background.mobile_url : '';
const color1 = background.color;
const color2 = background.color2 || background.color;
const backgroundStyle = `background: url(//img.fantaskycdn.com/${pcImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
const backgroundMobileStyle = `background: url(//img.fantaskycdn.com/${mobileImage}) center / ${backgroundSize} no-repeat, linear-gradient(to right, ${color1}, ${color2});`;
return `
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
${textArr.map((text) => {
return `
${text}
`
}).join(',')}
`
})
}()}