// saas-search Frontend - Modern UI (Multi-Tenant) // API 基础地址策略(优先级从高到低): // 1. window.API_BASE_URL:若在 HTML / 部署层显式注入,则直接使用(可为绝对 URL 或相对路径,如 "/api") // 2. 默认:空前缀(同源调用),由 6003 前端服务在服务端转发到本机 6002 // // 说明:在“外网 web 服务器对 6002 另加认证”的场景下, // 浏览器不应直接调用 web:6002,而应调用同源 web:6003/search/*, // 再由 frontend_server.py 代理到 GPU 机本地 6002。 (function initApiBaseUrl() { let base; if (window.API_BASE_URL) { base = window.API_BASE_URL; } else { base = ''; } window.API_BASE_URL = base; const apiUrlEl = document.getElementById('apiUrl'); if (apiUrlEl) { apiUrlEl.textContent = base || '(same-origin)'; } })(); const API_BASE_URL = window.API_BASE_URL; // Get tenant ID from select function getTenantId() { const tenantSelect = document.getElementById('tenantSelect'); if (tenantSelect) { return tenantSelect.value.trim(); } return ''; } // Get sku_filter_dimension (as list) from input function getSkuFilterDimension() { const skuFilterInput = document.getElementById('skuFilterDimension'); if (skuFilterInput) { const value = skuFilterInput.value.trim(); if (!value.length) { return null; } // 支持用逗号分隔多个维度,例如:color,size 或 option1,color const parts = value.split(',').map(v => v.trim()).filter(v => v.length > 0); return parts.length > 0 ? parts : null; } return null; } // State Management let state = { query: '', currentPage: 1, pageSize: 20, totalResults: 0, filters: {}, rangeFilters: {}, sortBy: '', sortOrder: 'desc', facets: null, lastSearchData: null, debug: true // Always enable debug mode for test frontend }; // Initialize function initializeApp() { // 初始化租户下拉框和分面面板 console.log('Initializing app...'); initTenantSelect(); setupProductGridResultDocToggle(); const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.focus(); } } /** Delegated handler: toggle inline current result JSON under each result card (survives innerHTML refresh on re-search). */ function setupProductGridResultDocToggle() { const grid = document.getElementById('productGrid'); if (!grid || grid.dataset.resultDocToggleBound === '1') { return; } grid.dataset.resultDocToggleBound = '1'; grid.addEventListener('click', onProductGridResultDocToggleClick); } function onProductGridResultDocToggleClick(event) { const btn = event.target.closest('[data-action="toggle-result-inline-doc"]'); if (!btn) { return; } event.preventDefault(); const debugRoot = btn.closest('.product-debug'); if (!debugRoot) { return; } const panel = debugRoot.querySelector('.product-result-doc-panel'); const pre = debugRoot.querySelector('.product-result-doc-pre'); if (!panel || !pre) { return; } if (debugRoot.dataset.resultInlineOpen === '1') { panel.setAttribute('hidden', ''); debugRoot.classList.remove('product-debug--result-expanded'); debugRoot.dataset.resultInlineOpen = '0'; btn.textContent = '在结果中显示当前结果数据'; return; } panel.removeAttribute('hidden'); debugRoot.classList.add('product-debug--result-expanded'); debugRoot.dataset.resultInlineOpen = '1'; btn.textContent = '隐藏当前结果数据'; if (pre.textContent.length === 0) { pre.textContent = btn.getAttribute('data-result-json') || '{}'; } panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } // 在 DOM 加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeApp); } else { // DOM 已经加载完成,直接执行 initializeApp(); } // 备用初始化:如果上面的初始化失败,在 window.onload 时再试一次 window.addEventListener('load', function() { const tenantList = document.getElementById('tenantList'); if (tenantList && tenantList.options.length === 0) { console.log('Retrying tenant select initialization on window.load...'); initTenantSelect(); } }); // 最后尝试:延迟执行,确保所有脚本都已加载 setTimeout(function() { const tenantList = document.getElementById('tenantList'); if (tenantList && tenantList.options.length === 0) { console.log('Final retry: Initializing tenant select after delay...'); if (typeof getAvailableTenantIds === 'function') { initTenantSelect(); } else { console.error('getAvailableTenantIds still not available after delay'); } } }, 100); // Keyboard handler function handleKeyPress(event) { if (event.key === 'Enter') { performSearch(); } } // 初始化租户输入框(带 162/170 等候选,可自行填写任意 tenant ID) function initTenantSelect() { const tenantSelect = document.getElementById('tenantSelect'); const tenantList = document.getElementById('tenantList'); if (!tenantSelect || !tenantList) { console.error('tenantSelect or tenantList element not found'); return; } // 检查函数是否可用 if (typeof getAvailableTenantIds !== 'function') { console.error('getAvailableTenantIds function not found. Make sure tenant_facets_config.js is loaded before app.js'); return; } const availableTenants = getAvailableTenantIds(); console.log('Available tenants:', availableTenants); // 清空 datalist 现有选项 tenantList.innerHTML = ''; if (availableTenants && availableTenants.length > 0) { availableTenants.forEach(tenantId => { const option = document.createElement('option'); option.value = tenantId; tenantList.appendChild(option); }); // 设置默认值(仅当输入框为空时) if (!tenantSelect.value.trim()) { tenantSelect.value = availableTenants.includes('0') ? '0' : availableTenants[0]; } } // 初始化分面面板 renderFacetsPanel(); } // 租户ID改变时的处理 function onTenantIdChange() { renderFacetsPanel(); // 清空当前的分面数据 clearFacetsData(); } // 根据当前 tenant_id 渲染分面面板结构 function renderFacetsPanel() { const tenantId = getTenantId(); const config = getTenantFacetsConfig(tenantId); const container = document.getElementById('specificationFacetsContainer'); if (!container) return; // 清空现有规格分面 container.innerHTML = ''; // 为每个规格字段创建分面行 config.specificationFields.forEach(specField => { const row = document.createElement('div'); row.className = 'filter-row'; row.setAttribute('data-facet-field', specField.field); row.innerHTML = `
Try different keywords or filters
${escapeHtml(customStringify(debug.rerank_input))}${escapeHtml(customStringify(debug.style_intent_sku))}${escapeHtml(customStringify(debug.matched_queries))}${escapeHtml(customStringify(debugInfo.page_fill))}`;
html += '${escapeHtml(customStringify(debugInfo.es_query))}`;
html += '${escapeHtml(customStringify(debugInfo.es_query_context))}`;
html += '