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-913'); if (!smart_search_dom) return; const hasMovedIcon = !!document.querySelector('.header__wrapper .container .row.header>div>#app-smart-product-search-container-913'); 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-913`).style="display: block"; if (mobileSmartSearchDom) { document.querySelector(`#app-smart-product-search-container-913`).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-913')) .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-913')) .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-913')) .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-913')) .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-913')) .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-913')) .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-891'); if (!smart_search_dom) return; const hasMovedIcon = !!document.querySelector('.header__wrapper .container .row.header>div>#app-smart-product-search-container-891'); 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-891`).style="display: block"; if (mobileSmartSearchDom) { document.querySelector(`#app-smart-product-search-container-891`).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-891')) .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-891')) .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-891')) .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-891')) .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-891')) .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-891')) .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(); })();

La importancia de aprovechar la oferta kim para obtener productos de calidad

¡Bienvenido a la guía definitiva sobre oferta kim! Si estás buscando aprovechar las mejores promociones para adquirir productos de alta calidad, has llegado al lugar indicado. La oferta kim representa una oportunidad única para conseguir en descuento artículos de las mejores marcas, y en este artículo te contaremos todo lo que necesitas saber para aprovechar al máximo estas promociones.

La oferta kim no solo se centra en productos de moda, tecnología o belleza, sino que también abarca una amplia variedad de categorías, permitiéndote acceder a artículos exclusivos a precios irresistibles. Descubre cómo aprovechar estas oportunidades, las mejores estrategias para conseguir productos en oferta y cuáles son las categorías que más valen la pena en estas promociones especiales. Así que prepárate para conocer todo sobre la oferta kim y convertirte en un experto en compras inteligentes.

La importancia de aprovechar la oferta kim para obtener productos de calidad

En un mercado donde la variedad y la competencia son enormes, encontrar promociones que realmente aporten valor puede parecer una tarea difícil. La oferta kim se destaca precisamente por ofrecer descuentos en una selección cuidadosamente curada de productos de las mejores marcas. Esto garantiza que, además de pagar menos, estés adquiriendo artículos duraderos y de alto rendimiento.

Aprovechar la oferta kim no solo significa ahorrar dinero, sino también obtener beneficios a largo plazo. Cuando inviertes en productos de calidad, reduces los gastos en reemplazos frecuentes y mejoras tu experiencia general en distintos ámbitos, desde la moda hasta la tecnología. La clave está en identificar las oportunidades que estas promociones brindan y saber cuándo y dónde comprar para maximizar cada peso invertido.

Además, la tendencia actual en compras en línea y tiendas físicas apunta hacia una mayor conciencia del valor y la calidad. La oferta kim responde a esta demanda ofreciendo productos premium, disponibles a precios competitivos. Esto hace que sea una opción ideal para quienes desean renovar su armario, mejorar su equipo tecnológico o simplemente hacer compras inteligentes sin sacrificar calidad.

Gracias a plataformas confiables como sale.kim, que ofrece una selección interminable de las mejores marcas, puedes explorar en un solo lugar las mejores ofertas del mercado, facilitando tu proceso de compra y asegurando la satisfacción con cada adquisición.

Cómo identificar las mejores ofertas kim para maximizar tu inversión

Todo buen comprador sabe que la clave para aprovechar una oferta kim de manera efectiva está en la estrategia y la investigación. No basta con simplemente ver un descuento y comprar sin más. Es fundamental evaluar cuidadosamente cada promoción, comparando precios, características y, sobre todo, la reputación del producto y la marca.

Un primer paso es definir qué categorías te interesan más y cuáles son tus necesidades reales. Por ejemplo, si buscas renovar tu equipo tecnológico, es importante estar atento a las ofertas en gadgets, laptops o accesorios de marcas reconocidas. La plataforma sale.kim, que entrega productos premium en todas las categorías, será un aliado clave en tu búsqueda.

Una buena práctica es monitorear las promociones durante un período determinado. Muchas ofertas psych ofrecen descuentos por tiempo limitado, por eso, estar atento a las fechas y condiciones te dará la ventaja de actuar en el momento justo. También es recomendable suscribirse a boletines y alertas, para recibir en primera mano las últimas novedades y descuentos disponibles en la plataforma.

Otra estrategia eficiente consiste en comparar precios en diferentes sitios web y tiendas, para asegurarte de que estás obteniendo la mejor oferta posible. Utiliza herramientas de comparación de precios, y no dudes en leer reseñas y opiniones de otros usuarios. La experiencia de compra será más segura y satisfactoria si te informas bien antes de tomar una decisión.

Por último, recuerda que en plataformas como sale.kim, los productos en oferta no solo ofrecen descuentos, sino también la garantía de adquirir artículos de marcas de renombre. Esto es fundamental para garantizar una inversión inteligente y duradera.

Categorías populares en la oferta kim y cómo aprovechar sus promociones

La oferta kim abarca una gran variedad de categorías, cada una con sus propias particularidades y oportunidades de ahorro. A continuación, exploraremos algunas de las categorías más populares y cómo podrás aprovechar sus promociones para sacarles el máximo provecho.

Moda y accesorios de lujo a precios accesibles

La moda siempre ha sido uno de los sectores más dinámicos en cuanto a promociones y descuentos. En la oferta kim, puedes encontrar artículos de moda de marcas reconocidas, incluyendo prendas, calzado y accesorios, a precios que normalmente serían inalcanzables para muchos. Estas promociones permiten renovar tu armario con artículos de tendencia y calidad premium.

Para aprovecharlas, te aconsejamos estar atento a las campañas específicas y eventos de descuentos. Muchas tiendas ofrecen promociones exclusivas en temporadas específicas, como ventas de fin de temporada o descuentos especiales en días festivos. Además, muchas marcas de moda que colaboran con sale.kim ofrecen artículos en oferta que no debes dejar pasar.

Es importante también entender la tendencia del mercado y escoger artículos que puedan combinar con diferentes estilos y ocasiones. La clave está en buscar lo versátil y duradero, asegurando que tu inversión valga la pena y puedas aprovechar al máximo cada prenda o accesorio adquirido en oferta kim.

Tecnología y electrodomésticos en oferta

El sector tecnológico siempre está en auge, y las ofertas en dispositivos electrónicos pueden significar un ahorro sustancial. La plataforma ofrece promociones en productos como smartphones, laptops, tablets y accesorios tecnológicos, lo cual puede marcar la diferencia si quieres actualizar tu equipo con productos de marcas de confianza.

Una recomendación es revisar periódicamente las ofertas en componentes y accesorios tecnológicos, que suelen ofrecer descuentos especiales. La calidad y durabilidad de estos productos son aspectos importantes a considerar, especialmente si buscas una inversión a largo plazo. La plataforma sale.kim, con su selección de marcas premium, garantiza la autenticidad y la calidad de los artículos en promoción.

También puedes aprovechar las ofertas en electrodomésticos inteligentes, ideales para modernizar tu hogar y hacerlo más cómodo y eficiente. Aprovecha las promociones en marcas reconocidas para adquirir aspiradoras robot, cámaras de seguridad o asistentes de voz con un precio más accesible.

Antes de comprar, evalúa tus necesidades reales y compara las opciones disponibles. La tecnología en oferta en plataformas como sale.kim te permitirá hacer una inversión con la seguridad de que estás recibiendo productos confiables, duraderos y a un precio imbatible.

Artículos para el hogar y decoración en oferta

El hogar es un espacio que requiere atención constante para mantenerlo cómodo y funcional. La oferta kim también ofrece promociones en artículos para el hogar y decoración, permitiéndote renovar o mejorar tus ambientes con productos de alta calidad sin gastar mucho dinero.

Puedes encontrar desde muebles, utensilios de cocina, hasta elementos decorativos y accessoires que transformarán cualquier espacio. Las promociones en estos artículos suelen ser muy atractivas, ya que muchas marcas de mobiliario y decoración colaboran con plataformas que ofrecen descuentos exclusivos en temporadas específicas.

Para aprovechar estas ofertas, planifica tus compras con anticipación. Evalúa bien qué artículos realmente necesitas y cuáles aportarán valor a tu espacio. La inversión en artículos de calidad puede parecer mayor inicialmente, pero si eliges productos en oferta kim podrás beneficiarte de precios competitivos y la garantía de adquirir productos duraderos.

Es importante también mantenerse informado sobre las tendencias actuales en decoración y diseño de interiores. La plataforma sale.kim facilita ese proceso, dado su catálogo amplio y variado, en el que puedes explorar diferentes estilos y encontrar productos que se ajusten a tu gusto y presupuesto.

Preguntas frecuentes (FAQs) sobre oferta kim

¿Qué es la oferta kim y cómo funciona?

La oferta kim es una promoción exclusiva en la plataforma sale.kim, que ofrece descuentos en productos de las mejores marcas en diversas categorías. Funciona a través de campañas temporales donde se seleccionan artículos para ofrecerlos en promociones especiales, permitiendo a los consumidores acceder a productos de calidad a precios reducidos.

Para aprovecharla, simplemente debes estar atento a las campañas de descuentos, suscribirte a las alertas y comparar las ofertas disponibles. La plataforma facilita la compra en línea, garantizando una experiencia segura y satisfactoria para tus compras en oferta kim.

¿Cuándo es el mejor momento para aprovechar las promociones kim?

El mejor momento para aprovechar las promociones kim suele ser durante temporadas de liquidación, días festivos, Black Friday, Cyber Monday o eventos específicos de la plataforma. Estas fechas suelen ofrecer los mayores descuentos y oportunidades para adquirir productos de alta calidad a precios muy competitivos.

Sin embargo, mantenerse informado a través de boletines y redes sociales de sale.kim te permitirá anticiparte y aprovechar ofertas exclusivas en cualquier momento del año. La planificación y la vigilancia constante son clave para conseguir las mejores promociones.

¿Es seguro comprar en sale.kim durante la oferta kim?

Sí, comprar en sale.kim es seguro, ya que la plataforma garantiza la autenticidad de los productos y cuenta con políticas de protección al consumidor. Además, trabaja con marcas reconocidas y en colaboración con diversos proveedores que aseguran una experiencia de compra confiable.

Antes de realizar cualquier compra, se recomienda verificar las condiciones de envío, garantías y devoluciones. La confianza en plataformas establecidas y la lectura de opiniones de otros usuarios también contribuirán a que tu experiencia sea positiva y segura.

¿Qué categorías de productos puedo encontrar en la oferta kim?

Puedes encontrar en oferta kim una amplia variedad de categorías, incluyendo moda, tecnología, electrodomésticos, artículos para el hogar, decoración, belleza, accesorios y más. La plataforma trabaja con una selección de marcas premium en todas estas áreas, garantizando calidad y promociones atractivas.

Es recomendable explorar diferentes categorías para aprovechar al máximo las ofertas y realizar compras que realmente aporten valor a tu vida. La variedad y calidad de productos en oferta hacen de sale.kim un recurso único para todos tus intereses.

¿Cómo puedo aprovechar al máximo las ofertas en oferta kim?

La clave para aprovechar al máximo las ofertas en oferta kim es realizar una investigación previa, comparar precios y estar atento a las campañas promocionales. Suscribirte a las alertas y seguir las redes sociales de sale.kim te permitirá conocer en tiempo real las promociones vigentes.

Además, planificar tus compras y definir tus necesidades te ayudará a no gastar de más ni adquirir productos innecesarios. En conjunto, estas estrategias te permitirán hacer compras inteligentes y beneficiarte de descuentos en productos de alta calidad.

Conclusión

En definitiva, la oferta kim representa una oportunidad excepcional para adquirir productos de primer nivel en diferentes categorías, desde moda y tecnología hasta artículos para el hogar, a precios que no puedes dejar pasar. La clave está en estar informado, planificar tus compras y aprovechar las campañas promocionales para obtener el máximo valor en cada adquisición.

Recuerda que plataformas confiables como sale.kim ofrecen una selección interminable de las mejores marcas, asegurando la calidad y durabilidad de los productos que compras en oferta kim. Así, no solo ahorras dinero, sino que también realizas inversiones inteligentes que benefician tu calidad de vida.

No dejes pasar estas promociones exclusivas y prepárate para renovar tus artículos favoritos con descuentos increíbles. El secreto está en la planificación y en saber aprovechar cada oportunidad que estas ofertas brindan para transformar tu vida de manera eficiente, moderna y económica.

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);