New Products Daily

Worldwide Shipping from Europe

Premium Brands Across All Categories

New Products Daily

Worldwide Shipping from Europe

Premium Brands Across All Categories

const template_name = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SMART_PRODUCR_SEARCH_WRAP_CLASSNAME = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g,''); const BREAKPOINT = 960; const DELAY = 300; function diffThemeName(themeNameA, themeNameB){ return themeNameA.toLocaleLowerCase().includes(themeNameB.toLocaleLowerCase()) } const HEADER_DOM_MAP = { eva: 'header .header_grid_layout', geek: `.header-mobile-inner-container`, onePage: 'header .header', wind: 'header #header-nav', nova: 'header .header', hero: 'header .header__nav', 'flash': '#shoplaza-section-header>div>div', 'lifestyle': '#shoplaza-section-header .header__wrapper' } let HEADER_DOM = 'header'; Object.keys(HEADER_DOM_MAP) .map(themeName=>{ if (diffThemeName(THEME_NAME, themeName)) { HEADER_DOM = HEADER_DOM_MAP[themeName]; } }) const SEARCH_ICON_CLASS_MAP = { 'flash': 'app-smart-icon-search-large-flash', 'hero': 'app-smart-icon-search-large-hero', 'geek': 'app-smart-icon-search-large-geek', 'nova': 'app-smart-icon-search-large-nova', }; let SEARCH_ICON_CLASS = 'app-smart-icon-search-large-default'; Object.keys(SEARCH_ICON_CLASS_MAP) .map(themeName=>{ if (diffThemeName(THEME_NAME, themeName)) { SEARCH_ICON_CLASS = SEARCH_ICON_CLASS_MAP[themeName]; } }) class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.mobileHeaderPluginParentEle = null; this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.init(); this.listenerResize(); this.initRegisterActions(); } addIconClass(){ document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>{ e.classList.add(SEARCH_ICON_CLASS) }); } moveIcon(){ if (!diffThemeName(THEME_NAME, 'lifestyle')) return; if (this.searchItemType === 'input') return; if (this.isDesktop()) return; const smart_search_dom = document.querySelector('#app-smart-product-search-container-882'); if (!smart_search_dom) return; const hasMovedIcon = !!document.querySelector('.header__wrapper .container .row.header>div>#app-smart-product-search-container-882'); if (hasMovedIcon) return; const headerDivList = document.querySelectorAll('.header__wrapper .container .row.header>div'); const iconBoxDom = headerDivList[headerDivList.length-1] iconBoxDom.appendChild(smart_search_dom, iconBoxDom.firstChild); } init() { this.addIconClass(); this.moveIcon(); if ( this.searchItemType === 'input' ) { document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>e.style.display = 'none'); const mobileSmartSearchDom = document.querySelector(`.smart-search-mobile-container .app-smart-product-search-wrap`); if ( this.isDesktop() ) { document.querySelector(`#app-smart-product-search-container-882`).style="display: block"; if (mobileSmartSearchDom) { document.querySelector(`#app-smart-product-search-container-882`).appendChild(mobileSmartSearchDom); } }else{ if( template_name=='search' ) return; if (!document.querySelector(`.smart-search-mobile-container`)) { const appSmartSearchContainer = document.createElement('div'); appSmartSearchContainer.classList.add('smart-search-mobile-container'); appSmartSearchContainer.classList.add('smart-search-mobile-container-'+THEME_NAME.toLocaleLowerCase()); document.querySelector(HEADER_DOM).appendChild(appSmartSearchContainer); } if (!mobileSmartSearchDom) { document.querySelector(`.smart-search-mobile-container`).appendChild( document.querySelector(`.app-smart-product-search-wrap`) ) } } }else{ document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>e.style.display = 'flex'); } // PC-end not load if (this.isDesktop()) { return; } if (!window.__isLoadAppSmartSearch__) { this.initSmartSearch(); console.log('__isLoadAppSmartSearch__'); } // B-end must reload if (window.self === window.top && !window.__isLoadAppSmartSearch__) { window.__isLoadAppSmartSearch__ = true; } } initSmartSearch() { if (this.hasMobileUpperRightPlugin()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } } initRegisterActions(){ this.registOnSearchInputChange(); this.registOnSearchFormSubmit(); this.registOnOutsideCarouselIndexChange(); this.registOnInsideCarouselIndexChange(); this.registGetSearchItemType(); this.registGenerateHotKeywordList(); this.registerAction('onTapHotWord',(invocation)=>{ this.onTapHotWord(invocation.args.type); }); } registOnSearchInputChange(){ this.registerAction('onSearchInputChange',(invocation)=>{ const keyword = invocation.args.keyword; if (keyword === null || !keyword.length) { document.querySelectorAll('.hot-words-carousel-inner-container').forEach(e=>{ e.style='display: block'; }); } else { document.querySelectorAll('.hot-words-carousel-inner-container').forEach(e=>{ e.style='display: none'; }); } }) } registOnSearchFormSubmit(){ this.registerAction('onSearchFormSubmit',(invocation)=>{ const event = invocation.args.event; const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword!==null && keyword.length) { this.handleSearchSubmit_(keywordArray,1); } else { this.onTapHotWord('inside') } }) } handleSearchSubmit_(value, retryNum){ SPZ.whenApiDefined(document.getElementById('app-smart-search-882')) .then((ljsSearch) => { try{ ljsSearch.handleSearchSubmit_({ value: value }) }catch(e){ console.log('catch error',retryNum) if( 3 > retryNum ){ this.handleSearchSubmit_(value, retryNum + 1); return; } const searchStr = value[0] || ''; const searchResult = ljsSearch.setThinkSearchData_(searchStr); ljsSearch.afterSearching({ query: searchResult.query, url: `${SEARCH_URL}?q=${searchStr}`, queryType: searchResult.queryType, }) } }) } registOnOutsideCarouselIndexChange(){ this.registerAction('onOutsideCarouselIndexChange',(invocation)=>{ this.outsideCarouselIndex = invocation.args.index || 0; }) } registOnInsideCarouselIndexChange(){ this.registerAction('onInsideCarouselIndexChange',(invocation)=>{ this.insideCarouselIndex = invocation.args.index || 0; }) } registGetSearchItemType(searchItemType){ this.registerAction('getSearchItemType',(invocation)=>{ SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-882')) .then((appSmartSearchOutsideItem) => { const search_item_type = appSmartSearchOutsideItem.getData()?.search_item_type; this.searchItemType = search_item_type || this.searchItemType; this.init(); }) }) } registGenerateHotKeywordList(){ this.registerAction('generateHotKeywordList',(invocation)=>{ const search_keywords = invocation.args?.data?.data?.hotKeywordList || []; const isShowHotKeyword = invocation.args?.data?.data?.isShowHotKeyword || false; SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-882')) .then((appSmartSearchOutsideItem) => { const hotwords = appSmartSearchOutsideItem.getData()?.search_keywords || []; const new_search_keywords = search_keywords.map((item, index) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(e=>e.word === item.word); if (hotwordItem) { item.icon = hotwordItem.icon || ''; } if (!item.urlObj || !item.urlObj.url) { item.urlObj = { ...item.url_obj, url: item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url, }; } return item; }); document.querySelectorAll('.app-hot-keyword-render-child') .forEach((ele) => { SPZ.whenApiDefined(ele) .then((hotWordsChildDom) => { hotWordsChildDom.render({ list: new_search_keywords, isShowHotKeyword: isShowHotKeyword, }); }) }); }) }); } onTapHotWord(type){ const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-882')) .then((appSmartSearchOutsideItem) => { const hotwords = appSmartSearchOutsideItem.getData()?.search_keywords || []; const currentHotwordItem = hotwords[index] || null; if (currentHotwordItem && currentHotwordItem.url_obj) { currentHotwordItem.url_obj.url = currentHotwordItem.url_obj.type === 'search' ? `${SEARCH_URL}?q=${currentHotwordItem.word}` : currentHotwordItem.url_obj.url; } SPZ.whenApiDefined(document.getElementById('app-smart-search-882')) .then((ljsSearch) => { if (currentHotwordItem) { ljsSearch.handleHotKeyword_({ word: currentHotwordItem.word, query_type: currentHotwordItem.type, url: currentHotwordItem.url_obj?.url, }); } else { this.handleSearchSubmit_([''],1); } }) }) } getOutsideCarouselConfig(){ return SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-882')) .then((appSmartSearchOutsideItem) => { return { ...appSmartSearchOutsideItem.getData(), outsideCarouselIndex: this.outsideCarouselIndex, } }) } listenerResize() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { this.init(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } isDesktop() { const mediaQueryList = window.matchMedia(`(min-width: ${BREAKPOINT}px)`); return mediaQueryList.matches; } hasMobileUpperRightPlugin() { return !['geek', 'flash', 'boost'].includes(THEME_NAME.toLocaleLowerCase()); } showMobileSmartSearch() { this.mobileHeaderPluginParentEle = this.getMobileHeaderPluginParentEle(); this.setMobileHeaderIconsPluginStyle(this.mobileHeaderPluginParentEle); } getMobileHeaderPluginParentEle() { const MOBILE_HEADER_PLUGIN_PARENT_ELE_MAP = { nova: '.header__mobile #header__plugin-container', hero: '.header__icons .tw-flex.tw-justify-end.tw-items-center.tw-space-x-7', onePage: '.header__mobile #header__plugin-container', wind: '#header-icons .flex.justify-end.items-center', eva: '#header__icons .plugin_content' }; const headerPluginParentSelector = this.combineMultipleSelectors( Object.values(MOBILE_HEADER_PLUGIN_PARENT_ELE_MAP) ); return document.querySelector(headerPluginParentSelector); } setMobileHeaderIconsPluginStyle(pluginParentEle) { if (!pluginParentEle) { return; } const containHidden = pluginParentEle.classList.contains('md:hidden'); const containTwHidden = pluginParentEle.classList.contains('md:tw-hidden'); if (containHidden || containTwHidden) { Array.from(pluginParentEle.children).forEach((pluginElement) => { if (!this.hasSmartSearchPlugin(pluginElement)) { pluginElement.style.display = 'none'; } }); pluginParentEle.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchPluginElement = Array.from(pluginParentEle.children).find( (pluginElement) => { return this.hasSmartSearchPlugin(pluginElement); } ); smartSearchPluginElement.style.display = 'block'; } } hasSmartSearchPlugin(pluginElement) { return ( pluginElement.classList.contains(`${SMART_PRODUCR_SEARCH_WRAP_CLASSNAME}`) || pluginElement.querySelectorAll(`.${SMART_PRODUCR_SEARCH_WRAP_CLASSNAME}`).length > 0 ); } addMobileSmartSearch() { this.mobileHeaderIconsEle = this.getMobileHeaderIconsEle(); this.smartSearchWrapEle = this.getSmartSearchWrapEle(); this.appendSmartSearchToHeader(); } getMobileHeaderIconsEle() { // Must be the parent element of the plugin const MOBILE_HEADER_ICONS_ELE_MAP = { geek: '#header-mobile-container .flex.items-center.justify-end.flex-shrink-0', flash: '#header-layout .header__icons', boost: '.header__mobile-bottom .tw-flex.tw-items-center.tw-justify-end.tw-flex-1' }; const headerIconsSelector = this.combineMultipleSelectors( Object.values(MOBILE_HEADER_ICONS_ELE_MAP) ); return document.querySelector(headerIconsSelector); } getSmartSearchWrapEle() { const smartSearchWrapEle = document.querySelector(this.getSmartSearchWrapSelector()); if (!smartSearchWrapEle) { return null; } return smartSearchWrapEle; } appendSmartSearchToHeader() { if (!this.smartSearchWrapEle) { return; } this.mobileHeaderIconsEle.insertAdjacentElement('afterbegin', this.smartSearchWrapEle); } getSmartSearchWrapSelector() { const PLUGIN_POSITION = { DRAWER: 'drawer', HEADER_TOP: 'headerTop' }; // only one this plugin of ancestor element const MOBILE_PLUGIN_POSITION_ELE_MAP = { [PLUGIN_POSITION.DRAWER]: { geek: '#header-menu-mobile #menu-drawer', flash: '#menu-drawer .plugin__header-content', boost: '.header__drawer' }, [PLUGIN_POSITION.HEADER_TOP]: { geek: '#header-menu-mobile #menu-drawer', flash: '#menu-drawer .plugin__header-content', boost: '.header-content .logo-wrap' } }; const MbPluginPositionInTheme = [ ...Object.values(MOBILE_PLUGIN_POSITION_ELE_MAP[PLUGIN_POSITION.DRAWER]), ...Object.values(MOBILE_PLUGIN_POSITION_ELE_MAP[PLUGIN_POSITION.HEADER_TOP]) ]; return Object.values(MbPluginPositionInTheme).reduce((pre, ancestor) => { if (pre === '') { return `${ancestor} .app-smart-product-search-container`; } if (pre.includes(ancestor)) { return pre; } return `${pre},${ancestor} .app-smart-product-search-container`; }, ''); } combineMultipleSelectors(selectorList) { return selectorList.reduce((pre, selector) => { if (pre === '') { return `${selector}`; } if (pre.includes(selector)) { return pre; } return `${pre},${selector}`; }, ''); } } SPZ.defineElement(TAG, SpzCustomSmartSearchLocation);
const template_name = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SMART_PRODUCR_SEARCH_WRAP_CLASSNAME = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g,''); const BREAKPOINT = 960; const DELAY = 300; function diffThemeName(themeNameA, themeNameB){ return themeNameA.toLocaleLowerCase().includes(themeNameB.toLocaleLowerCase()) } const HEADER_DOM_MAP = { eva: 'header .header_grid_layout', geek: `.header-mobile-inner-container`, onePage: 'header .header', wind: 'header #header-nav', nova: 'header .header', hero: 'header .header__nav', 'flash': '#shoplaza-section-header>div>div', 'lifestyle': '#shoplaza-section-header .header__wrapper' } let HEADER_DOM = 'header'; Object.keys(HEADER_DOM_MAP) .map(themeName=>{ if (diffThemeName(THEME_NAME, themeName)) { HEADER_DOM = HEADER_DOM_MAP[themeName]; } }) const SEARCH_ICON_CLASS_MAP = { 'flash': 'app-smart-icon-search-large-flash', 'hero': 'app-smart-icon-search-large-hero', 'geek': 'app-smart-icon-search-large-geek', 'nova': 'app-smart-icon-search-large-nova', }; let SEARCH_ICON_CLASS = 'app-smart-icon-search-large-default'; Object.keys(SEARCH_ICON_CLASS_MAP) .map(themeName=>{ if (diffThemeName(THEME_NAME, themeName)) { SEARCH_ICON_CLASS = SEARCH_ICON_CLASS_MAP[themeName]; } }) class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.mobileHeaderPluginParentEle = null; this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.init(); this.listenerResize(); this.initRegisterActions(); } addIconClass(){ document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>{ e.classList.add(SEARCH_ICON_CLASS) }); } moveIcon(){ if (!diffThemeName(THEME_NAME, 'lifestyle')) return; if (this.searchItemType === 'input') return; if (this.isDesktop()) return; const smart_search_dom = document.querySelector('#app-smart-product-search-container-55'); if (!smart_search_dom) return; const hasMovedIcon = !!document.querySelector('.header__wrapper .container .row.header>div>#app-smart-product-search-container-55'); if (hasMovedIcon) return; const headerDivList = document.querySelectorAll('.header__wrapper .container .row.header>div'); const iconBoxDom = headerDivList[headerDivList.length-1] iconBoxDom.appendChild(smart_search_dom, iconBoxDom.firstChild); } init() { this.addIconClass(); this.moveIcon(); if ( this.searchItemType === 'input' ) { document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>e.style.display = 'none'); const mobileSmartSearchDom = document.querySelector(`.smart-search-mobile-container .app-smart-product-search-wrap`); if ( this.isDesktop() ) { document.querySelector(`#app-smart-product-search-container-55`).style="display: block"; if (mobileSmartSearchDom) { document.querySelector(`#app-smart-product-search-container-55`).appendChild(mobileSmartSearchDom); } }else{ if( template_name=='search' ) return; if (!document.querySelector(`.smart-search-mobile-container`)) { const appSmartSearchContainer = document.createElement('div'); appSmartSearchContainer.classList.add('smart-search-mobile-container'); appSmartSearchContainer.classList.add('smart-search-mobile-container-'+THEME_NAME.toLocaleLowerCase()); document.querySelector(HEADER_DOM).appendChild(appSmartSearchContainer); } if (!mobileSmartSearchDom) { document.querySelector(`.smart-search-mobile-container`).appendChild( document.querySelector(`.app-smart-product-search-wrap`) ) } } }else{ document.querySelectorAll('.app-smart-icon-search-large').forEach(e=>e.style.display = 'flex'); } // PC-end not load if (this.isDesktop()) { return; } if (!window.__isLoadAppSmartSearch__) { this.initSmartSearch(); console.log('__isLoadAppSmartSearch__'); } // B-end must reload if (window.self === window.top && !window.__isLoadAppSmartSearch__) { window.__isLoadAppSmartSearch__ = true; } } initSmartSearch() { if (this.hasMobileUpperRightPlugin()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } } initRegisterActions(){ this.registOnSearchInputChange(); this.registOnSearchFormSubmit(); this.registOnOutsideCarouselIndexChange(); this.registOnInsideCarouselIndexChange(); this.registGetSearchItemType(); this.registGenerateHotKeywordList(); this.registerAction('onTapHotWord',(invocation)=>{ this.onTapHotWord(invocation.args.type); }); } registOnSearchInputChange(){ this.registerAction('onSearchInputChange',(invocation)=>{ const keyword = invocation.args.keyword; if (keyword === null || !keyword.length) { document.querySelectorAll('.hot-words-carousel-inner-container').forEach(e=>{ e.style='display: block'; }); } else { document.querySelectorAll('.hot-words-carousel-inner-container').forEach(e=>{ e.style='display: none'; }); } }) } registOnSearchFormSubmit(){ this.registerAction('onSearchFormSubmit',(invocation)=>{ const event = invocation.args.event; const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword!==null && keyword.length) { this.handleSearchSubmit_(keywordArray,1); } else { this.onTapHotWord('inside') } }) } handleSearchSubmit_(value, retryNum){ SPZ.whenApiDefined(document.getElementById('app-smart-search-55')) .then((ljsSearch) => { try{ ljsSearch.handleSearchSubmit_({ value: value }) }catch(e){ console.log('catch error',retryNum) if( 3 > retryNum ){ this.handleSearchSubmit_(value, retryNum + 1); return; } const searchStr = value[0] || ''; const searchResult = ljsSearch.setThinkSearchData_(searchStr); ljsSearch.afterSearching({ query: searchResult.query, url: `${SEARCH_URL}?q=${searchStr}`, queryType: searchResult.queryType, }) } }) } registOnOutsideCarouselIndexChange(){ this.registerAction('onOutsideCarouselIndexChange',(invocation)=>{ this.outsideCarouselIndex = invocation.args.index || 0; }) } registOnInsideCarouselIndexChange(){ this.registerAction('onInsideCarouselIndexChange',(invocation)=>{ this.insideCarouselIndex = invocation.args.index || 0; }) } registGetSearchItemType(searchItemType){ this.registerAction('getSearchItemType',(invocation)=>{ SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-55')) .then((appSmartSearchOutsideItem) => { const search_item_type = appSmartSearchOutsideItem.getData()?.search_item_type; this.searchItemType = search_item_type || this.searchItemType; this.init(); }) }) } registGenerateHotKeywordList(){ this.registerAction('generateHotKeywordList',(invocation)=>{ const search_keywords = invocation.args?.data?.data?.hotKeywordList || []; const isShowHotKeyword = invocation.args?.data?.data?.isShowHotKeyword || false; SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-55')) .then((appSmartSearchOutsideItem) => { const hotwords = appSmartSearchOutsideItem.getData()?.search_keywords || []; const new_search_keywords = search_keywords.map((item, index) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(e=>e.word === item.word); if (hotwordItem) { item.icon = hotwordItem.icon || ''; } if (!item.urlObj || !item.urlObj.url) { item.urlObj = { ...item.url_obj, url: item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url, }; } return item; }); document.querySelectorAll('.app-hot-keyword-render-child') .forEach((ele) => { SPZ.whenApiDefined(ele) .then((hotWordsChildDom) => { hotWordsChildDom.render({ list: new_search_keywords, isShowHotKeyword: isShowHotKeyword, }); }) }); }) }); } onTapHotWord(type){ const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-55')) .then((appSmartSearchOutsideItem) => { const hotwords = appSmartSearchOutsideItem.getData()?.search_keywords || []; const currentHotwordItem = hotwords[index] || null; if (currentHotwordItem && currentHotwordItem.url_obj) { currentHotwordItem.url_obj.url = currentHotwordItem.url_obj.type === 'search' ? `${SEARCH_URL}?q=${currentHotwordItem.word}` : currentHotwordItem.url_obj.url; } SPZ.whenApiDefined(document.getElementById('app-smart-search-55')) .then((ljsSearch) => { if (currentHotwordItem) { ljsSearch.handleHotKeyword_({ word: currentHotwordItem.word, query_type: currentHotwordItem.type, url: currentHotwordItem.url_obj?.url, }); } else { this.handleSearchSubmit_([''],1); } }) }) } getOutsideCarouselConfig(){ return SPZ.whenApiDefined(document.getElementById('app-smart-search-outside-item-55')) .then((appSmartSearchOutsideItem) => { return { ...appSmartSearchOutsideItem.getData(), outsideCarouselIndex: this.outsideCarouselIndex, } }) } listenerResize() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { this.init(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } isDesktop() { const mediaQueryList = window.matchMedia(`(min-width: ${BREAKPOINT}px)`); return mediaQueryList.matches; } hasMobileUpperRightPlugin() { return !['geek', 'flash', 'boost'].includes(THEME_NAME.toLocaleLowerCase()); } showMobileSmartSearch() { this.mobileHeaderPluginParentEle = this.getMobileHeaderPluginParentEle(); this.setMobileHeaderIconsPluginStyle(this.mobileHeaderPluginParentEle); } getMobileHeaderPluginParentEle() { const MOBILE_HEADER_PLUGIN_PARENT_ELE_MAP = { nova: '.header__mobile #header__plugin-container', hero: '.header__icons .tw-flex.tw-justify-end.tw-items-center.tw-space-x-7', onePage: '.header__mobile #header__plugin-container', wind: '#header-icons .flex.justify-end.items-center', eva: '#header__icons .plugin_content' }; const headerPluginParentSelector = this.combineMultipleSelectors( Object.values(MOBILE_HEADER_PLUGIN_PARENT_ELE_MAP) ); return document.querySelector(headerPluginParentSelector); } setMobileHeaderIconsPluginStyle(pluginParentEle) { if (!pluginParentEle) { return; } const containHidden = pluginParentEle.classList.contains('md:hidden'); const containTwHidden = pluginParentEle.classList.contains('md:tw-hidden'); if (containHidden || containTwHidden) { Array.from(pluginParentEle.children).forEach((pluginElement) => { if (!this.hasSmartSearchPlugin(pluginElement)) { pluginElement.style.display = 'none'; } }); pluginParentEle.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchPluginElement = Array.from(pluginParentEle.children).find( (pluginElement) => { return this.hasSmartSearchPlugin(pluginElement); } ); smartSearchPluginElement.style.display = 'block'; } } hasSmartSearchPlugin(pluginElement) { return ( pluginElement.classList.contains(`${SMART_PRODUCR_SEARCH_WRAP_CLASSNAME}`) || pluginElement.querySelectorAll(`.${SMART_PRODUCR_SEARCH_WRAP_CLASSNAME}`).length > 0 ); } addMobileSmartSearch() { this.mobileHeaderIconsEle = this.getMobileHeaderIconsEle(); this.smartSearchWrapEle = this.getSmartSearchWrapEle(); this.appendSmartSearchToHeader(); } getMobileHeaderIconsEle() { // Must be the parent element of the plugin const MOBILE_HEADER_ICONS_ELE_MAP = { geek: '#header-mobile-container .flex.items-center.justify-end.flex-shrink-0', flash: '#header-layout .header__icons', boost: '.header__mobile-bottom .tw-flex.tw-items-center.tw-justify-end.tw-flex-1' }; const headerIconsSelector = this.combineMultipleSelectors( Object.values(MOBILE_HEADER_ICONS_ELE_MAP) ); return document.querySelector(headerIconsSelector); } getSmartSearchWrapEle() { const smartSearchWrapEle = document.querySelector(this.getSmartSearchWrapSelector()); if (!smartSearchWrapEle) { return null; } return smartSearchWrapEle; } appendSmartSearchToHeader() { if (!this.smartSearchWrapEle) { return; } this.mobileHeaderIconsEle.insertAdjacentElement('afterbegin', this.smartSearchWrapEle); } getSmartSearchWrapSelector() { const PLUGIN_POSITION = { DRAWER: 'drawer', HEADER_TOP: 'headerTop' }; // only one this plugin of ancestor element const MOBILE_PLUGIN_POSITION_ELE_MAP = { [PLUGIN_POSITION.DRAWER]: { geek: '#header-menu-mobile #menu-drawer', flash: '#menu-drawer .plugin__header-content', boost: '.header__drawer' }, [PLUGIN_POSITION.HEADER_TOP]: { geek: '#header-menu-mobile #menu-drawer', flash: '#menu-drawer .plugin__header-content', boost: '.header-content .logo-wrap' } }; const MbPluginPositionInTheme = [ ...Object.values(MOBILE_PLUGIN_POSITION_ELE_MAP[PLUGIN_POSITION.DRAWER]), ...Object.values(MOBILE_PLUGIN_POSITION_ELE_MAP[PLUGIN_POSITION.HEADER_TOP]) ]; return Object.values(MbPluginPositionInTheme).reduce((pre, ancestor) => { if (pre === '') { return `${ancestor} .app-smart-product-search-container`; } if (pre.includes(ancestor)) { return pre; } return `${pre},${ancestor} .app-smart-product-search-container`; }, ''); } combineMultipleSelectors(selectorList) { return selectorList.reduce((pre, selector) => { if (pre === '') { return `${selector}`; } if (pre.includes(selector)) { return pre; } return `${pre},${selector}`; }, ''); } } SPZ.defineElement(TAG, SpzCustomSmartSearchLocation);
(function(){ let w = window.innerWidth; function setHeaderCssVar() { const headerEle = document.getElementById('shoplaza-section-header'); if(!headerEle){ return }; document.body.style.setProperty('--window-height', `${window.innerHeight}px`); document.body.style.setProperty('--header-height', `${headerEle.clientHeight}px`); const mdScorllHideEle = headerEle.querySelector('.header__mobile .header__scroll_hide'); if (mdScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-md', `${mdScorllHideEle.clientHeight}px`); } const pcScorllHideEle = headerEle.querySelector('.header__desktop .header__scroll_hide'); if (pcScorllHideEle) { document.body.style.setProperty('--header-scroll-hide-height-pc', `${pcScorllHideEle.clientHeight}px`); } } function handlResize() { if(w == window.innerWidth){return}; w = window.innerWidth; setHeaderCssVar(); }; function init(){ setHeaderCssVar(); window.removeEventListener('resize', window._theme_header_listener) window._theme_header_listener = handlResize; window.addEventListener('resize', window._theme_header_listener); } init(); })();

Perché scegliere i saldi kim - Vantaggi e opportunità da non perdere

I saldi kim rappresentano una delle occasioni più attese dell’anno per chi desidera rinnovare il guardaroba, aggiornare l’arredamento di casa o acquistare prodotti di alta qualità a prezzi scontatissimi. Grazie alle offerte [[sale.kim]] puoi approfittare di sconti eccezionali su una vasta gamma di brand e categorie, rendendo ogni acquisto un investimento intelligente senza rinunciare alla qualità.

Se sei alla ricerca di come sfruttare al massimo i saldi kim, questa guida ti aiuterà a scoprire come individuare le promozioni più vantaggiose, quali prodotti scegliere e come pianificare i tuoi acquisti per ottenere il massimo dal risparmio. Non perdere l'opportunità di accedere a offerte esclusive con un semplice click!

Perché scegliere i saldi kim - Vantaggi e opportunità da non perdere

I saldi kim sono molto più di una semplice svendita: rappresentano l’opportunità di fare shopping con saggezza, puntando su prodotti di alta qualità senza spendere una fortuna. Questi saldi sono particolarmente apprezzati perché permettono di accedere a un’ampia selezione di brand premium, molte delle quali sono tra le più apprezzate in Italia e nel mondo.

In questa sezione, scoprirai i principali motivi per cui i saldi kim sono un'occasione unica e come sfruttare al meglio le offerte disponibili.

La vasta scelta di prodotti e brand di punta

Grazie all’assortimento di sale.kim, puoi trovare una selezione infinita di prodotti provenienti dai brand più rinomati, tra cui abbigliamento, accessori, elettronica e articoli per la casa. La varietà di prodotti disponibili è uno dei punti di forza di questa piattaforma, che collabora con marchi di alta qualità per offrire sempre il meglio ai clienti.

Se vuoi acquistare un capo di abbigliamento di tendenza o un accessorio di lusso a un prezzo molto conveniente, i saldi kim ti permettono di farlo senza compromessi sulla qualità. Ricorda comunque di valutare attentamente ogni offerta, considerando sempre il rapporto qualità-prezzo e la reale necessità dell’acquisto.

La qualità come priorità assoluta

Uno dei principali vantaggi dei saldi kim è l’affidabilità dei prodotti disponibili. La selezione di marchi premium garantisce che, anche durante i saldi, si possano acquistare articoli durevoli, realizzati con materiali di prima scelta e con cura artigianale. Questo rispone alle esigenze di chi non vuole rinunciare a qualità anche nel periodo delle promozioni.

Inoltre, grazie alle partnership con i marchi più prestigiosi, si può contare su prodotti originali ed esclusivi, garantendo così un acquisto sicuro e soddisfacente. Attraverso questa piattaforma, puoi avere accesso a novità e collezioni che altrimenti sarebbero difficili da trovare a prezzi simili.

Risparmio senza compromessi: il vero significato dei saldi kim

Il risparmio è il principale obiettivo di chi partecipa ai saldi kim. Durante questa stagione di offerte, molte delle categorie più richieste sono scontate anche fino al 70%, consentendo di acquistare prodotti di marca a costi decisamente inferiori rispetto al prezzo di listino.

Questo tipo di promozioni rappresenta un investimento intelligente, soprattutto se si pianificano gli acquisti con attenzione. Per esempio, acquistare capi di abbigliamento di qualità o articoli per la casa durante i saldi può risparmiare centinaia di euro rispetto ai prezzi standard, permettendo di ottenere il massimo valore dal denaro speso.

Come individuare le offerte più vantaggiose durante i saldi kim

Per ottenere i migliori affari durante i saldi kim, è fondamentale seguire alcune strategie e consigli pratici. La pianificazione prima dell’inizio delle promozioni, la conoscenza dei brand preferiti e l’utilizzo di strumenti digitali sono elementi chiave per non lasciarsi sfuggire le occasioni più interessanti.

In questa sezione ti forniremo una panoramica su come affrontare i saldi in modo intelligente e strategico, con particolare attenzione alla selezione di prodotti che abbiano un reale rapporto qualità-prezzo.

Pianificare gli acquisti: conoscere prima le offerte

Un aspetto fondamentale dei saldi kim è la pianificazione: conoscere in anticipo le offerte e le promozioni consente di evitare acquisti compulsivi e di concentrarsi sui prodotti che si desiderano realmente. Per fare ciò, puoi consultare con regolarità il sito web [[sale.kim]] e iscriverti alle newsletter per ricevere aggiornamenti sulle nuove promozioni.

Inoltre, studiare le collezioni delle firme più amate ti permette di individuare con anticipo ciò che desideri acquistare, monitorando i prezzi nel tempo. La conoscenza dei prodotti ti aiuterà anche a capire quando un’offerta rappresenta un vero affare e quando, invece, può essere meglio aspettare ulteriori sconti.

La selezione di brand e prodotti di qualità

Per sfruttare appieno i saldi kim, è essenziale concentrarsi sui brand di alta qualità e sui prodotti che si adattino alle proprie necessità. La piattaforma offre un vasto assortimento di categorie, incluse moda, tecnologia, arredamento e accessori, ma non tutti i prodotti sono uguali.

Focalizzarsi sui marchi di punta e verificare attentamente le caratteristiche dei prodotti permette di investire in articoli durevoli e di valore, evitando acquisti impulsivi o di bassa qualità. Solo così si potrà godere di un rapporto soddisfacente tra prezzo e qualità, facendo davvero un affare.

Come confrontare i prezzi e valutare le offerte

Anche se i saldi kim permettono di ottenere sconti considerevoli, è importante confrontare sempre i prezzi tra diversi rivenditori e piattaforme. Utilizzare strumenti di confronto prezzo può aiutarti a capire se le offerte sono veramente vantaggiose o se si tratta di promozioni che riscaldano il prezzo originale.

Oltre a confrontare i prezzi, valuta anche le recensioni di altri clienti e le politiche di reso e assistenza post-vendita. Un buon acquisto non si basa solo sul prezzo scontato, ma sulla sicurezza di ricevere un prodotto conforme alle aspettative.

Gli errori da evitare durante i saldi kim

Anche se le offerte sono allettanti, per non rischiare di commettere errori comuni durante i saldi kim, è importante mantenere un approccio razionale e organizzato. Qui ti illustreremo quali sono gli errori più frequenti e come evitarli, per fare acquisti intelligenti e soddisfacenti.

Spesa impulsiva e acquisti non mirati

Uno dei rischi maggiori durante i saldi è lasciarsi trascinare dall’emozione e fare acquisti impulsivi. Questo può portare a spendere più del necessario e ad acquistare prodotti inutili. Per evitare ciò, pianifica in anticipo cosa ti serve realmente e cerca di rispettare una lista di priorità.

Imparare a differenziare tra ciò di cui hai bisogno e ciò che desideri in modo impulsivo ti aiuta a risparmiare e ad acquistare solo quanto veramente utile. Ricorda che i saldi kim sono un’ottima occasione, ma solo se si pianifica con attenzione.

Ignorare il confronto prezzi e le recensioni

Un altro errore comune è acquistare senza aver confrontato i prezzi o senza aver letto le recensioni dei clienti. Spesso si rischia di acquistare a prezzi più elevati rispetto alla media del mercato o di ricevere un prodotto di qualità inferiore.

Dedica qualche minuto a verificare i prezzi in altri negozi online e a leggere le opinioni di chi ha già acquistato. Questo ti aiuterà a compiere scelte più informate e a evitare delusioni.

Acquisti di prodotti fuori stagione o inutili

Per trarre il massimo beneficio dai saldi kim, focalizzati principalmente su articoli che ti saranno utili nel breve o medio termine. Acquistare prodotti fuori stagione o inutili rischia di essere uno spreco di denaro e spazio.

Ad esempio, comprare capi di abbigliamento estivi a fine inverno o articoli tecnologici che non hai bisogno immediatamente potrebbe non essere il modo più saggio di usare gli sconti.

FAQs sui saldi kim – Domande frequenti

Qual è il periodo migliore per approfittare dei saldi kim?

Il periodo migliore per i saldi kim è generalmente alla fine di ogni stagione, quando le offerte sono più competitive e le promozioni più allettanti. Tuttavia, molte piattaforme lanciano promozioni durante eventi speciali o festività, quindi è importante monitorare regolarmente [[sale.kim]] per non perdere nessuna occasione.

Posso trovare prodotti di marca a prezzi scontati durante i saldi kim?

Assolutamente sì. I saldi kim si concentrano su brand di alta qualità, offrendo sconti significativi su prodotti di marca. È la soluzione ideale per chi desidera acquistare articoli di tendenza senza spendere troppo.

Come posso essere sicuro che un'offerta sia reale?

Per verificare la reale convenienza di un’offerta, confronta i prezzi con altri rivenditori e analizza le recensioni dei clienti. Un buon indicatore è anche la durata dello sconto e la presenza di autentici marchi partner.

È possibile acquistare online in modo sicuro durante i saldi kim?

Sì, grazie alle piattaforme affidabili come [[sale.kim]], puoi fare acquisti in tutta sicurezza. Assicurati di usare connessioni sicure, verificare le politiche di reso e preferire pagamenti tracciabili.

Quali categorie di prodotti sono più convenienti durante i saldi?

Le categorie più vantaggiose sono abbigliamento, accessori moda, elettronica, articoli per la casa e prodotti di design. Questi settori spesso vedono sconti molto consistenti, permettendo di fare ottimi affari.

Conclusione

I saldi kim rappresentano un’occasione imperdibile per ottenere prodotti di alta qualità a prezzi incredibili, grazie a un’ampia scelta di brand e categorie. Per massimizzare i benefici di questa promozione, è fondamentale pianificare gli acquisti, confrontare i prezzi e orientarsi verso prodotti di valore reale. Approfittare delle offerte [[sale.kim]] ti permette di risparmiare senza compromessi sulla qualità, trasformando ogni spesa in un investimento intelligente.

Se sei un amante dello shopping consapevole e desideri approfittare delle migliori occasioni, questa guida ti accompagnerà passo dopo passo nel mondo dei saldi kim, aiutandoti a fare scelte più informate e soddisfacenti. Buono shopping!

Latest posts

Featured product

sale.kim | Luxury One-Liners
Every luxury brand, one destination
Premium Selection
Where excellence meets everything
Curated Luxury
Premium
400,000 luxury products, one standard
www.sale.kim
The world's finest selection
Global Curation
Luxury without limits
Endless Excellence
Premium
All the best brands, none of the compromise
www.sale.kim
Premium brands, limitless choices
Exclusive Selection
Curated excellence at scale
Quality & Quantity
Premium
The luxury of choice, perfected
www.sale.kim
Where high-end meets high-volume
Premium Scale
Every luxury category, mastered
Complete Curation
Premium
The art of luxury, the science of selection
www.sale.kim
const debounce = function(fn, delay, immediate = false) { let timer; let promise; return function () { const context = this; const args = arguments; // 如果已经存在 promise,直接返回(等待正在执行的) if (promise) { return promise; } // 创建新的 promise promise = new Promise((resolve, reject) => { if (immediate) { // 立即执行 try { const result = fn.apply(context, args); // 如果结果是 promise,等待它完成 if (result && typeof result.then === 'function') { result.then(resolve).catch(reject); } else { resolve(result); } } catch (error) { reject(error); } // 设置定时器,在 delay 时间后重置 promise,允许下次调用 timer = setTimeout(() => { promise = null; }, delay); } else { // 延迟执行 timer = setTimeout(() => { try { const result = fn.apply(context, args); // 如果结果是 promise,等待它完成 if (result && typeof result.then === 'function') { result.then(resolve).catch(reject); } else { resolve(result); } } catch (error) { reject(error); } // 重置 promise promise = null; }, delay); } }); return promise; }; }; class HomeData { constructor() { this.earnPointsPlan = Promise.resolve({}); this.redeemPlan = Promise.resolve({}); this.getMemberDetailDebounce = debounce( this.getMemberDetail.bind(this), 200, true // 首次立即执行 ); this.memberDetail = this.getMemberDetailDebounce(); } refreshAllData() { this.getMemberDetailDebounce(); } getMemberDetail() { const memberPromise = fetch( "\/api\/loyalty-server\/member" ).then((response) => { // not login // 用null 和undefined来区分用户状态,因为已经很多地方用!!data.member来判断了,这是最简单的方式。 // null: not member // undefined: not login if (response.status === 401) { return undefined; } else if (response.status === 404) { // not member return null } else if (!response.ok) { return null } return response.json(); }); const tierDetail = fetch( "\/api\/loyalty-server\/tier-details" ).then((response) => response.json()); const fetchPromise = Promise.all([memberPromise, tierDetail]).then(([memberDetail, tierList]) => { const currentTierIndex = tierList.tier_details.findIndex((tier) => tier.id === memberDetail?.tier_id) return { member: memberDetail, current_tier: tierList.tier_details[currentTierIndex === -1 ? 0 : currentTierIndex], next_tier: currentTierIndex === tierList.tier_details.length - 1 ? null : tierList.tier_details[currentTierIndex + 1] } }) this.memberDetail = fetchPromise; return fetchPromise; } getPointPlans(eventType) { const url = "\/api\/loyalty-server\/earn-points\/campaigns" const fetchPromise = fetch(`${url}${eventType ? `?event_types=${eventType}` : ''}`).then((response) => response.json()); this.earnPointsPlan = fetchPromise; return fetchPromise; } getPointsDeduction() { const fetchPromise = fetch( "\/api\/loyalty-server\/points-deduction\/campaign" ).then((response) => response.json()); this.pointsDeduction = fetchPromise; return fetchPromise; } getCompleteTipStorage() { return Promise.resolve(window.sessionStorage.getItem('loyalty-complete-tip-status')); } saveCompleteTipStorage() { return Promise.resolve(window.sessionStorage.setItem('loyalty-complete-tip-status', 'true')); } } const initData = new HomeData(); exportFunction("memberDetail", () => initData.memberDetail); exportFunction("earnPointData", () => initData.earnPointsPlan); exportFunction("refreshAllData", initData.refreshAllData.bind(initData)); exportFunction("refreshMemberDetail", initData.getMemberDetail.bind(initData)); exportFunction("refreshPointData", initData.getPointPlans.bind(initData)); exportFunction("getCompleteTipStorage", initData.getCompleteTipStorage.bind(initData)); exportFunction("saveCompleteTipStorage", initData.saveCompleteTipStorage.bind(initData)); class SpzCustomLoyaltyPanel extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { const configPromiseList = [ SPZ.whenApiDefined(document.getElementById('loyalty-app-config-launch')).then((api) => { return api._getAllConfig() }), SPZ.whenApiDefined(document.getElementById('loyalty-entry-config')).then((api) => { return api._getSectionConfig() }) ]; Promise.all(configPromiseList) .then(([allConfig, config]) => { // 全局开关,关闭了就不展示入口(目前只有B端商户欠费时会出现这个场景) if (!allConfig.is_display) { throw new Error('overdue'); } this.config = config; // 校验是否可展示的页面 if (!this.checkDisplay_()) { throw new Error("Loyalty panel is not configured to display on this page."); } else { // 展示入口 this.element.hidden = false; } return SPZ.whenApiDefined(this.element.querySelector('#loyalty-panel-entry-render')); }).then((api) => { return api.render(); }).then(() => { this.setupAction_(); this.firstOpen = true; const launchIconContainer = this.element.querySelector(".loyalty-launch-icon"); this.openIcon = launchIconContainer.querySelector(".loyalty-launch__open"); this.closeIcon = launchIconContainer.querySelector(".loyalty-launch__close"); this.panel = this.element.querySelector(".loyalty-app__panel-body"); launchIconContainer.addEventListener("click", () => { if (this.openIcon.hidden) { this.close_(); } else { this.open_(); } }); this.viewport_ = this.getViewport(); this.registerAutoOpen_(); }).catch((error) => { console.error(error); }); } setupAction_() { this.registerAction("open", (invocation) => { const { event } = invocation.args; this.open_(event); }); this.registerAction("close", (invocation) => { const { event } = invocation.args; this.close_(event); }); } open_(page) { SPZCore.Dom.toggle(this.closeIcon, true); SPZCore.Dom.toggle(this.openIcon, false); SPZCore.Dom.toggle(this.panel, true); this.element.classList.remove("closed"); this.element.classList.add("opened"); if (this.firstOpen) { this.firstOpen = false; LoyaltyRouter.push("home"); const ele = document.getElementById('loyalty-async-get-data'); SPZ.whenApiDefined(ele).then((api) => { api.callFunction('refreshAllData') if (page && page !== 'home') { //打开后打开特定页面 LoyaltyRouter.push(page); } }); } if (this.viewport_.getWidth() < 960) { // 如果是在M端,则锁住外层滚动 this.viewport_.enterOverlayMode(); } } close_() { this.viewport_.leaveOverlayMode(); SPZCore.Dom.toggle(this.closeIcon, false); SPZCore.Dom.toggle(this.openIcon, true); SPZCore.Dom.toggle(this.panel, false); this.element.classList.remove("opened"); this.element.classList.add("closed"); } registerAutoOpen_() { const params = window.SPZUtils.Urls.parseQueryString(location.search); if(params.open_loyalty) { this.open_(params.open_loyalty) } } checkDisplay_() { const { show_pages } = this.config.settings; if (!show_pages || (show_pages.length === 1 && show_pages[0] === "all")) { // 1. 如果show_pages只有一个值且为'all',则表示在所有页面都显示 return true; } const currentPage = window.C_SETTINGS.meta.page.template_name; // 2. 如果show_pages中包含当前页面,则返回true if (show_pages.includes(currentPage)) { return true; } // promotions对应的页面 const promotionsPages = [ "flashsaleCollection", "couponCollection", "couponsCollection", "rebateCollection", "automaticCollection", "discountComplex", ]; // 3. 如果当前页面在promotions对应的页面中,并且show_pages中包含promotions,则返回true if (promotionsPages.includes(currentPage) && show_pages.includes("promotions")) { return true; } // account对应的页面 const accountPages = [ "customers/track", "customers/order", "customers/addresses", "customers/account", "customers/coupon", "order", "track", "addresses", "account", "coupon", ]; // 4. 如果当前页面在account对应的页面中,并且show_pages中包含account,则返回true if (accountPages.includes(currentPage) && show_pages.includes("account")) { return true; } // 5. 都没匹配到,则返回false return false; } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } static deferredMount() { return false; } } SPZ.defineElement("spz-custom-loyalty-panel", SpzCustomLoyaltyPanel); function matchDynamicRoute(path, routePattern) { // 正则表达式匹配动态路由,其中 :id 表示一个动态参数 const reg = new RegExp(`^${routePattern.replace(/:\w+/g, "([^/]+)")}$`); const match = path.match(reg); if (match) { // 去除第一个匹配项(整个匹配的字符串),剩下的就是参数数组 const params = match.slice(1); // 将参数与路由模式中的动态段对应起来,形成一个对象 const paramNames = routePattern.match(/:\w+/g) || []; const paramsObject = paramNames.reduce((acc, name, index) => { acc[name.slice(1)] = params[index]; return acc; }, {}); return paramsObject; } else { return null; } } class LoyaltyRouter extends SPZ.BaseElement { static routers = {}; static currIndex = -1; static stack = []; static deferredMount() { return false; } constructor(element) { super(element); } buildCallback() { const routePath = this.element.dataset.path; this.routePath = routePath; this.routeName = this.element.dataset.name; LoyaltyRouter.routers[routePath] = this; // default hidden,show when push SPZCore.Dom.toggle(this.element, false); this.element.classList.add("spz-custom-loyalty-router"); this.container = document.querySelector( "#loyalty-app__panel .loyalty-app__panel-body > .loyalty-app__panel-body-wrapper" ); this.action_ = SPZServices.actionServiceForDoc(this.element); if (this.moved) return; this.moved = true; this.container.appendChild(this.element); this.setupAction_(); } triggerRefreshEvent_(data) { this.showPageLoading_(); const event = SPZUtils.Event.create( this.win, "spz-custom-loyalty-router.refresh", data ); this.action_.trigger(this.element, "refresh", event); } triggerRenderEvent_(data) { const event = SPZUtils.Event.create( this.win, "spz-custom-loyalty-router.render", data ); this.action_.trigger(this.element, "render", event); } static push(path, options) { if (LoyaltyRouter.routers[path]) { LoyaltyRouter.stack.push(path); LoyaltyRouter.currIndex++; LoyaltyRouter.routers[path].render_(path, options); } else { // 进行动态路由匹配 const matchResult = Object.keys(LoyaltyRouter.routers).find((router) => { return matchDynamicRoute(path, router); }); if (matchResult) { const pathParams = matchDynamicRoute(path, matchResult); LoyaltyRouter.stack.push(matchResult); LoyaltyRouter.currIndex++; LoyaltyRouter.routers[matchResult].render_(matchResult, { ...options, params: { ...pathParams, ...(options.params || {}), }, }); } else { console.error(`Route '${path}' not found!`); } } } back() { LoyaltyRouter.stack.pop(); if (LoyaltyRouter.currIndex > 0) { LoyaltyRouter.currIndex--; // 返回时如果是到首页则展示首页出来,避免首页超长 if (LoyaltyRouter.currIndex === 0) { SPZCore.Dom.toggle(LoyaltyRouter.routers["home"].element, true); } const prevPage = LoyaltyRouter.routers[LoyaltyRouter.stack[LoyaltyRouter.currIndex]]; if (prevPage.element.classList.contains("loyalty-router-initialized")) { // 触发refresh事件 prevPage.triggerRefreshEvent_({}); } this.element.style.zIndex = 0; this.element.classList.add("loyalty-closed-page"); } } render_(path, options = {}) { const { pageTitle } = options; SPZCore.Dom.toggle(LoyaltyRouter.routers[path].element, true); if (LoyaltyRouter.currIndex > 0 && path !== "home") { this.showPageLoading_(); SPZCore.Dom.toggle(LoyaltyRouter.routers["home"].element, false); const template = document.getElementById( "loyalty-panel-with-back" ).content; let shadow = this.element.shadowRoot; if (!shadow) { shadow = this.element.attachShadow({ mode: "open" }); shadow.appendChild(template.cloneNode(true)); // 获取按钮元素并添加点击事件 const button = shadow.querySelector(".loyalty-panel__back"); button.addEventListener("click", () => { this.back(); }); const closeBtn = shadow.querySelector(".loyalty-panel__inner-close"); closeBtn.addEventListener("click", () => { SPZ.whenApiDefined( document.getElementById("loyalty-app__panel") ).then((apis) => { apis.close_(); }); }); } else if (shadow.querySelector("button")) { // 证明已经打开过 } this.setTitle_( shadow, pageTitle || LoyaltyRouter.routers[path].routeName ); } if (path === "home" && LoyaltyRouter.currIndex > 0) { // close all Object.keys(LoyaltyRouter.routers) .filter((path) => path !== "home") .forEach((path) => { LoyaltyRouter.routers[path].back(); }); } if (this.element.classList.contains("loyalty-router-initialized")) { // 触发refresh事件 this.triggerRefreshEvent_(options); } this.triggerRenderEvent_(options); this.element.style.zIndex = LoyaltyRouter.currIndex + 1; this.element.classList.remove("loyalty-closed-page"); this.element.classList.add("loyalty-router-initialized"); } showPageLoading_() { SPZCore.Dom.toggle(document.querySelector(".loyalty-global-loading"), true); } setupAction_() { // 用于动态设置页面标题 this.registerAction("setTitle", (invocation) => { const { args } = invocation; const {pageTitle} = args; this.setTitle_(this.element.shadowRoot, pageTitle); }); // 用于手动返回 this.registerAction("back", () => { this.back(); }); } setTitle_(shadow, pageTitle) { shadow.querySelector(".loyalty-panel__back-name").innerHTML = pageTitle; } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-router", LoyaltyRouter); class LoyaltyLink extends SPZ.BaseElement { constructor(element) { super(element); const params = Object.keys(element.dataset) .filter((key) => key.startsWith("param")) .reduce((acc, key) => { acc[key.replace("param", "").toLowerCase()] = element.dataset[key]; return acc; }, {}); this.clickEventHandler = (e) => { // 如果子元素点击包含禁止冒泡属性则不处理冒泡跳转 if (e.target.attributes['prevent-link']) { return; } if (element.dataset.path) { LoyaltyRouter.push(element.dataset.path, { pageTitle: element.dataset.name, params, }); } }; element.addEventListener("click", this.clickEventHandler); } unmountCallback() { this.element.removeEventListener("click", this.clickEventHandler); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-link", LoyaltyLink); class SpzCustomLoyaltyEvent extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.setupAction_(); this.action_ = SPZServices.actionServiceForDoc(this.element); this.origin = this.element.dataset.origin; const attributes = this.element.attributes; for (let i = 0; i < attributes.length; i++) { const attributeName = attributes[i].name; if (attributeName.startsWith('@event:')) { const eventName = attributeName.replace('@event:', ''); window.SPZUtils.Event.listen( window, eventName, (data) => { if(data.detail.origin !== this.origin) { this.triggerEvent_(`event:${eventName}`, data); } } ) } } } triggerEvent_(eventName, data) { const event = SPZUtils.Event.create( this.win, `spz-custom-loyalty-event.${eventName}`, data ); this.action_.trigger(this.element, eventName, event); } setupAction_() { this.registerAction("emit", (invocation) => { const { args } = invocation; const {eventName} = args; this.emit_(eventName, args); }); } emit_(eventName, args) { const event = window.SPZUtils.Event.create( window, eventName, args ); window.dispatchEvent(event); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-event", SpzCustomLoyaltyEvent); class SpzCustomLoyaltyTrack extends SPZ.BaseElement { static observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { SPZ.whenApiDefined(entry.target).then((api) => { api.track_(); }) } }); }, { root: null, ratio: 0.6, }); constructor(element) { super(element); this.hasTrack = false; const { trackType, trackEvent_developer, ...dataset } = this.element.dataset; this.trackType = trackType; this.eventDeveloper = trackEvent_developer; const trackEventInfo = {}; Object.keys(dataset).forEach((dataKey) => { if (dataKey.startsWith('track')) { trackEventInfo[dataKey.replace('track', '').toLowerCase()] = dataset[dataKey]; } }) this.trackEventInfo = trackEventInfo; } mountCallback() { if (this.trackType === 'function_expose') { SpzCustomLoyaltyTrack.observer.observe(this.element); } else { this.element.addEventListener("click", () => { this.track_(); }); } } unmountCallback() { if (this.trackType === 'function_expose') { SpzCustomLoyaltyTrack.observer.unobserve(this.element); } else { this.element.removeEventListener("click", () => { this.track_(); }); } } track_() { if (this.hasTrack && this.trackType !== "click") return; SPZ.whenApiDefined(document.querySelector('.loyalty-init-data')).then((api) => { return api.callFunction('memberDetail') }).then((rst) => { // 默认游客 let membership_status = 'guest'; // 未登录时先赋值非会员 if (!!window.C_SETTINGS.customer.customer_id) membership_status = 'no_member'; // 是会员则上报会员名称 if (rst.member) membership_status = `member_${rst.current_tier.name}`; const {email_id} = window.SPZUtils.Urls.parseQueryString(window.location.search); const eventTypeMap = { 'function_expose': 'expose', 'click': 'click' } window.sa.track(this.trackType, { function_name: 'loyalty', plugin_name: 'loyalty', module_type: 'loyalty', module: 'apps', business_type: 'product_plugin', event_developer: this.eventDeveloper, event_type: eventTypeMap[this.trackType], event_info: JSON.stringify({ ...this.trackEventInfo, membership_status: membership_status, source_channels: email_id ? 'email' : 'Store', email_id: email_id }) }); if (this.trackType !== "click") this.hasTrack = true; }) } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER || layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-track", SpzCustomLoyaltyTrack); class SpzCustomLoyaltyPoint extends SPZ.BaseElement { constructor(element) { super(element); this.value_ = element.getAttribute('value'); } buildCallback() { if (this.win.__loyalty_settings__) { this.win.__loyalty_settings__.then((settings) => { this.pointName_ = (settings.points_rule && settings.points_rule.points_name) || "Points"; this.render_(); }); } } mutatedAttributesCallback(mutations) { if (!SPZCore.Types.hasOwn(mutations, 'value')) { return; } this.value_ = mutations.value; this.render_(); } render_() { if (this.element.childElementCount > 0) { this.element.innerHTML = ''; } this.container_ = document.createElement("span"); this.container_.classList.add("loyalty-point"); this.container_.innerHTML = `${this.value_ !== null ? `${this.value_} ` : ''}${this.pointName_}`; this.element.appendChild(this.container_); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-point", SpzCustomLoyaltyPoint); class SpzCustomLoyaltySeeMore extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.setupAction_(); } setupAction_() { this.registerAction("open", (invocation) => { this.render_(invocation.args); }); } render_(data) { SPZ.whenApiDefined(this.element.querySelector(':scope > ljs-render')).then((api) => { api.render(data, true) }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-see-more", SpzCustomLoyaltySeeMore); class SpzCustomLoyaltyConfig extends SPZ.BaseElement { static configPromise = null; static getConfig() { // 如果已经有 Promise,返回它 if (this.configPromise) { return this.configPromise; } // 否则创建新的 Promise this.configPromise = new Promise((resolve) => { if (window.__loyalty_settings__) { window.__loyalty_settings__.then((allConfig) => { let newConfig = {}; Object.keys(allConfig.edit_config ?? {}).forEach((key) => { newConfig[key] = JSON.parse(allConfig.edit_config[key]); }); resolve(newConfig); }); } else { resolve({}); } }); return this.configPromise; } constructor(element) { super(element); this.value_ = element.getAttribute("value"); this.configType_ = element.dataset.configType; this.sectionName_ = element.dataset.sectionName; } buildCallback() { this.setupAction_(); } setupAction_() { this.registerAction("getConfig", () => { return this._getConfig(); }); } _getSectionConfig() { const emptyConfig = { id: '', settings: {}, }; return SpzCustomLoyaltyConfig.getConfig().then((config) => { if (!config) { console.error("Edit config is empty or not loaded."); return emptyConfig; } const sectionConfig = config[this.configType_]?.sections?.[this.sectionName_]; if (!sectionConfig) { console.log(`Section ${this.sectionName_} not found in config.`); return emptyConfig; } return { ...sectionConfig, id: this.sectionName_ }; }); } _getConfig() { return SpzCustomLoyaltyConfig.getConfig().then((config) => { if (!config) { console.error("Edit config is empty or not loaded."); return null; } return config[this.configType_] || {}; }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-config", SpzCustomLoyaltyConfig); const replaceImportValue = (content, dynamicObjects) => { const dynamic = { ...dynamicObjects, point_name: `<spz-custom-loyalty-point layout="container"></spz-custom-loyalty-point>`, } if (!content) { return ''; } let res = content; Object.keys(dynamic).forEach((key) => { res = res.replaceAll(`\{\{.${key}\}\}`, dynamic[key]); }); return res; }; class SpzCustomLoyaltyDynamicContent extends SPZ.BaseElement { constructor(element) { super(element); this.content = element.dataset.content; // 获取所有以data-dynamic-*开头的data属性 this.dynamicObjects = {}; const attributes = element.attributes; for (let i = 0; i < attributes.length; i++) { const attributeName = attributes[i].name; if (attributeName.startsWith('data-dynamic-')) { const key = attributeName.replace('data-dynamic-', ''); this.dynamicObjects[key] = attributes[i].value; } } } layoutCallback() { this.render_(); } mutatedAttributesCallback(mutations) { if (!SPZCore.Types.hasOwn(mutations, "data-content")) { return; } this.content = mutations['data-content']; this.render_(); } render_() { this.element.innerHTML = replaceImportValue(this.content, this.dynamicObjects); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement( "spz-custom-loyalty-dynamic-content", SpzCustomLoyaltyDynamicContent ); const TAG = 'spz-custom-loyalty-app-config'; class SpzCustomLoyaltyAppConfig extends SPZ.BaseElement { constructor(element) { super(element); this.allConfig = null; this.configPath = element.getAttribute('data-config-path'); } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(); window.__loyalty_settings__.then((allConfig) => { this.allConfig = allConfig; const eventName = this.allConfig[this.configPath] ? 'configPass' : 'configFail'; const event = SPZUtils.Event.create(this.win, `${TAG}.${eventName}`, {}); this.action_.trigger(this.element, eventName, event); }); } _getAllConfig() { if (this.allConfig) { return this.allConfig; } return window.__loyalty_settings__.then((allConfig) => { this.allConfig = allConfig; return this.allConfig; }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement(TAG, SpzCustomLoyaltyAppConfig); class SpzCustomLoyaltyQueryModal extends SPZ.BaseElement { constructor(element) { super(element); this.queryParam = element.dataset.queryParam; } static deferredMount() { return false; } buildCallback() { this.checkMember_(); this.setupAction_(); } checkMember_() { const params = window.SPZUtils.Urls.parseQueryString(window.location.search); if (params[this.queryParam]) { if (!window.C_SETTINGS.customer?.customer_id) { window.location.href="\/account\/login"; } else { // 已登陆 SPZ.whenApiDefined(document.getElementById('loyalty-async-get-data')).then((api) => { return api.callFunction('memberDetail'); }).then((data) => { if (data.member) { // 已入会才弹窗 SPZ.whenApiDefined(this.element.querySelector(':scope>ljs-lightbox')).then((modal) => { // 用于在弹窗打开前执行一些操作 this.beforeOpen_(params[this.queryParam], params); modal.open(); }); } else { // 未入会直接移除参数 this.removeQuery_(); } }); } } } setupAction_() { this.registerAction("close", () => { this.removeQuery_(); }); } // 用于在弹窗打开前执行一些操作 beforeOpen_() {} removeQuery_() { const currentUrl = window.location.href; // 使用 URLSearchParams 解析查询参数 const url = new URL(currentUrl); const params = SPZUtils.Urls.addOrReplaceParams(window.location.href, {[this.queryParam]: null}); window.history.replaceState( null, '', `${url.origin}${params}` ); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-query-modal", SpzCustomLoyaltyQueryModal);