- TikTok/Shopee/速卖通/Walmart 各 46-47 条已在待发布 - Ozon 47 条已建、改价 40/47 - SHEIN 配方跑通第一条;Temu 差「预览图」一步 - 核心产出:MYJ.message.error 钩子诊断法(解开了三个平台的保存静默拦截) - 含下一轮提示词 NEXT-ROUND-PROMPT.md
141 lines
8.3 KiB
JavaScript
141 lines
8.3 KiB
JavaScript
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||
import fs from 'fs';
|
||
|
||
/* ── Walmart (Snowmountain, shopId 7661631) 建品配方 ──
|
||
node wmbatch.mjs <oid>
|
||
选店铺 → 选类目(索引) → Product Name/SKU/UPC(EAN)/Brand → 价格+库存+运输重量
|
||
→ 图片 → 描述+key feature → 保存 → 回读 */
|
||
|
||
const OID = process.argv[2];
|
||
if (!OID) { console.log('用法: node wmbatch.mjs <oid>'); process.exit(1); }
|
||
const IN = JSON.parse(fs.readFileSync('/tmp/wm_input.json', 'utf8'))[OID];
|
||
if (!IN) { console.log('无输入数据', OID); process.exit(1); }
|
||
const CATS = JSON.parse(fs.readFileSync('/tmp/wm_cats.json', 'utf8'));
|
||
const CAT = CATS[IN.cat_index];
|
||
|
||
const SHOP = 'Snowmountain';
|
||
const SKU = 'WM-' + OID;
|
||
const BRAND = process.env.WM_BRAND || 'Generic';
|
||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||
const log = (...a) => console.log(' ', ...a);
|
||
|
||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||
let k = 0;
|
||
for (const t of tl.filter(x => x.type === 'page' && /walmart/i.test(x.url))) { await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {}); k++; }
|
||
if (k) { log(`清理 walmart 页 ${k} 个`); await wait(1200); }
|
||
|
||
const t = await newTarget('about:blank');
|
||
const c = await CDP.connect(t.webSocketDebuggerUrl);
|
||
await c.send('Page.enable'); await c.send('Runtime.enable');
|
||
await c.send('Page.navigate', { url: 'https://www.dianxiaomi.com/web/walmartProduct/add' });
|
||
await wait(24000);
|
||
|
||
// 1. 店铺
|
||
const spos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||
.find(x=>{const l=x.querySelector('label'); return l&&/店铺/.test(l.innerText||'');});
|
||
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'}); const r=s.getBoundingClientRect();
|
||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: spos[0], y: spos[1], button: 'left', clickCount: 1 });
|
||
await wait(2500);
|
||
const pk = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||
if(!dd)return'no-dd'; const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>new RegExp('^\\\\s*${SHOP}\\\\s*$','i').test(o.innerText||''));
|
||
if(!hit)return'no-hit'; for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'ok';})()`);
|
||
log('1) 店铺:', pk);
|
||
await wait(9000);
|
||
|
||
// 2. 类目
|
||
await c.evalJS(`document.body.dispatchEvent(new MouseEvent('mousedown',{bubbles:true}));document.body.click();`);
|
||
await wait(1200);
|
||
const cpos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
|
||
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'}); const r=s.getBoundingClientRect();
|
||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: cpos[0], y: cpos[1], button: 'left', clickCount: 1 });
|
||
await wait(3000);
|
||
let catRes = 'nf';
|
||
for (let i = 0; i < 70; i++) {
|
||
const r = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||
if(!dd)return'no-dd'; const target=${JSON.stringify(CAT)};
|
||
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===target);
|
||
if(!hit)return'scroll';
|
||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||
return 'picked';})()`);
|
||
if (r === 'picked') { catRes = 'picked:' + CAT; break; }
|
||
if (r === 'no-dd') { catRes = 'no-dd'; break; }
|
||
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||
if(!dd)return 0; const rc=dd.querySelector('.rc-virtual-list-holder')||dd; rc.scrollTop=rc.scrollTop+340; return rc.scrollTop;})()`);
|
||
await wait(330);
|
||
}
|
||
log('2) 类目:', catRes);
|
||
if (catRes === 'nf' || catRes === 'no-dd') { log('❌ 类目未选中,中止'); process.exit(2); }
|
||
await wait(16000);
|
||
|
||
// 3. 全部字段直填 store
|
||
const wr = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||
const g=n=>p._s.get(n).$state;
|
||
const basic=g('walmartBasicStore').formState;
|
||
basic.hasVariant=false;
|
||
const pr=g('walmartProductStore').formState;
|
||
pr.productName=${JSON.stringify(IN.title)};
|
||
pr.productIdType='UPC';
|
||
pr.productId=${JSON.stringify(IN.ean)};
|
||
pr.sku=${JSON.stringify(SKU)};
|
||
const at=g('walmartAttrStore').formState;
|
||
at.brand=${JSON.stringify(BRAND)};
|
||
const ps=g('walmartPriceShippingStore').formState;
|
||
ps.price=${JSON.stringify(String(IN.price_usd))};
|
||
ps.inventory='999';
|
||
ps.shippingWeight=${JSON.stringify(String(IN.weight_lbs))};
|
||
const ds=g('walmartDescStore').formState;
|
||
ds.description=${JSON.stringify(IN.bullets.map(b => '• ' + b).join('<br>'))};
|
||
ds.keyFeatures=${JSON.stringify(IN.bullets.slice(0, 3).map(b => b.slice(0, 100)))};
|
||
const im=g('walmartImgStore').formState;
|
||
const urls=${JSON.stringify(IN.imgs)};
|
||
im.mainImage=urls.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800}));
|
||
return JSON.stringify({name:pr.productName.length, upc:pr.productId, price:ps.price, w:ps.shippingWeight, imgs:urls.length, kf:ds.keyFeatures.length});})()`);
|
||
log('3) 字段:', wr);
|
||
await wait(2500);
|
||
|
||
// 3b. DOM 兜底:把关键文本字段再按真实事件写一遍(Brand/价格/库存/重量等 store 不绑 DOM)
|
||
const setByLabel = (rx, val) => c.evalJS(`(()=>{
|
||
const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||
.find(x=>{const l=x.querySelector('label'); return l&&new RegExp(${JSON.stringify(rx)}).test(l.innerText||'');});
|
||
if(!it)return'nf';
|
||
const el=it.querySelector('textarea')||it.querySelector('input:not([type=hidden])');
|
||
if(!el)return'no-el';
|
||
const proto=el.tagName==='TEXTAREA'?window.HTMLTextAreaElement.prototype:window.HTMLInputElement.prototype;
|
||
Object.getOwnPropertyDescriptor(proto,'value').set.call(el, ${JSON.stringify(String(val))});
|
||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true}));
|
||
return 'ok';})()`);
|
||
const fills = [
|
||
['Product Name|产品名称', IN.title],
|
||
['^\\s*SKU', SKU],
|
||
['^\\s*Brand|品牌', BRAND],
|
||
['^\\s*价格', String(IN.price_usd)],
|
||
['^\\s*库存', '999'],
|
||
['运输重量', String(IN.weight_lbs)],
|
||
['产品描述', IN.bullets.map(b => '• ' + b).join('\n')],
|
||
['key feature', IN.bullets.slice(0, 3).join('\n')],
|
||
];
|
||
const fillRes = [];
|
||
for (const [rx, v] of fills) fillRes.push(rx.split('|')[0] + '=' + (await setByLabel(rx, v)));
|
||
log('3b) DOM兜底:', fillRes.join(' '));
|
||
await wait(2000);
|
||
|
||
// 4. 保存
|
||
const sv = await c.evalJS(`(()=>{const b=[...document.querySelectorAll('button,a,span')].filter(e=>e.offsetParent!==null&&/^\\s*保存\\s*$/.test(e.innerText||''));
|
||
if(!b.length)return'nf'; for(const ev of ['mousedown','mouseup','click']) b[b.length-1].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`);
|
||
log('4) 保存:', sv);
|
||
const seen = [];
|
||
for (let i = 0; i < 18; i++) {
|
||
await wait(700);
|
||
const m = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal-confirm')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean); return JSON.stringify(ms.slice(0,3));})()`);
|
||
JSON.parse(String(m)).forEach(x => seen.push(x));
|
||
}
|
||
log('5) 提示:', SAN([...new Set(seen)].join(' || ')).slice(0, 420) || '(无)');
|
||
const er = await c.evalJS(`(()=>{const e=[...document.querySelectorAll('.ant-form-item-has-error')].map(x=>{const l=x.querySelector('label'); return (l?l.innerText.trim():'?');});
|
||
return JSON.stringify([...new Set(e)].slice(0,8));})()`);
|
||
log('6) 仍报错的字段:', SAN(String(er)).slice(0, 200));
|
||
await c.shot('/tmp/wm_pilot.jpeg').catch(() => {});
|
||
process.exit(0);
|