From 0836879e98f50861fe2757e9fd5db2d1bf35d6d7 Mon Sep 17 00:00:00 2001 From: admin9webs Date: Sun, 20 Sep 2026 23:37:32 +0800 Subject: [PATCH] =?UTF-8?q?Lazada=20=E4=BE=A6=E5=AF=9F=E6=A1=A3=EF=BC=9A?= =?UTF-8?q?=E5=BA=97=E9=93=BA=E6=8C=89=E7=AB=99=E7=82=B9=E5=88=86=E5=BA=97?= =?UTF-8?q?=EF=BC=88=E7=AC=AC=E4=B8=80=E5=AE=B6=20Shehoney=5FMY=EF=BC=89?= =?UTF-8?q?=E3=80=81=E5=BF=85=E5=A1=AB=E5=90=AB=E5=93=81=E7=89=8C+?= =?UTF-8?q?=E7=A8=8E=E3=80=81store=20=E9=80=89=E5=AE=8C=E7=B1=BB=E7=9B=AE?= =?UTF-8?q?=E6=89=8D=E6=87=92=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NEXT-ROUND-PROMPT.md | 2 +- lazada建品/RECIPE.md | 34 ++++++++++++++++++++++++++++++++++ lazada建品/lz_probe1.mjs | 28 ++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 lazada建品/RECIPE.md create mode 100644 lazada建品/lz_probe1.mjs diff --git a/NEXT-ROUND-PROMPT.md b/NEXT-ROUND-PROMPT.md index cbd6126..5de43bb 100644 --- a/NEXT-ROUND-PROMPT.md +++ b/NEXT-ROUND-PROMPT.md @@ -76,7 +76,7 @@ function wrap(o,n,l){const f=o[n];o[n]=function(){window.__cap.push(l+': '+[...a |---|---|---|---|---| | Joom | `/web/joomProduct/add` | **复选框组** 85 个(全部+84 店) | 可搜索 `.ant-select` 平铺列表 | `smtChoice*` + `joom*`(已跑通) | | aliChoice | `/web/smtChoice/add` | 下拉(实测 2 家) | 弹窗 | `smtChoice*`:标题 `.subject`、描述要写 **CKEditor setData**、保存要**两遍**(第二遍前补「物流属性=普货」)——**已跑通**;类目要先 `alcdry.mjs` dry 扫一遍(避开强制「商品说明书」的类目,结果写 `alc_route.json`),再 `alcbatch.mjs` 批量。剩余卡点:`请选择SKU分类`、类目属性 `材质/尺寸` 选了不生效 | - | Lazada | `/web/lazada/add` | 下拉 | 弹窗 | 选完店铺才注册 `lazada*` store(懒加载) | + | Lazada | `/web/lazada/add` | 下拉(`Shehoney_MY/_SG/_TH/_ID/_PH/_VN` + `Nicegar_*`,第一家 `Shehoney_MY` shopId 3336185) | 弹窗(同 SHEIN 那套) | 选完店铺**还没有** `lazada*` store(选完类目才懒加载);必填含**品牌**(要求真实品牌库或「无品牌」)与**税** —— 侦察档 `lazada建品/RECIPE.md` | | fruugo | `/web/fruugoProduct/add` | 下拉 | 弹窗 | `fruugoAdd*`(Common/Basic/Dxm/Product/Variation/Image/Describe/Form) | (pddkj = Temu 全托管,已完成。)店铺一律取**自然序第一家**,SHEIN 那种锁品类的除外。 diff --git a/lazada建品/RECIPE.md b/lazada建品/RECIPE.md new file mode 100644 index 0000000..b6b93f6 --- /dev/null +++ b/lazada建品/RECIPE.md @@ -0,0 +1,34 @@ +# Lazada 建品侦察 — 未跑通,下一轮从这里接着做 + +路由:`https://www.dianxiaomi.com/web/lazada/add` 列表:`/web/lazada/{draft=采集箱, offline=待发布}` + +## 已勘明(`lz_probe1.mjs`) + +### 1. 店铺 = 下拉(按站点后缀分店) +第一家 = **`Shehoney_MY`(shopId 3336185)**。下拉前 10 项: +`Shehoney_MY / _SG / _TH / _ID / _PH / _VN`、`Nicegar_MY / _SG / _TH / _ID …` +—— 同一运营主体按站点拆店,**「自然序第一家」= `Shehoney_MY`**。 + +### 2. 类目 +`分类 ---- 请选择分类 ---- 选择分类`(`选择分类` 按钮在),形态大概率与 SHEIN/Temu/aliChoice +同一套弹窗 → 直接用 `shein建品/sh_build.mjs` 的 `selectCategory(c, kw, kw)`。 + +### 3. 必填项(表单项标签带 `*`) +`店铺 · 分类 · 产品标题(≤255) · 品牌 · 税 · 产品图片` +非必填:店小秘分类、来源URL、包装内容(≤1000)、是否预售(白名单功能)、产品视频、产品描述。 + +⚠️ **品牌**有坑:提示语写「请选择下拉提示框品牌或者填写 Lazada 后台已有品牌,如无品牌则填写…」 +(原文被截断)——大概率要求**品牌库里的真实品牌**或填「无品牌」占位,和 Walmart 的「无品牌」同类。 + +### 4. store 家族 +选完店铺时 `pinia` 里**没有任何 `lazada*` store**(`stores:{}`)——说明 +**store 是选完类目后才懒加载注册的**。下一轮选完类目再 dump 一次 store 名单即可。 + +## 下一轮的顺序建议 +1. 选店铺(`Shehoney_MY`)→ 2. 选类目(弹窗)→ 3. dump `lazada*` store 名单与字段落点 +→ 4. 标题/图片/品牌/税 → 5. **挂钩子再保存**(Lazada 大概率也有一套层层递进的必填) + +## 已知可复用 +- `shein建品/sh_lib.mjs`(`newPage`/`closePage`/`installHook`/`readHook`) +- `shein建品/sh_build.mjs`:`selectCategory`、`fillRequiredSelects`、`fillRequiredTextInputs`、`fillNativeSelects` +- 骨架直接抄 `joom建品/jmbatch.mjs` 或 `alichoice建品/alcbatch.mjs` diff --git a/lazada建品/lz_probe1.mjs b/lazada建品/lz_probe1.mjs new file mode 100644 index 0000000..d3a949a --- /dev/null +++ b/lazada建品/lz_probe1.mjs @@ -0,0 +1,28 @@ +import { newPage, closePage, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { SAN as S } from '/tmp/cdp3.mjs'; +const P = `document.querySelector('#app').__vue_app__.config.globalProperties.$pinia`; +const c = await newPage('https://www.dianxiaomi.com/web/lazada/add'); +await wait(12000); +console.log('初始:', S(String(await c.evalJS(`JSON.stringify([...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).slice(0,8).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,46)))`))).slice(0,600)); +// 选店铺 +const p = JSON.parse(String(await c.evalJS(`(()=>{const f=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).find(e=>/店铺/.test((e.querySelector('label')||{}).innerText||'')); + const s=f?f.querySelector('.ant-select-selector'):null; if(!s) return '[]'; 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)]);})()`))); +await c.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:p[0],y:p[1],buttons:0}); await wait(300); +for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:p[0],y:p[1],button:'left',clickCount:1,buttons:1}); +await wait(2500); +console.log('店铺选项:', S(String(await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop(); + if(!dd) return 'no-dd'; return JSON.stringify([...dd.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').trim()).slice(0,10));})()`))).slice(0,400)); +await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop(); + const op=[...dd.querySelectorAll('.ant-select-item-option')][0]; if(!op) return 0; const r=op.getBoundingClientRect(); + for(const ev of ['mousedown','mouseup','click']) op.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window,clientX:r.x+r.width/2,clientY:r.y+r.height/2})); return 1;})()`); +await wait(7000); +const st = await c.evalJS(`(()=>{const p=${P}; + const ids=(p._s instanceof Map?[...p._s.keys()]:Object.keys(p._s)).filter(x=>/lazada/i.test(x)); + const out={}; + for(const id of ids){ try{ const s=(p._s.get?p._s.get(id):p._s[id]).$state; out[id]=s.formState?JSON.stringify(s.formState).slice(0,300):('keys='+Object.keys(s).slice(0,8).join(',')); }catch(e){out[id]='ERR';} } + return JSON.stringify({stores:out, items:[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).slice(0,16).map(e=>{const l=e.querySelector('label'); + return (l&&/required/.test(String(l.className))?'*':'')+((e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,40));})});})()`); +console.log('状态:', S(String(st)).slice(0,1500)); +try{ await c.shot('/tmp/lz1.jpg'); console.log('shot ok'); }catch(e){} +await closePage(c); process.exit(0);