Spaces:
Running
Running
| /** | |
| * V.AI STUDIO — Product Editor v7 | |
| * | |
| * Approach: Monkey-patch showDetail() để CHẮC CHẮN inject edit icons | |
| * sau khi detail content được render. Không dùng MutationObserver. | |
| * | |
| * Edit bằng prompt() — đơn giản, hoạt động mọi browser. | |
| */ | |
| (function(){ | |
| ; | |
| var AUTH_USER='V.AISTUDIO',AUTH_PASS='Khongbiet'; | |
| var EDITS_KEY='vai_product_edits',CUSTOM_KEY='vai_custom_products'; | |
| function getEdits(){try{return JSON.parse(localStorage.getItem(EDITS_KEY)||'{}');}catch(e){return {};}} | |
| function saveEdits(e){localStorage.setItem(EDITS_KEY,JSON.stringify(e));} | |
| function getCustom(){try{return JSON.parse(localStorage.getItem(CUSTOM_KEY)||'[]');}catch(e){return [];}} | |
| function saveCustom(a){localStorage.setItem(CUSTOM_KEY,JSON.stringify(a));} | |
| function getD(){return window.D||[];} | |
| function checkAuth(){return sessionStorage.getItem('vai_editor_auth')==='1';} | |
| function doLogin(cb){ | |
| if(checkAuth()){cb();return;} | |
| var pw=prompt('🔐 Nhập mật khẩu quản trị:'); | |
| if(pw===AUTH_PASS){sessionStorage.setItem('vai_editor_auth','1');cb();} | |
| else if(pw!==null)alert('Sai mật khẩu'); | |
| } | |
| // === Apply saved edits to D[] on load === | |
| function applyEdits(){ | |
| var D=getD();if(!D.length)return; | |
| var edits=getEdits(),applied=0; | |
| for(var i=0;i<D.length;i++){ | |
| var p=D[i],edit=edits[p.slug];if(!edit)continue; | |
| if(edit.price!==undefined){p.priceNum=edit.price;p.price=edit.price.toLocaleString('vi-VN')+'đ';} | |
| if(edit.image){p.image=edit.image;if(p.images&&p.images.length)p.images[0]=edit.image;else p.images=[edit.image];} | |
| if(edit.name)p.name=edit.name; | |
| if(edit.model)p.model=edit.model; | |
| applied++; | |
| } | |
| var customs=getCustom(); | |
| if(customs.length){ | |
| var slugs={};D.forEach(function(p){if(p.slug)slugs[p.slug]=true;}); | |
| customs.forEach(function(cp){if(!slugs[cp.slug]){D.push(cp);slugs[cp.slug]=true;}}); | |
| } | |
| if(applied||customs.length)console.log('[Editor] Applied '+applied+' edits, '+customs.length+' custom'); | |
| } | |
| // === MONKEY-PATCH showDetail — inject edit icons after render === | |
| function patchShowDetail(){ | |
| if(typeof window.showDetail!=='function')return false; | |
| if(window._showDetailPatched)return true; | |
| var original=window.showDetail; | |
| window.showDetail=function(idx){ | |
| original.apply(this,arguments); | |
| // After original sets innerHTML, inject edit icons | |
| setTimeout(function(){injectIcons(idx);},100); | |
| }; | |
| window._showDetailPatched=true; | |
| console.log('[Editor] showDetail patched ✓'); | |
| return true; | |
| } | |
| // === Inject edit icons into current detail view === | |
| function injectIcons(idx){ | |
| var dv=document.getElementById('detailView'); | |
| if(!dv)return; | |
| var p=getD()[idx]; | |
| if(!p)return; | |
| var ICON='<span class="vai-eic" style="display:inline-block;margin-left:8px;cursor:pointer;font-size:13px;opacity:0.5;transition:opacity .15s" onmouseenter="this.style.opacity=1" onmouseleave="this.style.opacity=0.5">✏️</span>'; | |
| // Add icon to price | |
| var priceEl=dv.querySelector('.detail-price'); | |
| if(priceEl&&!priceEl.querySelector('.vai-eic')){ | |
| priceEl.insertAdjacentHTML('beforeend',ICON); | |
| priceEl.querySelector('.vai-eic').onclick=function(e){ | |
| e.stopPropagation(); | |
| doLogin(function(){ | |
| var val=prompt('💰 Nhập giá mới (số):',String(p.priceNum||0)); | |
| if(val===null)return; | |
| var num=parseInt(val.replace(/\D/g,''))||0; | |
| if(num>0){ | |
| p.priceNum=num;p.price=num.toLocaleString('vi-VN')+'đ'; | |
| var edits=getEdits();if(!edits[p.slug])edits[p.slug]={}; | |
| edits[p.slug].price=num;saveEdits(edits); | |
| priceEl.firstChild.textContent=p.price; | |
| } | |
| }); | |
| }; | |
| } | |
| // Add icon to name | |
| var nameEl=dv.querySelector('.detail-name'); | |
| if(nameEl&&!nameEl.querySelector('.vai-eic')){ | |
| nameEl.insertAdjacentHTML('beforeend',ICON); | |
| nameEl.querySelector('.vai-eic').onclick=function(e){ | |
| e.stopPropagation(); | |
| doLogin(function(){ | |
| var val=prompt('📝 Tên sản phẩm:',p.name||''); | |
| if(val===null||!val.trim())return; | |
| p.name=val.trim(); | |
| var edits=getEdits();if(!edits[p.slug])edits[p.slug]={}; | |
| edits[p.slug].name=val.trim();saveEdits(edits); | |
| nameEl.firstChild.textContent=val.trim(); | |
| }); | |
| }; | |
| } | |
| // Add icon on gallery (for image edit) | |
| var gallery=dv.querySelector('.gallery,.gallery-carousel'); | |
| if(gallery&&!gallery.querySelector('.vai-eic')){ | |
| var imgBtn=document.createElement('span'); | |
| imgBtn.className='vai-eic'; | |
| imgBtn.textContent='✏️'; | |
| imgBtn.style.cssText='position:absolute;top:10px;right:10px;z-index:50;background:rgba(255,255,255,.92);padding:5px 8px;border-radius:8px;cursor:pointer;font-size:15px;box-shadow:0 2px 8px rgba(0,0,0,.15);opacity:0.7'; | |
| imgBtn.onmouseenter=function(){this.style.opacity='1';}; | |
| imgBtn.onmouseleave=function(){this.style.opacity='0.7';}; | |
| imgBtn.onclick=function(e){ | |
| e.stopPropagation(); | |
| doLogin(function(){ | |
| var val=prompt('🖼️ URL hình ảnh mới:',p.image||''); | |
| if(val===null||!val.trim())return; | |
| p.image=val.trim(); | |
| if(p.images&&p.images.length)p.images[0]=val.trim(); | |
| var edits=getEdits();if(!edits[p.slug])edits[p.slug]={}; | |
| edits[p.slug].image=val.trim();saveEdits(edits); | |
| var img=dv.querySelector('.gallery-slide img,img'); | |
| if(img)img.src=val.trim(); | |
| }); | |
| }; | |
| gallery.style.position='relative'; | |
| gallery.appendChild(imgBtn); | |
| } | |
| } | |
| // === Thêm SP === | |
| var PROXIES=[function(u){return 'https://api.allorigins.win/get?url='+encodeURIComponent(u);}]; | |
| function openAddUrl(){ | |
| var o=document.getElementById('vai-addurl');if(o)o.remove(); | |
| var d=document.createElement('div');d.id='vai-addurl'; | |
| d.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99999;display:flex;align-items:center;justify-content:center;padding:16px'; | |
| d.onclick=function(e){if(e.target===d)d.remove();}; | |
| d.innerHTML='<div style="background:#fff;border-radius:14px;max-width:420px;width:100%;overflow:hidden"><div style="padding:12px 16px;background:#0369a1;color:#fff;font-weight:800;font-size:13px">➕ Thêm SP</div><div style="padding:14px"><div style="display:flex;gap:4px;margin-bottom:8px"><input id="va-url" placeholder="URL sản phẩm (tuỳ chọn)" style="flex:1;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px"><button id="va-fetch" style="padding:7px 10px;background:#0369a1;color:#fff;border:none;border-radius:6px;font-size:11px;cursor:pointer">Crawl</button></div><div id="va-st" style="font-size:10px;color:#64748b;margin-bottom:6px"></div><input id="va-name" placeholder="Tên SP *" style="width:100%;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px;margin-bottom:5px;box-sizing:border-box"><input id="va-model" placeholder="Model/Mã" style="width:100%;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px;margin-bottom:5px;box-sizing:border-box"><input id="va-price" placeholder="Giá (VNĐ)" style="width:100%;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px;margin-bottom:5px;box-sizing:border-box"><input id="va-img" placeholder="URL hình ảnh" style="width:100%;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px;margin-bottom:5px;box-sizing:border-box"><input id="va-brand" placeholder="Thương hiệu" style="width:100%;padding:7px;border:1px solid #ddd;border-radius:6px;font-size:11px;margin-bottom:10px;box-sizing:border-box"><div style="display:flex;gap:6px"><button id="va-save" style="flex:1;padding:9px;background:#059669;color:#fff;border:none;border-radius:7px;font-weight:700;cursor:pointer">Thêm vào kho</button><button id="va-x" style="padding:9px 14px;background:#e2e8f0;border:none;border-radius:7px;cursor:pointer">✕</button></div></div></div>'; | |
| document.body.appendChild(d); | |
| document.getElementById('va-x').onclick=function(){d.remove();}; | |
| document.getElementById('va-fetch').onclick=function(){ | |
| var url=document.getElementById('va-url').value.trim();if(!url)return; | |
| document.getElementById('va-st').textContent='Đang crawl...'; | |
| fetch(PROXIES[0](url)).then(function(r){return r.json();}).then(function(j){ | |
| var html=j.contents||'';if(!html){document.getElementById('va-st').textContent='Lỗi';return;} | |
| var doc=(new DOMParser()).parseFromString(html,'text/html'); | |
| var h1=doc.querySelector('h1');if(h1)document.getElementById('va-name').value=h1.textContent.trim(); | |
| var og=doc.querySelector('meta[property="og:image"]');if(og)document.getElementById('va-img').value=og.content||''; | |
| var pr=doc.querySelector('[itemprop="price"],.price,[class*="price"]');if(pr){var n=(pr.content||pr.textContent||'').replace(/[^\d]/g,'');if(n&&parseInt(n)>1000)document.getElementById('va-price').value=n;} | |
| document.getElementById('va-st').textContent='✓ OK'; | |
| }).catch(function(){document.getElementById('va-st').textContent='Lỗi crawl';}); | |
| }; | |
| document.getElementById('va-save').onclick=function(){ | |
| var name=document.getElementById('va-name').value.trim();if(!name){alert('Nhập tên SP');return;} | |
| var model=document.getElementById('va-model').value.trim(); | |
| var price=parseInt((document.getElementById('va-price').value||'').replace(/\D/g,''))||0; | |
| var image=document.getElementById('va-img').value.trim(); | |
| var brand=document.getElementById('va-brand').value.trim(); | |
| var slug='c'+Date.now(); | |
| var _idx=(model+' '+brand+' '+name).toLowerCase(); | |
| if(typeof buildSearchIndex==='function')try{_idx=buildSearchIndex({name:name,model:model,sku:model,brand:brand,cat:'custom',specs:{}});}catch(e){} | |
| var np={name:name,model:model,sku:model,slug:slug,price:price?price.toLocaleString('vi-VN')+'đ':'Liên hệ',priceNum:price,discPrice:price,image:image,images:image?[image]:[],link:document.getElementById('va-url').value.trim(),cat:'custom',catSlug:'custom',brand:brand,_idx:_idx}; | |
| getD().push(np);var c=getCustom();c.push(np);saveCustom(c); | |
| d.remove(); | |
| if(typeof init==='function')try{init();}catch(e){} | |
| alert('✅ Đã thêm: '+name); | |
| }; | |
| } | |
| // === FAB button === | |
| function injectFAB(){ | |
| if(document.getElementById('vai-fab'))return; | |
| var b=document.createElement('button');b.id='vai-fab'; | |
| b.style.cssText='position:fixed;bottom:20px;left:20px;z-index:9000;width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,#0369a1,#059669);color:#fff;border:none;font-size:22px;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,.25);transition:transform .2s'; | |
| b.textContent='+'; | |
| b.title='Thêm sản phẩm'; | |
| b.onmouseenter=function(){b.style.transform='scale(1.1)';}; | |
| b.onmouseleave=function(){b.style.transform='';}; | |
| b.onclick=function(){doLogin(openAddUrl);}; | |
| document.body.appendChild(b); | |
| } | |
| // === INIT === | |
| // Wait for D[] and showDetail to exist, then patch | |
| var _patchInterval=setInterval(function(){ | |
| if(getD().length>50&&typeof window.showDetail==='function'){ | |
| clearInterval(_patchInterval); | |
| applyEdits(); | |
| patchShowDetail(); | |
| } | |
| },300); | |
| setTimeout(function(){clearInterval(_patchInterval);},30000); | |
| setTimeout(injectFAB,1000); | |
| window.VAI_EDITOR={add:openAddUrl,patch:patchShowDetail}; | |
| console.log('[Editor v7] ready'); | |
| })(); | |