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

Upptäck det breda utbudet av varumärken och kategorier på rea kim

Att hitta kvalitetsprodukter till förmånliga priser kan vara en utmaning, men med rea kim blir det enklare att handla bekvämt och smart. Rea Kim erbjuder en omfattande samling av premiumvarumärken inom olika kategorier, vilket gör att du kan göra fynd utan att kompromissa med kvaliteten. Den här artikeln tar dig igenom hur du kan dra nytta av rea kim, vilka produkter som är mest populära, och hur du maximerar din shoppingupplevelse. Oavsett om du är ute efter mode, teknik eller andra livsstilsprodukter, finns det något för alla på rea kim.

rea kim handlar inte bara om att spara pengar – det handlar om att göra medvetna val och att investera i produkter som håller länge och tillför värde till ditt liv. För att verkligen förstå varför rea kim är ett smart val, är det viktigt att granska de olika aspekterna av deras utbud, hur man hittar de bästa erbjudandena, och vilka strategier som gör att du kan shoppa effektivt och hållbart.

Upptäck det breda utbudet av varumärken och kategorier på rea kim

När du besöker rea kim, möts du av en oväntat stor och varierad kollektion av premiumprodukter. Det är det som gör att många återvänder gång på gång för att göra fynd och få tillgång till exklusiva varor till ett mycket bättre pris än ordinarie. Oavsett om du är intresserad av mode, heminredning, teknik eller skönhetsprodukter, finns det något för varje smak och behov.

Något av det mest tilltalande med rea kim är deras breda sortiment av varumärken. Här kan du hitta allt från välkända internationella märken till mindre, exklusiva designers. Det ger en känsla av att ha tillgång till en mini-butik fylld av kvalitetsprodukter, men till priser som gör att du kan unna dig mer än vanligt. Dessutom är den ständigt uppdaterade produktsortimenten ett bevis på att du alltid kan göra fynd oavsett säsong.

För att få ut det mesta av rea kim bör man inte bara titta på ett enda kategori utan utforska hela sidan. Det kan vara smart att följa deras senaste kampanjer och erbjudanden för att inte missa tidsbegränsade rabatter. De ofta utannonserar specialrabatter under högtider, rea-helger eller unika kampanjer, vilket gör det ännu mer ekonomiskt att köpa när priserna sjunker.

Det omfattande produktsortimentet – ett smörgåsbord av möjligheter

Det som gör rea kim så attraktiv är den enorma variationen av produkter. Från de senaste trenderna inom mode till klassiska inredningsdetaljer och innovativ teknik. Detta tillåter konsumenter att handla för hela familjen på ett och samma ställe, utan att kompromissa med kvaliteten.

Varje kategori på sidan har sina unika erbjudanden som möjliggör att du kan förbättra din livsstil utan att spräcka budgeten. Dessutom är det ofta lätt att navigera mellan olika varumärken och prisklasser, vilket gör att du kan skräddarsy din shopping efter behov och rådande trender. Att kunna jämföra produkter och priser på ett så smidigt sätt gör eye-openers till ett av rea kims starka sidor.

Det är också värt att nämna att rea kim inte bara fokuserar på reor – det är ett omdömesgillt ställe att ta del av exklusiva före- och efterkampanjer som gör att du kan planera din shopping för långsiktig nytta. Oavsett om du vill förbättra hemmet, förnya garderoben eller investera i teknik, finns det alltid något att upptäcka.

Strategier för att maximera dina fynd – Så shoppar du på rea kim

Att handla smart vid rea kim kräver lite förberedelse och strategisk planering. Utan rätt tillvägagångssätt kan de utmärkta rabatterna glida undan för dig. Här är några tips för att du ska lyckas maximera dina fynd och få ut mest av dina pengar.

Först och främst bör du läsa igenom deras nyhetsbrev och följa rea kim på sociala medier. Där annonseras ofta de bästa erbjudandena, speciellt för deras medlemsklubb eller lojalitetsprogram. Att vara först med att klicka kan innebära att du får tag på begränsade lager eller exklusiva rabatter.

Sedan är det klokt att ha en tydlig lista på vad du vill köpa. På så sätt kan du undvika att impulsshopping tar överhanden och istället fokusera på att investera i saker du verkligen behöver eller vill ha. Att veta vad du söker gör också att du lättare kan jämföra priser och varumärken för att hitta det bästa erbjudandet.

När du hittar en produkt du är intresserad av, ta dig tid att läsa recensioner och säkerställa att kvaliteten motsvarar förväntningarna. Det är ofta lätt att bli lockad av lockande rabatter, men det är viktigt att kontrollera produktens detaljer för att undvika missnöje i efterhand.

Så hittar du de bästa erbjudandena

Det finns flera sätt att upptäcka de bästa erbjudandena på rea kim. Förutom att hålla koll på deras kampanjsidor och sociala medier, kan du använda filter och bevakningar för att få omedelbara notiser när något i din prisklass eller kategori får en märkbar rabatt.

Även att skapa ett konto på sidan kan ge tillgång till exklusiva erbjudanden och förhandsinformation om kommande reor. Många e-handelsplatser ger sina kunder ett försprång genom att erbjuda tidiga access till reor via medlemskap, vilket gör att du kan planera och vara ute i god tid.

En annan effektiv metod är att jämföra priser på olika plattformar före du beställer, för att säkerställa att du får den bästa dealen. Det kan ofta löna sig att vänta en dag eller två för bättre rabatter, speciellt om du har tålamod och vill få mer för pengarna.

Produktkategori Rea Kim - Rabatterad prisnivå Exempel på produkter Sista chansen att köpa
Mode Upp till 50% rabatt Kläder, skor, accessoarer Under kampanjperioden
Hem & Inredning Upp till 60% rabatt Möbel, textilier, dekoration Säsongsavslutning
Teknik & Gadgets Upp till 40% rabatt Smartphones, laptops, tillbehör Begränsad tid

Vanliga frågor om rea kim

Hur ofta finns det rea-kampanjer på rea kim?

Rea kim har regelbundna kampanjer, med extra rabatter under högtider, säsongsbyten och speciella events. Det är alltid en bra idé att prenumerera på deras nyhetsbrev för att inte missa några tillfällen.

Kan jag returnera produkter köpta på rea kim?

Ja, produkterna kan vanligtvis returneras enligt deras villkor, men det är viktigt att läsa in deras returpolicy noggrant för att vara säker på att du följer rätt procedurer, då vissa rabatter kan ha särskilda begränsningar.

Är varorna på rea kim av hög kvalitet?

Absolut. På rea kim är fokus på att erbjuda produkter från premiumvarumärken, så även de rabatterade artiklarna är av hög kvalitet.

Hur vet jag om en produkt är begränsad i lager?

Det finns ofta en tydlig indikator på hemsidan, som visar om en produkt är slut eller om det finns limited edition-varianten kvar. Att agera snabbt är nyckeln till att inte missa fynden.

Kan jag använda rabatter vid köp på rea kim?

Ja, i de flesta fall kan du kombinera kampanjer och kuponger för att ytterligare sänka priset, men det är bra att dubbelkolla deras villkor för varje erbjudande.

Sammanfattning och avslutning

Att handla på rea kim är en utmärkt metod för att ta del av högkvalitativa varumärken till förmånliga priser. Med ett brett sortiment och regelbundna kampanjer finns det alltid möjlighet att göra fynd och förbättra din livsstil utan att spräcka budgeten. Genom att vara förberedd, följa kampanjer och jämföra erbjudanden kan du maximera dina köp och säkerställa att du investerar i produkter som håller i längden.

Oavsett om du söker mode, heminredning eller teknik – rea kim erbjuder något för alla. Missa inte chansen att ta del av deras exklusiva rabatter och göra hållbara, välavvägda val. Sparar du smart och strategiskt, kan du alltid dra nytta av deras fantastiska utbud och göra shopping till en angenäm och lönsam upplevelse.

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