Woman

Man

Child

Search
search Search
search Search
search Search
No results for search
const templateName = SHOPLAZZA?.meta?.page?.template_name || ''; const SEARCH_URL = '/search'; const TAG = 'spz-custom-smart-search-location'; const SEARCH_CONTAINER_CLASS = 'app-smart-product-search-container'; const THEME_NAME = window.SHOPLAZZA.theme.merchant_theme_name.replace(/ /g, ''); const BREAKPOINT = 960; const DELAY = 300; const DEFAULT_SEARCH_STYLE_CONFIG = { styleType: 'searchBox', borderRadius: 4, marginEnabled: false, margin: { mobile: { left: 0, right: 0, linked: true }, pc: { left: 0, right: 0, linked: true }, }, showSearchButton: true, searchButtonType: 'text', iconEnabled: true, iconType: 'system', customIcon: '', textEnabled: false, fontStyle: 'normal', fontBold: false, fontSize: 14, fontItalic: false, searchText: 'Search', colorType: 'theme', iconColor: '#202020', textColor: '#202020', buttonTextColor: '#FFFFFF', buttonIconColor: '#FFFFFF', buttonColor: '#202020', searchBorderColor: '#202020', searchBgColor: '#FFFFFF', inputIconTextColor: '#6D7175', }; const DEFAULT_CLICK_SEARCH_STYLE_CONFIG = { buttonType: 'text', iconType: 'system', customIcon: '', fontStyle: 'normal', fontBold: false, fontSize: 14, fontItalic: false, searchText: 'Search', borderRadius: 4, colorType: 'theme', iconColor: '#FFFFFF', textColor: '#FFFFFF', buttonColor: '#202020', searchBorderColor: '#202020', searchBgColor: '#FFFFFF', inputIconTextColor: '#6D7175', hotSearchBgStartColor: '#FFE5E6', hotSearchBgEndColor: '#FFFCFC', }; // --- 工具函数 --- function parseHeaderStyle(headerStyleStr) { const hasHeaderStyle = headerStyleStr && headerStyleStr !== '' && headerStyleStr !== '{}'; if (!hasHeaderStyle) { return { searchStyleConfig: { ...DEFAULT_SEARCH_STYLE_CONFIG }, clickSearchStyleConfig: { ...DEFAULT_CLICK_SEARCH_STYLE_CONFIG }, }; } try { const parsed = typeof headerStyleStr === 'string' ? JSON.parse(headerStyleStr) : headerStyleStr; return { searchStyleConfig: { ...DEFAULT_SEARCH_STYLE_CONFIG, ...(parsed.searchStyleConfig || {}) }, clickSearchStyleConfig: { ...DEFAULT_CLICK_SEARCH_STYLE_CONFIG, ...(parsed.clickSearchStyleConfig || {}) }, }; } catch (e) { console.error('parseHeaderStyle error:', e); return { searchStyleConfig: { ...DEFAULT_SEARCH_STYLE_CONFIG }, clickSearchStyleConfig: { ...DEFAULT_CLICK_SEARCH_STYLE_CONFIG }, }; } } function matchTheme(target) { return THEME_NAME.toLocaleLowerCase().includes(target.toLocaleLowerCase()); } function resolveThemeValue(themeMap, defaultValue) { let result = defaultValue; for (const key of Object.keys(themeMap)) { if (matchTheme(key)) result = themeMap[key]; } return result; } function joinSelectors(selectorList) { return [...new Set(selectorList)].join(','); } function isDesktop() { return window.matchMedia(`(min-width: ${BREAKPOINT}px)`).matches; } // --- 元素查找 Mixin --- const ElementFinderMixin = { getBlockWrap() { return this.element.closest('.app-smart-product-search-wrap') || document.querySelector('.app-smart-product-search-wrap'); }, getBlockContainer() { return this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('.' + SEARCH_CONTAINER_CLASS); }, resolveBlockElement(selector, fallbackId) { const wrap = this.getBlockWrap(); const el = wrap?.querySelector(selector) || document.getElementById(fallbackId); return el ? SPZ.whenApiDefined(el) : Promise.resolve(null); }, getSmartSearchEl() { return this.resolveBlockElement('ljs-search', 'app-smart-search-24'); }, getOutsideItemEl() { return this.resolveBlockElement('.app-smart-search-outside-item', 'app-smart-search-outside-item-24'); }, }; // --- 主题配置 --- const HEADER_SELECTOR = resolveThemeValue({ 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: '.header__wrapper', reformia: 'header#header', }, 'header'); const SEARCH_ICON_CLASS = resolveThemeValue({ 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', }, 'app-smart-icon-search-large-default'); const PLUGIN_RELOCATION_CONFIG = resolveThemeValue({ reformia: { pc: '.header-layout .header__actions', mobile: '.header-layout .header__actions', }, }, null); // --- 布局 Mixin --- const MobileLayoutMixin = { relocatePlugin() { if (!PLUGIN_RELOCATION_CONFIG) return; const targetSelector = isDesktop() ? PLUGIN_RELOCATION_CONFIG.pc : PLUGIN_RELOCATION_CONFIG.mobile; if (!targetSelector) return; if (this._relocateTimer) { clearInterval(this._relocateTimer); } const attemptRelocate = () => { const container = this.element.closest('.' + SEARCH_CONTAINER_CLASS) || document.querySelector('#app-smart-product-search-container-24'); if (!container || !document.body.contains(container)) return false; const target = document.querySelector(targetSelector); if (!target) return false; if (target.contains(container)) return true; target.insertBefore(container, target.firstChild); return true; }; if (attemptRelocate()) return; let attempts = 0; this._relocateTimer = setInterval(() => { attempts++; if (attemptRelocate() || attempts >= 20) { clearInterval(this._relocateTimer); this._relocateTimer = null; } }, 500); }, applySearchIconClass() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.classList.add(SEARCH_ICON_CLASS); }); }, adjustLifestyleIcon() { if (!matchTheme('lifestyle') || this.searchItemType === 'input' || isDesktop()) return; if (window.__smartSearchLifestyleIconMoved__) { this._lifestyleIconMoved = true; return; } const container = this.getBlockContainer(); if (!container) return; const alreadyMoved = !!document.querySelector( '.header__wrapper .container .row.header>div>.app-smart-product-search-container' ); if (alreadyMoved) { this._lifestyleIconMoved = true; window.__smartSearchLifestyleIconMoved__ = true; return; } const headerDivs = document.querySelectorAll('.header__wrapper .container .row.header>div'); if (!headerDivs.length) return; const lastDiv = headerDivs[headerDivs.length - 1]; lastDiv.appendChild(container); this._lifestyleIconMoved = true; window.__smartSearchLifestyleIconMoved__ = true; }, initInputMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'none'; }); const searchWrap = this.getBlockWrap(); const pcContainer = this.getBlockContainer(); const mobileContainer = document.querySelector('.smart-search-mobile-container'); if (!this._originalSearchWrapParent && searchWrap && searchWrap.parentElement) { this._originalSearchWrapParent = searchWrap.parentElement; } if (isDesktop()) { if (mobileContainer) mobileContainer.style.display = 'none'; if (searchWrap && this._originalSearchWrapParent) { if (mobileContainer && mobileContainer.contains(searchWrap)) { this._originalSearchWrapParent.appendChild(searchWrap); } } if (pcContainer) pcContainer.style.display = 'block'; return; } if (templateName === 'search') { this._skipMobileInit = true; return; } if (pcContainer) pcContainer.style.display = 'none'; this.ensureMobileSearchContainer(); const mobileContainerAfterEnsure = document.querySelector('.smart-search-mobile-container'); if (!mobileContainerAfterEnsure) return; const existingWrap = mobileContainerAfterEnsure.querySelector('.app-smart-product-search-wrap'); if (existingWrap && existingWrap !== searchWrap) { return; } if (searchWrap && !mobileContainerAfterEnsure.contains(searchWrap)) { mobileContainerAfterEnsure.appendChild(searchWrap); } mobileContainerAfterEnsure.style.display = ''; }, ensureMobileSearchContainer() { if (document.querySelector('.smart-search-mobile-container')) return; const header = document.querySelector(HEADER_SELECTOR); if (!header) return; const container = document.createElement('div'); container.classList.add('smart-search-mobile-container'); container.classList.add('smart-search-mobile-container-' + THEME_NAME.toLocaleLowerCase()); header.appendChild(container); }, initIconMode() { document.querySelectorAll('.app-smart-icon-search-large').forEach(el => { el.style.display = 'flex'; }); const mobileContainer = document.querySelector('.smart-search-mobile-container'); if (mobileContainer) mobileContainer.style.display = 'none'; const pcContainer = this.getBlockContainer(); if (pcContainer) pcContainer.style.display = ''; }, hasMobilePluginParent() { return !['geek', 'flash', 'boost', 'reformia'].includes(THEME_NAME.toLocaleLowerCase()); }, showMobileSmartSearch() { if (this._mobileSearchShown) return; const PLUGIN_PARENT_SELECTORS = { 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 parentEl = document.querySelector( joinSelectors(Object.values(PLUGIN_PARENT_SELECTORS)) ); if (!parentEl) return; const hasHiddenClass = parentEl.classList.contains('md:hidden') || parentEl.classList.contains('md:tw-hidden'); if (hasHiddenClass) { Array.from(parentEl.children).forEach((child) => { if (!this.isSmartSearchElement(child)) { child.style.display = 'none'; } }); parentEl.classList.remove('md:hidden', 'md:tw-hidden'); } else { const smartSearchEl = Array.from(parentEl.children).find( (child) => this.isSmartSearchElement(child) ); if (smartSearchEl) { smartSearchEl.style.display = 'block'; } } this._mobileSearchShown = true; }, isSmartSearchElement(el) { return ( el.classList.contains(SEARCH_CONTAINER_CLASS) || el.querySelectorAll(`.${SEARCH_CONTAINER_CLASS}`).length > 0 ); }, addMobileSmartSearch() { if (this._mobileSearchAdded) return; const HEADER_ICONS_SELECTORS = { 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', reformia: '.header-layout .header__actions', }; const SMART_SEARCH_ANCESTORS = [ '#header-menu-mobile #menu-drawer', '#menu-drawer .plugin__header-content', '.header__drawer', '.header-content .logo-wrap', '.header_hamburger_sidebar-container', ]; const iconsEl = document.querySelector( joinSelectors(Object.values(HEADER_ICONS_SELECTORS)) ); const searchWrapSelector = joinSelectors( SMART_SEARCH_ANCESTORS.map(a => `${a} .${SEARCH_CONTAINER_CLASS}`) ); const searchWrapEl = document.querySelector(searchWrapSelector); if (!iconsEl || !searchWrapEl) return; iconsEl.insertAdjacentElement('afterbegin', searchWrapEl); this._mobileSearchAdded = true; }, initMobileSmartSearch() { if (this._lifestyleIconMoved) return; if (this.hasMobilePluginParent()) { this.showMobileSmartSearch(); } else { this.addMobileSmartSearch(); } }, }; const StyleApplicatorMixin = { _getMarginConfig(config) { if (!config.marginEnabled || !config.margin) { return { left: 0, right: 0 }; } const device = isDesktop() ? 'pc' : 'mobile'; return config.margin[device] || { left: 0, right: 0 }; }, _applySearchInlineStyles(searchWrap, config) { const isCustom = config.colorType === 'custom'; const find = (sel) => searchWrap.querySelector(sel); const borderRadius = `${config.borderRadius}px`; const fontSize = `${config.fontSize}px`; const fontWeight = config.fontBold ? 'bold' : 'normal'; const fontStyle = config.fontItalic ? 'italic' : 'normal'; const inputContainer = find('.smart-search-outside-input-container'); if (inputContainer) { inputContainer.style.setProperty('border-radius', borderRadius, 'important'); if (isCustom) { inputContainer.style.setProperty('border-color', config.searchBorderColor, 'important'); inputContainer.style.setProperty('background-color', config.searchBgColor, 'important'); } else { inputContainer.style.removeProperty('border-color'); inputContainer.style.removeProperty('background-color'); } } const outsideButtons = searchWrap.querySelectorAll('.smart-search-outside-input-button'); outsideButtons.forEach((btn) => { if (isCustom) { btn.style.setProperty('background-color', config.buttonColor, 'important'); } else { btn.style.removeProperty('background-color'); } }); const btnSystemIcons = searchWrap.querySelectorAll('.smart-search-button-system-icon'); btnSystemIcons.forEach((icon) => { if (isCustom) { icon.style.setProperty('color', config.buttonIconColor, 'important'); } else { icon.style.removeProperty('color'); } }); const btnTexts = searchWrap.querySelectorAll('.smart-search-button-text'); btnTexts.forEach((txt) => { txt.style.setProperty('font-size', fontSize, 'important'); txt.style.setProperty('font-weight', fontWeight, 'important'); txt.style.setProperty('font-style', fontStyle, 'important'); if (isCustom) { txt.style.setProperty('color', config.buttonTextColor, 'important'); } else { txt.style.removeProperty('color'); } }); const inputIcons = searchWrap.querySelectorAll('.smart-search-outside-input-icon, .smart-search-outside-input-icon svg'); inputIcons.forEach((el) => { if (isCustom) { el.style.setProperty('color', config.inputIconTextColor, 'important'); el.style.setProperty('fill', config.inputIconTextColor, 'important'); } else { el.style.removeProperty('color'); el.style.removeProperty('fill'); } }); const placeholderTexts = searchWrap.querySelectorAll('.smart-search-outside-input-placeholder-text'); placeholderTexts.forEach((el) => { if (isCustom) { el.style.setProperty('color', config.inputIconTextColor, 'important'); } else { el.style.removeProperty('color'); } }); const systemIcons = searchWrap.querySelectorAll('.smart-search-system-icon, .smart-search-system-icon svg'); systemIcons.forEach((el) => { if (isCustom) { el.style.setProperty('color', config.iconColor, 'important'); el.style.setProperty('fill', config.iconColor, 'important'); } else { el.style.removeProperty('color'); el.style.removeProperty('fill'); } }); const iconText = find('.smart-search-icon-text'); if (iconText) { iconText.style.setProperty('font-size', fontSize, 'important'); iconText.style.setProperty('font-weight', fontWeight, 'important'); iconText.style.setProperty('font-style', fontStyle, 'important'); if (isCustom) { iconText.style.setProperty('color', config.textColor, 'important'); } else { iconText.style.removeProperty('color'); } } }, _applyClickSearchInlineStyles(sidebar, config) { const isCustom = config.colorType === 'custom'; const find = (sel) => sidebar.querySelector(sel) || document.querySelector(sel); const searchForm = find('.smart-search-form'); if (searchForm) { searchForm.style.setProperty('border-radius', `${config.borderRadius}px`, 'important'); searchForm.style.setProperty('border-width', '1px', 'important'); searchForm.style.setProperty('border-style', 'solid', 'important'); searchForm.style.setProperty('overflow', 'hidden'); } const submitBtn = find('.smart-search-submit-btn'); const buttonText = find('.smart-search-sidebar-button-text'); if (buttonText) { buttonText.style.setProperty('font-size', `${config.fontSize}px`, 'important'); buttonText.style.setProperty('font-weight', config.fontBold ? 'bold' : 'normal', 'important'); buttonText.style.setProperty('font-style', config.fontItalic ? 'italic' : 'normal', 'important'); } if (submitBtn) { const hotWordsCarousel = sidebar.querySelector('.hot-words-carousel'); if (hotWordsCarousel) { const btnWidth = submitBtn.offsetWidth || 66; hotWordsCarousel.style.setProperty('right', `${btnWidth + 8}px`, 'important'); } } if (isCustom) { if (searchForm) { searchForm.style.setProperty('border-color', config.searchBorderColor, 'important'); } const inputContent = find('.smart-search-input-content'); if (inputContent) { inputContent.style.setProperty('background', config.searchBgColor, 'important'); inputContent.style.setProperty('border-color', 'transparent', 'important'); } if (submitBtn) { submitBtn.style.setProperty('background-color', config.buttonColor, 'important'); submitBtn.style.setProperty('color', config.textColor, 'important'); } const systemIcon = find('.smart-search-sidebar-button-system-icon'); if (systemIcon) { systemIcon.style.setProperty('color', config.iconColor, 'important'); const svg = systemIcon.querySelector('svg'); if (svg) svg.style.setProperty('fill', config.iconColor, 'important'); } if (buttonText) { buttonText.style.setProperty('color', config.textColor, 'important'); } const insideIcon = find('.smart-search-inside-system-icon'); if (insideIcon) { insideIcon.style.setProperty('color', config.inputIconTextColor, 'important'); const svg = insideIcon.querySelector('svg'); if (svg) svg.style.setProperty('fill', config.inputIconTextColor, 'important'); } const searchInput = find('.smart-search-input'); if (searchInput) { searchInput.style.setProperty('color', config.inputIconTextColor, 'important'); } } this._injectClickSearchScopedStyle(config); }, _injectClickSearchScopedStyle(config) { const isCustom = config.colorType === 'custom'; const styleId = 'smart-search-click-scoped-style'; let style = document.getElementById(styleId); if (!style) { style = document.createElement('style'); style.id = styleId; document.head.appendChild(style); } let css = ''; if (isCustom) { css = ` .hot-search { background: linear-gradient(180deg, ${config.hotSearchBgStartColor} 0%, ${config.hotSearchBgEndColor} 100%) !important; } .smart-search-input::placeholder { color: ${config.inputIconTextColor} !important; opacity: 0.6; } .hot-words-carousel-word { color: ${config.inputIconTextColor} !important; opacity: 0.6; } `; } style.textContent = css; }, applySearchStyleConfig(retryCount = 0) { const config = this.searchStyleConfig; const searchWrap = this.getBlockWrap(); if (!searchWrap) return; // 设置锁标记,防止 resize 期间重复调用 this._isApplyingStyle = true; // 重构后:静态内容在 ljs-render 外部,DOM 元素始终存在 const iconSearchLarge = searchWrap.querySelector('.app-smart-icon-search-large'); const isSearchBox = config.styleType === 'searchBox'; // 检查轮播是否已渲染,如果没有则短暂等待后重试 const placeholder = searchWrap.querySelector('.smart-search-outside-input-placeholder'); const carousel = searchWrap.querySelector('.app-smart-search-outside-carousel'); // 轮播组件可能需要额外时间渲染,等待后重试 if (placeholder && !carousel && retryCount < 10) { setTimeout(() => this.applySearchStyleConfig(retryCount + 1), 50); return; } // 轮播已渲染,隐藏默认占位文本 if (placeholder && carousel) { placeholder.classList.add('has-carousel'); } this._applySearchInlineStyles(searchWrap, config); const searchBtn = searchWrap.querySelector('.app-smart-search-btn'); const outsideInputContainer = searchWrap.querySelector('.smart-search-outside-input-container'); const buttonType = config.searchButtonType || config.buttonType; if (searchBtn) { const marginConfig = this._getMarginConfig(config); searchBtn.style.marginLeft = `${marginConfig.left}px`; searchBtn.style.marginRight = `${marginConfig.right}px`; // 使用 data 属性控制样式类型显示(searchBox 或 imageText) searchBtn.dataset.styleType = isSearchBox ? 'searchBox' : 'imageText'; } // 搜索按钮显示/隐藏控制 const outsideButtons = searchWrap.querySelectorAll('.smart-search-outside-input-button'); outsideButtons.forEach((btn) => { btn.style.display = config.showSearchButton ? '' : 'none'; this._applyButtonContentDisplay(btn, config, { customIconSelector: '.smart-search-button-custom-icon', systemIconSelector: '.smart-search-button-system-icon', textSelector: '.smart-search-button-text', }); }); // 图文样式配置 - 使用 data 属性控制显示 if (iconSearchLarge) { const customIcon = iconSearchLarge.querySelector('.smart-search-custom-icon'); const iconText = iconSearchLarge.querySelector('.smart-search-icon-text'); const mobile = !isDesktop(); // 设置 data 属性,让 CSS 控制显示 iconSearchLarge.dataset.iconEnabled = config.iconEnabled ? 'true' : 'false'; iconSearchLarge.dataset.iconType = config.iconType || 'system'; if (mobile) { // 移动端:只显示图标,不显示文本 iconSearchLarge.dataset.textEnabled = 'false'; } else { iconSearchLarge.dataset.textEnabled = config.textEnabled ? 'true' : 'false'; } // 设置自定义图标 src(如果有) if (config.iconType === 'custom' && config.customIcon && customIcon) { customIcon.src = config.customIcon; } // 设置文本内容 if (iconText && config.textEnabled) { iconText.textContent = config.searchText || 'Search'; } } if (searchBtn) { searchBtn.classList.add('style-ready'); } // 解除锁标记 this._isApplyingStyle = false; }, _applyButtonContentDisplay(btn, config, selectors) { const { customIconSelector, systemIconSelector, textSelector } = selectors; const customIcon = btn.querySelector(customIconSelector); const systemIcon = btn.querySelector(systemIconSelector); const textSpan = btn.querySelector(textSelector); const buttonType = config.searchButtonType || config.buttonType; // 使用 data 属性驱动 CSS 显示,确保降级和一致性 btn.dataset.buttonType = buttonType || 'text'; if (buttonType === 'icon') { // 设置图标类型:custom 或 system(默认) const iconType = (config.iconType === 'custom' && config.customIcon) ? 'custom' : 'system'; btn.dataset.iconType = iconType; // 如果是自定义图标,设置 src if (iconType === 'custom' && customIcon) { customIcon.src = config.customIcon; } } else { // text 类型:移除 iconType 属性,更新文本内容 delete btn.dataset.iconType; if (textSpan) { textSpan.textContent = config.searchText || 'Search'; } } }, applyClickSearchStyleConfig(retryCount = 0) { const config = this.clickSearchStyleConfig; const sidebar = this._getSidebarOverlay ? this._getSidebarOverlay() : document.querySelector('.smart-search-sidebar-overlay'); if (!sidebar) return; const findElement = (selector) => sidebar.querySelector(selector) || document.querySelector(selector); const submitBtn = findElement('.smart-search-submit-btn'); if (!submitBtn && retryCount < 10) { setTimeout(() => this.applyClickSearchStyleConfig(retryCount + 1), 100); return; } if (this._sidebarObserver) this._sidebarObserver.disconnect(); this._applyClickSearchInlineStyles(sidebar, config); if (submitBtn) { this._applyButtonContentDisplay(submitBtn, config, { customIconSelector: '.smart-search-sidebar-button-custom-icon', systemIconSelector: '.smart-search-sidebar-button-system-icon', textSelector: '.smart-search-sidebar-button-text', }); } if (this._sidebarObserver) { this._sidebarObserver.observe(sidebar, { childList: true, subtree: true }); } }, }; // --- 侧边栏管理 Mixin(预加载模式)--- const SidebarManagerMixin = { // 侧边栏状态: idle -> preloading -> ready -> open -> idle _sidebarState: 'idle', _sidebarReady: false, _contentPreloaded: false, _contentPreloadPromise: null, _getSidebarOverlay() { const wrap = this.getBlockWrap(); if (wrap) { const overlay = wrap.querySelector('.smart-search-sidebar-overlay'); if (overlay) return overlay; } return document.querySelector('.smart-search-sidebar-overlay'); }, _getSidebarPanel() { const wrap = this.getBlockWrap(); if (wrap) { const panel = wrap.querySelector('.smart-search-sidebar'); if (panel) return panel; } return document.querySelector('.smart-search-sidebar'); }, _findVisibleSearchEntry() { const wrap = this.getBlockWrap(); if (wrap) { const content = wrap.querySelector('[id^="app-smart-product-search-content-"]'); if (content) return content; } return document.querySelector('[id^="app-smart-product-search-content-"]'); }, _alignSidebarToOutsideInput() { if (!isDesktop()) return; const panel = this._getSidebarPanel(); if (!panel) return; const refEl = this._findVisibleSearchEntry(); let topPos = 80; let rightPos = 20; if (refEl) { const rect = refEl.getBoundingClientRect(); const viewportWidth = document.documentElement.clientWidth; topPos = Math.max(0, rect.top); rightPos = Math.max(0, viewportWidth - rect.right); } panel.style.setProperty('top', topPos + 'px', 'important'); panel.style.setProperty('right', rightPos + 'px', 'important'); }, preloadSidebar() { const overlay = this._getSidebarOverlay(); if (!overlay) return; const currentState = overlay.getAttribute('data-state'); if (this._sidebarReady || this._sidebarState === 'preloading' || this._sidebarState === 'open') return; if (currentState === 'open' || currentState === 'preloading') return; this._sidebarState = 'preloading'; overlay.setAttribute('data-state', 'preloading'); const configReady = new Promise((resolve) => { if (this._configLoaded) { resolve(); return; } this.getOutsideItemEl().then((outsideItem) => { if (outsideItem) { const apiData = outsideItem.getData() || {}; const { clickSearchStyleConfig } = parseHeaderStyle(apiData.header_style); this.clickSearchStyleConfig = clickSearchStyleConfig; this._configLoaded = true; } resolve(); }).catch(() => resolve()); }); // 先渲染内容(创建 DOM),再应用样式(依赖 DOM 存在) configReady.then(() => { const currentState = overlay.getAttribute('data-state'); if (currentState === 'open') return; this._contentPreloaded = false; this._contentPreloadPromise = this.initSidebarContent().then(() => { this.applyClickSearchStyleConfig(); this._contentPreloaded = true; }).catch(() => { this._contentPreloaded = false; this._contentPreloadPromise = null; }); this._sidebarReady = true; this._sidebarState = 'ready'; overlay.setAttribute('data-state', 'ready'); }); }, // 打开侧边栏 openSidebar() { const overlay = this._getSidebarOverlay(); if (!overlay) return; this._sidebarOpenWidth = window.innerWidth; this._sidebarOpenedAsDesktop = isDesktop(); this._savedScrollbarWidth = parseFloat(getComputedStyle(document.documentElement).marginRight) || 0; const configReady = new Promise((resolve) => { if (this._configLoaded) { resolve(); return; } this.getOutsideItemEl().then((outsideItem) => { if (outsideItem) { const apiData = outsideItem.getData() || {}; const { clickSearchStyleConfig } = parseHeaderStyle(apiData.header_style); this.clickSearchStyleConfig = clickSearchStyleConfig; this._configLoaded = true; } resolve(); }).catch(() => resolve()); }); configReady.then(() => { this.applyClickSearchStyleConfig(); this._sidebarReady = true; this._showSidebar(overlay); }); document.body.style.overflow = 'hidden'; if (!this._resizeHandler) { this._resizeHandler = () => this._alignSidebarToOutsideInput(); window.addEventListener('resize', this._resizeHandler); } this._setupSidebarObserver(overlay); }, _showSidebar(overlay) { const panel = this._getSidebarPanel(); if (!panel) return; this._sidebarState = 'open'; overlay.setAttribute('data-state', 'open'); const _isDesktop = isDesktop(); const ANIM_DURATION = 280; const _setupPanelLayout = () => { const smartSearchWrap = panel.querySelector('.smart-search-wrap'); if (smartSearchWrap) { smartSearchWrap.style.cssText = _isDesktop ? 'display: block !important; width: 100% !important;' : 'display: flex !important; flex-direction: column !important; width: 100% !important; flex: 1 !important; min-height: 0 !important;'; } const pageContent = panel.querySelector('.page-content'); if (pageContent) { pageContent.style.cssText = _isDesktop ? 'display: flex !important; flex-direction: column !important; width: 100% !important;' : 'display: flex !important; flex-direction: column !important; width: 100% !important; flex: 1 !important; min-height: 0 !important;'; } }; const _applyPanelPosition = () => { if (_isDesktop) { const refEl = this._findVisibleSearchEntry(); let topPos = 80; let rightPos = 20; if (refEl) { const rect = refEl.getBoundingClientRect(); const viewportWidth = document.documentElement.clientWidth; topPos = Math.max(0, rect.top); rightPos = Math.max(0, viewportWidth - rect.right); } panel.style.cssText = ` display: block !important; position: fixed !important; top: ${topPos}px !important; right: ${rightPos}px !important; left: auto !important; bottom: auto !important; width: 520px !important; max-width: 520px !important; background: #fff !important; visibility: visible !important; opacity: 1 !important; z-index: 10000 !important; padding: 16px !important; border-radius: 6px !important; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15) !important; pointer-events: auto !important; `; } else { panel.style.cssText = ` display: flex !important; flex-direction: column !important; position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; max-width: 100vw !important; background: #fff !important; visibility: visible !important; opacity: 1 !important; z-index: 10000 !important; padding: 16px !important; border-radius: 0 !important; box-shadow: none !important; pointer-events: auto !important; transform: translateX(100%) !important; `; } }; const revealSidebar = () => { _applyPanelPosition(); _setupPanelLayout(); this.applyClickSearchStyleConfig(); overlay.style.cssText = ` position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; z-index: 9999 !important; visibility: visible !important; pointer-events: auto !important; display: block !important; `; this.checkHistoryOverflow(); if (!_isDesktop) { const contentWrap = panel.querySelector('.smart-search-wrap'); console.log('contentWrap'); if (contentWrap) { contentWrap.style.setProperty('opacity', '0', 'important'); } requestAnimationFrame(() => { panel.style.setProperty('transition', `transform ${ANIM_DURATION}ms cubic-bezier(0.16, 1, 0.3, 1)`, 'important'); panel.style.setProperty('transform', 'translateX(0)', 'important'); }); const fontsReady = (document.fonts && document.fonts.ready) ? document.fonts.ready : Promise.resolve(); const stableDelay = new Promise(r => setTimeout(r, ANIM_DURATION)); Promise.all([fontsReady, stableDelay]).then(() => { requestAnimationFrame(() => { if (contentWrap) { contentWrap.style.setProperty('opacity', '1', 'important'); } }); }); } const input = overlay.querySelector('.smart-search-input'); if (input) { setTimeout(() => input.focus(), _isDesktop ? 50 : ANIM_DURATION); } this._syncInsideCarousel(overlay); }; if (this._contentPreloaded) { revealSidebar(); return; } overlay.style.cssText = ` position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; width: 100vw !important; z-index: 9999 !important; visibility: hidden !important; pointer-events: none !important; display: block !important; opacity: 0 !important; `; _applyPanelPosition(); _setupPanelLayout(); const contentReady = this._contentPreloadPromise || this.initSidebarContent(); contentReady.then(() => { requestAnimationFrame(() => revealSidebar()); }).catch(() => { revealSidebar(); }); }, _setupSidebarObserver(overlay) { if (this._sidebarObserver) { this._sidebarObserver.disconnect(); } this._sidebarObserver = new MutationObserver(() => { if (this._applyStyleDebounceTimer) { clearTimeout(this._applyStyleDebounceTimer); } this._applyStyleDebounceTimer = setTimeout(() => { const submitBtn = overlay.querySelector('.smart-search-submit-btn'); if (submitBtn) { this.applyClickSearchStyleConfig(); } this.checkHistoryOverflow(); }, 50); }); this._sidebarObserver.observe(overlay, { childList: true, subtree: true }); }, _syncOutsideCarousel() { if (this.insideCarouselIndex === this.outsideCarouselIndex) return; this.outsideCarouselIndex = this.insideCarouselIndex; const outsideEl = document.querySelector('ljs-carousel[id^="app-smart-search-outside-carousel-"]'); if (!outsideEl) return; SPZ.whenApiDefined(outsideEl).then((api) => { try { api.goToSlide(String(this.outsideCarouselIndex)); } catch(e) {} }); }, closeSidebar() { const overlay = this._getSidebarOverlay(); if (!overlay) return; const overlayState = overlay.getAttribute('data-state'); if (this._sidebarState === 'closing') return; if (this._sidebarState === 'idle' && overlayState !== 'open') return; this._syncOutsideCarousel(); this._sidebarState = 'closing'; const panel = this._getSidebarPanel(); const _wasDesktop = this._sidebarOpenedAsDesktop !== undefined ? this._sidebarOpenedAsDesktop : isDesktop(); const ANIM_DURATION = 250; const _cleanup = () => { this._sidebarState = 'idle'; this._sidebarReady = false; this._contentPreloaded = false; this._contentPreloadPromise = null; this._sidebarOpenedAsDesktop = undefined; overlay.setAttribute('data-state', 'idle'); overlay.style.cssText = ''; if (panel) { panel.style.cssText = ''; const smartSearchWrap = panel.querySelector('.smart-search-wrap'); if (smartSearchWrap) smartSearchWrap.style.cssText = ''; const pageContent = panel.querySelector('.page-content'); if (pageContent) pageContent.style.cssText = ''; const input = panel.querySelector('.smart-search-input'); if (input) { input.value = ''; input.removeAttribute('has-value'); } panel.removeAttribute('has-value'); panel.removeAttribute('data-empty'); panel.removeAttribute('loading'); const loadingEl = panel.querySelector('.smart-search-loading'); if (loadingEl) { loadingEl.removeAttribute('show'); loadingEl.setAttribute('hide', ''); } document.querySelectorAll('.hot-words-carousel-inner-container').forEach(el => { el.style.display = 'block'; }); } document.body.style.overflow = ''; const scrollbarWidth = this._savedScrollbarWidth || 0; if (scrollbarWidth > 0) { const html = document.documentElement; html.style.setProperty('overflow', 'hidden', 'important'); html.style.setProperty('margin-right', scrollbarWidth + 'px', 'important'); setTimeout(() => { html.style.removeProperty('overflow'); html.style.removeProperty('margin-right'); }, 50); } this._savedScrollbarWidth = 0; if (this._sidebarObserver) { this._sidebarObserver.disconnect(); this._sidebarObserver = null; } if (this._applyStyleDebounceTimer) { clearTimeout(this._applyStyleDebounceTimer); this._applyStyleDebounceTimer = null; } if (this._resizeHandler) { window.removeEventListener('resize', this._resizeHandler); this._resizeHandler = null; } this._historyExpanded = false; }; if (panel && !_wasDesktop) { panel.style.setProperty('transition', `transform ${ANIM_DURATION}ms cubic-bezier(0.5, 0, 0.7, 0.4)`, 'important'); panel.style.setProperty('transform', 'translateX(100%)', 'important'); setTimeout(_cleanup, ANIM_DURATION); } else { _cleanup(); } const sectionPrefix = 'shoplaza-section'; const announcement = document.getElementById(sectionPrefix + '-announcement'); const header = document.getElementById(sectionPrefix + '-header'); if (announcement) announcement.classList.remove('header_mask_open'); if (header) header.classList.remove('header_mask_open'); }, _restartCarouselAutoplay(carouselEl) { carouselEl.removeAttribute('autoplay'); carouselEl.setAttribute('pause', ''); setTimeout(() => { carouselEl.setAttribute('autoplay', ''); carouselEl.removeAttribute('pause'); }, 50); }, _syncInsideCarousel(overlay) { const targetIndex = this.outsideCarouselIndex || 0; const doSync = () => { const carouselEl = overlay.querySelector('ljs-carousel[id^="inside-hot-words-carousel-"]'); if (!carouselEl) return; SPZ.whenApiDefined(carouselEl).then((carouselApi) => { try { carouselApi.goToSlide(String(targetIndex)); } catch(e) {} this._restartCarouselAutoplay(carouselEl); }); }; const renderEl = overlay.querySelector('ljs-render[id^="hot-words-carousel-"]'); if (renderEl) { const existingCarousel = overlay.querySelector('ljs-carousel[id^="inside-hot-words-carousel-"]'); if (existingCarousel && existingCarousel.hasAttribute('dom-mounted')) { doSync(); } else { const observer = new MutationObserver(() => { const el = overlay.querySelector('ljs-carousel[id^="inside-hot-words-carousel-"]'); if (el && el.hasAttribute('dom-mounted')) { observer.disconnect(); doSync(); } }); observer.observe(renderEl, { childList: true, subtree: true, attributes: true }); setTimeout(() => { observer.disconnect(); doSync(); }, 3000); } } else { doSync(); } }, // 兼容旧的 onSidebarOpen/Close 方法 onSidebarOpen() { this.openSidebar(); }, onSidebarClose() { this.closeSidebar(); }, }; // --- 搜索历史折叠/展开 Mixin --- const HISTORY_COLLAPSED_ROWS = 6; const HISTORY_EXPAND_THRESHOLD = 10; const HistoryOverflowMixin = { _findHistoryList(root) { const sources = [root, document]; for (const src of sources) { const list = src.querySelector('.recently-history-list'); if (list) return list; const els = src.querySelectorAll('*'); for (const el of els) { if (el.shadowRoot) { const l = el.shadowRoot.querySelector('.recently-history-list'); if (l) return l; } } } return null; }, _getRowHeight(list) { const item = list.querySelector('.recently-history-item'); if (!item || item.getBoundingClientRect().height === 0) return 0; const rowGap = parseFloat(getComputedStyle(list).rowGap) || 0; return item.getBoundingClientRect().height + rowGap; }, _findToggleBtn(list) { const parent = list.closest('.recently-history-content'); return parent ? parent.querySelector('.history-toggle-btn') : null; }, _applyMaxHeight(list) { const item = list.querySelector('.recently-history-item'); if (!item || item.getBoundingClientRect().height === 0) return; const itemHeight = item.getBoundingClientRect().height; const rowGap = parseFloat(getComputedStyle(list).rowGap) || 0; const paddingTop = parseFloat(getComputedStyle(list).paddingTop) || 0; var maxHeight = Math.ceil(itemHeight * HISTORY_COLLAPSED_ROWS + rowGap * (HISTORY_COLLAPSED_ROWS - 1)) + paddingTop; if (isDesktop()) maxHeight--; list.style.setProperty('max-height', maxHeight + 'px'); }, checkHistoryOverflow() { const overlay = this._getSidebarOverlay(); if (!overlay) return; const list = this._findHistoryList(overlay); if (!list) return; const items = list.querySelectorAll('.recently-history-item'); if (items.length === 0) return; this._applyMaxHeight(list); const needsCollapse = items.length > HISTORY_EXPAND_THRESHOLD; if (needsCollapse && !this._historyExpanded) { list.classList.add('history-collapsed'); } else { list.classList.remove('history-collapsed'); } const toggleBtn = this._findToggleBtn(list); if (toggleBtn) { if (needsCollapse && !this._historyExpanded) { toggleBtn.classList.remove('hidden'); } else { toggleBtn.classList.add('hidden'); } } }, expandHistory() { const overlay = this._getSidebarOverlay(); if (!overlay) return; const list = this._findHistoryList(overlay); if (!list) return; this._historyExpanded = true; list.classList.remove('history-collapsed'); this._applyMaxHeight(list); const toggleBtn = this._findToggleBtn(list); if (toggleBtn) toggleBtn.classList.add('hidden'); }, }; const HotKeywordsMixin = { generateHotKeywordList(data) { const searchKeywords = data?.hotKeywordList || []; const isShowHotKeyword = data?.isShowHotKeyword || false; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const hotwords = outsideItem.getData()?.search_keywords || []; const enrichedKeywords = this.enrichKeywords(searchKeywords, hotwords); this.renderHotKeywords(enrichedKeywords, isShowHotKeyword); }); }, enrichKeywords(keywords, hotwords) { return keywords.map((item) => { item.url_obj = item.url_obj || {}; const hotwordItem = hotwords.find(h => h.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; }); }, renderHotKeywords(keywords, isShowHotKeyword) { document.querySelectorAll('.app-hot-keyword-render-child').forEach((el) => { SPZ.whenApiDefined(el).then((hotWordsChild) => { hotWordsChild.render({ list: keywords, isShowHotKeyword }); }); }); }, normalizeOutsideKeywords(findKeywords, searchKeywords, findKeywordEnable) { if (findKeywordEnable === false) return []; if (findKeywords && findKeywords.length > 0) { return findKeywords.map(keyword => ({ word: keyword, icon: '', pic: '', type: 'find_keyword', url_obj: { type: 'search', url: `${SEARCH_URL}?q=${keyword}`, }, })); } return searchKeywords || []; }, normalizeKeywordUrl(item) { if (!item) return null; if (item.url_obj) { item.url_obj.url = item.url_obj.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj.url; } return item; }, onTapHotWord(type) { const index = type === 'inside' ? this.insideCarouselIndex : this.outsideCarouselIndex; this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; const findKeywordEnable = apiData?.find_keyword_enable !== false; const keywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords, findKeywordEnable); const currentItem = this.normalizeKeywordUrl(keywords[index] || null); if (currentItem) { this.handleHotKeyword({ args: { word: currentItem.word, query_type: currentItem.type, url: currentItem.url_obj?.url, } }); } else { this.executeSearch([''], 1); } }); }, getOutsideCarouselConfig() { return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return { outsideCarouselIndex: this.outsideCarouselIndex }; const apiData = outsideItem.getData(); const findKeywords = apiData?.find_keywords || []; const searchKeywords = apiData?.search_keywords || []; const findKeywordEnable = apiData?.find_keyword_enable !== false; const carouselKeywords = this.normalizeOutsideKeywords(findKeywords, searchKeywords, findKeywordEnable); return { ...apiData, search_keywords: carouselKeywords, outsideCarouselIndex: this.outsideCarouselIndex, }; }); }, }; const HISTORY_CACHE_KEY = 'smart_search_history'; const HISTORY_MAX_LEN = 30; const HOT_SEARCH_LEN = 6; const SMART_SEARCH_THINK_URL = '/api/search/suggestion'; const SearchControllerMixin = { // 搜索历史缓存 _historyCache: null, _searchData: null, _hotList: [], _curFindKeyword: '', // 初始化搜索历史 initHistoryCache() { if (this._historyCache) return; try { const cached = localStorage.getItem(HISTORY_CACHE_KEY); this._historyCache = cached ? JSON.parse(cached) : []; } catch (e) { this._historyCache = []; } }, // 获取历史列表 getHistoryList() { try { const cached = localStorage.getItem(HISTORY_CACHE_KEY); const historyList = cached ? JSON.parse(cached) : []; return historyList.slice().reverse(); } catch (e) { return []; } }, // 添加历史记录 addHistory(keyword) { if (!keyword || !keyword.trim()) return; this.initHistoryCache(); const index = this._historyCache.indexOf(keyword); if (index > -1) { this._historyCache.splice(index, 1); } this._historyCache.push(keyword); if (this._historyCache.length > HISTORY_MAX_LEN) { this._historyCache.shift(); } try { localStorage.setItem(HISTORY_CACHE_KEY, JSON.stringify(this._historyCache)); } catch (e) {} }, // 清除历史 clearHistory() { this._historyCache = []; try { localStorage.removeItem(HISTORY_CACHE_KEY); } catch (e) {} }, // 渲染表单区域 renderSearchForm() { const panel = this._getSidebarPanel(); if (!panel) return Promise.resolve(); return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData() || {}; const formData = { isOpenAutoThink: apiData.auto_think_enable || false, isOpenFindKeyword: apiData.find_keyword_enable || false, findKeywordList: apiData.find_keywords || [], }; const formRender = panel.querySelector('[role="form"]'); if (formRender) { return SPZ.whenApiDefined(formRender).then((api) => { api.render(formData); }); } }); }, // 渲染历史区域 renderSearchHistory() { const panel = this._getSidebarPanel(); if (!panel) return Promise.resolve(); return this.getOutsideItemEl().then((outsideItem) => { const apiData = outsideItem?.getData() || {}; const historyList = this.getHistoryList(); const historyData = { isShowHistory: (apiData.user_history_enable !== false) && historyList.length > 0, historyList: historyList, }; const historyRender = panel.querySelector('[role="history"]'); if (historyRender) { return SPZ.whenApiDefined(historyRender).then((api) => { api.render(historyData); }); } }); }, // 渲染联想结果 renderThinkResult(thinkResult) { const panel = this._getSidebarPanel(); if (!panel) return; const thinkRender = panel.querySelector('[role="thinkresult"]'); if (thinkRender) { SPZ.whenApiDefined(thinkRender).then((api) => { api.render({ thinkResult }); }); } }, // 获取联想结果 fetchThinkResult(keyword) { if (!keyword || !keyword.trim()) { this.setThinkResultStatus(false, false); return Promise.resolve([]); } this.setThinkResultStatus(true, false); this.setLoadingStatus(true); return fetch(`${SMART_SEARCH_THINK_URL}?surface=autocomplete&keyword=${encodeURIComponent(keyword)}`) .then(res => res.json()) .then(res => { this.setLoadingStatus(false); const items = res.items || []; this.setThinkResultStatus(true, items.length === 0); const lowerKeyword = keyword.toLowerCase(); const thinkResult = items.map(item => ({ ...item, highlightHtml: item.word.replace( new RegExp(lowerKeyword, 'gi'), `${lowerKeyword}` ), })); this.renderThinkResult(thinkResult); return thinkResult; }) .catch(() => { this.setLoadingStatus(false); this.setThinkResultStatus(true, true); return []; }); }, // 设置联想结果状态 setThinkResultStatus(hasValue, isEmpty) { const panel = this._getSidebarPanel(); if (!panel) return; if (hasValue) { panel.setAttribute('has-value', ''); } else { panel.removeAttribute('has-value'); } if (isEmpty) { panel.setAttribute('data-empty', ''); } else { panel.removeAttribute('data-empty'); } }, // 设置 loading 状态 setLoadingStatus(loading) { const panel = this._getSidebarPanel(); if (!panel) return; if (loading) { panel.setAttribute('loading', ''); } else { panel.removeAttribute('loading'); } const loadingEl = panel.querySelector('.smart-search-loading'); if (loadingEl) { if (loading) { loadingEl.removeAttribute('hide'); loadingEl.setAttribute('show', ''); } else { loadingEl.removeAttribute('show'); loadingEl.setAttribute('hide', ''); } } }, // 处理表单输入 handleFormInput(invocation) { const keyword = invocation?.args?.keyword ?? invocation?.keyword ?? ''; this.getOutsideItemEl().then((outsideItem) => { const apiData = outsideItem?.getData() || {}; if (!apiData.auto_think_enable) return; this.fetchThinkResult(keyword); }); }, // 处理搜索提交 handleSearchSubmit(value) { const searchStr = Array.isArray(value) ? value[0] : value; if (!searchStr || !searchStr.trim()) { window.location.href = SEARCH_URL; return; } this.addHistory(searchStr); this.trackSearch(searchStr, 'user_input'); window.location.href = `${SEARCH_URL}?q=${encodeURIComponent(searchStr)}`; }, // 处理历史点击 handleHistory(invocation) { const value = invocation?.args?.value ?? ''; if (!value) return; this.addHistory(value); this.trackSearch(value, 'user_history'); window.location.href = `${SEARCH_URL}?q=${encodeURIComponent(value)}`; }, // 处理热词点击 handleHotKeyword(invocation) { const word = invocation?.args?.word ?? ''; const queryType = invocation?.args?.query_type ?? 'user_keyword'; const url = invocation?.args?.url ?? ''; if (!word) return; this.addHistory(word); this.trackSearch(word, queryType); if (url && !url.includes(SEARCH_URL)) { window.location.href = url; } else { window.location.href = `${SEARCH_URL}?q=${encodeURIComponent(word)}`; } }, // 处理联想结果点击 handleThinkResult(invocation) { const word = invocation?.args?.word ?? ''; if (!word) return; this.addHistory(word); this.trackSearch(word, 'auto_think'); window.location.href = `${SEARCH_URL}?q=${encodeURIComponent(word)}`; }, // 处理清除历史 handleClearHistory() { this.clearHistory(); this.renderSearchHistory(); }, // 处理刷新热词 handleRefreshHot() { this.getOutsideItemEl().then((outsideItem) => { const apiData = outsideItem?.getData() || {}; const searchKeywords = apiData.search_keywords || []; if (searchKeywords.length <= HOT_SEARCH_LEN) return; // 直接调用渲染方法(会使用 _hotList 的分页逻辑) this.renderHotKeywordDirect(); }); }, // 埋点 trackSearch(query, queryType) { const trackQueryType = { 'user_input': 1, 'user_history': 2, 'user_keyword': 3, 'smart_keyword': 4, 'auto_think': 5, 'find_keyword': 6, }; if (window.sa) { window.sa.track('search_request', { event_info: JSON.stringify({ query, query_type: queryType }), function_name: 'smart_search', }); window.sa.registerAID(`smart_search.${trackQueryType[queryType] || 1}.${query}`); } }, // 初始化侧边栏内容渲染(返回 Promise,所有内容渲染完成后 resolve) initSidebarContent() { return Promise.all([ this.renderSearchForm(), this.renderSearchHistory(), this.renderHotKeywordDirect(), ]); }, // 直接渲染热搜词 renderHotKeywordDirect() { const panel = this._getSidebarPanel(); if (!panel) return Promise.resolve(); return this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) return; const apiData = outsideItem.getData() || {}; const searchKeywords = apiData.search_keywords || []; const hotKeywordList = this.getHotKeywordList(searchKeywords); const hotKeywordData = { isShowHotKeyword: searchKeywords.length > 0, list: hotKeywordList, }; const hotKeywordRender = panel.querySelector('[role="hotkeyword"].app-hot-keyword-render-child'); if (hotKeywordRender) { return SPZ.whenApiDefined(hotKeywordRender).then((api) => { api.render(hotKeywordData); }); } }); }, // 获取热搜词列表(带分页逻辑) getHotKeywordList(searchKeywords) { const enrichedList = searchKeywords.map(item => ({ ...item, urlObj: { ...item.url_obj, url: item.url_obj?.type === 'search' ? `${SEARCH_URL}?q=${item.word}` : item.url_obj?.url, }, })); // 用于刷新功能的分页逻辑 if (!this._hotListIndex) { this._hotListIndex = 0; } const startIndex = this._hotListIndex; const endIndex = startIndex + HOT_SEARCH_LEN; const result = enrichedList.slice(startIndex, endIndex); // 更新索引,循环使用 this._hotListIndex = endIndex >= enrichedList.length ? 0 : endIndex; return result.length > 0 ? result : enrichedList.slice(0, HOT_SEARCH_LEN); }, }; // --- 主组件 --- class SpzCustomSmartSearchLocation extends SPZ.BaseElement { constructor(element) { super(element); this.outsideCarouselIndex = 0; this.insideCarouselIndex = 0; this.searchItemType = 'icon'; this._originalSearchWrapParent = null; this._skipMobileInit = false; this.searchStyleConfig = { ...DEFAULT_SEARCH_STYLE_CONFIG }; this.clickSearchStyleConfig = { ...DEFAULT_CLICK_SEARCH_STYLE_CONFIG }; } static deferredMount() { return false; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.bindResizeListener(); this.registerActions(); // 设置超时降级:如果 API 在 5 秒内没有返回,显示默认样式 this._styleFallbackTimer = setTimeout(() => { if (!this._configLoaded) { this._applyStyleFallback(); } }, 5000); } mountCallback(){ this.safeInit(); } // API 超时/报错时的降级处理 _applyStyleFallback() { const searchWrap = this.getBlockWrap(); if (!searchWrap) return; const searchBtn = searchWrap.querySelector('.app-smart-search-btn'); if (searchBtn && !searchBtn.classList.contains('style-ready')) { searchBtn.classList.add('style-fallback'); } } // 清除降级定时器 _clearFallbackTimer() { if (this._styleFallbackTimer) { clearTimeout(this._styleFallbackTimer); this._styleFallbackTimer = null; } } unmountCallback(){ this.unbindResizeListener(); this.unregisterActions(); } // --- 初始化 --- safeInit() { this.relocatePlugin(); this.applySearchIconClass(); this.adjustLifestyleIcon(); if (!isDesktop() && !this._skipMobileInit && !this._mobileInitDone) { this.initMobileSmartSearch(); this._mobileInitDone = true; } } init() { this.safeInit(); if (this.searchItemType === 'input') { this.initInputMode(); return; } this.initIconMode(); } // --- Action 注册 --- registerActions() { this.registerAction('onSearchInputChange', (invocation) => { this.onSearchInputChange(invocation.args.keyword); }); this.registerAction('onSearchFormSubmit', (invocation) => { this.onSearchFormSubmit(invocation.args.event); }); this.registerAction('onOutsideCarouselIndexChange', (invocation) => { this.outsideCarouselIndex = invocation.args.index || 0; }); this.registerAction('onInsideCarouselIndexChange', (invocation) => { this.insideCarouselIndex = invocation.args.index || 0; }); this.registerAction('getSearchItemType', () => { this.fetchAndApplySearchItemType(); }); this.registerAction('generateHotKeywordList', (invocation) => { this.generateHotKeywordList(invocation.args?.data?.data); }); this.registerAction('onTapHotWord', (invocation) => { this.onTapHotWord(invocation.args.type); }); this.registerAction('onSidebarOpen', () => { this.onSidebarOpen(); }); this.registerAction('onSidebarClose', () => { this.onSidebarClose(); }); this.registerAction('openSidebar', () => { this.openSidebar(); }); this.registerAction('closeSidebar', () => { this.closeSidebar(); }); this.registerAction('expandHistory', () => { this.expandHistory(); }); // 搜索控制器 actions this.registerAction('handleFormInput', (invocation) => { this.handleFormInput(invocation); }); this.registerAction('handleHistory', (invocation) => { this.handleHistory(invocation); }); this.registerAction('handleHotKeyword', (invocation) => { this.handleHotKeyword(invocation); }); this.registerAction('handleThinkResult', (invocation) => { this.handleThinkResult(invocation); }); this.registerAction('handleClearHistory', () => { this.handleClearHistory(); }); this.registerAction('handleRefreshHot', () => { this.handleRefreshHot(); }); } // --- 搜索输入 & 提交 --- onSearchInputChange(keyword) { const hasValue = keyword && keyword.length > 0; const display = hasValue ? 'none' : 'block'; // 控制热词轮播显示 document.querySelectorAll('.hot-words-carousel-inner-container').forEach(el => { el.style.display = display; }); // 设置 input 元素的 has-value 属性(控制清除按钮和热词轮播的 CSS 样式) const panel = this._getSidebarPanel(); if (panel) { const input = panel.querySelector('.smart-search-input'); if (input) { if (hasValue) { input.setAttribute('has-value', ''); } else { input.removeAttribute('has-value'); } } } } onSearchFormSubmit(event) { const keywordArray = event.q || []; const keyword = keywordArray[0]; if (keyword !== null && keyword.length) { this.executeSearch(keywordArray, 1); } else { this.onTapHotWord('inside'); } } executeSearch(value, retryCount) { const searchStr = Array.isArray(value) ? value[0] : value; this.handleSearchSubmit(searchStr); } // --- 搜索项类型 --- fetchAndApplySearchItemType() { this.getOutsideItemEl().then((outsideItem) => { if (!outsideItem) { // API 获取失败,应用降级样式 this._applyStyleFallback(); return; } // 清除降级定时器 this._clearFallbackTimer(); const apiData = outsideItem.getData() || {}; const { searchStyleConfig, clickSearchStyleConfig } = parseHeaderStyle(apiData.header_style); this.searchStyleConfig = searchStyleConfig; this.clickSearchStyleConfig = clickSearchStyleConfig; this._configLoaded = true; this.searchItemType = searchStyleConfig.styleType === 'searchBox' ? 'input' : 'icon'; if (!apiData.header_style || apiData.header_style === '' || apiData.header_style === '{}') { const type = apiData.search_item_type; this.searchItemType = type || this.searchItemType; this.searchStyleConfig.styleType = this.searchItemType === 'input' ? 'searchBox' : 'imageText'; } this.applySearchStyleConfig(); this.init(); // 接口数据加载完成后,预加载侧边栏 this.preloadSidebar(); }).catch(() => { // API 报错,应用降级样式 this._applyStyleFallback(); }); } // --- 窗口监听 --- bindResizeListener() { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = SPZCore.Types.debounce( this.win, () => { console.log('bindResizeListener'); // 防止在 ljs-render 渲染过程中触发重复操作 if (this._isApplyingStyle) return; const widthChanged = !this._sidebarOpenWidth || window.innerWidth !== this._sidebarOpenWidth; const sidebarVisible = this._sidebarState === 'open' || this._sidebarState === 'ready' || this._sidebarState === 'preloading'; const overlay = this._getSidebarOverlay && this._getSidebarOverlay(); const overlayOpen = overlay && overlay.getAttribute('data-state') === 'open'; if (sidebarVisible || overlayOpen) { if (!widthChanged) return; this.closeSidebar(); } this.fetchAndApplySearchItemType(); }, DELAY ); window.addEventListener('resize', window.smartSearchResizeCallback); } unbindResizeListener() { if (window.smartSearchResizeCallback) { window.removeEventListener('resize', window.smartSearchResizeCallback); window.smartSearchResizeCallback = null; } if (this._relocateTimer) { clearInterval(this._relocateTimer); this._relocateTimer = null; } } unregisterActions() { const actionNames = [ 'onSearchInputChange', 'onSearchFormSubmit', 'onOutsideCarouselIndexChange', 'onInsideCarouselIndexChange', 'getSearchItemType', 'generateHotKeywordList', 'onTapHotWord', 'expandHistory', ]; actionNames.forEach((name) => { this.registerAction(name, () => {}); }); } } Object.assign(SpzCustomSmartSearchLocation.prototype, ElementFinderMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, StyleApplicatorMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, SidebarManagerMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, HistoryOverflowMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, HotKeywordsMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, MobileLayoutMixin); Object.assign(SpzCustomSmartSearchLocation.prototype, SearchControllerMixin); SPZ.defineElement(TAG, SpzCustomSmartSearchLocation); class SpzCustomSmartSearchToast extends SPZ.BaseElement { constructor(element) { super(element); this.toastDom = null; this.toastTimeout = null; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback(){ this.init(); } init(){ const toast = document.createElement('div'); toast.id = 'spz-custom-smart-search-toast-24'; toast.className = 'spz-custom-smart-search-toast'; document.body.appendChild(toast); this.toastDom = toast; this.registerAction('showToast',(invocation)=>{ this.showToast(invocation.args); }); this.registerAction('hideToast',(invocation)=>{ this.hideToast(invocation.args); }); } showToast({ message, duration = 2000 }){ if( !this.toastDom ) return; this.toastDom.innerHTML = message; this.toastDom.classList.add('smart-search-toast-show'); clearTimeout(this.toastTimeout); this.toastTimeout = setTimeout(() => { this.hideToast(); }, duration); } hideToast(){ if( !this.toastDom ) return; this.toastDom.classList.remove('smart-search-toast-show'); } } SPZ.defineElement('spz-custom-smart-search-toast', SpzCustomSmartSearchToast); class SpzCustomSmartSearchCookie extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.registerAction('getCookie',(invocation)=>{ this.getCookie(invocation.args); }); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } getCookie(key) { let cookieMap = {} document.cookie.split(';').map(item=>{ let [key, value] = item.trim().split('=') cookieMap[key] = value }) return cookieMap[key] || ''; } } SPZ.defineElement('spz-custom-smart-search-cookie', SpzCustomSmartSearchCookie); const default_function_name = 'smart_search'; const default_plugin_name = 'smart_search'; const default_module_type = 'smart_search'; const default_module = 'apps'; const default_business_type = 'product_plugin'; const default_event_developer = 'ray'; class SpzCustomSmartSearchTrack extends SPZ.BaseElement { constructor(element) { super(element); } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.registerAction('track', (invocation) => { const { trackType, trackData } = invocation.args; this.track({trackType, trackData}); }); } track({trackType, trackData}) { const { function_name, plugin_name, module_type, module, business_type, event_developer, event_type, event_desc, trackEventInfo, ...otherTrackData } = trackData; window.sa.track(trackType, { function_name: function_name || default_function_name, plugin_name: plugin_name || default_plugin_name, module_type: module_type || default_module_type, module: module || default_module, business_type: business_type || default_business_type, event_developer: event_developer || default_event_developer, event_type: event_type, event_desc: event_desc, ...otherTrackData, event_info: JSON.stringify({ ...(trackEventInfo || {}), }), }); } } SPZ.defineElement('spz-custom-smart-search-track', SpzCustomSmartSearchTrack);
BOUTIQUE
BOUTIQUE
  • Products

    hot

    PoE Security System
    • 单品1
    • 单品2
    • 3
    • 4
    Battery Powered Camera
    • 单品3
    • a
    • b
    • c
    WiFi Camera & System
    • 1
    • 2
    • 3
    • 4
    Analog Security System
    • 1
    • 2
    • 3
    • 4
    Add-ons & Accessories
    • 1
    • 2
    • 3
    • 4
  • Deals
  • APP
    Collection
  • Supports
    Center
    Contact us
  • track
  • track718
  • Black Organza Dress
  • Description
  • Fashion

WHO OUR CLIENTS

About Our Brand

Our products are designed with passion and understanding of our customers' needs. We are committed to using only the highest quality materials and ethical manufacturing processes.

See how we work

WHO OUR CLIENTS

About Our Brand

Our products are designed with passion and understanding of our customers' needs. We are committed to using only the highest quality materials and ethical manufacturing processes.

See how we work

HELP

Wireless chargers

Power banks

Shipping

Returns and exchanges

SHOP

Wireless chargers

Power banks

This is your heading text.

ABOUT US

Wireless chargers

Power banks

This is your heading text.

JOIN US

We send great emails!

Please enter your email address.
Please enter a valid email address.
Thank you being one of us!
© 2026. BOUTIQUE
Refund Policy Privacy Policy About Us Contact Us Shipping Policy track

Cart

Your cart is empty

Summary

Taxes and shipping calculated at checkout

Added to your cart

Your cart is reserved for !

View cart

No record yet

No offers yet

Loading...

Discount code

Cancel membership

You've successfully cancelled.