| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
| |
| |
| function ready(fn) { |
| if (document.readyState !== 'loading') fn(); |
| else document.addEventListener('DOMContentLoaded', fn); |
| } |
| |
| ready(function() { |
| |
| |
| |
| window.aiSearch = async function(query, products, token) { |
| if (!products || !products.length) { |
| return { results: [], aiAnswer: null, categories: [], constraints: [], budget: 0 }; |
| } |
| |
| const norm = (s) => String(s || '').toLowerCase() |
| .normalize('NFD').replace(/[\u0300-\u036f]/g, '') |
| .replace(/[đĐ]/g, 'd').replace(/[.\-\s]/g, ''); |
| |
| const q = norm(query); |
| let budget = 0; |
| let categories = []; |
| let constraints = []; |
| |
| |
| const budgetMatch = query.toLowerCase().match(/(dưới|trên|trong khoảng)?\s*([\d,.]+)\s*(triệu|nghìn|k|đồng)/g); |
| if (budgetMatch) { |
| const amt = parseFloat(budgetMatch[0].replace(/[^\d,.]/g, '').replace(',', '.')); |
| const unit = budgetMatch[0].match(/(triệu|nghìn|k|đồng)/g); |
| if (unit && unit[0] === 'triệu') budget = amt * 1000000; |
| else if (unit && (unit[0] === 'nghìn' || unit[0] === 'k')) budget = amt * 1000; |
| else budget = amt; |
| constraints.push({ label: budgetMatch[0].trim() }); |
| } |
| |
| |
| const catMap = { |
| 'bep': 'Bếp điện từ', |
| 'bep-tu': 'Bếp từ', |
| 'bep-gas': 'Bếp gas', |
| 'hut-mui': 'Máy hút mùi', |
| 'hut-khoi': 'Máy hút khói', |
| 'chau-rua': 'Chậu rửa', |
| 'voi-rua': 'Vòi rửa', |
| 'lo-nuong': 'Lò nướng', |
| 'tu-lanh': 'Tủ lạnh', |
| 'may-rua-chen': 'Máy rửa chén' |
| }; |
| |
| Object.keys(catMap).forEach(key => { |
| if (q.includes(key)) { |
| categories.push(catMap[key]); |
| } |
| }); |
| |
| |
| const brandMap = { |
| 'malloca': 'Malloca', |
| 'eurogold': 'Eurogold', |
| 'grob': 'Grob', |
| 'canzy': 'Canzy', |
| 'demax': 'Demax', |
| 'hafele': 'Hafele', |
| 'garis': 'Garis' |
| }; |
| |
| |
| let results = []; |
| for (let i = 0; i < products.length && results.length < 30; i++) { |
| const p = products[i]; |
| let score = 0; |
| const name = norm(p.name || ''); |
| const sku = norm(p.sku || p.mod || ''); |
| const brand = norm(p.brand || ''); |
| const cat = norm(p.cat || ''); |
| |
| |
| if (name.includes(q)) score += 10; |
| if (sku.includes(q)) score += 8; |
| if (brand.includes(q)) score += 6; |
| if (cat.includes(q)) score += 4; |
| |
| |
| const words = q.split(/\s+/).filter(w => w.length > 1); |
| words.forEach(w => { |
| if (name.includes(w)) score += 2; |
| if (sku.includes(w)) score += 1; |
| if (brand.includes(w)) score += 1; |
| }); |
| |
| |
| if (budget > 0 && p.priceNum && p.priceNum > budget) { |
| score = 0; |
| } |
| |
| |
| for (let b in brandMap) { |
| if (q.includes(b)) { |
| if (brand.includes(b)) score += 5; |
| else if (!brand.includes(b)) score = 0; |
| } |
| } |
| |
| if (score > 0) { |
| const labels = []; |
| if (p.priceNum && budget > 0 && p.priceNum <= budget) labels.push('Trong ngân sách'); |
| results.push({ p, idx: i, score, labels }); |
| } |
| } |
| |
| |
| results.sort((a, b) => b.score - a.score); |
| |
| |
| let aiAnswer = ''; |
| if (results.length > 0) { |
| aiAnswer = `Tìm thấy ${results.length} sản phẩm phù hợp. `; |
| if (categories.length) aiAnswer += `Danh mục: ${categories.join(', ')}. `; |
| if (budget > 0) aiAnswer += `Ngân sách: ${(budget/1000000).toFixed(0)} triệu.`; |
| } |
| |
| return { results, aiAnswer, categories, constraints, budget }; |
| }; |
| |
| |
| |
| |
| |
| |
| |
| |
| window.formatPrice = function(priceNum) { |
| if (!priceNum || priceNum <= 0) return 'Liên hệ'; |
| return priceNum.toLocaleString('vi-VN') + 'đ'; |
| }; |
| |
| |
| |
| if (!window._showOrderPicker) { |
| window._showOrderPicker = function(product, callback) { |
| |
| if (product && typeof addToCart === 'function') { |
| |
| var idx = -1; |
| if (window.D) { |
| for (var i = 0; i < window.D.length; i++) { |
| if (window.D[i] && (window.D[i].slug === product.slug || |
| window.D[i].sku === product.sku || |
| window.D[i].model === product.model)) { |
| idx = i; break; |
| } |
| } |
| } |
| if (idx >= 0) addToCart(idx); |
| callback && callback('ok'); |
| } |
| }; |
| } |
| |
| |
| if (typeof updateCartBadge === 'function') { |
| updateCartBadge(); |
| } |
| |
| console.log('V.AI STUDIO Patch v1048 initialized'); |
| }); |
| })(); |