店小秘多平台铺货配方:7 平台建品脚本 + 静默拦截诊断法 + 项目状态
- TikTok/Shopee/速卖通/Walmart 各 46-47 条已在待发布 - Ozon 47 条已建、改价 40/47 - SHEIN 配方跑通第一条;Temu 差「预览图」一步 - 核心产出:MYJ.message.error 钩子诊断法(解开了三个平台的保存静默拦截) - 含下一轮提示词 NEXT-ROUND-PROMPT.md
This commit is contained in:
commit
f5720ad613
94 files changed
+21561
No files matched your search
@@ -0,0 +1,5 @@
|
||||
*_logs/
|
||||
*_log/
|
||||
*.jpeg
|
||||
*.jpg
|
||||
*.png
|
||||
@@ -0,0 +1,57 @@
|
||||
# 下一轮提示词(直接复制给新会话)
|
||||
|
||||
---
|
||||
|
||||
我在做「同一批 48 个 1688 商品铺到店小秘各平台待发布」的活儿。上一轮的完整配方和踩坑记录
|
||||
已经推到 Gitea:`http://192.168.9.206:3000/admin9webs/dianxiaomi-crossborder-playbook`
|
||||
(本机也有一份:`/home/admin9webs/gitea-push/dianxiaomi-crossborder-playbook/`)。
|
||||
**先读仓库的 `README.md` 和 `静默拦截诊断/HOOK.md`**,再动手。
|
||||
|
||||
## 已完成
|
||||
TikTok 47 / Shopee 47 / 速卖通 47 / Walmart 46 条已在待发布;Ozon 47 条已建、改价 40/47。
|
||||
各平台配方脚本、输入数据、逐条日志都在仓库对应目录里。
|
||||
|
||||
## 这轮要做的(按优先级)
|
||||
|
||||
1. **Temu 解「请上传预览图」**
|
||||
Temu 的报错是层层递进的,前面 4 层我已经解掉了(省份 → 变种 → 尺寸 → 净重<重量),
|
||||
现在只剩 `MYJ.error: 请上传预览图`。SKU 表「预览图」列需要**每个变种行的真实图片**,
|
||||
不是文本。大概率是点单元格弹图片空间。参考 Walmart 的解法:**hover 触发的下拉要先
|
||||
`Input.dispatchMouseEvent` 发 `mouseMoved` 再 `click`**。
|
||||
Temu 的规格是 `.theme-value-add` 块(值输入 `input[class*="!w-200"]` + 添加按钮),
|
||||
**必须手动输入值再点添加**,直接改 store 无效。
|
||||
解出来后把 `temu建品/` 的配方补完。
|
||||
|
||||
2. **SHEIN 批量**
|
||||
`shein建品/RECIPE.md` 已经有跑通第一条的完整步骤。把它写成 `shbatch.mjs` 循环跑 40 条,
|
||||
按品类分到 4 家店(美妆 18 / 文具 13 / 箱包 8 / 内睡袜子 1),路由见 `shein_route.json`。
|
||||
**注意顺序**:先选规格 → 再填 SKU 行 → 再填尺码表(加规格会重建 SKU 行)。
|
||||
|
||||
3. **Temu 批量**(解完预览图之后)
|
||||
|
||||
4. **继续铺剩余店小秘平台**:Joom(84 家)、Lazada(24 家)、aliChoice(64 家)、
|
||||
pddkj(15 家)、fruugo(3 家)。店铺用**字母序/圈号自然序第一家**(除 SHEIN 那种锁品类的)。
|
||||
|
||||
5. **妙手各平台**(店小秘全部完成后)
|
||||
|
||||
## 硬约束
|
||||
- 平台「首字母第一家店」规则(SHEIN 除外,它锁品类必须分店)
|
||||
- 图片一律**外链 alicdn**,且必须**无水印**
|
||||
- **EAN 台账在 S03 的 `/root/ean_*.txt`**,KVM-2 够不到。本批已分配 pc 3400~3446(Walmart),
|
||||
Temu 用 3447 起。**用了就要在 S03 落盘**,否则撞号。
|
||||
|
||||
## 遇到「点了保存没反应」怎么办
|
||||
**第一件事是挂钩子**,不要猜字段:
|
||||
```js
|
||||
window.__cap=[];
|
||||
function wrap(o,n,l){const f=o[n];o[n]=function(){window.__cap.push(l+': '+[...arguments].map(a=>typeof a==='string'?a:JSON.stringify(a)).join(' '));return f.apply(o,arguments);};}
|
||||
['error','warning','info','success'].forEach(k=>wrap(MYJ.message,k,'MYJ.'+k));
|
||||
['error','warning','info','success'].forEach(k=>wrap(antd.message,k,'antd.'+k));
|
||||
```
|
||||
点保存 → 读 `window.__cap`。上一轮我在 SHEIN 和 Temu 上盲试了几十轮没进展,
|
||||
挂上钩子后**两个平台一次全暴露**。
|
||||
|
||||
## 环境
|
||||
- Chrome CDP **9223**,profile `/tmp/dxm7`;VNC `http://192.168.9.122:6097/vnc.html`
|
||||
- CDP helper `/tmp/cdp3.mjs`(ESM,`newTarget`/`attach`/`CDP`/`SAN`)
|
||||
- 批量跑动辄 1 小时,用 `nohup ... &` 后台跑 + 逐条日志,别前台等
|
||||
@@ -0,0 +1,93 @@
|
||||
# 店小秘多平台铺货配方(dianxiaomi crossborder playbook)
|
||||
|
||||
同一批 **48 个 1688 商品**铺到店小秘各平台的「待发布」。本仓库是**可复用的配方 + 踩坑记录**,
|
||||
不是一次性脚本堆积——每个平台一个目录,含配方脚本、输入数据、逐条结果。
|
||||
|
||||
## 战果
|
||||
|
||||
| 平台 | 店铺 | 待发布 | 状态 |
|
||||
|---|---|---|---|
|
||||
| TikTok | AutoMavenShop_MY | **47** | 完成 |
|
||||
| Shopee | Ljbgkj | **47** | 完成 |
|
||||
| 速卖通 | 4024039 | **47** | 完成(含清理 4 份重复) |
|
||||
| Walmart | Snowmountain | **46** | 完成 |
|
||||
| Ozon | Yejet | **47** | 已建;改价 40/47 |
|
||||
| SHEIN | 4 家分品类 | 0 | 配方跑通第一条,40 条待批量 |
|
||||
| Temu 全托管 | ①Alimingpro | 0 | 差「预览图」一步 |
|
||||
|
||||
> `601964534137`(遥控器保护套)18 张图全带水印、无干净图,所有平台统一跳过。
|
||||
|
||||
## ⭐ 最重要的一节:保存静默拦截诊断法
|
||||
|
||||
**症状**:点「保存」→ 不发 POST、无界面提示、无 `.ant-form-item-has-error`。
|
||||
|
||||
**根因**:校验确实失败了,但报错被 **`MYJ.message.error` 吞掉**;而且报错是**层层递进**的
|
||||
(修完 A 才显示 B),所以肉眼和 DOM 扫描都抓不到。
|
||||
|
||||
**解法**——在点保存**之前**挂钩子:
|
||||
|
||||
```js
|
||||
window.__cap = [];
|
||||
function wrap(o, n, l) {
|
||||
const f = o[n];
|
||||
o[n] = function () {
|
||||
window.__cap.push(l + ': ' + [...arguments].map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' '));
|
||||
return f.apply(o, arguments);
|
||||
};
|
||||
}
|
||||
['error','warning','info','success'].forEach(k => wrap(MYJ.message, k, 'MYJ.' + k));
|
||||
['error','warning','info','success'].forEach(k => wrap(antd.message, k, 'antd.' + k));
|
||||
```
|
||||
|
||||
点保存 → 读 `window.__cap`。
|
||||
|
||||
**为什么之前的「必填扫描」不够**:扫 `.ant-form-item-required` 且为空,漏掉两类——
|
||||
**条件必填**(Temu 的省份,只有「产地=中国」才出现)和**类目相关的文件字段**
|
||||
(速卖通的商品说明书,有些类目强制、有些不强制)。
|
||||
|
||||
详见 `静默拦截诊断/HOOK.md`。
|
||||
|
||||
## 各平台要点速查
|
||||
|
||||
| 平台 | 目录 | 最独特的一点 |
|
||||
|---|---|---|
|
||||
| Ozon | `ozon改价/` | 保存按钮是**「保存并移入待发布」**;价格是 **CNY**;改价必须同时抬高原价,否则报「售价需小于原价」 |
|
||||
| TikTok | `tiktok建品/` | 价格 **MYR**;图片 store 直接吃**外链数组** |
|
||||
| Shopee | `shopee建品/` | 用**单件模式**绕开变种;价格 **CNY**;列表字段是 `itemName` 不是 title |
|
||||
| Walmart | `walmart建品/` | 保存体是 **zip 的 multipart**;Brand 必须点「无品牌」;图片必须 **hover** 开下拉走「网络图片」 |
|
||||
| 速卖通 | `速卖通建品/` | `check_box` 属性值**必须是数组**;`idStr ≠ id` |
|
||||
| SHEIN | `shein建品/` | 店铺**锁品类**;类目要**两个规格**;**加规格会重建 SKU 行** |
|
||||
| Temu | `temu建品/` | 类目 1038 个从 store 直读;规格是 `.theme-value-add`(与 SHEIN 不同构) |
|
||||
|
||||
## 通用技术事实
|
||||
|
||||
- Chrome CDP 端口 **9223**,profile `/tmp/dxm7`
|
||||
- 站点是 Vue3 + Pinia:
|
||||
`document.querySelector('#app').__vue_app__.config.globalProperties.$pinia._s.get('<store>').$state`
|
||||
- CDP helper:`cdp3.mjs`(`newTarget`/`attach`/`CDP`/`SAN`),ESM,**不能用 require**
|
||||
- VNC:`http://192.168.9.122:6097/vnc.html` 或 `:6100`(免密)
|
||||
|
||||
## 十条会重复踩的坑
|
||||
|
||||
1. **保存静默拦截** → 挂钩子,别猜
|
||||
2. **`idStr ≠ id`** → 大整数精度丢失,服务端操作一律用 `idStr`
|
||||
3. **`!w-200` 不是合法 CSS 类选择器** → 用 `input[class*="!w-200"]`
|
||||
4. **antd select 吃 DOM click**,但有些组件只吃真实鼠标事件
|
||||
5. **hover 触发的下拉**点不开 → 先 `mouseMoved` 再 `click`
|
||||
6. **加规格会重建 SKU 行** → 顺序「规格 → SKU → 尺码表」
|
||||
7. **store 赋值后 UI 异步同步** → 当次校验可能仍失败,再点一次保存
|
||||
8. **类目决定是否强制资质文件**(速卖通某些类目要上传说明书)
|
||||
9. **SHEIN 店铺锁品类** → 不能用「首字母第一家店」
|
||||
10. `pkill -f <pat>` 会自杀 → 用 `[.]` 技巧(`pgrep -f 'foo[.]sh'`)
|
||||
|
||||
## EAN-13 台账(湖北易千亿彩 · 69803093 段)
|
||||
|
||||
- 结构:8 位前缀 `69803093` + 4 位产品码 + 1 位校验
|
||||
- 校验位:`(10 - sum(d*(1 if i%2==0 else 3) for i,d in enumerate(d12)) % 10) % 10`
|
||||
(左起权重 1,3,1,3…;UPC-A 权重相反,别混用)
|
||||
- 台账唯一来源:**S03 主机 `/root/ean_*.txt`**(KVM-2 够不到)
|
||||
- 已用 3190,下一号 pc 3400 → `6980309334003`
|
||||
- **本批已分配**:Walmart pc **3400~3446**(见 `walmart建品/ean_69803093_3400-3446.txt`);
|
||||
Temu 拟用 pc **3447+**
|
||||
- ⚠️ **必须在 S03 落盘**,否则下次续号会撞号
|
||||
- 说明书 §1 的例子 `pc 1000 → 6980309310004` **是错的**,正确是 `6980309310007`
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""EAN-13 生成本地复算(湖北易千亿彩 · 69803093 段)
|
||||
结构: 8位前缀 69803093 + 4位产品码 + 1位校验
|
||||
校验: 前12位左起权重 1,3,1,3... ; check=(10-sum%10)%10"""
|
||||
def ean13_check(d12: str) -> int:
|
||||
total = sum(int(d) * (1 if i % 2 == 0 else 3) for i, d in enumerate(d12))
|
||||
return (10 - total % 10) % 10
|
||||
|
||||
def make(pc: int) -> str:
|
||||
d12 = "69803093" + f"{pc:04d}"
|
||||
return d12 + str(ean13_check(d12))
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 1) 复算文档里的例子,验证算法
|
||||
print("=== 算法自检 ===")
|
||||
for pc, expect in ((2900, "6980309329009"), (3400, "6980309334003"), (9999, "6980309399996")):
|
||||
got = make(pc)
|
||||
print(f" pc {pc:>4} → {got} {'✅' if got == expect else '❌ 期望 ' + expect}")
|
||||
doc_wrong = make(1000)
|
||||
print(f" pc 1000 → {doc_wrong} (文档写 6980309310004,实为 {doc_wrong} ← 文档该例有误)")
|
||||
print(f" pc 0000 → {make(0)}")
|
||||
print()
|
||||
# 2) 为 Walmart 分配 47 个(pc 3400..3446)
|
||||
START, N = 3400, 47
|
||||
codes = [make(START + i) for i in range(N)]
|
||||
assert len(set(codes)) == N, "批内重复"
|
||||
for c in codes:
|
||||
assert ean13_check(c[:12]) == int(c[12]), f"校验位错 {c}"
|
||||
print(f"=== 分配 pc {START}~{START+N-1} 共 {N} 个(校验位全部复核通过)===")
|
||||
for i, c in enumerate(codes):
|
||||
print(f" {START+i:>4} {c}")
|
||||
open("/home/admin9webs/walmart建品/ean_69803093_%d-%d.txt" % (START, START + N - 1), "w").write("\n".join(codes) + "\n")
|
||||
print(f"\n已落盘: ean_69803093_{START}-{START+N-1}.txt")
|
||||
print(f"下一号应为 pc {START+N} → {make(START+N)};剩余 {9999-(START+N)+1} 个")
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,37 @@
|
||||
import json, collections
|
||||
j=json.load(open('/tmp/oz_draft_all.json')); items=j['items']
|
||||
RATE=1500/113.0
|
||||
MARGINS=[0,0.13,0.15,0.20,0.25,0.30,0.35,0.40]
|
||||
def F(g): return 4.2+0.045*g
|
||||
def price(b,g,r): return (b+F(g))/(1-0.2-r) if r<0.8 else None
|
||||
out={}
|
||||
stats=collections.Counter()
|
||||
for p in items:
|
||||
sp=p['vars'][0].get('sp'); eu=p['eu']
|
||||
b=float(sp) if sp else (float(eu)*6.68 if eu else None)
|
||||
w=max([float(v.get('w') or 0) for v in p['vars']] or [0])
|
||||
key=p['oid'] or p['id']
|
||||
tier = 'extra_small_1-500g' if 0<w<=500 else ('budget_500-2500g' if 500<w<=2500 else ('small_1-2000g' if 2000<w else ('UNKNOWN_w0' if w==0 else 'other')))
|
||||
stats[('tier',tier)]+=1
|
||||
if b is None: stats[('nocost',tier)]+=1
|
||||
rec={'oid':p['oid'],'id':p['id'],'name':p['name'],'cost_cny':b,'weight_g':w,'tier':tier,
|
||||
'freight_cny':round(F(w),2),'total_cost_cny':round(b+F(w),2) if b is not None else None,
|
||||
'price_cny':{f'{int(r*100)}%':round(price(b,w,r),2) for r in MARGINS} if b is not None else None,
|
||||
'price_rub':{f'{int(r*100)}%':round(price(b,w,r)*RATE,1) for r in MARGINS} if b is not None else None}
|
||||
out.setdefault(key,[]).append(rec)
|
||||
json.dump(out,open('/tmp/oz_price_500.json','w'),ensure_ascii=False,indent=1)
|
||||
print('=== 分层统计 ===')
|
||||
for k,v in sorted(stats.items()):
|
||||
if k[0]=='tier': print(f' {k[1]:<20} {v}')
|
||||
print('=== 缺成本 ===')
|
||||
for k,v in sorted(stats.items()):
|
||||
if k[0]=='nocost': print(f' {k[1]:<20} {v}')
|
||||
es=[r for v in out.values() for r in v if r['tier']=='extra_small_1-500g']
|
||||
print(f'\n≤500g 档: {len(es)} 条;其中成本齐全 {sum(1 for r in es if r["cost_cny"] is not None)}')
|
||||
print('\n=== ≤500g 定价阶梯(含运费, 单位¥ / ₽) ===')
|
||||
for b in (5,10,20,30):
|
||||
print(f'\n 进货价 {b}元')
|
||||
print(' 重量 | '+' | '.join(f'{int(r*100):>3}%' for r in MARGINS))
|
||||
for g in (50,100,200,300,400,500):
|
||||
cells=' | '.join(f'{price(b,g,r):5.1f}' for r in MARGINS)
|
||||
print(f' {g:>4}g | {cells} → 25%档 {price(b,g,0.25)*RATE:6.0f}₽')
|
||||
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"708420302844": {
|
||||
"price": "36.45",
|
||||
"readback": "API 回读: {\"n\":12,\"prices\":[43.74,43.74,43.74],\"sales\":[36.45,36.45,36.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
36.45,
|
||||
36.45,
|
||||
36.45
|
||||
]
|
||||
},
|
||||
"547183997437": {
|
||||
"price": "36.45",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[43.74],\"sales\":[36.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
36.45
|
||||
]
|
||||
},
|
||||
"657617635303": {
|
||||
"price": "36.45",
|
||||
"readback": "API 回读: {\"n\":5,\"prices\":[43.74,43.74,43.74],\"sales\":[36.45,36.45,36.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
36.45,
|
||||
36.45,
|
||||
36.45
|
||||
]
|
||||
},
|
||||
"641673262387": {
|
||||
"price": "36.45",
|
||||
"readback": "API 回读: {\"n\":32,\"prices\":[43.74,43.74,43.74],\"sales\":[36.45,36.45,36.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
36.45,
|
||||
36.45,
|
||||
36.45
|
||||
]
|
||||
},
|
||||
"764423265986": {
|
||||
"price": "34.0",
|
||||
"readback": "{\"n\":2,\"prices\":[2.81,2.81],\"sales\":[2.81,2.81]}",
|
||||
"save": "[]",
|
||||
"ok": false,
|
||||
"sales": [
|
||||
2.81,
|
||||
2.81
|
||||
]
|
||||
},
|
||||
"735147938123": {
|
||||
"price": "34.0",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[40.8],\"sales\":[34]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
34.0
|
||||
]
|
||||
},
|
||||
"548459586741": {
|
||||
"price": "34.0",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[40.8],\"sales\":[34]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
34.0
|
||||
]
|
||||
},
|
||||
"610164857063": {
|
||||
"price": "34.0",
|
||||
"readback": "API 回读: {\"n\":10,\"prices\":[40.8,40.8,40.8],\"sales\":[34,34,34]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
34.0,
|
||||
34.0,
|
||||
34.0
|
||||
]
|
||||
},
|
||||
"676236596821": {
|
||||
"price": "34.0",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[40.8],\"sales\":[34]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true,
|
||||
"sales": [
|
||||
34.0
|
||||
]
|
||||
},
|
||||
"866537106657": {
|
||||
"price": "34.0",
|
||||
"readback": "{\"n\":9,\"prices\":[1.14,1.27,1.27],\"sales\":[1.14,1.27,1.27]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"591994902720": {
|
||||
"price": "31.91",
|
||||
"readback": "API 回读: {\"n\":4,\"prices\":[38.3,38.3,38.3],\"sales\":[31.91,31.91,31.91]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"836512561982": {
|
||||
"price": "30.2",
|
||||
"readback": "API 回读: {\"n\":4,\"prices\":[36.24,36.24,36.24],\"sales\":[30.2,30.2,30.2]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"714492799386": {
|
||||
"price": "28.36",
|
||||
"readback": "API 回读: {\"n\":7,\"prices\":[34.04,34.04,34.04],\"sales\":[28.36,28.36,28.36]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"902440558099": {
|
||||
"price": "27.27",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[32.73],\"sales\":[27.27]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"823838532886": {
|
||||
"price": "27.0",
|
||||
"readback": "API 回读: {\"n\":6,\"prices\":[32.4,32.4,32.4],\"sales\":[27,27,27]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"661190598139": {
|
||||
"price": "26.64",
|
||||
"readback": "API 回读: {\"n\":28,\"prices\":[31.97,31.97,31.97],\"sales\":[26.64,26.64,26.64]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"685456757807": {
|
||||
"price": "26.64",
|
||||
"readback": "API 回读: {\"n\":26,\"prices\":[31.97,31.97,31.97],\"sales\":[26.64,26.64,26.64]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"893592264148": {
|
||||
"price": "26.45",
|
||||
"readback": "API 回读: {\"n\":6,\"prices\":[31.74,31.74,31.74],\"sales\":[26.45,26.45,26.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"639409307567": {
|
||||
"price": "25.82",
|
||||
"readback": "{\"n\":2,\"prices\":[5.48,5.48],\"sales\":[5.48,5.48]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"896469995553": {
|
||||
"price": "25.18",
|
||||
"readback": "API 回读: {\"n\":2,\"prices\":[30.22,30.22],\"sales\":[25.18,25.18]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"719391062058": {
|
||||
"price": "23.71",
|
||||
"readback": "API 回读: {\"n\":6,\"prices\":[28.46,28.46,28.46],\"sales\":[23.71,23.71,23.71]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"698782391248": {
|
||||
"price": "23.6",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[28.32],\"sales\":[23.6]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"823560564174": {
|
||||
"price": "23.11",
|
||||
"readback": "API 回读: {\"n\":9,\"prices\":[27.74,27.74,27.74],\"sales\":[23.11,23.11,23.11]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"857781294719": {
|
||||
"price": "22.73",
|
||||
"readback": "API 回读: {\"n\":3,\"prices\":[27.28,27.28,27.28],\"sales\":[22.73,22.73,22.73]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"707826905566": {
|
||||
"price": "22.55",
|
||||
"readback": "API 回读: {\"n\":4,\"prices\":[27.06,27.06,27.06],\"sales\":[22.55,22.55,22.55]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"1027332215847": {
|
||||
"price": "22.55",
|
||||
"readback": "API 回读: {\"n\":10,\"prices\":[27.06,27.06,27.06],\"sales\":[22.55,22.55,22.55]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"607330049419": {
|
||||
"price": "22.24",
|
||||
"readback": "API 回读: {\"n\":9,\"prices\":[26.69,26.69,26.69],\"sales\":[22.24,22.24,22.24]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"944470567338": {
|
||||
"price": "22.18",
|
||||
"readback": "API 回读: {\"n\":2,\"prices\":[26.62,26.62],\"sales\":[22.18,22.18]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"787261097583": {
|
||||
"price": "22.18",
|
||||
"readback": "{\"n\":12,\"prices\":[3.48,3.48,3.48],\"sales\":[3.48,3.48,3.48]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"694637537727": {
|
||||
"price": "22.18",
|
||||
"readback": "API 回读: {\"n\":5,\"prices\":[26.62,26.62,26.62],\"sales\":[22.18,22.18,22.18]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"621300323670": {
|
||||
"price": "21.89",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[26.27],\"sales\":[21.89]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"682195499823": {
|
||||
"price": "21.35",
|
||||
"readback": "{\"n\":3,\"prices\":[3.01,3.01,3.01],\"sales\":[3.01,3.01,3.01]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"623011262210": {
|
||||
"price": "21.27",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[25.53],\"sales\":[21.27]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"833753630925": {
|
||||
"price": "21.27",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[25.53],\"sales\":[21.27]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"854003999648": {
|
||||
"price": "21.15",
|
||||
"readback": "API 回读: {\"n\":27,\"prices\":[25.38,25.38,25.38],\"sales\":[21.15,21.15,21.15]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"703822717997": {
|
||||
"price": "20.77",
|
||||
"readback": "API 回读: {\"n\":2,\"prices\":[24.93,24.93],\"sales\":[20.77,20.77]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"863524134410": {
|
||||
"price": "20.73",
|
||||
"readback": "API 回读: {\"n\":1,\"prices\":[24.88],\"sales\":[20.73]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"853414558425": {
|
||||
"price": "20.45",
|
||||
"readback": "API 回读: {\"n\":22,\"prices\":[24.54,24.54,24.54],\"sales\":[20.45,20.45,20.45]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"750271307455": {
|
||||
"price": "20.18",
|
||||
"readback": "API 回读: {\"n\":3,\"prices\":[24.22,24.22,24.22],\"sales\":[20.18,20.18,20.18]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"726442972676": {
|
||||
"price": "20.09",
|
||||
"readback": "API 回读: {\"n\":24,\"prices\":[24.11,24.11,24.11],\"sales\":[20.09,20.09,20.09]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"953869908708": {
|
||||
"price": "19.91",
|
||||
"readback": "API 回读: {\"n\":21,\"prices\":[23.9,23.9,23.9],\"sales\":[19.91,19.91,19.91]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"775057912118": {
|
||||
"price": "19.84",
|
||||
"readback": "API 回读: {\"n\":26,\"prices\":[23.81,23.81,23.81],\"sales\":[19.84,19.84,19.84]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"863384503032": {
|
||||
"price": "19.82",
|
||||
"readback": "{\"n\":1,\"prices\":[2.21],\"sales\":[2.21]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"739147684759": {
|
||||
"price": "19.64",
|
||||
"readback": "{\"n\":3,\"prices\":[2.07,2.07,2.07],\"sales\":[2.07,2.07,2.07]}",
|
||||
"save": "[]",
|
||||
"ok": false
|
||||
},
|
||||
"774494945453": {
|
||||
"price": "19.45",
|
||||
"readback": "{\"n\":1,\"prices\":[23.34],\"sales\":[19.45]}",
|
||||
"save": "[\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"997450831819": {
|
||||
"price": "18.07",
|
||||
"readback": "{\"n\":1,\"prices\":[21.69],\"sales\":[18.07]}",
|
||||
"save": "[\"POST\"]",
|
||||
"ok": true
|
||||
},
|
||||
"601964534137": {
|
||||
"price": "17.91",
|
||||
"readback": "API 回读: {\"n\":5,\"prices\":[21.5,21.5,21.5],\"sales\":[17.91,17.91,17.91]}",
|
||||
"save": "保存请求: [\"POST\"]",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"oid": "866537106657",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
},
|
||||
{
|
||||
"oid": "639409307567",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
},
|
||||
{
|
||||
"oid": "787261097583",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
},
|
||||
{
|
||||
"oid": "682195499823",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
},
|
||||
{
|
||||
"oid": "863384503032",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
},
|
||||
{
|
||||
"oid": "739147684759",
|
||||
"red": [
|
||||
"变种图片",
|
||||
"请视频上传7天内完成产品发布",
|
||||
"请填写SKU",
|
||||
"请填写售价",
|
||||
"包装尺寸请输入1~13500之间的数值!",
|
||||
"重量请输入1~1000000之间的数值!",
|
||||
"变种图不能为空"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,331 @@
|
||||
[
|
||||
{
|
||||
"oid": "708420302844",
|
||||
"pid": "184807701684297335",
|
||||
"price": 36.45,
|
||||
"w": 130,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "547183997437",
|
||||
"pid": "184807701684297331",
|
||||
"price": 36.45,
|
||||
"w": 130,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "657617635303",
|
||||
"pid": "184807701684297329",
|
||||
"price": 36.45,
|
||||
"w": 130,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "641673262387",
|
||||
"pid": "184807701684297327",
|
||||
"price": 36.45,
|
||||
"w": 130,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "764423265986",
|
||||
"pid": "184807701684297353",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "735147938123",
|
||||
"pid": "184807701684297347",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "548459586741",
|
||||
"pid": "184807701684297343",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "610164857063",
|
||||
"pid": "184807701684297341",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "676236596821",
|
||||
"pid": "184807701684297339",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "866537106657",
|
||||
"pid": "184807701684297333",
|
||||
"price": 34.0,
|
||||
"w": 100,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "591994902720",
|
||||
"pid": "184807701684307653",
|
||||
"price": 31.91,
|
||||
"w": 130,
|
||||
"cost": 7.5
|
||||
},
|
||||
{
|
||||
"oid": "836512561982",
|
||||
"pid": "184807701684307649",
|
||||
"price": 30.2,
|
||||
"w": 130,
|
||||
"cost": 6.56
|
||||
},
|
||||
{
|
||||
"oid": "714492799386",
|
||||
"pid": "184807701684307651",
|
||||
"price": 28.36,
|
||||
"w": 100,
|
||||
"cost": 6.9
|
||||
},
|
||||
{
|
||||
"oid": "902440558099",
|
||||
"pid": "184807701684308549",
|
||||
"price": 27.27,
|
||||
"w": 130,
|
||||
"cost": 4.95
|
||||
},
|
||||
{
|
||||
"oid": "823838532886",
|
||||
"pid": "184807701684308547",
|
||||
"price": 27.0,
|
||||
"w": 130,
|
||||
"cost": 4.8
|
||||
},
|
||||
{
|
||||
"oid": "661190598139",
|
||||
"pid": "184807701684297355",
|
||||
"price": 26.64,
|
||||
"w": 10,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "685456757807",
|
||||
"pid": "184807701684297349",
|
||||
"price": 26.64,
|
||||
"w": 10,
|
||||
"cost": 10.0
|
||||
},
|
||||
{
|
||||
"oid": "893592264148",
|
||||
"pid": "184807701684308575",
|
||||
"price": 26.45,
|
||||
"w": 130,
|
||||
"cost": 4.5
|
||||
},
|
||||
{
|
||||
"oid": "639409307567",
|
||||
"pid": "184807701684308551",
|
||||
"price": 25.82,
|
||||
"w": 100,
|
||||
"cost": 5.5
|
||||
},
|
||||
{
|
||||
"oid": "896469995553",
|
||||
"pid": "184807701684308597",
|
||||
"price": 25.18,
|
||||
"w": 130,
|
||||
"cost": 3.8
|
||||
},
|
||||
{
|
||||
"oid": "719391062058",
|
||||
"pid": "184807701684308589",
|
||||
"price": 23.71,
|
||||
"w": 130,
|
||||
"cost": 2.99
|
||||
},
|
||||
{
|
||||
"oid": "698782391248",
|
||||
"pid": "184807701684308545",
|
||||
"price": 23.6,
|
||||
"w": 100,
|
||||
"cost": 4.28
|
||||
},
|
||||
{
|
||||
"oid": "823560564174",
|
||||
"pid": "184807701684308537",
|
||||
"price": 23.11,
|
||||
"w": 130,
|
||||
"cost": 2.66
|
||||
},
|
||||
{
|
||||
"oid": "857781294719",
|
||||
"pid": "184807701684308587",
|
||||
"price": 22.73,
|
||||
"w": 130,
|
||||
"cost": 2.45
|
||||
},
|
||||
{
|
||||
"oid": "707826905566",
|
||||
"pid": "184807701684308543",
|
||||
"price": 22.55,
|
||||
"w": 100,
|
||||
"cost": 3.7
|
||||
},
|
||||
{
|
||||
"oid": "1027332215847",
|
||||
"pid": "184807701684308541",
|
||||
"price": 22.55,
|
||||
"w": 100,
|
||||
"cost": 3.7
|
||||
},
|
||||
{
|
||||
"oid": "607330049419",
|
||||
"pid": "184807701684308573",
|
||||
"price": 22.24,
|
||||
"w": 130,
|
||||
"cost": 2.18
|
||||
},
|
||||
{
|
||||
"oid": "944470567338",
|
||||
"pid": "184807701684308595",
|
||||
"price": 22.18,
|
||||
"w": 100,
|
||||
"cost": 3.5
|
||||
},
|
||||
{
|
||||
"oid": "787261097583",
|
||||
"pid": "184807701684308593",
|
||||
"price": 22.18,
|
||||
"w": 100,
|
||||
"cost": 3.5
|
||||
},
|
||||
{
|
||||
"oid": "694637537727",
|
||||
"pid": "184807701684307645",
|
||||
"price": 22.18,
|
||||
"w": 130,
|
||||
"cost": 2.15
|
||||
},
|
||||
{
|
||||
"oid": "621300323670",
|
||||
"pid": "184807701684308533",
|
||||
"price": 21.89,
|
||||
"w": 130,
|
||||
"cost": 1.99
|
||||
},
|
||||
{
|
||||
"oid": "682195499823",
|
||||
"pid": "184807701684308539",
|
||||
"price": 21.35,
|
||||
"w": 100,
|
||||
"cost": 3.04
|
||||
},
|
||||
{
|
||||
"oid": "623011262210",
|
||||
"pid": "184807701684307647",
|
||||
"price": 21.27,
|
||||
"w": 100,
|
||||
"cost": 3.0
|
||||
},
|
||||
{
|
||||
"oid": "833753630925",
|
||||
"pid": "184807701684308591",
|
||||
"price": 21.27,
|
||||
"w": 100,
|
||||
"cost": 3.0
|
||||
},
|
||||
{
|
||||
"oid": "854003999648",
|
||||
"pid": "184807701684308571",
|
||||
"price": 21.15,
|
||||
"w": 130,
|
||||
"cost": 1.58
|
||||
},
|
||||
{
|
||||
"oid": "703822717997",
|
||||
"pid": "184807701684307641",
|
||||
"price": 20.77,
|
||||
"w": 130,
|
||||
"cost": 1.375
|
||||
},
|
||||
{
|
||||
"oid": "863524134410",
|
||||
"pid": "184807701684307643",
|
||||
"price": 20.73,
|
||||
"w": 100,
|
||||
"cost": 2.7
|
||||
},
|
||||
{
|
||||
"oid": "853414558425",
|
||||
"pid": "184807701684307655",
|
||||
"price": 20.45,
|
||||
"w": 130,
|
||||
"cost": 1.2
|
||||
},
|
||||
{
|
||||
"oid": "750271307455",
|
||||
"pid": "184807701684308581",
|
||||
"price": 20.18,
|
||||
"w": 130,
|
||||
"cost": 1.05
|
||||
},
|
||||
{
|
||||
"oid": "726442972676",
|
||||
"pid": "184807701684308579",
|
||||
"price": 20.09,
|
||||
"w": 130,
|
||||
"cost": 1.0
|
||||
},
|
||||
{
|
||||
"oid": "953869908708",
|
||||
"pid": "184807701684307639",
|
||||
"price": 19.91,
|
||||
"w": 130,
|
||||
"cost": 0.9
|
||||
},
|
||||
{
|
||||
"oid": "775057912118",
|
||||
"pid": "184807701684308577",
|
||||
"price": 19.84,
|
||||
"w": 130,
|
||||
"cost": 0.86
|
||||
},
|
||||
{
|
||||
"oid": "863384503032",
|
||||
"pid": "184807701684308585",
|
||||
"price": 19.82,
|
||||
"w": 100,
|
||||
"cost": 2.2
|
||||
},
|
||||
{
|
||||
"oid": "739147684759",
|
||||
"pid": "184807701684308535",
|
||||
"price": 19.64,
|
||||
"w": 100,
|
||||
"cost": 2.1
|
||||
},
|
||||
{
|
||||
"oid": "774494945453",
|
||||
"pid": "184807701684308583",
|
||||
"price": 19.45,
|
||||
"w": 100,
|
||||
"cost": 2.0
|
||||
},
|
||||
{
|
||||
"oid": "997450831819",
|
||||
"pid": "184807701684307657",
|
||||
"price": 18.07,
|
||||
"w": 100,
|
||||
"cost": 1.24
|
||||
},
|
||||
{
|
||||
"oid": "601964534137",
|
||||
"pid": "184807701684308531",
|
||||
"price": 17.91,
|
||||
"w": 100,
|
||||
"cost": 1.15
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,100 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
|
||||
/* ── Ozon 改价(≤500g 档,售价 = 合计成本 ÷ (1 − 20% − 利润率))──
|
||||
node ozprice_set.mjs <pid> <priceCNY> [dry]
|
||||
dry = 只填不保存 */
|
||||
|
||||
const PID = process.argv[2];
|
||||
const PRICE = process.argv[3];
|
||||
const DRY = process.argv[4] === 'dry';
|
||||
if (!PID || !PRICE) { console.log('用法: node ozprice_set.mjs <pid> <priceCNY> [dry]'); process.exit(1); }
|
||||
|
||||
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' && x.url.includes('ozonProduct/edit'))) { await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {}); k++; }
|
||||
if (k) { log(`清理残留编辑页 ${k} 个`); await wait(1100); }
|
||||
|
||||
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/ozonProduct/edit?id=${PID}` });
|
||||
await wait(32000);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const x = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
if(!ms.length)return'n'; ms.forEach(m=>{const cb=m.querySelector('.ant-modal-close'); if(cb&&cb.offsetParent!==null)cb.click();}); return 'c';})()`);
|
||||
if (x === 'n') break; await wait(1000);
|
||||
}
|
||||
await wait(2000);
|
||||
|
||||
// 1. 找 SKU 表 + 价格列
|
||||
const map = await c.evalJS(`(()=>{
|
||||
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null);
|
||||
for(let ti=0;ti<tbls.length;ti++){
|
||||
const ths=[...tbls[ti].querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
if(!ths.some(h=>/价格|售价|₽|RUB/i.test(h))) continue;
|
||||
const rows=[...tbls[ti].querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length);
|
||||
if(!rows.length) continue;
|
||||
return JSON.stringify({ti, ths, rowCount: rows.length,
|
||||
vals: rows.slice(0,3).map(tr=>[...tr.querySelectorAll('input')].map(i=>(i.value||'').slice(0,12)))});
|
||||
}
|
||||
return 'no-table';})()`);
|
||||
log('SKU 表:', SAN(String(map)).slice(0, 500));
|
||||
if (map === 'no-table') { log('❌ 找不到价格表'); process.exit(1); }
|
||||
const M = JSON.parse(String(map));
|
||||
|
||||
const IDX_PRICE = M.ths.findIndex(h => /价格|售价|₽|RUB/i.test(h) && !/划线|原价|市场价|最低/i.test(h));
|
||||
const IDX_OLD = M.ths.findIndex(h => /划线|原价|市场价/i.test(h));
|
||||
const OLD_PRICE = (Math.ceil(Number(PRICE) * 1.2 * 100) / 100).toFixed(2);
|
||||
log('价格列:', IDX_PRICE, JSON.stringify(M.ths[IDX_PRICE]), '| 原价列:', IDX_OLD, IDX_OLD >= 0 ? JSON.stringify(M.ths[IDX_OLD]) : '(无)', '→ 原价设为', OLD_PRICE);
|
||||
|
||||
// 2. 填价
|
||||
const r = await c.evalJS(`(()=>{
|
||||
const setV=(el,v)=>{ Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,String(v));
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); };
|
||||
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null);
|
||||
const tbl=tbls[${M.ti}]; if(!tbl)return'no-table';
|
||||
const rows=[...tbl.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length);
|
||||
let n=0;
|
||||
rows.forEach(tr=>{ const tds=[...tr.querySelectorAll('td')]; const td=tds[${IDX_PRICE}]; if(!td)return;
|
||||
const inp=td.querySelector('input'); if(!inp)return; setV(inp,${JSON.stringify(PRICE)}); n++;
|
||||
const od=tds[${IDX_OLD}]; if(od){ const oi=od.querySelector('input'); if(oi) setV(oi, ${JSON.stringify(OLD_PRICE)}); } });
|
||||
return 'set:'+n+'/'+rows.length;})()`);
|
||||
log('填价:', r);
|
||||
await wait(2500);
|
||||
|
||||
// 3. 回读
|
||||
const back = await c.evalJS(`(()=>{const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null);
|
||||
const tbl=tbls[${M.ti}]; const rows=[...tbl.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length);
|
||||
return JSON.stringify(rows.slice(0,5).map(tr=>[...tr.querySelectorAll('td')].map(td=>{const i=td.querySelector('input'); return i?i.value:'';})));})()`);
|
||||
log('回读:', SAN(String(back)).slice(0, 300));
|
||||
|
||||
if (DRY) { log('dry-run,不保存'); process.exit(0); }
|
||||
|
||||
// 4. 保存(优先「保存并移入待发布」,与 fixone3 一致)
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(700);
|
||||
const reqs = [];
|
||||
c.on(m => { if (m.method === 'Network.requestWillBeSent') { const u = m.params.request.url; if (/ozonProduct\/add\.json/.test(u)) reqs.push(m.params.request.method); } });
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const sv = await c.evalJS(`(()=>{const xs=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null);
|
||||
let x=xs.find(e=>/保存并移入待发布/.test((e.innerText||'').trim())); let tag='保存并移入待发布';
|
||||
if(!x){ x=xs.find(e=>/^保\\s*存$/.test((e.innerText||'').trim())); tag='保存'; }
|
||||
if(!x) return 'nf';
|
||||
for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return tag;})()`);
|
||||
log('保存按钮:', sv);
|
||||
await wait(13000);
|
||||
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
|
||||
await wait(5000);
|
||||
log('保存请求:', JSON.stringify(reqs));
|
||||
|
||||
// 5. API 回读(权威)
|
||||
const back2 = await c.evalJS(`(async()=>{const rr=await fetch('/api/ozonProduct/add.json?id=${PID}&_t='+Date.now(),{headers:{'X-Requested-With':'XMLHttpRequest','Cache-Control':'no-cache'}});
|
||||
const tx=await rr.text(); if(!tx.startsWith('{'))return'(err)';
|
||||
const p=(JSON.parse(tx).data||{}).product||{}; const vl=p.variantList||[];
|
||||
return JSON.stringify({n:vl.length, prices:vl.slice(0,3).map(v=>v.price), sales:vl.slice(0,3).map(v=>v.salePrice)});})()`, true);
|
||||
log('★ API 回读:', SAN(String(back2)).slice(0, 260));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,25 @@
|
||||
# SHEIN 建品配方(已跑通第一条)
|
||||
|
||||
## 完整流程
|
||||
1. `https://www.dianxiaomi.com/web/sheinProduct/add`
|
||||
2. **选店铺**(按品类分店,见 shein_route.json)
|
||||
3. **选类目**:点「选择分类」→ 弹窗 `input[placeholder="搜索分类名称"]` → 搜索 → 点 `.search-result-item`
|
||||
4. **标题**:`sheinAddProductStore.formState.productName`
|
||||
5. **★ 加第二个规格**:点 `.theme-select-box` 里的 `尺寸(Size)`
|
||||
—— 这是之前一直卡住的关键!类目要求**颜色+尺寸两个规格**,只选颜色不行
|
||||
6. **选规格值**:两个 `.sku-info-box` 里各点一个 `.sku-option`(要点里面的 `label.d-checkbox`)
|
||||
7. **填 SKU 行**:`sheinSkuDataStore.formState.skuDataList[]`
|
||||
`skcSupplierCode / skcRetailPrice / supplyPrice / stockVariety / weight / length / width / height`
|
||||
⚠️ **加第二个规格会重建 SKU 行,之前的填充会丢** —— 必须在规格选完之后再填
|
||||
8. **尺码表**:点「编辑尺码表」→ 弹窗内 6 个 `placeholder="请输入"` 的 input → 点「确定」
|
||||
9. **图片**:`sheinImgStore.formState.mainImgData.multipleSpuImgs` → **数组**
|
||||
10. **保存** → `POST /api/sheinProduct/add.json`
|
||||
|
||||
## 关键教训
|
||||
- 报错 `有必选规格未选择` 的真实含义是:**类目定义的规格没选全**,不是主规格没值
|
||||
- 加规格会重置 SKU 行 → **顺序不能错**:先规格、后 SKU、再尺码表
|
||||
- 所有报错都会被 `MYJ.message.error` 吞掉,必须挂钩子才看得到
|
||||
|
||||
## 待批量
|
||||
- 40 条按品类分到 4 家店(美妆18 / 文具13 / 箱包8 / 内睡袜子1)
|
||||
- 需要把上述流程写成 `shbatch.mjs`,逐条跑
|
||||
@@ -0,0 +1,22 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const r = await c.evalJS(`(()=>{const box=[...document.querySelectorAll('.theme-select-box')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!box)return'nf';
|
||||
const chips=[...box.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/^\\s*尺寸\\(Size\\)\\s*$/.test((e.innerText||'').trim()));
|
||||
if(!chips.length)return'no-chip';
|
||||
const el=chips[chips.length-1];
|
||||
for(const ev of ['mousedown','mouseup','click']) el.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'clicked';})()`);
|
||||
console.log('点尺寸:', String(r));
|
||||
await wait(4000);
|
||||
const o = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
const boxes=[...document.querySelectorAll('.sku-info-box')].filter(e=>e.offsetParent!==null).map((b,i)=>({
|
||||
i, txt:(b.innerText||'').replace(/\\s+/g,' ').slice(0,90), opts:b.querySelectorAll('.sku-option').length,
|
||||
sels:[...b.querySelectorAll('.ant-select')].map(s=>(s.innerText||'').replace(/\\s+/g,' ').slice(0,16)),
|
||||
ins:[...b.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.type!=='checkbox').map(i=>({ph:i.placeholder||'',cls:String(i.className||'').slice(0,40)})).slice(0,4)}));
|
||||
const btns=[...document.querySelectorAll('button')].filter(b=>b.offsetParent!==null&&/添加/.test(b.innerText||'')).map(b=>({t:(b.innerText||'').trim(),dis:b.disabled}));
|
||||
return JSON.stringify({attrs:(a.formState.skuAttrs||[]).map(x=>({n:x.name, opts:(x.options||[]).length})), boxes, btns},null,1);})()`);
|
||||
console.log(String(o).slice(0,1400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,21 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const btns = await c.evalJS(`(()=>[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).map((b,i)=>{
|
||||
const r=b.getBoundingClientRect(); return {i, t:(b.innerText||'').replace(/\\s+/g,' ').trim().slice(0,14), y:Math.round(r.y), dis:b.disabled};}).filter(x=>x.t))()`);
|
||||
console.log('按钮:', JSON.stringify(btns).slice(0,700));
|
||||
// 点“保存”并立刻轮询任何反馈
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x){ for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); } return 1;})()`);
|
||||
const seen=[];
|
||||
for(let i=0;i<18;i++){ await wait(700);
|
||||
const m=await c.evalJS(`(()=>{const a=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal-confirm,.ant-modal')].filter(e=>e.offsetParent!==null)
|
||||
.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean); return JSON.stringify(a.slice(0,3));})()`);
|
||||
JSON.parse(String(m)).forEach(x=>seen.push(x)); }
|
||||
console.log('反馈:', [...new Set(seen)].slice(0,4).join(' || ').slice(0,400));
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,22 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const o = 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||'');});
|
||||
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,120), html:it.innerHTML.slice(0,600)});})()`);
|
||||
console.log('分类控件:', String(o).slice(0,900));
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const el=it.querySelector('.ant-select-selector')||it.querySelector('input');
|
||||
if(!el)return'[]'; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if(pos.length){ for(const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1}); await wait(4000); }
|
||||
const o2 = await c.evalJS(`(()=>{
|
||||
const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).map(m=>({t:(m.querySelector('.ant-modal-title')||{}).innerText||'',
|
||||
inputs:[...m.querySelectorAll('input')].map(i=>i.placeholder||i.type).slice(0,5),
|
||||
btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,6),
|
||||
txt:(m.innerText||'').replace(/\\s+/g,' ').slice(0,300)}));
|
||||
const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).map(d=>(d.innerText||'').replace(/\\s+/g,' ').slice(0,260));
|
||||
return JSON.stringify({modals:ms, dropdowns:dd},null,1);})()`);
|
||||
console.log(String(o2).slice(0,1400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,26 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const pick = 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||'');});
|
||||
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
|
||||
const opts=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
|
||||
const hit=opts.find(e=>/Black\\(/.test(e.innerText||''))||opts[0];
|
||||
if(!hit)return'no-opt';
|
||||
const lab=hit.querySelector('label'); const inp=hit.querySelector('input[type=checkbox]');
|
||||
if(lab) for(const ev of ['mousedown','mouseup','click']) lab.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
if(inp && !inp.checked) inp.click();
|
||||
return 'clicked:'+(hit.innerText||'').trim()+' checked='+(inp?inp.checked:'?');})()`);
|
||||
console.log('点色:', pick);
|
||||
await wait(7000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,15),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
return JSON.stringify({tbls, n:(d.formState.skuDataList||[]).length, row0:(d.formState.skuDataList||[])[0],
|
||||
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, vals:(x.attributeValues||[]).map(v=>v.attribute_value||v)}))},null,1);})()`);
|
||||
console.log(String(o).slice(0,1800));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,34 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
// 打开主规格颜色下拉
|
||||
const pos = 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 t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(3000);
|
||||
const opts = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
|
||||
return JSON.stringify({n:its.length, first:its.slice(0,6).map(e=>(e.innerText||'').trim())});})()`);
|
||||
console.log('颜色选项:', String(opts).slice(0,400));
|
||||
// 选 Multicolor / Black(找不到就第一个)
|
||||
const pick = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
|
||||
let hit=its.find(o=>/Multicolor|Multi-color/i.test(o.innerText||'')) || its.find(o=>/^\\s*Black\\(/.test((o.innerText||'').trim())) || its[0];
|
||||
if(!hit)return'no-opt';
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked:'+(hit.innerText||'').trim();})()`);
|
||||
console.log('选择:', pick);
|
||||
await wait(6000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,14),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
return JSON.stringify({tbls, skuDataList:(d.formState.skuDataList||[]).slice(0,2), n:(d.formState.skuDataList||[]).length,
|
||||
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, n:(x.attributeValues||x._chosen||[]).length}))},null,1);})()`);
|
||||
console.log(String(o).slice(0,1800));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,62 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const SHOP = process.argv[2] || '③shein-易捷天宇-美妆';
|
||||
const CATKW = process.argv[3] || '发夹';
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
|
||||
await wait(1000);
|
||||
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/sheinProduct/add'});
|
||||
await wait(21000);
|
||||
// 店铺
|
||||
const pos = 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||document).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:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(2500);
|
||||
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
|
||||
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(8000);
|
||||
// 开分类下拉并选目标
|
||||
const cp = 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:cp[0],y:cp[1],button:'left',clickCount:1});
|
||||
await wait(3500);
|
||||
const pk = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
|
||||
const hit=its.find(o=>new RegExp('^\\\\s*'+${JSON.stringify(CATKW)}).test((o.innerText||'').trim()));
|
||||
if(!hit)return'no-hit:'+its.slice(0,5).map(o=>o.innerText.trim()).join(',');
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked:'+(hit.innerText||'').trim();})()`);
|
||||
console.log('类目:', pk);
|
||||
await wait(12000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,24).padEnd(25)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,22);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const cut=(x,d=0)=>(d>3?'…':(Array.isArray(x)?(x.length?'['+x.length+'] '+JSON.stringify(x[0]).slice(0,220):'[]'):(x&&typeof x==='object'?Object.fromEntries(Object.entries(x).map(([k,v])=>[k,cut(v,d+1)])):x)));
|
||||
const dump={};
|
||||
for(const n of ['sheinAddProductStore','sheinSkuDataStore','sheinSkuAttrStore','sheinAddBasicStore','sheinImgStore'])
|
||||
{ try{dump[n]=cut(JSON.parse(JSON.stringify(p._s.get(n).$state)));}catch(e){dump[n]='ERR';} }
|
||||
return JSON.stringify({items, tables, dump},null,1);})()`);
|
||||
fs.writeFileSync('/tmp/shein_deep.json', String(o));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('=== form ==='); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('=== tables ==='); console.log(JSON.stringify(j.tables));
|
||||
console.log('=== stores ===');
|
||||
for(const [n,v] of Object.entries(j.dump)) console.log(' ##',n,JSON.stringify(v).slice(0,520));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,189 @@
|
||||
{
|
||||
"items": [
|
||||
"* 店铺账号 [select] ③shein-易捷天宇-美妆",
|
||||
"* 产品分类 [select] 发夹(Hair Clips)",
|
||||
" 产品属性 [select] No(否)",
|
||||
" 品牌 [input] ",
|
||||
" 是否含木质材料 (Whether it cont [select] No(否)",
|
||||
" 敏感类别 (Hazard Category) [input] ",
|
||||
" 关税种类 (Tariff Type) [select] Hairpin(发夹)",
|
||||
" 材质 (Material) [select] Zinc Alloy(锌合金)",
|
||||
" 风格 (Style) [input] ",
|
||||
" 数量 (Quantity) [select] piece(s)(件)",
|
||||
" 季节 (Season) [input] ",
|
||||
" 细化图案 (Specific Pattern) [input] ",
|
||||
" 类型 (Type) [input] ",
|
||||
" 特征 (Features) [input] ",
|
||||
" 证书编号/检测机构 (Certificate N [input] ",
|
||||
" 是否有型号 (Is there a Model [input] ",
|
||||
" 电池使用方式 (Battery Usage Ty [input] ",
|
||||
" 版型 (Version) [input] ",
|
||||
" 包装类型 (type of packaging) [input] ",
|
||||
" 成分 (Composition) [input] ",
|
||||
" 次要材质 (Other Material) [input] ",
|
||||
" 发圈类型 (hair tie type) [input] ",
|
||||
" 元素 (Element) [select] None(无)",
|
||||
" 制作工艺 (Craftsmanship Type [input] ",
|
||||
" 企划风格 (Design Style) [input] ",
|
||||
" 产品型号 (Product Model) [input] ",
|
||||
" 镀贵金属类型 (Plating) [input] ",
|
||||
" 镶嵌类型 (Inlay type) [input] ",
|
||||
" 织造方式 (Fabric Weave Type) [input] ",
|
||||
" NCM-BR专属 (Ncm-Br Exclusi [input] ",
|
||||
" 系列 (Series) [input] ",
|
||||
" 是否多套件 [-] ",
|
||||
" 店小秘分类 [-] ",
|
||||
" 来源URL [input] ",
|
||||
" [input] ",
|
||||
"* 产品标题: [input] ",
|
||||
" 商品IP [input] ",
|
||||
" 图片模式 [input] 1",
|
||||
" 产品视频 [select] SHEIN国际站",
|
||||
"* 主规格 [select] 颜色(Color)",
|
||||
" 其他规格 [-] ",
|
||||
"* 尺码表 [-] "
|
||||
],
|
||||
"tables": [],
|
||||
"dump": {
|
||||
"sheinAddProductStore": {
|
||||
"formState": {
|
||||
"imageType": 0,
|
||||
"productName": ""
|
||||
},
|
||||
"dataState": {
|
||||
"newSizeGroups": "[]",
|
||||
"newBootSizeGroups": "[]"
|
||||
}
|
||||
},
|
||||
"sheinSkuDataStore": {
|
||||
"formState": {
|
||||
"identifierCodeType": 1,
|
||||
"supplierType": "EAN",
|
||||
"skuDataList": "[]",
|
||||
"deleteCombinationSkus": "[]",
|
||||
"siteTableInfoData": "[]",
|
||||
"sampleInfoObj": {
|
||||
"sampleJudgeType": "2",
|
||||
"reserveSampleFlag": "2",
|
||||
"spotFlag": "1"
|
||||
}
|
||||
},
|
||||
"dataState": {
|
||||
"skcRetailPriceEdit": true,
|
||||
"stockEdit": true,
|
||||
"specialPriceEdit": true,
|
||||
"supplyPriceEdit": true,
|
||||
"priceEdit": true
|
||||
},
|
||||
"siteStockStr": "_stock",
|
||||
"recordMainVariant": {}
|
||||
},
|
||||
"sheinSkuAttrStore": {
|
||||
"formState": {
|
||||
"skuAttrs": "[1] {\"attributeId\":27,\"name\":\"颜色\",\"nameEn\":\"Color\",\"_options\":[{\"attribute_value\":\"Apricot\",\"attribute_value_en\":\"Apricot\",\"attribute_value_id\":78,\"is_show\":1,\"supplier_id\":0,\"attribute_value_cn\":\"杏色\"},{\"attribute_value\":\"Be",
|
||||
"sizeTableInfo": "[]"
|
||||
},
|
||||
"dataState": {
|
||||
"mainVariantId": 27,
|
||||
"mainVariantList": "[1] {\"id\":4810372362706115,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":27,\"name\":\"颜色\",\"nameEn\":\"Color\",\"show\":1,\"label\":1,\"mode\":2,\"source\":1,\"status\":3,\"type\":1,\"dimensi",
|
||||
"variantList": "[3] {\"id\":4810372362706123,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":87,\"name\":\"尺寸\",\"nameEn\":\"Size\",\"show\":1,\"label\":0,\"mode\":2,\"source\":1,\"status\":3,\"type\":1,\"dimensio",
|
||||
"isHasSizeChart": true,
|
||||
"cacheIsHasSizeChart": true,
|
||||
"sizeTableData": "[6] {\"id\":4810372362706189,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":55,\"name\":\"长度 (cm)\",\"nameEn\":\"Length (cm)\",\"show\":1,\"label\":0,\"mode\":0,\"source\":1,\"status\":3,\"type\"",
|
||||
"isAllowEditSkcData": "[]",
|
||||
"collectDetailImages": ""
|
||||
},
|
||||
"customAttrOptions": {
|
||||
"27": "[]"
|
||||
},
|
||||
"recordData": {
|
||||
"oldOption": "[]",
|
||||
"newOption": "[]"
|
||||
}
|
||||
},
|
||||
"sheinAddBasicStore": {
|
||||
"formState": {
|
||||
"shopId": "6226408",
|
||||
"categoryId": 2734
|
||||
},
|
||||
"shopList": "[11] {\"id\":5986682,\"puid\":70000,\"name\":\"⑧Shein-易世通达-文具\",\"apiKey\":null,\"merchantId\":\"14349C4899B04FE59CECE6A4EA6CDB9C\",\"merchantUserName\":\"2\",\"awsAccessKeyId\":\"zh-cn|CNY\",\"marketPlaceId\":\"4328424\",\"lastSyncTimeProduct\":null,\"l",
|
||||
"dataState": {
|
||||
"categories": "[67] {\"id\":4810372411213177,\"puid\":70000,\"shopId\":6226408,\"categoryId\":2735,\"productTypeId\":604,\"parentId\":2732,\"name\":\"发爪\",\"nameEn\":\"Hair Claws\",\"nodePath\":\"服饰装饰品(Apparel Accessories)/女士配饰(Women Accessories)/女士发饰(Women Hair ",
|
||||
"categoryPathName": "服饰装饰品(Apparel Accessories) > 女士配饰(Women Accessories) > 女士发饰(Women Hair Accessories) > 发夹(Hair Clips)",
|
||||
"shopIds": "[1] \"6226408\"",
|
||||
"euSiteList": "[]",
|
||||
"shopSiteList": "[57] {\"id\":4810364349509615,\"puid\":70000,\"shopId\":6226408,\"currency\":\"USD\",\"siteAbbr\":\"shein-www\",\"siteName\":\"SHEIN国际站\",\"mainSite\":\"shein\",\"mainSiteName\":\"SHEIN\",\"storeType\":0,\"symbolLeft\":\"$\",\"symbolRight\":\"\",\"rateInfo\":\"[{\\",
|
||||
"warehouseList": "[]",
|
||||
"euSiteStockShopList": "[]",
|
||||
"categoryUpdated": false
|
||||
},
|
||||
"currencyCode": "CNY",
|
||||
"isEurSite": false,
|
||||
"isAllowEditOnlineProductFlag": false,
|
||||
"infoDisplayRules": {
|
||||
"image_module_show": true,
|
||||
"reference_module_show": false,
|
||||
"allow_switch_spu_picture": true,
|
||||
"sample_spec_show": false,
|
||||
"sample_spec_required": false,
|
||||
"minimum_stock_quantity_show": false,
|
||||
"minimum_stock_quantity_required": false,
|
||||
"reference_product_link_show": false,
|
||||
"reference_product_link_required": false,
|
||||
"proof_of_stock_show": false,
|
||||
"proof_of_stock_required": false,
|
||||
"skc_retail_price_show": true,
|
||||
"skc_retail_price_required": false,
|
||||
"quantity_info_show": true,
|
||||
"quantity_info_required": false,
|
||||
"package_type_show": true,
|
||||
"package_type_required": false,
|
||||
"supplier_barcode_show": false,
|
||||
"supplier_barcode_required": false,
|
||||
"goods_ip_show": true,
|
||||
"goods_ip_required": false,
|
||||
"product_video_show": true,
|
||||
"product_video_required": false,
|
||||
"shelf_require_show": true,
|
||||
"shelf_require_required": false,
|
||||
"title_max_length": 150
|
||||
},
|
||||
"imageDisplayRules": {
|
||||
"switch_spu_picture": false,
|
||||
"spu_image_detail_show": false,
|
||||
"spu_image_detail_required": false,
|
||||
"spu_image_detail_single": false,
|
||||
"spu_image_square_show": false,
|
||||
"spu_image_square_required": false,
|
||||
"skc_image_detail_show": true,
|
||||
"skc_image_detail_required": true,
|
||||
"skc_image_detail_single": false,
|
||||
"skc_image_square_show": true,
|
||||
"skc_image_square_required": true,
|
||||
"sku_image_detail_show": true,
|
||||
"sku_image_detail_required": false,
|
||||
"package_type_show": false,
|
||||
"package_type_required": false,
|
||||
"supplier_barcode_show": false,
|
||||
"supplier_barcode_required": false,
|
||||
"shelf_require_show": false,
|
||||
"shelf_require_required": false,
|
||||
"title_max_length": 1000
|
||||
},
|
||||
"isMxSite": false,
|
||||
"isJapanSite": false,
|
||||
"isKoreanSite": false
|
||||
},
|
||||
"sheinImgStore": {
|
||||
"formState": {
|
||||
"mainImgData": {
|
||||
"multipleSpuImgs": "[]"
|
||||
}
|
||||
},
|
||||
"dataState": {
|
||||
"newSizeGroups": "[]",
|
||||
"newBootSizeGroups": "[]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"stores": [
|
||||
"sheinAddStore",
|
||||
"sheinAddProductStore",
|
||||
"sheinSkuDataStore",
|
||||
"sheinSkuAttrStore",
|
||||
"sheinAddBasicStore",
|
||||
"sheinAddOtherStore",
|
||||
"sheinQualoficaStore",
|
||||
"sheinProductVideoStore",
|
||||
"sheinImgStore"
|
||||
],
|
||||
"items": [
|
||||
"* 店铺账号 [select] ①Shein-武汉九个网-箱包",
|
||||
"* 产品分类 [input] ",
|
||||
" 是否多套件 [-] ",
|
||||
" 店小秘分类 [-] ",
|
||||
" 来源URL [input] ",
|
||||
" [input] ",
|
||||
"* 产品标题: [input] ",
|
||||
" 图片模式 [input] 1"
|
||||
],
|
||||
"dump": {
|
||||
"sheinAddStore": {
|
||||
"attrsInfo.showSizeAttr": "false",
|
||||
"sharedData.isFeedBacking": "false",
|
||||
"sharedData.isAllowEditOnline": "false",
|
||||
"sharedData.variantImageNum": "0",
|
||||
"dataState.isVip": "true"
|
||||
},
|
||||
"sheinAddProductStore": {
|
||||
"formState.imageType": "0"
|
||||
},
|
||||
"sheinSkuDataStore": {
|
||||
"formState.identifierCodeType": "1",
|
||||
"formState.supplierType": "EAN",
|
||||
"dataState.skcRetailPriceEdit": "true",
|
||||
"dataState.stockEdit": "true",
|
||||
"dataState.specialPriceEdit": "true",
|
||||
"dataState.supplyPriceEdit": "true",
|
||||
"dataState.priceEdit": "true",
|
||||
"siteStockStr": "_stock"
|
||||
},
|
||||
"sheinSkuAttrStore": {
|
||||
"dataState.isHasSizeChart": "false",
|
||||
"dataState.cacheIsHasSizeChart": "false"
|
||||
},
|
||||
"sheinAddBasicStore": {
|
||||
"formState.shopId": "6229819",
|
||||
"dataState.categoryUpdated": "false",
|
||||
"currencyCode": "CNY",
|
||||
"isEurSite": "false",
|
||||
"isAllowEditOnlineProductFlag": "false",
|
||||
"infoDisplayRules.image_module_show": "false",
|
||||
"infoDisplayRules.reference_module_show": "false",
|
||||
"infoDisplayRules.allow_switch_spu_picture": "false",
|
||||
"infoDisplayRules.sample_spec_show": "false",
|
||||
"infoDisplayRules.sample_spec_required": "false",
|
||||
"infoDisplayRules.minimum_stock_quantity_show": "false",
|
||||
"infoDisplayRules.minimum_stock_quantity_required": "false",
|
||||
"infoDisplayRules.reference_product_link_show": "false",
|
||||
"infoDisplayRules.reference_product_link_required": "false",
|
||||
"infoDisplayRules.proof_of_stock_show": "false",
|
||||
"infoDisplayRules.proof_of_stock_required": "false",
|
||||
"infoDisplayRules.skc_retail_price_show": "false",
|
||||
"infoDisplayRules.skc_retail_price_required": "false",
|
||||
"infoDisplayRules.quantity_info_show": "false",
|
||||
"infoDisplayRules.quantity_info_required": "false",
|
||||
"infoDisplayRules.package_type_show": "false",
|
||||
"infoDisplayRules.package_type_required": "false",
|
||||
"infoDisplayRules.supplier_barcode_show": "false",
|
||||
"infoDisplayRules.supplier_barcode_required": "false",
|
||||
"infoDisplayRules.goods_ip_show": "false",
|
||||
"infoDisplayRules.goods_ip_required": "false",
|
||||
"infoDisplayRules.product_video_show": "false",
|
||||
"infoDisplayRules.product_video_required": "false",
|
||||
"infoDisplayRules.shelf_require_show": "false",
|
||||
"infoDisplayRules.shelf_require_required": "false",
|
||||
"infoDisplayRules.title_max_length": "1000",
|
||||
"imageDisplayRules.switch_spu_picture": "false",
|
||||
"imageDisplayRules.spu_image_detail_show": "false",
|
||||
"imageDisplayRules.spu_image_detail_required": "false",
|
||||
"imageDisplayRules.spu_image_detail_single": "false",
|
||||
"imageDisplayRules.spu_image_square_show": "false",
|
||||
"imageDisplayRules.spu_image_square_required": "false",
|
||||
"imageDisplayRules.skc_image_detail_show": "true",
|
||||
"imageDisplayRules.skc_image_detail_required": "true",
|
||||
"imageDisplayRules.skc_image_detail_single": "false",
|
||||
"imageDisplayRules.skc_image_square_show": "true",
|
||||
"imageDisplayRules.skc_image_square_required": "true",
|
||||
"imageDisplayRules.sku_image_detail_show": "true",
|
||||
"imageDisplayRules.sku_image_detail_required": "false",
|
||||
"imageDisplayRules.package_type_show": "false",
|
||||
"imageDisplayRules.package_type_required": "false",
|
||||
"imageDisplayRules.supplier_barcode_show": "false",
|
||||
"imageDisplayRules.supplier_barcode_required": "false",
|
||||
"imageDisplayRules.shelf_require_show": "false",
|
||||
"imageDisplayRules.shelf_require_required": "false",
|
||||
"imageDisplayRules.title_max_length": "1000",
|
||||
"isMxSite": "false",
|
||||
"isJapanSite": "false",
|
||||
"isKoreanSite": "false"
|
||||
},
|
||||
"sheinAddOtherStore": {},
|
||||
"sheinQualoficaStore": {
|
||||
"formData.isGrayShow": "false",
|
||||
"formData.isShow": "false"
|
||||
},
|
||||
"sheinProductVideoStore": {
|
||||
"dataState.supported": "false"
|
||||
},
|
||||
"sheinImgStore": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"美妆": {
|
||||
"shop": "③shein-易捷天宇-美妆",
|
||||
"kw": {
|
||||
"661190598139": "大肠圈",
|
||||
"902440558099": "大肠圈",
|
||||
"823560564174": "大肠圈",
|
||||
"607330049419": "大肠圈",
|
||||
"854003999648": "大肠圈",
|
||||
"735147938123": "发箍",
|
||||
"610164857063": "发箍",
|
||||
"833753630925": "发箍",
|
||||
"676236596821": "发夹",
|
||||
"944470567338": "发夹",
|
||||
"787261097583": "发夹",
|
||||
"857781294719": "发夹",
|
||||
"863384503032": "发夹",
|
||||
"726442972676": "发夹",
|
||||
"893592264148": "发夹",
|
||||
"836512561982": "化妆包",
|
||||
"714492799386": "化妆包",
|
||||
"719391062058": "化妆包"
|
||||
}
|
||||
},
|
||||
"文具": {
|
||||
"shop": "⑥Shein-南京亿千-文具",
|
||||
"kw": {
|
||||
"953869908708": "涂鸦贴纸",
|
||||
"863524134410": "涂鸦贴纸",
|
||||
"623011262210": "涂鸦贴纸",
|
||||
"997450831819": "涂鸦贴纸",
|
||||
"639409307567": "涂鸦贴纸",
|
||||
"698782391248": "涂鸦贴纸",
|
||||
"591994902720": "笔袋",
|
||||
"682195499823": "笔袋",
|
||||
"657617635303": "回形针",
|
||||
"703822717997": "回形针",
|
||||
"621300323670": "桌面摆件",
|
||||
"823838532886": "卡套",
|
||||
"1027332215847": "文具收纳盒"
|
||||
}
|
||||
},
|
||||
"箱包": {
|
||||
"shop": "①Shein-武汉九个网-箱包",
|
||||
"kw": {
|
||||
"641673262387": "其他收纳包",
|
||||
"707826905566": "卡包",
|
||||
"694637537727": "保温午餐包",
|
||||
"708420302844": "包包挂件",
|
||||
"750271307455": "包包挂件",
|
||||
"774494945453": "包包挂件",
|
||||
"775057912118": "包包挂件",
|
||||
"896469995553": "包包挂件"
|
||||
}
|
||||
},
|
||||
"内睡袜子": {
|
||||
"shop": "⑤Shein-惠州九衢-内睡(袜子)",
|
||||
"kw": {
|
||||
"627879474686": "袜子"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const IMGS=['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg'];
|
||||
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const d=p._s.get('sheinSkuDataStore').$state.formState;
|
||||
const rows=d.skuDataList||[]; let n=0;
|
||||
rows.forEach((x,i)=>{ x.skcSupplierCode='SHEIN-TEST-'+(i+1); x.skcRetailPrice='12.90'; x.supplyPrice='8.50';
|
||||
x.stockVariety='999'; x.weight='100'; x.length='200'; x.width='150'; x.height='30'; n++; });
|
||||
const im=p._s.get('sheinImgStore').$state.formState;
|
||||
im.mainImgData.multipleSpuImgs=${JSON.stringify(IMGS.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800})))};
|
||||
return JSON.stringify({rows:n, imgs:im.mainImgData.multipleSpuImgs.length, imgType:Array.isArray(im.mainImgData.multipleSpuImgs)?'array':typeof im.mainImgData.multipleSpuImgs});})()`);
|
||||
console.log('填充:', r);
|
||||
await wait(3500);
|
||||
const chk = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/图片/.test(e.innerText||'')&&e.querySelectorAll('img').length>0);
|
||||
return JSON.stringify({domImgs:it.length?it[0].querySelectorAll('img').length:0});})()`);
|
||||
console.log('UI 图片:', String(chk));
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
const sv = await c.evalJS(`(()=>{const xs=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null);
|
||||
let x=xs.find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()))||xs.find(e=>/保存/.test(e.innerText||''));
|
||||
if(!x)return'nf'; for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return (x.innerText||'').trim();})()`);
|
||||
console.log('保存按钮:', sv);
|
||||
await wait(13000);
|
||||
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
|
||||
await wait(4000);
|
||||
console.log('POST 请求:', JSON.stringify([...new Set(reqs)].slice(0,5)));
|
||||
const red = await c.evalJS(`(()=>{const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
|
||||
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
|
||||
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
|
||||
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
|
||||
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
return JSON.stringify({red:[...new Set(red)].slice(0,10), toasts:[...new Set(toasts)].slice(0,3)});})()`);
|
||||
console.log('红字/提示:', String(red).slice(0,600));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,38 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
// 两个规格组各点一个 .sku-option
|
||||
const r = await c.evalJS(`(()=>{const boxes=[...document.querySelectorAll('.sku-info-box')].filter(e=>e.offsetParent!==null);
|
||||
const done=[];
|
||||
boxes.forEach((b,i)=>{ const opts=[...b.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
|
||||
if(!opts.length){done.push(i+':no-opt');return;}
|
||||
const inp=opts[0].querySelector('input[type=checkbox]');
|
||||
if(inp&&inp.checked){done.push(i+':already');return;}
|
||||
const lab=opts[0].querySelector('label')||opts[0];
|
||||
for(const ev of ['mousedown','mouseup','click']) lab.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
if(inp&&!inp.checked) inp.click();
|
||||
done.push(i+':'+(opts[0].innerText||'').trim().slice(0,18)); });
|
||||
return done.join(' | ');})()`);
|
||||
console.log('选规格值:', String(r));
|
||||
await wait(7000);
|
||||
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
|
||||
return JSON.stringify({attrs:(a.formState.skuAttrs||[]).map(x=>({n:x.name, chosen:(x.options||[]).length})),
|
||||
skuRows:(d.formState.skuDataList||[]).length});})()`);
|
||||
console.log('状态:', String(st));
|
||||
// 保存
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(12000);
|
||||
const cap = await c.evalJS(`JSON.stringify(window.__cap||[])`);
|
||||
console.log('捕获:', String(cap).slice(0,300));
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,3)));
|
||||
const t = await c.evalJS(`JSON.stringify([...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean).slice(0,3))`);
|
||||
console.log('提示:', String(t).slice(0,400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,60 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const SHOP='③shein-易捷天宇-美妆', CATKW='发夹';
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const x of tl.filter(y=>y.type==='page'&&/shein/i.test(y.url))) await fetch(`http://127.0.0.1:9223/json/close/${x.id}`).catch(()=>{});
|
||||
await wait(900);
|
||||
const nt = await newTarget('about:blank');
|
||||
const c = await CDP.connect(nt.webSocketDebuggerUrl);
|
||||
await c.send('Page.enable'); await c.send('Runtime.enable');
|
||||
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/sheinProduct/add'});
|
||||
await wait(21000);
|
||||
// 店铺
|
||||
const sp=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||document).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 t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:sp[0],y:sp[1],button:'left',clickCount:1});
|
||||
await wait(2400);
|
||||
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
|
||||
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(8000);
|
||||
// 类目
|
||||
const cp=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 t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:cp[0],y:cp[1],button:'left',clickCount:1});
|
||||
await wait(3500);
|
||||
console.log('类目:', String(await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
|
||||
const hit=its.find(o=>new RegExp('^\\\\s*'+${JSON.stringify(CATKW)}).test((o.innerText||'').trim()));
|
||||
if(!hit)return'no-hit'; for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return (hit.innerText||'').trim();})()`)));
|
||||
await wait(12000);
|
||||
// 标题
|
||||
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 el=it.querySelector('input:not([type=hidden])'); el.focus();
|
||||
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,'Test Hair Clip Set for Women Girls Daily Use Fashion Accessory');
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
p._s.get('sheinAddProductStore').$state.formState.productName='Test Hair Clip Set for Women Girls Daily Use Fashion Accessory';
|
||||
return 1;})()`);
|
||||
await wait(1500);
|
||||
// 选主规格颜色(取第一个可选值)—— 先看控件结构
|
||||
const mg = 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||'');});
|
||||
if(!it)return'nf';
|
||||
const sels=[...it.querySelectorAll('.ant-select')].map(s=>({txt:(s.innerText||'').replace(/\\s+/g,' ').slice(0,40), cls:String(s.className).slice(0,50)}));
|
||||
const cbs=[...it.querySelectorAll('.ant-checkbox-wrapper,.ant-tag,[class*=tag]')].slice(0,6).map(e=>(e.innerText||'').trim().slice(0,16));
|
||||
return JSON.stringify({sels, cbs, html:it.innerHTML.slice(0,400)});})()`);
|
||||
console.log('主规格控件:', String(mg).slice(0,800));
|
||||
// 图片字段测试:数组 vs 字符串
|
||||
const imgTest = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const im=p._s.get('sheinImgStore').$state.formState;
|
||||
const before=JSON.stringify(im).slice(0,120);
|
||||
return JSON.stringify({before});})()`);
|
||||
console.log('图片字段现状:', String(imgTest).slice(0,300));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,50 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const SHOP = process.argv[2] || '①Shein-武汉九个网-箱包';
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
|
||||
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/sheinProduct/add'});
|
||||
await wait(22000);
|
||||
// 选店铺
|
||||
const pos = 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||document).querySelector('.ant-select-selector');
|
||||
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)]);})()`)));
|
||||
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[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=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
|
||||
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';})()`);
|
||||
console.log('选店铺:', pk, SHOP);
|
||||
await wait(9000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const stores=[...p._s.keys()].filter(k=>/shein/i.test(k));
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,26).padEnd(27)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,26);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const dump={};
|
||||
for(const n of stores){ try{ const s=p._s.get(n).$state; const o2={};
|
||||
for(const k of Object.keys(s)){ const v=s[k];
|
||||
if(v===null||['string','number','boolean'].includes(typeof v)){ if(v!==''&&v!==null&&String(v).length<55) o2[k]=String(v); }
|
||||
else if(v&&typeof v==='object'&&!Array.isArray(v)){ for(const k2 of Object.keys(v)){ const v2=v[k2];
|
||||
if(v2===null||['string','number','boolean'].includes(typeof v2)){ if(v2!==''&&v2!==null&&String(v2).length<45) o2[k+'.'+k2]=String(v2); } } } }
|
||||
dump[n]=o2; }catch(e){ dump[n]='ERR'; } }
|
||||
return JSON.stringify({stores, items, dump},null,1);})()`);
|
||||
fs.writeFileSync('/tmp/shein_map.json', String(o));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('stores:', j.stores.join(', '));
|
||||
console.log('--- form ---'); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('--- store 关键字段 ---');
|
||||
for(const [n,v] of Object.entries(j.dump)){ const hit=Object.entries(v||{}).filter(([k])=>/shop|site|currency|cat|brand|price|weight/i.test(k));
|
||||
if(hit.length) console.log(' ##',n,hit.map(([k,val])=>k+'='+val).join(' | ').slice(0,320)); }
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,27 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
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('Network.enable');
|
||||
const caps=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent' && /shop/i.test(m.params.request.url))
|
||||
caps.push({m:m.params.request.method,u:m.params.request.url.replace('https://www.dianxiaomi.com',''),p:String(m.params.request.postData||'').slice(0,150)}); });
|
||||
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/ozonProduct/draft'});
|
||||
await new Promise(r=>setTimeout(r,24000));
|
||||
for(let i=0;i<8;i++){
|
||||
const x=await c.evalJS(`(()=>{const v=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
if(!v.length)return'n'; const b=v[0].querySelector('.ant-modal-close'); if(b){b.click();return'c';} return's';})()`);
|
||||
if(x==='n'){await new Promise(z=>setTimeout(z,900));continue;} if(x==='s')break;
|
||||
await new Promise(z=>setTimeout(z,1200));
|
||||
}
|
||||
console.log(' ══ 含 shop 的接口 ══');
|
||||
[...new Set(caps.map(x=>x.m+' '+x.u))].slice(0,10).forEach(x=>console.log(' '+x));
|
||||
// 直接调最可能的那个拿映射
|
||||
const r=await c.evalJS(`(async()=>{
|
||||
const u=( ${JSON.stringify(caps.map(x=>x.u).filter(x=>/shop/i.test(x)))} )[0];
|
||||
if(!u) return 'no-url';
|
||||
const res=await fetch(u,{headers:{'X-Requested-With':'XMLHttpRequest'}});
|
||||
const tx=await res.text();
|
||||
return u+' -> '+tx.slice(0,900);})()`, true);
|
||||
console.log('\n ══ 店铺列表内容 ══');
|
||||
console.log(' '+SAN(String(r)).slice(0,900));
|
||||
c.close();
|
||||
@@ -0,0 +1,45 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const URLX = process.argv[2];
|
||||
const TAG = process.argv[3] || 'x';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x => x.type==='page' && x.url.includes(TAG))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
|
||||
await wait(1000);
|
||||
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:URLX});
|
||||
await wait(22000);
|
||||
console.log('URL:', await c.evalJS('location.href'));
|
||||
const loc = await c.evalJS(`(()=>{
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null);
|
||||
for(let i=0;i<items.length;i++){ const l=items[i].querySelector('label'); if(l&&/店铺/.test(l.innerText||'')){
|
||||
const s=items[i].querySelector('.ant-select'); if(s){ const r=s.getBoundingClientRect(); return JSON.stringify({i, label:l.innerText.trim(), x:Math.round(r.x+r.width/2), y:Math.round(r.y+r.height/2), cur:(s.querySelector('.ant-select-selection-item')||{}).innerText||''}); } } }
|
||||
const s=[...document.querySelectorAll('.ant-select')].filter(e=>e.offsetParent!==null)[0];
|
||||
if(!s) return 'no-select'; const r=s.getBoundingClientRect();
|
||||
return JSON.stringify({i:-1, label:'(第一个)', x:Math.round(r.x+r.width/2), y:Math.round(r.y+r.height/2), cur:''});})()`);
|
||||
console.log('店铺选择器:', loc);
|
||||
const L = JSON.parse(String(loc));
|
||||
if (L.x) {
|
||||
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:L.x,y:L.y,button:'left',clickCount:1});
|
||||
await wait(2500);
|
||||
const all = new Set();
|
||||
for (let i=0;i<20;i++){
|
||||
const got = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
|
||||
if(!dd.length)return'[]'; const d=dd[dd.length-1];
|
||||
return JSON.stringify([...d.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').trim()).filter(Boolean));})()`);
|
||||
const arr=JSON.parse(String(got)); const before=all.size; arr.forEach(x=>all.add(x));
|
||||
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
|
||||
if(!dd.length)return 0; const d=dd[dd.length-1];
|
||||
const rc=d.querySelector('.rc-virtual-list-holder')||d; rc.scrollTop=rc.scrollTop+800; return rc.scrollTop;})()`);
|
||||
await wait(700);
|
||||
if (all.size===before && i>2) break;
|
||||
}
|
||||
const list=[...all];
|
||||
fs.writeFileSync('/tmp/shops_'+TAG+'.json', JSON.stringify(list,null,1));
|
||||
console.log('店铺数:', list.length);
|
||||
list.sort((a,b)=>a.localeCompare(b));
|
||||
list.forEach((n,i)=>console.log(' '+String(i+1).padStart(2)+'. '+n));
|
||||
}
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
|
||||
if(!t){console.log(' ❌');process.exit(0);}
|
||||
const c=await CDP.connect(t.webSocketDebuggerUrl);
|
||||
await c.send('Runtime.enable');
|
||||
const r=await c.evalJS(`(async()=>{
|
||||
const out=[];
|
||||
const cands=[['/api/ozonProduct/getShop.json','GET'],['/api/shop/getShopList.json','GET'],
|
||||
['/api/ozonProduct/shopList.json','GET'],['/api/ozonProduct/getAuthShopList.json','GET'],
|
||||
['/api/shop/list.json','GET'],['/api/ozonProduct/getShopList.json','POST']];
|
||||
for(const [u,m] of cands){
|
||||
try{
|
||||
const res=await Promise.race([
|
||||
fetch(u, m==='POST'?{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'},body:''}
|
||||
:{headers:{'X-Requested-With':'XMLHttpRequest'}}),
|
||||
new Promise((_,rj)=>setTimeout(()=>rj(new Error('to')),10000))]);
|
||||
const tx=await res.text();
|
||||
if(res.status===200 && !tx.includes('<!DOCTYPE')) out.push(u+' -> '+tx.slice(0,420));
|
||||
}catch(e){}
|
||||
}
|
||||
return JSON.stringify(out);})()`, true);
|
||||
const arr=JSON.parse(r);
|
||||
if(!arr.length) console.log(' 店铺接口都没找到');
|
||||
arr.forEach(x=>console.log(' ★ '+SAN(x).slice(0,420)));
|
||||
// 直接在页面上找店铺下拉里的映射
|
||||
const dom=await c.evalJS(`(()=>{
|
||||
const txt=document.body.innerText;
|
||||
const i=txt.indexOf('Yejet');
|
||||
return JSON.stringify({has:txt.includes('Yejet'), around:i>=0?txt.slice(Math.max(0,i-60),i+60).replace(/\\n/g,' | '):''});})()`);
|
||||
console.log('\n 页面含 Yejet:', SAN(dom).slice(0,220));
|
||||
c.close();
|
||||
@@ -0,0 +1,21 @@
|
||||
import { CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
|
||||
if(!t){console.log(' ❌');process.exit(0);}
|
||||
const c=await CDP.connect(t.webSocketDebuggerUrl);
|
||||
await c.send('Runtime.enable');
|
||||
const r=await c.evalJS(`(async()=>{
|
||||
const out={};
|
||||
// userIn 里通常带店铺
|
||||
try{ const res=await fetch('/api/userIn.json',{headers:{'X-Requested-With':'XMLHttpRequest'}});
|
||||
const tx=await res.text(); out.userIn=tx.slice(0,1200); }catch(e){ out.userIn='ERR'; }
|
||||
// 页面上是否嵌了 shop 映射
|
||||
out.htmlHas4248777 = document.documentElement.innerHTML.includes('4248777');
|
||||
const m=document.documentElement.innerHTML.match(/.{0,80}4248777.{0,120}/g);
|
||||
out.ctx = m? m.slice(0,3).map(x=>x.replace(/\\s+/g,' ')) : [];
|
||||
return JSON.stringify(out);})()`, true);
|
||||
const d=JSON.parse(r);
|
||||
console.log(' userIn 片段:', SAN(String(d.userIn)).slice(0,600));
|
||||
console.log('\n HTML 含 4248777:', d.htmlHas4248777);
|
||||
(d.ctx||[]).forEach(x=>console.log(' 上下文: '+SAN(String(x)).slice(0,190)));
|
||||
c.close();
|
||||
@@ -0,0 +1,18 @@
|
||||
import { CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
|
||||
if(!t){console.log(' ❌');process.exit(0);}
|
||||
const c=await CDP.connect(t.webSocketDebuggerUrl);
|
||||
await c.send('Runtime.enable');
|
||||
const r=await c.evalJS(`(async()=>{
|
||||
const j=await (await fetch('/api/userIn.json',{headers:{'X-Requested-With':'XMLHttpRequest'}})).json();
|
||||
const sm=(j.data||{}).shopMap||{};
|
||||
const rows=Object.keys(sm).map(k=>({id:k, name:String(sm[k].name||''), plat:String(sm[k].platform||sm[k].site||'')}));
|
||||
const target=rows.find(x=>x.id==='4248777');
|
||||
return JSON.stringify({n:rows.length, target, yejet:rows.filter(x=>/yejet/i.test(x.name)).slice(0,8)});})()`, true);
|
||||
const d=JSON.parse(r);
|
||||
console.log(' shopMap 共', d.n, '个店铺');
|
||||
console.log(' ══ shopId=4248777 是 ══');
|
||||
console.log(' ', SAN(JSON.stringify(d.target)).slice(0,220));
|
||||
console.log('\n 名称含 yejet 的店铺:', SAN(JSON.stringify(d.yejet)).slice(0,300));
|
||||
c.close();
|
||||
@@ -0,0 +1,40 @@
|
||||
import { attach, SAN } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/ozonProduct/draft') || await attach('dianxiaomi.com');
|
||||
if(!c){ console.log('✗ 无页面'); process.exit(1); }
|
||||
await c.send('Runtime.enable');
|
||||
const r = await c.evalJS(`(async()=>{
|
||||
const rr = await fetch('/api/userInfo.json?_t='+Date.now(), {headers:{'X-Requested-With':'XMLHttpRequest'}});
|
||||
const tx = await rr.text();
|
||||
if(!tx.startsWith('{')) return JSON.stringify({err:'notjson', head:tx.slice(0,120)});
|
||||
const j = JSON.parse(tx); const d = j.data||{};
|
||||
const out = { topKeys: Object.keys(d) };
|
||||
// shopMap 结构
|
||||
const sm = d.shopMap || d.shops || {};
|
||||
out.shopMapType = Array.isArray(sm)?'array':typeof sm;
|
||||
if (Array.isArray(sm)) {
|
||||
out.shops = sm.map(s=>({id:s.id, idStr:s.idStr, name:s.name||s.shopName, platform:s.platform||s.platformName, site:s.site}));
|
||||
} else {
|
||||
out.shopMapKeys = Object.keys(sm).slice(0,40);
|
||||
out.shops = [];
|
||||
for (const [k,v] of Object.entries(sm)) {
|
||||
if (Array.isArray(v)) v.forEach(s=>out.shops.push({platform:k, id:s.id, idStr:s.idStr, name:s.name||s.shopName, site:s.site}));
|
||||
else if (v && typeof v === 'object') out.shops.push({platform:k, id:v.id, idStr:v.idStr, name:v.name||v.shopName});
|
||||
}
|
||||
}
|
||||
// 平台列表
|
||||
out.platformList = d.platformList || d.platforms || d.shopPlatformList || null;
|
||||
return JSON.stringify(out);
|
||||
})()`, true);
|
||||
try{
|
||||
const d = JSON.parse(String(r));
|
||||
if (d.err) { console.log(' ✗', d.err, d.head); process.exit(0); }
|
||||
console.log(' userInfo 顶层字段:', (d.topKeys||[]).join(', ').slice(0,300));
|
||||
console.log(' shopMap 类型:', d.shopMapType);
|
||||
if (d.shopMapKeys) console.log(' shopMap 键(平台):', d.shopMapKeys.join(', '));
|
||||
console.log();
|
||||
console.log(' ── 全部店铺 ──');
|
||||
(d.shops||[]).forEach(s=>console.log(' ', String(s.platform).padEnd(16), 'id='+String(s.id||s.idStr).padEnd(12), s.name||''));
|
||||
if (d.platformList) console.log();
|
||||
if (d.platformList) console.log(' platformList:', SAN(JSON.stringify(d.platformList)).slice(0,400));
|
||||
}catch(e){ console.log('err', e.message, SAN(String(r)).slice(0,600)); }
|
||||
c.close();
|
||||
@@ -0,0 +1,43 @@
|
||||
import { attach, SAN } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/ozonProduct/draft') || await attach('dianxiaomi.com');
|
||||
await c.send('Runtime.enable');
|
||||
const r = await c.evalJS(`(async()=>{
|
||||
const rr = await fetch('/api/userInfo.json?_t='+Date.now(), {headers:{'X-Requested-With':'XMLHttpRequest'}});
|
||||
const d = JSON.parse(await rr.text()).data;
|
||||
const out = {};
|
||||
// shopMap 一个条目的完整结构
|
||||
const sm = d.shopMap||{};
|
||||
const firstKey = Object.keys(sm)[0];
|
||||
out.shopMapSample = JSON.stringify(sm[firstKey]).slice(0, 400);
|
||||
// shopSmtTypeMap
|
||||
out.smtTypeMap = JSON.stringify(d.shopSmtTypeMap||{}).slice(0, 400);
|
||||
out.smtShopId = String(d.smtShopId);
|
||||
out.shopId = String(d.shopId);
|
||||
// platformPermission
|
||||
out.platformPermission = JSON.stringify(d.platformPermission||{}).slice(0, 500);
|
||||
// shopDataMap
|
||||
const sdm = d.shopDataMap||{};
|
||||
const k2 = Object.keys(sdm)[0];
|
||||
out.shopDataMapSample = k2? (k2+' => '+JSON.stringify(sdm[k2]).slice(0,300)) : '(空)';
|
||||
// 按 platform 归类
|
||||
const byPlat = {};
|
||||
for (const [k,v] of Object.entries(sm)) {
|
||||
const p = (v&&(v.platform||v.platformName||v.type))||'?';
|
||||
(byPlat[p]=byPlat[p]||[]).push((v&&(v.name||v.shopName))||k);
|
||||
}
|
||||
out.byPlatform = byPlat;
|
||||
return JSON.stringify(out);
|
||||
})()`, true);
|
||||
try{
|
||||
const d = JSON.parse(String(r));
|
||||
console.log(' smtShopId:', d.smtShopId, '| shopId:', d.shopId);
|
||||
console.log();
|
||||
console.log(' shopMap 条目结构:'); console.log(' ', SAN(d.shopMapSample).slice(0,380));
|
||||
console.log(' shopDataMap 样例:'); console.log(' ', SAN(d.shopDataMapSample).slice(0,300));
|
||||
console.log(' shopSmtTypeMap:'); console.log(' ', SAN(d.smtTypeMap).slice(0,380));
|
||||
console.log(' platformPermission:'); console.log(' ', SAN(d.platformPermission).slice(0,480));
|
||||
console.log();
|
||||
console.log(' ── 按平台归类 ──');
|
||||
for (const [p, arr] of Object.entries(d.byPlatform||{})) console.log(' ' + p + ' (' + arr.length + '): ' + arr.slice(0,8).join(', '));
|
||||
}catch(e){ console.log('err', e.message, SAN(String(r)).slice(0,700)); }
|
||||
c.close();
|
||||
@@ -0,0 +1,13 @@
|
||||
[
|
||||
"⑧Shein-易世通达-文具",
|
||||
"⑦shein-易捷天宇-家居",
|
||||
"⑩Shein-南京九个网-美妆",
|
||||
"⑨Shein-南京亿千-运动",
|
||||
"③shein-易捷天宇-美妆",
|
||||
"⑤Shein-惠州九衢-内睡(袜子)",
|
||||
"④Shein-武汉玖璨-家居",
|
||||
"⑥Shein-南京亿千-文具",
|
||||
"①Shein-武汉九个网-箱包",
|
||||
"②Shein-易世通达-箱包",
|
||||
"⑪-南京九个网-家居"
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
"①Alimingpro",
|
||||
"②ALuckycao",
|
||||
"③Bagbelle",
|
||||
"④Caoup",
|
||||
"⑤Bookinq",
|
||||
"⑥Wxzgz",
|
||||
"⑦Zdcjl",
|
||||
"⑧Ewhe",
|
||||
"⑨wowimartq",
|
||||
"⑩JoyJumbe",
|
||||
"⑪ Chugupi",
|
||||
"⑫Qapple",
|
||||
"⑬Wbalana",
|
||||
"⑮Tapeach",
|
||||
"⑭HoHaven"
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const o = 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||'');});
|
||||
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
|
||||
const first=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null)[0];
|
||||
if(!first)return'no-opt';
|
||||
return JSON.stringify({outer:first.outerHTML.slice(0,600),
|
||||
parentCls:String(first.parentElement.className||'').slice(0,60),
|
||||
sibCount:first.parentElement.children.length});})()`);
|
||||
console.log(String(o).slice(0,900));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,31 @@
|
||||
import { attach, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const all=JSON.parse(fs.readFileSync('/tmp/all48.json','utf8'));
|
||||
const wm=JSON.parse(fs.readFileSync('/tmp/wm_verdicts.json','utf8'));
|
||||
const clean=JSON.parse(fs.readFileSync('/tmp/wm_clean.json','utf8'));
|
||||
const SHORT=['1027332215847','601964534137','750271307455'];
|
||||
const c=await attach('/web/smt/smtProductList/draft')||await attach('dianxiaomi.com');
|
||||
await c.send('Runtime.enable');
|
||||
for(const oid of SHORT){
|
||||
const info=all[oid];
|
||||
const raw=await c.evalJS(`(async()=>{
|
||||
const rr=await fetch('/api/ozonProduct/add.json?id=${info.pid}&_t='+Date.now(),{headers:{'X-Requested-With':'XMLHttpRequest'}});
|
||||
const tx=await rr.text(); if(!tx.startsWith('{'))return'{}';
|
||||
const p=JSON.parse(tx).data.product;
|
||||
const s=new Set(); if(p.mainImage) s.add(p.mainImage);
|
||||
(p.extraImages||'').split(';').filter(Boolean).forEach(u=>s.add(u));
|
||||
return JSON.stringify({name:(p.name||'').slice(0,40), main:p.mainImage, urls:[...s]});
|
||||
})()`,true);
|
||||
const d=JSON.parse(String(raw));
|
||||
console.log('\n ════ '+oid+' ════');
|
||||
console.log(' '+d.name);
|
||||
console.log(' 干净图: '+(clean[oid]||[]).length+' 张');
|
||||
console.log(' ── 全部候选图的判定 ──');
|
||||
(d.urls||[]).slice(0,18).forEach((u,i)=>{
|
||||
const v=wm[u];
|
||||
const tag = !v ? '?' : (v.err? '✗'+v.err : (v.wm||v.contact? '⚠水印':'✅干净'));
|
||||
const txt = v && (v.text||v.reason) ? (' | '+String(v.text||v.reason).slice(0,50)) : '';
|
||||
console.log(` [${i}] ${tag.padEnd(8)} ${u.split('/').pop().split('_')[0].slice(0,26)}${txt}`);
|
||||
});
|
||||
}
|
||||
c.close();
|
||||
@@ -0,0 +1,24 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const pick = 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||'');});
|
||||
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
|
||||
const opts=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
|
||||
const hit=opts.find(e=>/Multicolor|Black\\(/.test(e.innerText||''))||opts[0];
|
||||
if(!hit)return'no-opt';
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked:'+(hit.innerText||'').trim();})()`);
|
||||
console.log('选色:', pick);
|
||||
await wait(6000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,15),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
return JSON.stringify({tbls, n:(d.formState.skuDataList||[]).length, row0:(d.formState.skuDataList||[])[0],
|
||||
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, vals:(x.attributeValues||[]).map(v=>v.attribute_value||v) }))},null,1);})()`);
|
||||
console.log(String(o).slice(0,1900));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,53 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
// 重填 SKU 行
|
||||
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const d=p._s.get('sheinSkuDataStore').$state.formState;
|
||||
const rows=d.skuDataList||[]; let n=0;
|
||||
rows.forEach((x,i)=>{ x.skcSupplierCode='SHEIN-'+String(i+1).padStart(3,'0'); x.skcRetailPrice='12.90';
|
||||
x.supplyPrice='8.50'; x.stockVariety='999'; x.weight='100'; x.length='200'; x.width='150'; x.height='30'; n++; });
|
||||
return 'rows:'+n;})()`);
|
||||
console.log('重填 SKU:', r);
|
||||
await wait(2500);
|
||||
// 重填尺码表(走弹窗)
|
||||
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||'');});
|
||||
if(!it)return 0; const el=it.querySelector('.link')||it.querySelector('a,span'); if(!el)return 0;
|
||||
el.scrollIntoView({block:'center'});
|
||||
for(const ev of ['mousedown','mouseup','click']) el.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(5000);
|
||||
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.placeholder==='请输入');
|
||||
const vals=['10','18','3','2','18','5']; let n=0;
|
||||
ins.forEach((el,i)=>{ if(i>=vals.length)return; el.focus();
|
||||
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,vals[i]);
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
|
||||
return 'filled:'+n;})()`);
|
||||
console.log('尺码表:', f);
|
||||
await wait(1200);
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(m){const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||'')); if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));} return 1;})()`);
|
||||
await wait(4000);
|
||||
// 重填图片(可能也被清了)
|
||||
await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const im=p._s.get('sheinImgStore').$state.formState;
|
||||
const cur=im.mainImgData.multipleSpuImgs;
|
||||
const urls=${JSON.stringify(['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg'])};
|
||||
if(!Array.isArray(cur)||cur.length<4){ im.mainImgData.multipleSpuImgs=urls.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800})); }
|
||||
return 'imgs:'+(Array.isArray(im.mainImgData.multipleSpuImgs)?im.mainImgData.multipleSpuImgs.length:'?');})()`);
|
||||
await wait(2500);
|
||||
// 保存
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(12000);
|
||||
console.log('捕获:', String(await c.evalJS(`JSON.stringify(window.__cap||[])`)).slice(0,300));
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,3)));
|
||||
const t = await c.evalJS(`JSON.stringify([...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean).slice(0,3))`);
|
||||
console.log('提示:', String(t).slice(0,400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const SHOP = process.argv[2];
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
|
||||
await wait(1000);
|
||||
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/sheinProduct/add'});
|
||||
await wait(21000);
|
||||
const pos = 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||document).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:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(2500);
|
||||
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
|
||||
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(8000);
|
||||
// 开分类下拉
|
||||
const cp = 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:cp[0],y:cp[1],button:'left',clickCount:1});
|
||||
await wait(3500);
|
||||
const o = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd';
|
||||
const opts=[...dd.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
return JSON.stringify({n:opts.length, opts:opts.slice(0,26)});})()`);
|
||||
console.log('════', SHOP);
|
||||
console.log(String(o).slice(0,1100));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,21 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const el=it.querySelector('.link')||it.querySelector('a,span');
|
||||
if(!el)return'[]'; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
console.log('编辑尺码表坐标:', JSON.stringify(pos));
|
||||
if(pos.length){ for(const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1}); }
|
||||
await wait(6000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).map(m=>({t:(m.querySelector('.ant-modal-title')||{}).innerText||'',
|
||||
inputs:[...m.querySelectorAll('input')].map(i=>i.placeholder||i.type).slice(0,8),
|
||||
btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,8),
|
||||
txt:(m.innerText||'').replace(/\\s+/g,' ').slice(0,320)}));
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
let sz=null; try{ sz=JSON.stringify(p._s.get('sheinSkuAttrStore').$state.formState.sizeTableInfo).slice(0,200);}catch(e){}
|
||||
return JSON.stringify({modals:ms, sizeTableInfo:sz},null,1);})()`);
|
||||
console.log(String(o).slice(0,1400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,18 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
// 打开尺码表弹窗
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const el=it.querySelector('.link')||it.querySelector('a,span'); if(!el)return'[]';
|
||||
el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1}); }
|
||||
await wait(6000);
|
||||
const o = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal';
|
||||
const rows=[...m.querySelectorAll('tr')].slice(0,10).map(tr=>[...tr.querySelectorAll('td,th')].map(td=>{
|
||||
const i=td.querySelector('input'); return i?('IN:'+(i.placeholder||i.value||'')):(td.innerText||'').replace(/\\s+/g,' ').trim().slice(0,18);}));
|
||||
return JSON.stringify({title:(m.querySelector('.ant-modal-title')||{}).innerText||'', rows, btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,10)},null,1);})()`);
|
||||
console.log(String(o).slice(0,1500));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,47 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
// 填 6 格
|
||||
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null);
|
||||
const vals=['10','18','3','2','18','5']; let n=0;
|
||||
ins.forEach((el,i)=>{ if(i>=vals.length)return;
|
||||
el.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el, vals[i]);
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
|
||||
return 'filled:'+n+'/'+ins.length;})()`);
|
||||
console.log('填尺码表:', f);
|
||||
await wait(1500);
|
||||
// 确定
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return 0; const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(5000);
|
||||
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=p._s.get('sheinSkuAttrStore').$state.formState;
|
||||
const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||||
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
|
||||
return JSON.stringify({sizeTableInfo:String(JSON.stringify(a.sizeTableInfo)).slice(0,200), ui:(it?it.innerText:'').replace(/\\s+/g,' ').slice(0,60)});})()`);
|
||||
console.log('尺码表状态:', String(st).slice(0,300));
|
||||
// 保存
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const xs=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null);
|
||||
const x=xs.find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim())); if(!x)return 0;
|
||||
for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(14000);
|
||||
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认|继续编辑/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
|
||||
await wait(4000);
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
|
||||
const red = await c.evalJS(`(()=>{const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
|
||||
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
|
||||
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
|
||||
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
|
||||
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean);
|
||||
return JSON.stringify({red:[...new Set(red)].slice(0,10), toasts:[...new Set(toasts)].slice(0,3)});})()`);
|
||||
console.log('红字/提示:', String(red).slice(0,600));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,38 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal';
|
||||
const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.placeholder==='请输入');
|
||||
const vals=['10','18','3','2','18','5']; let n=0;
|
||||
ins.forEach((el,i)=>{ if(i>=vals.length)return;
|
||||
el.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el, vals[i]);
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
|
||||
return 'filled:'+n+'/'+ins.length+' vals='+ins.map(i=>i.value).join(',');})()`);
|
||||
console.log('填:', f);
|
||||
await wait(1500);
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return 0; const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(5000);
|
||||
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=p._s.get('sheinSkuAttrStore').$state.formState;
|
||||
const openM=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null&&/编辑尺码表/.test(e.innerText||'')).length;
|
||||
return JSON.stringify({sizeTableInfo:String(JSON.stringify(a.sizeTableInfo)).slice(0,240), modalOpen:openM});})()`);
|
||||
console.log('尺码表:', String(st).slice(0,340));
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(!x)return 0; for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(14000);
|
||||
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认|继续编辑/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
|
||||
await wait(4000);
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
|
||||
const t = await c.evalJS(`(()=>[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,120)).filter(Boolean).slice(0,3))()`);
|
||||
console.log('提示:', String(t).slice(0,400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,15 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
const o = 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||'');});
|
||||
if(!it)return'nf';
|
||||
const box=it.querySelector('.sku-info-box')||it;
|
||||
// 列出所有叶子的 tag/class/文本
|
||||
const leaves=[...box.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&!e.children.length&&(e.innerText||'').trim())
|
||||
.slice(0,14).map(e=>({tag:e.tagName, cls:String(e.className||'').slice(0,55), t:(e.innerText||'').trim().slice(0,18)}));
|
||||
const clickable=[...box.querySelectorAll('button,.ant-btn,.ant-tag,.ant-checkbox-wrapper,[class*=tag],[class*=item],[class*=option]')]
|
||||
.filter(e=>e.offsetParent!==null).slice(0,12).map(e=>({tag:e.tagName, cls:String(e.className||'').slice(0,50), t:(e.innerText||'').trim().slice(0,16)}));
|
||||
return JSON.stringify({leafCount:box.querySelectorAll('*').length, leaves, clickable},null,1);})()`);
|
||||
console.log(String(o).slice(0,2000));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,30 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
// 1. 主规格控件
|
||||
const o1 = 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||'');});
|
||||
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,160), html:it.innerHTML.slice(0,500)});})()`);
|
||||
console.log('主规格:', String(o1).slice(0,700));
|
||||
// 2. 尺码表控件
|
||||
const o2 = 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||'');});
|
||||
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,160), html:it.innerHTML.slice(0,400)});})()`);
|
||||
console.log('尺码表:', String(o2).slice(0,600));
|
||||
// 3. 图片区
|
||||
const o3 = 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||'');});
|
||||
if(!it)return'nf'; return JSON.stringify({lab:(it.querySelector('label')||{}).innerText||'', txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,200)});})()`);
|
||||
console.log('图片区:', String(o3).slice(0,500));
|
||||
// 4. 主规格可选值(第一个属性)
|
||||
const o4 = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
|
||||
const at=(a.formState.skuAttrs||[])[0]||{};
|
||||
return JSON.stringify({attrName:at.name, attrId:at.attributeId, optCount:(at._options||[]).length,
|
||||
opt0:(at._options||[])[0], chosen:at.attributeValues||at.checked||null});})()`);
|
||||
console.log('主规格属性:', String(o4).slice(0,600));
|
||||
// 5. 是否有必填标记的项
|
||||
const o5 = await c.evalJS(`(()=>[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null&&e.querySelector('.ant-form-item-required'))
|
||||
.map(x=>{const l=x.querySelector('label'); return l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';}).filter(Boolean).slice(0,15))()`);
|
||||
console.log('必填项:', String(o5).slice(0,400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"548459586741": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/52]:Health(保健)/Sexual Wellness(情趣用品)/Sex Toys(情趣玩具) "
|
||||
},
|
||||
"764423265986": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Health(保健)/Medical Supplies(医疗保健)/Health Monitors & Tests(健康仪器与测试仪)/Thermometers(温度计) "
|
||||
},
|
||||
"685456757807": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Home Appliances(家用电器)/Small Household Appliances(小型家电)/Sewing Machines & Accessories(缝纫机 & 配件) "
|
||||
},
|
||||
"735147938123": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fashion Accessories(时尚配饰)/Hair Accessories(发饰)/Hair Clips & Hair Pins(发夹) "
|
||||
},
|
||||
"627879474686": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Baby & Kids Fashion(童装)/Baby & Kids Accessories(婴儿与儿童配饰)/Socks(袜子) "
|
||||
},
|
||||
"610164857063": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fashion Accessories(时尚配饰)/Hair Accessories(发饰)/Hair Clips & Hair Pins(发夹) "
|
||||
},
|
||||
"676236596821": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Baby & Kids Fashion(童装)/Baby & Kids Accessories(婴儿与儿童配饰)/Hair Accessories(发饰) "
|
||||
},
|
||||
"708420302844": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobiles(汽车类)/Automotive Keychains & Key Covers(汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"866537106657": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Home Appliance (家用电器)/Small Hou ehold Appliance (小型家电)/Sewing Machine & Acce orie (缝纫机 & 配件) "
|
||||
},
|
||||
"547183997437": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/3]:Fa hion Acce orie (时尚配饰)/Fine Jewelry(轻奢珠宝)/Necklace (项链) "
|
||||
},
|
||||
"657617635303": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/56]:Stationery(文具)/Art Supplie (美术用品)/Acrylic Paint(亚克力颜料) "
|
||||
},
|
||||
"641673262387": {
|
||||
"ok": true,
|
||||
"cat": "picked[14/15]:Home & Living(家居生活)/Home Organizer (居家收纳)/Jewelry Organizer (首饰收纳) "
|
||||
},
|
||||
"953869908708": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Automobile (汽车类)/Automobile Exterior Acce orie (车外配件)/Sticker , Logo & Emblem (车贴、车标改装) "
|
||||
},
|
||||
"703822717997": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/56]:Stationery(文具)/Art Supplie (美术用品)/Acrylic Paint(亚克力颜料) "
|
||||
},
|
||||
"863524134410": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Automobile (汽车类)/Automobile Exterior Acce orie (车外配件)/Sticker , Logo & Emblem (车贴、车标改装) "
|
||||
},
|
||||
"694637537727": {
|
||||
"ok": true,
|
||||
"cat": "picked[5/67]:Beauty(美妆保健)/Beauty Tool (美容工具)/Makeup Acce orie (化妆工具)/Makeup Bag & Organizer (化妆包与收纳袋) "
|
||||
},
|
||||
"623011262210": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Automobile (汽车类)/Automobile Exterior Acce orie (车外配件)/Sticker , Logo & Emblem (车贴、车标改装) "
|
||||
},
|
||||
"836512561982": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Beauty(美妆保健)/Beauty Tool (美容工具)/Makeup Acce orie (化妆工具)/Makeup Bag & Organizer (化妆包与收纳袋) "
|
||||
},
|
||||
"714492799386": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Beauty(美妆保健)/Beauty Tool (美容工具)/Makeup Acce orie (化妆工具)/Makeup Bag & Organizer (化妆包与收纳袋) "
|
||||
},
|
||||
"591994902720": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Stationery(文具)/School & Office Equipment(学校与办公用品)/Pencil Ca e (文具盒/笔袋) "
|
||||
},
|
||||
"853414558425": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobile (汽车类)/Automotive Keychain & Key Cover (汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"997450831819": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Automobile (汽车类)/Automobile Exterior Acce orie (车外配件)/Sticker , Logo & Emblem (车贴、车标改装) "
|
||||
},
|
||||
"639409307567": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Home & Living(家居生活)/Decoration(装饰)/Wallpaper & Wall Sticker (墙纸与窗贴) "
|
||||
},
|
||||
"902440558099": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"823838532886": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Men Bag (男包)/Wallet (钱包)/Card Holder (卡包) "
|
||||
},
|
||||
"698782391248": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Automobile (汽车类)/Automobile Exterior Acce orie (车外配件)/Sticker , Logo & Emblem (车贴、车标改装) "
|
||||
},
|
||||
"707826905566": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/13]:Men Bag (男包)/Wallet (钱包)/Bifold & Trifold Wallet (双折&三折钱包) "
|
||||
},
|
||||
"1027332215847": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Home & Living(家居生活)/Home Organizer (居家收纳)/Shoe Storage Boxe (鞋盒) "
|
||||
},
|
||||
"682195499823": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/56]:Stationery(文具)/Art Supplie (美术用品)/Acrylic Paint(亚克力颜料) "
|
||||
},
|
||||
"823560564174": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"739147684759": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Home Appliance (家用电器)/Small Hou ehold Appliance (小型家电)/Sewing Machine & Acce orie (缝纫机 & 配件) "
|
||||
},
|
||||
"621300323670": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/56]:Stationery(文具)/Art Supplie (美术用品)/Acrylic Paint(亚克力颜料) "
|
||||
},
|
||||
"896469995553": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobile (汽车类)/Automotive Keychain & Key Cover (汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"944470567338": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"787261097583": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"833753630925": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"719391062058": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Home & Living(家居生活)/Home Organizer (居家收纳)/Shoe Storage Boxe (鞋盒) "
|
||||
},
|
||||
"857781294719": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Baby & Kid Fa hion(童装)/Baby & Kid Acce orie (婴儿与儿童配饰)/Hair Acce orie (发饰) "
|
||||
},
|
||||
"863384503032": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"774494945453": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobile (汽车类)/Automotive Keychain & Key Cover (汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"750271307455": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobile (汽车类)/Automotive Keychain & Key Cover (汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"726442972676": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"775057912118": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Automobile (汽车类)/Automotive Keychain & Key Cover (汽车钥匙扣和钥匙套) "
|
||||
},
|
||||
"893592264148": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/8]:Baby & Kid Fa hion(童装)/Baby & Kid Acce orie (婴儿与儿童配饰)/Hair Acce orie (发饰) "
|
||||
},
|
||||
"607330049419": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
},
|
||||
"854003999648": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/8]:Fa hion Acce orie (时尚配饰)/Hair Acce orie (发饰)/Hair Clip & Hair Pin (发夹) "
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"n":172,"items":[{"id":"184807703510036273","oid":"854003999648","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510035741","oid":"607330049419","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510035057","oid":"893592264148","shop":6142973,"title":"","cat":101029,"catPath":""},{"id":"184807703510034417","oid":"775057912118","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510033855","oid":"726442972676","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510033465","oid":"750271307455","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510033029","oid":"774494945453","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510032637","oid":"863384503032","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510032293","oid":"857781294719","shop":6142973,"title":"","cat":101029,"catPath":""},{"id":"184807703510031901","oid":"719391062058","shop":6142973,"title":"","cat":101255,"catPath":""},{"id":"184807703510031383","oid":"833753630925","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510030917","oid":"787261097583","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510030569","oid":"944470567338","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510030139","oid":"896469995553","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510029645","oid":"621300323670","shop":6142973,"title":"","cat":101365,"catPath":""},{"id":"184807703510029069","oid":"739147684759","shop":6142973,"title":"","cat":100173,"catPath":""},{"id":"184807703510028623","oid":"823560564174","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510028233","oid":"682195499823","shop":6142973,"title":"","cat":101365,"catPath":""},{"id":"184807703510027575","oid":"1027332215847","shop":6142973,"title":"","cat":101255,"catPath":""},{"id":"184807703510026781","oid":"707826905566","shop":6142973,"title":"","cat":100611,"catPath":""},{"id":"184807703510025839","oid":"698782391248","shop":6142973,"title":"","cat":101435,"catPath":""},{"id":"184807703510025179","oid":"823838532886","shop":6142973,"title":"","cat":100608,"catPath":""},{"id":"184807703510024541","oid":"902440558099","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510023971","oid":"639409307567","shop":6142973,"title":"","cat":101157,"catPath":""},{"id":"184807703510023389","oid":"997450831819","shop":6142973,"title":"","cat":101435,"catPath":""},{"id":"184807703510022689","oid":"853414558425","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510022085","oid":"591994902720","shop":6142973,"title":"","cat":101359,"catPath":""},{"id":"184807703510021797","oid":"714492799386","shop":6142973,"title":"","cat":101650,"catPath":""},{"id":"184807703510021497","oid":"836512561982","shop":6142973,"title":"","cat":101650,"catPath":""},{"id":"184807703510021291","oid":"623011262210","shop":6142973,"title":"","cat":101435,"catPath":""},{"id":"184807703510021059","oid":"694637537727","shop":6142973,"title":"","cat":101650,"catPath":""},{"id":"184807703510020731","oid":"863524134410","shop":6142973,"title":"","cat":101435,"catPath":""},{"id":"184807703510020443","oid":"703822717997","shop":6142973,"title":"","cat":101365,"catPath":""},{"id":"184807703510020027","oid":"953869908708","shop":6142973,"title":"","cat":101435,"catPath":""},{"id":"184807703510019455","oid":"641673262387","shop":6142973,"title":"","cat":101260,"catPath":""},{"id":"184807703510018695","oid":"657617635303","shop":6142973,"title":"","cat":101365,"catPath":""},{"id":"184807703510018191","oid":"547183997437","shop":6142973,"title":"","cat":102064,"catPath":""},{"id":"184807703510017925","oid":"866537106657","shop":6142973,"title":"","cat":100173,"catPath":""},{"id":"184807703510017639","oid":"708420302844","shop":6142973,"title":"","cat":100753,"catPath":""},{"id":"184807703510017247","oid":"676236596821","shop":6142973,"title":"","cat":101029,"catPath":""},{"id":"184807703510016863","oid":"610164857063","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510016495","oid":"627879474686","shop":6142973,"title":"","cat":101032,"catPath":""},{"id":"184807703510016073","oid":"735147938123","shop":6142973,"title":"","cat":100147,"catPath":""},{"id":"184807703510015731","oid":"685456757807","shop":6142973,"title":"","cat":100173,"catPath":""},{"id":"184807703510014969","oid":"764423265986","shop":6142973,"title":"","cat":100423,"catPath":""},{"id":"184807703510014619","oid":"548459586741","shop":6142973,"title":"","cat":100142,"catPath":""},{"id":"184807703510012679","oid":"661190598139","shop":6142973,"title":"","cat":101029,"catPath":""},{"id":"4810365555229229","oid":"733058615988","shop":6100336,"title":"","cat":101349,"catPath":""},{"id":"4810365430386949","oid":"693118601337","shop":3377110,"title":"","cat":101639,"catPath":""},{"id":"4810365324256099","oid":"573Line truncated
|
||||
@@ -0,0 +1,172 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
|
||||
/* ── Shopee (Ljbgkj, shopId 6142973) 单件建品配方 ──
|
||||
node spbatch.mjs <oid> [catKeyword]
|
||||
选店铺 → 分类弹窗 → 单件 → 标题/描述 → 价格(CNY)/库存 → 重量/尺寸
|
||||
→ 图片 → 来源URL → 保存 → 回读 */
|
||||
|
||||
const OID = process.argv[2];
|
||||
const CAT_KW = process.argv[3] || '';
|
||||
if (!OID) { console.log('用法: node spbatch.mjs <oid> [catKeyword]'); process.exit(1); }
|
||||
const IN = JSON.parse(fs.readFileSync('/tmp/tk_input.json', 'utf8'))[OID];
|
||||
if (!IN) { console.log('无输入数据', OID); process.exit(1); }
|
||||
|
||||
const SHOP = 'Ljbgkj';
|
||||
const IS_KID = /baby|kid|infant|toddler|children|newborn/i.test(IN.title);
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const log = (...a) => console.log(' ', ...a);
|
||||
const DIMS = (() => { const W = Math.max(100, Math.round(IN.weight * 1.3));
|
||||
return { L: Math.max(60, Math.min(400, W)), Wi: Math.max(40, Math.min(300, Math.round(W * 0.6))), H: Math.max(10, Math.min(200, Math.round(W * 0.2))) }; })();
|
||||
|
||||
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' && /shopee/i.test(x.url))) { await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {}); k++; }
|
||||
if (k) { log(`清理 shopee 页 ${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/shopeeGlobal/add' });
|
||||
await wait(22000);
|
||||
|
||||
// 1. 店铺
|
||||
async function pickShop() {
|
||||
for (let a = 0; a < 3; a++) {
|
||||
const pos = 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?it.querySelector('.ant-select-selector'):[...document.querySelectorAll('.ant-select-selector')].filter(e=>e.offsetParent!==null)[0];
|
||||
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)]);})()`)));
|
||||
if (!pos.length) return 'no-select';
|
||||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: pos[0], y: pos[1], button: 'left', clickCount: 1 });
|
||||
await wait(2200);
|
||||
const r = 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 'picked';})()`);
|
||||
await wait(7000);
|
||||
const cur = 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&&it.querySelector('.ant-select-selection-item'); return s?s.innerText.trim():'';})()`));
|
||||
if (new RegExp(SHOP, 'i').test(cur)) return 'ok:' + cur;
|
||||
log(' 店铺重试', a + 1, r, JSON.stringify(cur));
|
||||
}
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
// 2. 分类(同 TikTok 结构)
|
||||
async function pickCategory(kw) {
|
||||
await c.evalJS(`(()=>{document.querySelectorAll('.ant-select-dropdown').forEach(d=>{if(d.offsetParent!==null)d.style.display='none';}); document.body.click(); return 1;})()`);
|
||||
await wait(900);
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const b=[...it.querySelectorAll('button,a,span')].filter(e=>e.offsetParent!==null&&/^\\s*选择分类\\s*$/.test((e.innerText||'').trim()));
|
||||
if(!b.length)return'[]'; const el=b[b.length-1]; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if (!pos.length) return 'no-btn';
|
||||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: pos[0], y: pos[1], button: 'left', clickCount: 1 });
|
||||
await wait(3500);
|
||||
const r = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const inp=m.querySelector('input[placeholder="搜索分类名称"]'); if(!inp)return'no-input';
|
||||
inp.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(inp,${JSON.stringify(kw)});
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
const b=[...m.querySelectorAll('button')].find(x=>/^\\s*搜索\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'searched';})()`);
|
||||
if (r !== 'searched') return r;
|
||||
await wait(4000);
|
||||
const pick = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const its=[...m.querySelectorAll('.search-result-item')]; if(!its.length)return'no-result';
|
||||
const BAD=/二手|Barang Terpakai|Second-?hand|Used|Pre-?owned|Refurb|Sexual|Sex Toy|情趣|Adult Toy|Dewasa/i;
|
||||
const KID=/Bayi|Baby|Kids|Kid|Infant|Toddler|Children|Kanak/i;
|
||||
const ISK=${IS_KID};
|
||||
const KW=${JSON.stringify(kw)}.toLowerCase();
|
||||
const texts=its.map(e=>(e.innerText||'').replace(/\s+/g,' ').trim());
|
||||
const lastSeg=t=>{const a=t.split('/'); return (a[a.length-1]||'').replace(/\(.*?\)/g,'').trim().toLowerCase();};
|
||||
let idx=texts.findIndex(t=>!BAD.test(t) && lastSeg(t).includes(KW));
|
||||
if(idx<0) idx=texts.findIndex(t=>!BAD.test(t) && t.toLowerCase().includes(KW) && (ISK || !KID.test(t)));
|
||||
if(idx<0) idx=texts.findIndex(t=>!BAD.test(t) && (ISK || !KID.test(t)));
|
||||
if(idx<0) idx=texts.findIndex(t=>!BAD.test(t));
|
||||
if(idx<0) idx=0;
|
||||
const hit=its[idx];
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked['+idx+'/'+texts.length+']:'+texts[idx];})()`);
|
||||
await wait(4500);
|
||||
return pick;
|
||||
}
|
||||
|
||||
async function pickCategoryAny(kws) {
|
||||
const tried = [];
|
||||
for (const kw of kws) {
|
||||
const r = String(await pickCategory(kw));
|
||||
tried.push(kw + '\u2192' + r.slice(0, 46));
|
||||
if (!/no-result|no-input|no-modal|no-btn/.test(r)) return { r, tried };
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(m){const x=m.querySelector('.ant-modal-close'); if(x&&x.offsetParent!==null)x.click();} return 1;})()`);
|
||||
await wait(1500);
|
||||
}
|
||||
return { r: 'ALL-FAILED', tried };
|
||||
}
|
||||
|
||||
log('=== Shopee 建品 ===', OID, IN.title.slice(0, 46));
|
||||
log('1) 店铺:', await pickShop());
|
||||
|
||||
const kw = CAT_KW || IN.sp_kw || 'Accessories';
|
||||
const catRes = await pickCategoryAny([kw].concat(IN.sp_fb || []));
|
||||
log('2) 分类:', SAN(String(catRes.r)).slice(0, 120), '| 尝试:', catRes.tried.join(' / '));
|
||||
if (/ALL-FAILED|no-result|no-input|no-btn/.test(String(catRes.r))) { log('\u274c 分类未选中,跳过保存'); process.exit(2); }
|
||||
|
||||
// 3. 单件模式 + 文案
|
||||
const wr = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const ps=p._s.get('shopeeGlobalProductStore').$state.formState;
|
||||
ps.type='single';
|
||||
ps.title=${JSON.stringify(IN.title)};
|
||||
const txt=${JSON.stringify(IN.bullets.map(b => '• ' + b).join('\n') + '\n\nKeywords: ' + (IN.keywords || ''))};
|
||||
ps.descriptionInfo={descriptionText:txt, descriptionHtml:'<p>'+${JSON.stringify(IN.bullets.join('<br>'))}+'</p>'};
|
||||
return 'ok:'+ps.title.length;})()`);
|
||||
log('3) 文案:', wr);
|
||||
await wait(2000);
|
||||
|
||||
// 4. 价格 / 库存 / 重量 / 尺寸
|
||||
const setItem = (rx, sel, 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||'');});
|
||||
const el=it?it.querySelector(${JSON.stringify(sel)}):null; if(!el)return'nf';
|
||||
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';})()`);
|
||||
log('4) 价格(CNY):', await setItem('价格', 'input:not([type=hidden])', IN.price_cny));
|
||||
log(' 库存:', await setItem('库存', 'input:not([type=hidden])', '999'));
|
||||
log(' 重量:', await setItem('重量', 'input:not([type=hidden])', IN.weight));
|
||||
const dim = 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||'');});
|
||||
if(!it)return'nf'; const ins=[...it.querySelectorAll('input')];
|
||||
const setV=(el,v)=>{ Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,String(v));
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); };
|
||||
const L=ins.find(i=>i.placeholder==='长')||ins[0], W2=ins.find(i=>i.placeholder==='宽')||ins[1], H=ins.find(i=>i.placeholder==='高')||ins[2];
|
||||
if(L)setV(L,${DIMS.L}); if(W2)setV(W2,${DIMS.Wi}); if(H)setV(H,${DIMS.H}); return 'ok:'+ins.length;})()`);
|
||||
log('5) 尺寸:', dim, JSON.stringify(DIMS));
|
||||
|
||||
// 5. 图片
|
||||
const ir = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const st=p._s.get('shopeeGlobalImageInfoStore').$state;
|
||||
const urls=${JSON.stringify(IN.imgs)};
|
||||
st.imageInfo.mainImages=urls.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800,checked:true}));
|
||||
return 'imgs:'+st.imageInfo.mainImages.length;})()`);
|
||||
log('6) 图片:', ir, '(共', IN.imgs.length, ')');
|
||||
await wait(2500);
|
||||
|
||||
log('7) 来源URL:', await setItem('来源URL', 'input:not([type=hidden])', 'https://detail.1688.com/offer/' + OID + '.html'));
|
||||
await wait(1200);
|
||||
|
||||
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const b=p._s.get('shopeeGlobalBasicStore').$state;
|
||||
return JSON.stringify({shopId:b.formState.shopId, cur:b.dataState.currency});})()`);
|
||||
log('8) 保存前:', st);
|
||||
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('9) 保存:', sv);
|
||||
await wait(9000);
|
||||
const msg = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null)
|
||||
.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,170)).filter(Boolean); return JSON.stringify({url:location.href, msgs:ms.slice(0,4)});})()`);
|
||||
log('10) 结果:', SAN(String(msg)).slice(0, 400));
|
||||
await c.shot('/tmp/sp_pilot.jpeg').catch(() => {});
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,94 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
|
||||
/* 就地改 Shopee 产品类目:node spfixcat.mjs <productId> <title> <kw1,kw2,...> */
|
||||
const PID = process.argv[2];
|
||||
const TITLE = process.argv[3] || '';
|
||||
const KWS = (process.argv[4] || '').split(',').filter(Boolean);
|
||||
if (!PID || !KWS.length) { console.log('用法: node spfixcat.mjs <productId> <title> <kw1,kw2>'); process.exit(1); }
|
||||
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const log = (...a) => console.log(' ', ...a);
|
||||
const IS_KID = /baby|kid|infant|toddler|children|newborn/i.test(TITLE);
|
||||
const IS_AUTO = /car|auto|moto|vehicle|truck|bicycle|bike/i.test(TITLE);
|
||||
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x => x.type === 'page' && /shopee/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {});
|
||||
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/shopeeGlobal/edit?id=${PID}` });
|
||||
await wait(26000);
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const x = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
|
||||
if(!ms.length)return'n'; ms.forEach(m=>{const cb=m.querySelector('.ant-modal-close'); if(cb&&cb.offsetParent!==null)cb.click();}); return 'c';})()`);
|
||||
if (x === 'n') break; await wait(1000);
|
||||
}
|
||||
await wait(2000);
|
||||
log('URL:', await c.evalJS('location.href'));
|
||||
log('店铺:', 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&&it.querySelector('.ant-select-selection-item'); return s?s.innerText.trim():'';})()`));
|
||||
|
||||
async function pickCategory(kw) {
|
||||
await c.evalJS(`(()=>{document.querySelectorAll('.ant-select-dropdown').forEach(d=>{if(d.offsetParent!==null)d.style.display='none';}); document.body.click(); return 1;})()`);
|
||||
await wait(900);
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const b=[...it.querySelectorAll('button,a,span')].filter(e=>e.offsetParent!==null&&/^\\s*选择分类\\s*$/.test((e.innerText||'').trim()));
|
||||
if(!b.length)return'[]'; const el=b[b.length-1]; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if (!pos.length) return 'no-btn';
|
||||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: pos[0], y: pos[1], button: 'left', clickCount: 1 });
|
||||
await wait(3500);
|
||||
const r = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const inp=m.querySelector('input[placeholder="搜索分类名称"]'); if(!inp)return'no-input';
|
||||
inp.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(inp,${JSON.stringify(kw)});
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
const b=[...m.querySelectorAll('button')].find(x=>/^\\s*搜索\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'searched';})()`);
|
||||
if (r !== 'searched') return r;
|
||||
await wait(4000);
|
||||
const pick = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const its=[...m.querySelectorAll('.search-result-item')]; if(!its.length)return'no-result';
|
||||
const BAD=/二手|Second-?hand|Used|Pre-?owned|Refurb|Sexual|Sex Toy|情趣|Adult Toy|Dewasa/i;
|
||||
const KID=/Bayi|Baby|Kids|Kid|Infant|Toddler|Children|Kanak/i;
|
||||
const AUTO=/Automobile|Automotive|Motosikal|汽车|车贴|Emblem/i;
|
||||
const ISK=${IS_KID}, ISA=${IS_AUTO};
|
||||
const KW=${JSON.stringify(kw)}.toLowerCase();
|
||||
const texts=its.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
const ok=t=>!BAD.test(t)&&(ISK||!KID.test(t))&&(ISA||!AUTO.test(t));
|
||||
const lastSeg=t=>{const a=t.split('/'); return (a[a.length-1]||'').replace(/\\(.*?\\)/g,'').trim().toLowerCase();};
|
||||
let idx=texts.findIndex(t=>ok(t)&&lastSeg(t).includes(KW));
|
||||
if(idx<0) idx=texts.findIndex(t=>ok(t)&&t.toLowerCase().includes(KW));
|
||||
if(idx<0) idx=texts.findIndex(t=>ok(t));
|
||||
if(idx<0) idx=texts.findIndex(t=>!BAD.test(t));
|
||||
if(idx<0) idx=0;
|
||||
const hit=its[idx];
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked['+idx+'/'+texts.length+']:'+texts[idx];})()`);
|
||||
await wait(4500);
|
||||
return pick;
|
||||
}
|
||||
|
||||
let done = null;
|
||||
for (const kw of KWS) {
|
||||
const r = String(await pickCategory(kw));
|
||||
log(' ', kw, '→', SAN(r).slice(0, 130));
|
||||
if (!/no-result|no-input|no-modal|no-btn/.test(r)) { done = r; break; }
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(m){const x=m.querySelector('.ant-modal-close'); if(x&&x.offsetParent!==null)x.click();} return 1;})()`);
|
||||
await wait(1500);
|
||||
}
|
||||
if (!done) { log('❌ 全部关键词无结果'); process.exit(2); }
|
||||
|
||||
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('保存:', sv);
|
||||
await wait(9000);
|
||||
const msg = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice')].filter(e=>e.offsetParent!==null)
|
||||
.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,140)).filter(Boolean); return JSON.stringify(ms.slice(0,3));})()`);
|
||||
log('结果:', SAN(String(msg)).slice(0, 260));
|
||||
process.exit(done ? 0 : 2);
|
||||
@@ -0,0 +1,58 @@
|
||||
# Temu 全托管(①Alimingpro, shopId 4021529)进展
|
||||
|
||||
## 已勘明
|
||||
- 类目:1038 个,直接从 `temuAddBasicStore.dataState.categories` 读(不用滚动)
|
||||
- 选类目:下拉需**先 scrollTop=0**(默认残留上次滚动位置)→ 滚动找 `.ant-select-item-option`
|
||||
- 外包装形状:不规则 / 长方体 / 圆柱体
|
||||
- 外包装类型:硬包装 / 软包装+硬物 / 软包装+软物
|
||||
- 字段落点:
|
||||
- `temuAddProductStore.formState.mainImage` → **数组**
|
||||
- `temuAddProductStore.formState.materialImage` → **对象**(单个)
|
||||
- `temuAddOtherStore.packageFormState` → `{packageShape, packageType, packageImages, inventoryRegion}`
|
||||
- `temuAddSkuAttrStore.formState.skuAttrs` → `[{type:2,parentSpecId:1001,parentSpecName:"颜色",options:[]}]`
|
||||
- `temuAddSkuDataStore.formState.skuDataList` → SKU 行
|
||||
- 必填 9 项:店铺/分类/标题/产地/轮播图/素材图/外包装形状/外包装类型/外包装图片
|
||||
|
||||
## 未解(卡点)
|
||||
1. 点「保存」不发任何 POST,且无校验红字 → 静默拦截
|
||||
2. 程序化塞 `skuAttrs[0].options` **不生成** `skuDataList` 行(需走 UI)
|
||||
3. 变种区只渲染出主题选择器(颜色/风格/材质…),**值输入控件没找到**;变种本身也非必填
|
||||
4. `产品素材图` 在 DOM 里仍显示为空(store 已设)
|
||||
5. 表单下方未发现价格/库存输入(可能随变种行生成)
|
||||
|
||||
---
|
||||
## 更新:静默拦截已定位(用 MYJ.message.error 钩子)
|
||||
|
||||
Temu 被吞掉的报错是**层层递进**的:
|
||||
1. ~~`产地选择中国时,省份必填,请完善后提交`~~ → 已解决
|
||||
路径:`temuAddProductStore.formState.region2Id`(候选在 `region2Id` 同级 `regionCode`,31 个省)
|
||||
注意:直接赋值 store 当时不生效,但**异步同步后 UI 会显示「北京市」**,再点保存即通过
|
||||
2. **`产品变种不能为空!`** ← 当前唯一剩下的卡点
|
||||
需要真正生成变种行(`temuAddSkuDataStore.formState.skuDataList`)
|
||||
程序化塞 `temuAddSkuAttrStore.skuAttrs[0].options` **无效**
|
||||
|
||||
## 变种区控件(待继续)
|
||||
- `.ant-select` #16 = 颜色主题选择器(主题候选:颜色/风格/材质/口味/…共18个)
|
||||
- `.ant-select` #17 = 尺码
|
||||
- 未找到独立的值输入框;值控件可能随主题选中后异步渲染
|
||||
|
||||
---
|
||||
## 更新2:照 SHEIN 的解法推进,报错序列已剥到最后一层
|
||||
|
||||
Temu 的规格机制和 SHEIN **不同构**——没有 `theme-select-box`,
|
||||
而是 **两个 `.theme-value-add` 块**(颜色 / 尺码),每块各有「值输入框(`input[class*="!w-200"]`) + 添加按钮」。
|
||||
**值必须手动输进去再点「添加」**,程序化塞进 `skuAttrs[0].options` 完全无效。
|
||||
|
||||
### 报错序列(每修完一层才露出下一层)
|
||||
| # | 报错 | 状态 | 解法 |
|
||||
|---|---|---|---|
|
||||
| 1 | 产地选择中国时,省份必填 | ✅ | `temuAddProductStore.formState.region2Id`(异步同步,需再点一次保存) |
|
||||
| 2 | 产品变种不能为空! | ✅ | 两个 theme-value-add 各输一个值点「添加」→ `skuDataList` 生成 2 行 |
|
||||
| 3 | 尺寸不能为0或空 | ✅ | SKU 表「尺寸(cm)」列每行有 **3 个子输入**(长/宽/高),要全填 |
|
||||
| 4 | 净重必须小于重量 | ✅ | 净重填 90、重量填 100(不能相等) |
|
||||
| 5 | **请上传预览图** | ⬜ 当前 | SKU 表「预览图」列需真实图片,不是文本 |
|
||||
|
||||
### SKU 表列(Temu 全托管)
|
||||
`预览图 | 颜色 | 尺码 | SKU货号 | EAN UPC ISBN | 申报价格(CNY) | 尺寸(cm) | 重量(g) | 净重(g) | 建议售价 | SKU分类`
|
||||
|
||||
⚠️ EAN 用公司台账下一段:pc 3447 起(Walmart 已用 3400~3446)
|
||||
@@ -0,0 +1,479 @@
|
||||
[
|
||||
"墙壁装饰图案",
|
||||
"遥控器",
|
||||
"瓷砖贴玻璃贴",
|
||||
"活页夹、回形针",
|
||||
"纽扣",
|
||||
"美发刷",
|
||||
"线缆收纳包和盒子",
|
||||
"护照夹、证件包",
|
||||
"行李牌",
|
||||
"女士钥匙环、钥匙链",
|
||||
"急救包",
|
||||
"手工艺胶带",
|
||||
"牙刷架",
|
||||
"登山扣饰和钥匙扣",
|
||||
"创可贴",
|
||||
"圆环活页夹",
|
||||
"其他(男装配饰)",
|
||||
"其他(墙贴和壁饰)",
|
||||
"办公隔间挂钩",
|
||||
"女士瑜伽瑜伽袜",
|
||||
"鼠标垫",
|
||||
"彩色文件夹",
|
||||
"手机装饰品",
|
||||
"女士胸针、领针、丝巾扣",
|
||||
"学科笔记本",
|
||||
"汽车挂饰",
|
||||
"别针夹子盒",
|
||||
"桌面订书机",
|
||||
"手动卷发器",
|
||||
"化妆粉扑和海绵",
|
||||
"MP3 播放器保护套和外壳",
|
||||
"男士眼镜盒",
|
||||
"头发定型别针",
|
||||
"气球",
|
||||
"其他(化妆工具)",
|
||||
"荧光笔",
|
||||
"记事簿",
|
||||
"笔记本电脑贴花",
|
||||
"安全别针",
|
||||
"剪刀",
|
||||
"其他(橡皮)",
|
||||
"贺卡",
|
||||
"反光衣/反光安全装备",
|
||||
"其他(包裹耗材)",
|
||||
"挤压玩具",
|
||||
"婴幼儿抽屉锁",
|
||||
"水族馆装饰主题摆件",
|
||||
"其他(新奇玩具)",
|
||||
"女士时尚束发带",
|
||||
"辅助挂钩",
|
||||
"盥洗洗漱包",
|
||||
"头发侧梳",
|
||||
"油漆笔和马克笔",
|
||||
"记事簿配件",
|
||||
"电话支架",
|
||||
"空白标签牌",
|
||||
"便携式包袋挂钩",
|
||||
"搞笑整蛊玩具",
|
||||
"其他(发型设计配件)",
|
||||
"女士薄丝袜",
|
||||
"美工刀",
|
||||
"削笔刀",
|
||||
"其他(日历及个人规划用品)",
|
||||
"胶水胶棒",
|
||||
"其他(教学耗材)",
|
||||
"线缆带",
|
||||
"其他(教室装饰)",
|
||||
"棉签",
|
||||
"拉链",
|
||||
"女婴袜子",
|
||||
"化妆品收纳盒",
|
||||
"书立",
|
||||
"自行车把套",
|
||||
"自行车喇叭",
|
||||
"门档",
|
||||
"耳塞",
|
||||
"海绵",
|
||||
"鞋垫",
|
||||
"午餐包、午餐袋",
|
||||
"工艺扇",
|
||||
"女婴发夹",
|
||||
"吉他背带",
|
||||
"冰块模具和托盘",
|
||||
"活页纸",
|
||||
"其他(办公家具配件)",
|
||||
"明信片",
|
||||
"其他(马克笔荧光笔标记笔)",
|
||||
"办公簿册装订环",
|
||||
"图钉",
|
||||
"手机挂绳与腕带",
|
||||
"智能手机拉环支架",
|
||||
"眉毛修剪器",
|
||||
"抽绳健身包",
|
||||
"洗脸毛巾",
|
||||
"其他(钓鱼用品)",
|
||||
"室内装饰石",
|
||||
"手动牙刷",
|
||||
"保险杠贴纸",
|
||||
"扁平餐具收纳",
|
||||
"行李整理包",
|
||||
"起钉器",
|
||||
"卷尺",
|
||||
"直尺",
|
||||
"自动铅笔",
|
||||
"绘图工具和套件",
|
||||
"显示器支架",
|
||||
"办公纸夹",
|
||||
"办公胶带座",
|
||||
"工艺粘合剂打蜡",
|
||||
"脱毛镊子",
|
||||
"其他(飞钓饵)",
|
||||
"小雕像摆件",
|
||||
"女士时尚垂坠式耳环",
|
||||
"其他大家电配件",
|
||||
"杯垫",
|
||||
"玻璃贴膜",
|
||||
"礼品袋",
|
||||
"卡纸",
|
||||
"流苏",
|
||||
"自行车水壶架",
|
||||
"真空压缩袋",
|
||||
"奶嘴链",
|
||||
"透明胶",
|
||||
"睫毛膏",
|
||||
"化妆造型用品",
|
||||
"女士发带",
|
||||
"纸张打孔器",
|
||||
"隔页板",
|
||||
"木质印章",
|
||||
"书托架",
|
||||
"其他(信封)",
|
||||
"办公家具靠垫、坐垫",
|
||||
"指南针",
|
||||
"其他(男士袜子)",
|
||||
"急救胶带",
|
||||
"搓澡巾",
|
||||
"舌刷、舌刮",
|
||||
"其他(派对装饰)",
|
||||
"猫薄荷玩具",
|
||||
"其他(益智思维游戏)",
|
||||
"女童新奇钱包",
|
||||
"口袋插笔套",
|
||||
"面部装饰贴",
|
||||
"礼品包装袋",
|
||||
"瓶塞",
|
||||
"圆髻和皇冠发型成型器",
|
||||
"可再填充的化妆品喷雾瓶",
|
||||
"缝纫绳扣和调节扣",
|
||||
"化妆刷套件",
|
||||
"内衬袜、船袜",
|
||||
"齿间牙签",
|
||||
"车用香水、空气清新",
|
||||
"三环装订相册",
|
||||
"美甲模板",
|
||||
"装饰挂钩",
|
||||
"棒棒糖、蛋糕模具",
|
||||
"手动头部按摩器",
|
||||
"化妆刷清洁用品",
|
||||
"办公收纳用品",
|
||||
"切纸机刀片",
|
||||
"键盘托架",
|
||||
"桌面吸尘器",
|
||||
"钱袋",
|
||||
"文具记事本",
|
||||
"橡皮擦",
|
||||
"自行车座杆",
|
||||
"其他(健身、训练用品)",
|
||||
"儿童画画衣",
|
||||
"微型创意玩具",
|
||||
"男婴袜子",
|
||||
"汽车摆件",
|
||||
"眉笔、眉粉",
|
||||
"清洁刷",
|
||||
"浇水壶",
|
||||
"眼影刷",
|
||||
"多用工具",
|
||||
"嫁接睫毛工具",
|
||||
"马桶垫",
|
||||
"手工折纸",
|
||||
"女士腰带",
|
||||
"手机防水袋",
|
||||
"蜡式脱毛刮片",
|
||||
"拉力带",
|
||||
"其他(皮肤护理)",
|
||||
"时尚、休闲腰包",
|
||||
"粉底刷",
|
||||
"其他(手机配件)",
|
||||
"折纸",
|
||||
"毛球、绒毛去除器",
|
||||
"食品过滤器",
|
||||
"装饰纸花球",
|
||||
"手机支架",
|
||||
"悬挂式鞋子收纳袋",
|
||||
"保湿脚套",
|
||||
"帽子",
|
||||
"阿育吠陀香膏和药膏",
|
||||
"猫清洁按摩刷",
|
||||
"婴幼儿防撞用品",
|
||||
"女士情趣连体衣、连体袜",
|
||||
"烫衣板",
|
||||
"其他(女婴发饰)",
|
||||
"女士钱包",
|
||||
"口红",
|
||||
"化妆品",
|
||||
"婴幼儿温度计",
|
||||
"S形挂钩",
|
||||
"其他(清洁工具)",
|
||||
"办公室计算器配件",
|
||||
"其他(商务公文包及电脑包)",
|
||||
"其他(切纸刀)",
|
||||
"双肩电脑包",
|
||||
"高线画笔",
|
||||
"粘合剂、胶水",
|
||||
"笔式橡皮",
|
||||
"强力胶",
|
||||
"索引卡归档和名片归档",
|
||||
"钥匙箱",
|
||||
"快递文件袋",
|
||||
"针尖式水笔",
|
||||
"D环活页夹",
|
||||
"女士小腿袜",
|
||||
"医用压缩丝袜",
|
||||
"茶包碟",
|
||||
"固定夹",
|
||||
"茶勺",
|
||||
"个人用电风扇",
|
||||
"压蒜器、蒜泥器",
|
||||
"钩子线",
|
||||
"狗飞盘",
|
||||
"手指套",
|
||||
"运动风汽车贴纸",
|
||||
"下水道疏通器",
|
||||
"编织制作工具",
|
||||
"狗保护罩",
|
||||
"女士新奇短袜",
|
||||
"女婴帽子",
|
||||
"角色扮演骰子",
|
||||
"运动胶带、绷带",
|
||||
"其他(大家电配件)",
|
||||
"后变速器",
|
||||
"化妆镜",
|
||||
"生日蜡烛",
|
||||
"派对用品套装",
|
||||
"食物存放和整理套装",
|
||||
"电源转换器",
|
||||
"手写笔",
|
||||
"透明印章",
|
||||
"女童装扮假发",
|
||||
"墙纸、壁纸",
|
||||
"其他(挂件吊饰)",
|
||||
"钓鱼帽",
|
||||
"派对钥匙圈礼品包",
|
||||
"女士斜挎包",
|
||||
"狗用理容梳",
|
||||
"女士盒剃刀",
|
||||
"双眼皮贴",
|
||||
"其他(饰品收纳)",
|
||||
"口腔温度计",
|
||||
"垃圾袋收纳器",
|
||||
"毛绒动物配件",
|
||||
"自行车配件",
|
||||
"百洁布",
|
||||
"其他(刷子)",
|
||||
"睡袍和浴巾挂钩",
|
||||
"小便池配件",
|
||||
"马桶刷、架",
|
||||
"游泳鼻夹",
|
||||
"渔具卡扣",
|
||||
"磨脚石",
|
||||
"门吊挂钩",
|
||||
"首饰托盘",
|
||||
"门锁",
|
||||
"行李秤",
|
||||
"旅行鞋子收纳包",
|
||||
"吉他拨片",
|
||||
"展示画架",
|
||||
"马桶替换件",
|
||||
"女士时尚贴颈项链",
|
||||
"女士户外袜子",
|
||||
"胶粘防撞垫",
|
||||
"男士新奇补丁贴布",
|
||||
"车迷钥匙链",
|
||||
"男士运动束发带",
|
||||
"日常脚部和指甲护理产品",
|
||||
"其他(面部护理工具)",
|
||||
"倒酒器",
|
||||
"商用去毛器",
|
||||
"USB 数据线",
|
||||
"其他(派对装饰品和用品)",
|
||||
"替换胶绵",
|
||||
"手动背部按摩器",
|
||||
"PlayStation Vita 保护套",
|
||||
"其他(熨烫用品)",
|
||||
"束发带",
|
||||
"时尚束发带",
|
||||
"加长型睫毛膏和打底膏",
|
||||
"女士运动紧身袜",
|
||||
"化妆棉",
|
||||
"日用鞋扣和鞋带辅助用品",
|
||||
"室内灯串",
|
||||
"水烟炭夹具",
|
||||
"缠绕护膝/缠绕运动绷带",
|
||||
"烧烤工具套件",
|
||||
"指甲清洁刷",
|
||||
"其他(端口转接头)",
|
||||
"女孩钱包",
|
||||
"洒水器",
|
||||
"男士新奇钥匙链",
|
||||
"日程表本",
|
||||
"挂孔纸",
|
||||
"考勤卡",
|
||||
"手提、单肩电脑包",
|
||||
"男士证件夹",
|
||||
"扫描仪配件",
|
||||
"可清除标签",
|
||||
"文件夹标签",
|
||||
"信件和信笺横格纸",
|
||||
"校对夹",
|
||||
"杂志架",
|
||||
"其他(索引卡归档产品)",
|
||||
"木制彩色铅笔",
|
||||
"其他(运动医学产品)",
|
||||
"管道胶带",
|
||||
"多功能书写工具",
|
||||
"圆规",
|
||||
"织物胶粘剂、粘合剂",
|
||||
"圣诞袜、节日装饰袜",
|
||||
"女袜袜套",
|
||||
"笔记本散热器",
|
||||
"女士围脖",
|
||||
"儿童装扮面罩",
|
||||
"一次性吸管",
|
||||
"水族箱入门套件",
|
||||
"其他(键盘乐器配件)",
|
||||
"公用叉勺服务餐具套装",
|
||||
"其他(装扮珠宝首饰)",
|
||||
"摩托车头盔面罩",
|
||||
"飘带",
|
||||
"手机防尘插头",
|
||||
"雕刻刀",
|
||||
"发条、弹簧类玩具",
|
||||
"挞盘",
|
||||
"婴幼儿牙刷",
|
||||
"拼图游戏",
|
||||
"女婴半身裙、裙裤",
|
||||
"其他(女童服饰配件)",
|
||||
"汽车牌照套",
|
||||
"其他(女童帽子)",
|
||||
"大码女士薄丝袜",
|
||||
"女婴连指、分指手套",
|
||||
"安抚奶嘴",
|
||||
"牙胶",
|
||||
"粘土",
|
||||
"游泳耳塞",
|
||||
"锅具配件",
|
||||
"毛绒公仔",
|
||||
"女士腕包",
|
||||
"相片角贴",
|
||||
"专业水笔",
|
||||
"女士时尚个性戒指",
|
||||
"锅盖架",
|
||||
"烤炉垫和地垫",
|
||||
"邮差包",
|
||||
"其他(眼睛护理)",
|
||||
"指甲刷",
|
||||
"指甲油固化灯",
|
||||
"药丸压碎器和切药丸器",
|
||||
"收纳篮",
|
||||
"洗车工具",
|
||||
"灶具旋钮",
|
||||
"其他(面部护肤)",
|
||||
"防磨脚贴",
|
||||
"熨衣板盖",
|
||||
"家庭马桶清洁剂",
|
||||
"男士项链",
|
||||
"实验室洗瓶",
|
||||
"其他(工艺品和缝纫用品储物)",
|
||||
"眼罩",
|
||||
"空白便条卡套装",
|
||||
"装饰性铃",
|
||||
"其他(女士手提包和钱包)",
|
||||
"相机隐私保护盖",
|
||||
"仿真食物",
|
||||
"中转袋",
|
||||
"眼线刷",
|
||||
"女士时尚珠子吊饰",
|
||||
"熨烫配件",
|
||||
"钢丝刷",
|
||||
"浴衣和毛巾挂钩",
|
||||
"婴幼儿指甲刀",
|
||||
"空顶遮阳帽",
|
||||
"男士户外帽子",
|
||||
"墙贴和壁饰",
|
||||
"文胸收纳包",
|
||||
"骰子游戏",
|
||||
"车载水杯架",
|
||||
"方格置物架",
|
||||
"自行车刹车片衬垫",
|
||||
"地毯",
|
||||
"女士空顶遮阳帽",
|
||||
"杯垫、隔热垫",
|
||||
"网格本与数据本",
|
||||
"女士时尚手串",
|
||||
"其他(卫浴毛巾)",
|
||||
"车身维修凹坑清除工具",
|
||||
"反光装置",
|
||||
"缝纫穿线器",
|
||||
"摩托车把手配件",
|
||||
"户外休闲袜",
|
||||
"商用食品袋封口器",
|
||||
"户外筷子、刀、叉",
|
||||
"其他(公用叉勺服务餐具)",
|
||||
"土豆捣碎器",
|
||||
"魔鬼蛋托盘",
|
||||
"浴室托盘",
|
||||
"战术袋",
|
||||
"甜点叉",
|
||||
"珠宝戒指坯与配件",
|
||||
"花园微缩雕塑",
|
||||
"首饰盒",
|
||||
"水果和蔬菜去核机",
|
||||
"虱子梳",
|
||||
"裤子衣架",
|
||||
"徒步旅行背包",
|
||||
"壁挂酒架",
|
||||
"其他(镜头配件)",
|
||||
"充气设备配件",
|
||||
"胡桃夹子",
|
||||
"餐饮服务工具和餐具",
|
||||
"巧克力模具",
|
||||
"饼干模具",
|
||||
"创意造型蛋糕模具",
|
||||
"小动物喂食碗",
|
||||
"刀盒、刀具收纳和保护器",
|
||||
"配件",
|
||||
"房车配件",
|
||||
"装饰杆头",
|
||||
"蛋糕装饰",
|
||||
"派对照相亭道具",
|
||||
"悬挂厨房篮",
|
||||
"开瓶器",
|
||||
"床单紧固件",
|
||||
"游泳装备包",
|
||||
"汽车遮阳板",
|
||||
"玻璃杯",
|
||||
"男士编织手链",
|
||||
"地刮、玻璃刮板",
|
||||
"女士时尚手链",
|
||||
"脚垫",
|
||||
"女士时尚开口手镯",
|
||||
"女士脚链",
|
||||
"收纳箱",
|
||||
"双筒望远镜、相机、摄像机挂带及背包",
|
||||
"猫抓板",
|
||||
"乐谱文件夹",
|
||||
"相册和相簿",
|
||||
"清洁海绵",
|
||||
"首饰架",
|
||||
"工艺品收纳盒和整理器",
|
||||
"发饰(头发装饰)",
|
||||
"女士防寒连指手套",
|
||||
"其他(面部护理和面膜)",
|
||||
"其他(狗清洁用品)",
|
||||
"浴球",
|
||||
"粘土模具",
|
||||
"其他(线缆管理)",
|
||||
"首饰链子",
|
||||
"派对头饰",
|
||||
"珠饰套件",
|
||||
"卫生纸架",
|
||||
"吉他维修工具",
|
||||
"墙贴",
|
||||
"女士公交卡套",
|
||||
"平板电脑贴纸",
|
||||
"车用收纳",
|
||||
"海绵粉扑",
|
||||
"面膜"
|
||||
]
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"items": [
|
||||
"* 店铺名称 [select] ①Alimingpro",
|
||||
"* 产品分类 [input] ",
|
||||
" 产品属性 [-] ",
|
||||
" 店小秘分类 [-] ",
|
||||
" 来源URL [input] ",
|
||||
" [input] ",
|
||||
"* 产品标题 [input] ",
|
||||
" 英文标题 [input] ",
|
||||
" 产品货号 [input] ",
|
||||
"* 产地 [select] 中国大陆",
|
||||
" 制造地点 [select] 中国大陆",
|
||||
" 产地说明书 [input] ",
|
||||
" 站外产品链接 [input] ",
|
||||
" 敏感属性 [input] 1",
|
||||
" 定制产品 [input] 1",
|
||||
"* 产品轮播图 [input] on",
|
||||
"* 产品素材图 [-] ",
|
||||
" 产品视频 [-] ",
|
||||
"* 外包装形状 [input] ",
|
||||
"* 外包装类型 [input] ",
|
||||
"* 外包装图片 [input] on",
|
||||
" 产品说明书 [input] ",
|
||||
" 描述视频 [-] ",
|
||||
" 产品描述 [-] "
|
||||
],
|
||||
"tables": [],
|
||||
"dump": {
|
||||
"temuAddProductStore": {
|
||||
"formState": {
|
||||
"mainImage": "[]",
|
||||
"videoData": {},
|
||||
"productNumber": "",
|
||||
"productName": "",
|
||||
"productEnName": "",
|
||||
"outerGoodsUrl": "",
|
||||
"productOrigin": "CN",
|
||||
"region2Id": "",
|
||||
"region1ShortNames": "[1] \"CN\"",
|
||||
"originFileUrl": "",
|
||||
"checkedSensitiveList": "[]",
|
||||
"sensitiveState": 0,
|
||||
"batteryCapacity": "",
|
||||
"maxLiquidCapacity": "",
|
||||
"maxKnifeLength": "",
|
||||
"knifeAngle": "",
|
||||
"personalizationSwitch": 0,
|
||||
"technology": 1,
|
||||
"firstType": "",
|
||||
"twiceType": "[]",
|
||||
"invitation": {
|
||||
"invitationName": "",
|
||||
"siteName": "",
|
||||
"id": ""
|
||||
}
|
||||
},
|
||||
"dataState": {
|
||||
"newSizeGroups": "[]",
|
||||
"newBootSizeGroups": "[]"
|
||||
},
|
||||
"countryCode": "[282] {\"value\":\"CN\",\"label\":\"中国大陆\"}",
|
||||
"regionCode": "[32] {\"value\":\"\",\"label\":\"请选择省份\"}",
|
||||
"title_limit": {
|
||||
"nameMin": 0,
|
||||
"nameMax": 500
|
||||
},
|
||||
"img_limit": {
|
||||
"popMinImgNum": 3,
|
||||
"pddkjMinImgNum": 5,
|
||||
"maxImgNum": 10
|
||||
}
|
||||
},
|
||||
"temuAddSkuDataStore": {
|
||||
"formState": {
|
||||
"skuDataList": "[]",
|
||||
"sitePrice": false,
|
||||
"codeType": "1"
|
||||
},
|
||||
"dataState": {
|
||||
"warehouseList": "[]",
|
||||
"goodsTemplateList": "[]",
|
||||
"warehouseCheckedList": "[]"
|
||||
}
|
||||
},
|
||||
"temuAddSkuAttrStore": {
|
||||
"formState": {
|
||||
"skuAttrs": "[]",
|
||||
"basePlateType": 0,
|
||||
"sizeChartList": "[]"
|
||||
},
|
||||
"dataState": {
|
||||
"newBootSizeGroups": "[]",
|
||||
"newSizeGroups": "[]",
|
||||
"customThemeOptions": "[]",
|
||||
"templateList": "[]"
|
||||
},
|
||||
"customAttrOptions": {}
|
||||
},
|
||||
"temuAddBasicStore": {
|
||||
"formState": {
|
||||
"shopId": "4021529",
|
||||
"productSemiManagedReq": "100"
|
||||
},
|
||||
"dataState": {
|
||||
"categories": "[1038] {\"id\":4810363372086185,\"puid\":70000,\"platform\":\"pddkj\",\"site\":null,\"shopId\":4021529,\"categoryId\":\"1432\",\"num\":385,\"classType\":-1,\"createTime\":null,\"updateTime\":null,\"createId\":null,\"nameZh\":\"证件内页\",\"nameEn\":\"证件内页\",\"nodePath\":\"办公用品/日常办公用品/标签指",
|
||||
"promiseTemplateList": "[]",
|
||||
"freightTemplateList": "[]",
|
||||
"commonHasNewBootSize": false,
|
||||
"commonHasNewSize": false,
|
||||
"categoryPathName": "",
|
||||
"categoryPathIds": "[]"
|
||||
},
|
||||
"keywordShowData": {
|
||||
"attrKeywords": "[]",
|
||||
"hotKeywords": "[]",
|
||||
"upKeywords": "[]",
|
||||
"lessKeywords": "[]"
|
||||
}
|
||||
},
|
||||
"temuAddAttrStore": {
|
||||
"formState": {
|
||||
"customAttrs": "[]",
|
||||
"motorIds": "[]",
|
||||
"motorAfid": "[]",
|
||||
"templateData": "[]"
|
||||
},
|
||||
"dataState": {
|
||||
"motorTemplate": "[]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"stores": [
|
||||
"temuCarDataStore",
|
||||
"temuAddProductStore",
|
||||
"temuAddStore",
|
||||
"temuAddAttrStore",
|
||||
"temuAddSkuDataStore",
|
||||
"temuAddSkuAttrStore",
|
||||
"temuAddBasicStore",
|
||||
"temuAddOtherStore"
|
||||
],
|
||||
"items": [
|
||||
"* 店铺名称 [select] ①Alimingpro",
|
||||
"* 产品分类 [input] ",
|
||||
" 产品属性 [-] ",
|
||||
" 店小秘分类 [-] ",
|
||||
" 来源URL [input] ",
|
||||
" [input] ",
|
||||
"* 产品标题 [input] ",
|
||||
" 英文标题 [input] ",
|
||||
" 产品货号 [input] ",
|
||||
"* 产地 [select] 中国大陆",
|
||||
" 制造地点 [select] 中国大陆",
|
||||
" 产地说明书 [input] ",
|
||||
" 站外产品链接 [input] ",
|
||||
" 敏感属性 [input] 1",
|
||||
" 定制产品 [input] 1",
|
||||
"* 产品轮播图 [input] on",
|
||||
"* 产品素材图 [-] ",
|
||||
" 产品视频 [-] ",
|
||||
"* 外包装形状 [input] ",
|
||||
"* 外包装类型 [input] ",
|
||||
"* 外包装图片 [input] on",
|
||||
" 产品说明书 [input] ",
|
||||
" 描述视频 [-] ",
|
||||
" 产品描述 [-] "
|
||||
],
|
||||
"dump": {
|
||||
"temuCarDataStore": {
|
||||
"supportCarAttr": "false",
|
||||
"supportCarCategory": "false",
|
||||
"showBackStatus": "loadingFinish"
|
||||
},
|
||||
"temuAddProductStore": {
|
||||
"formState.productOrigin": "CN",
|
||||
"formState.sensitiveState": "0",
|
||||
"formState.personalizationSwitch": "0",
|
||||
"formState.technology": "1",
|
||||
"title_limit.nameMin": "0",
|
||||
"title_limit.nameMax": "500",
|
||||
"img_limit.popMinImgNum": "3",
|
||||
"img_limit.pddkjMinImgNum": "5",
|
||||
"img_limit.maxImgNum": "10"
|
||||
},
|
||||
"temuAddStore": {
|
||||
"attrsInfo.categoryType": "-1",
|
||||
"attrsInfo.customThemeNum": "0",
|
||||
"dataState.supportBasePlat": "false"
|
||||
},
|
||||
"temuAddAttrStore": {},
|
||||
"temuAddSkuDataStore": {
|
||||
"formState.sitePrice": "false",
|
||||
"formState.codeType": "1"
|
||||
},
|
||||
"temuAddSkuAttrStore": {
|
||||
"formState.basePlateType": "0"
|
||||
},
|
||||
"temuAddBasicStore": {
|
||||
"formState.shopId": "4021529",
|
||||
"formState.productSemiManagedReq": "100",
|
||||
"dataState.commonHasNewBootSize": "false",
|
||||
"dataState.commonHasNewSize": "false"
|
||||
},
|
||||
"temuAddOtherStore": {
|
||||
"dataState.isJoinChoice": "false",
|
||||
"dataState.isShowQualificationModal": "false",
|
||||
"packageFormState.inventoryRegion": "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"548459586741": {
|
||||
"cat": "口哨",
|
||||
"catId": "18723"
|
||||
},
|
||||
"661190598139": {
|
||||
"cat": "发圈和头绳",
|
||||
"catId": "19285"
|
||||
},
|
||||
"764423265986": {
|
||||
"cat": "口腔温度计",
|
||||
"catId": "8167"
|
||||
},
|
||||
"685456757807": {
|
||||
"cat": "编织绳",
|
||||
"catId": "39534"
|
||||
},
|
||||
"735147938123": {
|
||||
"cat": "女士发箍",
|
||||
"catId": "29276"
|
||||
},
|
||||
"627879474686": {
|
||||
"cat": "女婴袜子",
|
||||
"catId": "26923"
|
||||
},
|
||||
"610164857063": {
|
||||
"cat": "女士发箍",
|
||||
"catId": "29276"
|
||||
},
|
||||
"676236596821": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"708420302844": {
|
||||
"cat": "女士钥匙环、钥匙链",
|
||||
"catId": "29284"
|
||||
},
|
||||
"866537106657": {
|
||||
"cat": "纽扣",
|
||||
"catId": "39592"
|
||||
},
|
||||
"547183997437": {
|
||||
"cat": "女士时尚链式项链",
|
||||
"catId": "29548"
|
||||
},
|
||||
"657617635303": {
|
||||
"cat": "办公收纳用品",
|
||||
"catId": "1239"
|
||||
},
|
||||
"641673262387": {
|
||||
"cat": "其他(饰品收纳)",
|
||||
"catId": "31137"
|
||||
},
|
||||
"953869908708": {
|
||||
"cat": "墙贴",
|
||||
"catId": "12169"
|
||||
},
|
||||
"703822717997": {
|
||||
"cat": "办公收纳用品",
|
||||
"catId": "1239"
|
||||
},
|
||||
"863524134410": {
|
||||
"cat": "贴纸",
|
||||
"catId": "39474"
|
||||
},
|
||||
"694637537727": {
|
||||
"cat": "午餐包、午餐袋",
|
||||
"catId": "10292"
|
||||
},
|
||||
"623011262210": {
|
||||
"cat": "墙贴",
|
||||
"catId": "12169"
|
||||
},
|
||||
"836512561982": {
|
||||
"cat": "化妆包",
|
||||
"catId": "19669"
|
||||
},
|
||||
"714492799386": {
|
||||
"cat": "化妆包",
|
||||
"catId": "19669"
|
||||
},
|
||||
"591994902720": {
|
||||
"cat": "笔袋",
|
||||
"catId": "40458"
|
||||
},
|
||||
"853414558425": {
|
||||
"cat": "女士钥匙环、钥匙链",
|
||||
"catId": "29284"
|
||||
},
|
||||
"997450831819": {
|
||||
"cat": "贴纸",
|
||||
"catId": "39474"
|
||||
},
|
||||
"639409307567": {
|
||||
"cat": "墙贴",
|
||||
"catId": "12169"
|
||||
},
|
||||
"902440558099": {
|
||||
"cat": "发圈和头绳",
|
||||
"catId": "19285"
|
||||
},
|
||||
"823838532886": {
|
||||
"cat": "证件卡夹卡套",
|
||||
"catId": "1431"
|
||||
},
|
||||
"698782391248": {
|
||||
"cat": "贴纸",
|
||||
"catId": "39474"
|
||||
},
|
||||
"707826905566": {
|
||||
"cat": "女士卡包、证件包",
|
||||
"catId": "29318"
|
||||
},
|
||||
"1027332215847": {
|
||||
"cat": "首饰盒",
|
||||
"catId": "12735"
|
||||
},
|
||||
"682195499823": {
|
||||
"cat": "笔袋",
|
||||
"catId": "40458"
|
||||
},
|
||||
"823560564174": {
|
||||
"cat": "发圈和头绳",
|
||||
"catId": "19285"
|
||||
},
|
||||
"739147684759": {
|
||||
"cat": "纽扣",
|
||||
"catId": "39592"
|
||||
},
|
||||
"621300323670": {
|
||||
"cat": "办公收纳用品",
|
||||
"catId": "1239"
|
||||
},
|
||||
"896469995553": {
|
||||
"cat": "登山扣饰和钥匙扣",
|
||||
"catId": "34174"
|
||||
},
|
||||
"944470567338": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"787261097583": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"833753630925": {
|
||||
"cat": "女士发箍",
|
||||
"catId": "29276"
|
||||
},
|
||||
"719391062058": {
|
||||
"cat": "化妆品收纳盒",
|
||||
"catId": "11800"
|
||||
},
|
||||
"857781294719": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"863384503032": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"774494945453": {
|
||||
"cat": "女士钥匙环、钥匙链",
|
||||
"catId": "29284"
|
||||
},
|
||||
"750271307455": {
|
||||
"cat": "女士钥匙环、钥匙链",
|
||||
"catId": "29284"
|
||||
},
|
||||
"726442972676": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"775057912118": {
|
||||
"cat": "女士钥匙环、钥匙链",
|
||||
"catId": "29284"
|
||||
},
|
||||
"893592264148": {
|
||||
"cat": "发夹",
|
||||
"catId": "19287"
|
||||
},
|
||||
"607330049419": {
|
||||
"cat": "发圈和头绳",
|
||||
"catId": "19285"
|
||||
},
|
||||
"854003999648": {
|
||||
"cat": "发圈和头绳",
|
||||
"catId": "19285"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
await c.evalJS(`window.scrollTo(0,document.body.scrollHeight)`); await wait(2000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
const r=x.getBoundingClientRect();
|
||||
return {y:Math.round(r.y), s:(req?'* ':' ')+lab.slice(0,20)+' ['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,20)};
|
||||
}).filter(x=>x.s.trim().length>4);
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
return JSON.stringify({tables, low:items.filter(x=>x.y>0).slice(-14).map(x=>x.s)},null,1);})()`);
|
||||
console.log(String(o).slice(0,1400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,22 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = 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||'');});
|
||||
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,120), html:it.innerHTML.slice(0,500)});})()`);
|
||||
console.log('分类控件:', String(o).slice(0,700));
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const el=it.querySelector('.ant-select-selector')||it.querySelector('input'); if(!el)return'[]';
|
||||
el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1}); await wait(4000); }
|
||||
const o2 = await c.evalJS(`(()=>{
|
||||
const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).map(m=>({t:(m.querySelector('.ant-modal-title')||{}).innerText||'',
|
||||
inputs:[...m.querySelectorAll('input')].map(i=>i.placeholder||i.type).slice(0,5),
|
||||
btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,6),
|
||||
txt:(m.innerText||'').replace(/\\s+/g,' ').slice(0,280)}));
|
||||
const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).map(d=>(d.innerText||'').replace(/\\s+/g,' ').slice(0,220));
|
||||
return JSON.stringify({modals:ms, dropdowns:dd},null,1);})()`);
|
||||
console.log(String(o2).slice(0,1200));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,16 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const s=p._s.get('temuAddBasicStore').$state.dataState;
|
||||
let cats=s.categories; if(typeof cats==='string') cats=JSON.parse(cats);
|
||||
return JSON.stringify(cats.map(x=>({id:String(x.categoryId), name:x.nameZh, path:x.nodePath||''})));})()`);
|
||||
const list = JSON.parse(String(o));
|
||||
fs.writeFileSync('/tmp/temu_cats_full.json', JSON.stringify(list,null,1));
|
||||
console.log('Temu 全量类目:', list.length);
|
||||
const KEYS=['发夹','发饰','发圈','发箍','头绳','贴纸','墙贴','钥匙','笔袋','卡包','卡夹','化妆包','袜','收纳','项链','书签','纽扣','口哨','毛线','温度计','围巾'];
|
||||
for(const k of KEYS){
|
||||
const hits=list.filter(x=>x.name.includes(k)).slice(0,5);
|
||||
if(hits.length) console.log(` [${k}] ` + hits.map(h=>h.name).join(' / '));
|
||||
}
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,19 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const all = new Set();
|
||||
for (let i=0;i<80;i++){
|
||||
const got = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'[]'; return JSON.stringify([...dd.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean));})()`);
|
||||
const arr=JSON.parse(String(got)); const before=all.size; arr.forEach(x=>all.add(x));
|
||||
const sc = 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+600; return rc.scrollTop;})()`);
|
||||
await wait(400);
|
||||
if (all.size===before && i>4 && Number(sc)===0) break;
|
||||
}
|
||||
const list=[...all];
|
||||
fs.writeFileSync('/tmp/temu_cats.json', JSON.stringify(list,null,1));
|
||||
console.log('Temu 类目数:', list.length);
|
||||
list.forEach((x,i)=>console.log(` ${String(i).padStart(3)} ${x}`));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,22 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
await c.evalJS(`document.body.dispatchEvent(new MouseEvent('mousedown',{bubbles:true}));document.body.click();`);
|
||||
await wait(1200);
|
||||
const pos = 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 el=it.querySelector('.ant-select-selector'); el.scrollIntoView({block:'center'});
|
||||
const r=el.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
console.log('坐标:', JSON.stringify(pos));
|
||||
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(3500);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const dds=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
|
||||
if(!dds.length) return 'no-dd';
|
||||
const dd=dds[dds.length-1];
|
||||
const opts=[...dd.querySelectorAll('.ant-select-item-option')];
|
||||
const anyItems=[...dd.querySelectorAll('[class*=item]')].slice(0,5).map(e=>String(e.className).slice(0,40));
|
||||
return JSON.stringify({n:opts.length, first:opts.slice(0,4).map(e=>JSON.stringify(e.innerText)), anyItems,
|
||||
txt:(dd.innerText||'').replace(/\\s+/g,' ').slice(0,200)});})()`);
|
||||
console.log('下拉:', String(o).slice(0,800));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const CAT='发饰(头发装饰)';
|
||||
// 选类目
|
||||
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=>(o.innerText||'').trim()===${JSON.stringify(CAT)});
|
||||
if(!hit)return'no-hit'; for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked';})()`);
|
||||
console.log('类目:', pk);
|
||||
await wait(12000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,22).padEnd(23)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,22);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const cut=(x,d=0)=>(d>3?'…':(Array.isArray(x)?(x.length?'['+x.length+'] '+JSON.stringify(x[0]).slice(0,240):'[]'):(x&&typeof x==='object'?Object.fromEntries(Object.entries(x).map(([k,v])=>[k,cut(v,d+1)])):x)));
|
||||
const dump={};
|
||||
for(const n of ['temuAddProductStore','temuAddSkuDataStore','temuAddSkuAttrStore','temuAddBasicStore','temuAddAttrStore'])
|
||||
{ try{dump[n]=cut(JSON.parse(JSON.stringify(p._s.get(n).$state)));}catch(e){dump[n]='ERR';} }
|
||||
return JSON.stringify({items, tables, dump},null,1);})()`);
|
||||
fs.writeFileSync('/tmp/temu_deep.json', String(o));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('=== form ==='); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('=== tables ==='); console.log(JSON.stringify(j.tables));
|
||||
console.log('=== stores ===');
|
||||
for(const [n,v] of Object.entries(j.dump)) console.log(' ##',n,JSON.stringify(v).slice(0,560));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,55 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const CAT=process.argv[2]||'发夹';
|
||||
await c.evalJS(`document.body.dispatchEvent(new MouseEvent('mousedown',{bubbles:true}));document.body.click();`);
|
||||
await wait(1200);
|
||||
const pos = 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 el=it.querySelector('.ant-select-selector'); el.scrollIntoView({block:'center'});
|
||||
const r=el.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(3000);
|
||||
let res='nf';
|
||||
for(let i=0;i<90;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'){res='picked';break;}
|
||||
if(r==='no-dd'){res='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+500; return rc.scrollTop;})()`);
|
||||
await wait(280);
|
||||
}
|
||||
console.log('类目:', res, CAT);
|
||||
if(res!=='picked') process.exit(1);
|
||||
await wait(14000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,22).padEnd(23)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,22);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const cut=(x,d=0)=>(d>3?'…':(Array.isArray(x)?(x.length?'['+x.length+'] '+JSON.stringify(x[0]).slice(0,260):'[]'):(x&&typeof x==='object'?Object.fromEntries(Object.entries(x).map(([k,v])=>[k,cut(v,d+1)])):x)));
|
||||
const dump={};
|
||||
for(const n of ['temuAddProductStore','temuAddSkuDataStore','temuAddSkuAttrStore','temuAddBasicStore','temuAddAttrStore'])
|
||||
{ try{dump[n]=cut(JSON.parse(JSON.stringify(p._s.get(n).$state)));}catch(e){dump[n]='ERR';} }
|
||||
const b=p._s.get('temuAddBasicStore').$state;
|
||||
return JSON.stringify({items, tables, dump, catId:b.formState.categoryId, path:b.dataState.categoryPathName},null,1);})()`);
|
||||
fs.writeFileSync('/tmp/temu_deep.json', String(o));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('catId:', j.catId, '| path:', j.path);
|
||||
console.log('=== form ==='); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('=== tables ==='); console.log(JSON.stringify(j.tables));
|
||||
console.log('=== stores ===');
|
||||
for(const [n,v] of Object.entries(j.dump)) console.log(' ##',n,JSON.stringify(v).slice(0,520));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,52 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/temu/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const IMGS=['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg'];
|
||||
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const f=p._s.get('temuAddProductStore').$state.formState;
|
||||
const imgs=${JSON.stringify(IMGS)};
|
||||
// 试探 mainImage 形状
|
||||
if(Array.isArray(f.mainImage)) f.mainImage=imgs.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800}));
|
||||
else f.mainImage=JSON.stringify(imgs.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800})));
|
||||
f.productName='Hair Clips Set for Women Girls Fashion Hair Accessories';
|
||||
const a=p._s.get('temuAddSkuAttrStore').$state.formState;
|
||||
if(a.skuAttrs&&a.skuAttrs[0]) a.skuAttrs[0].options=[{optionName:'Black',name:'Black',value:'Black',optionId:1}];
|
||||
return JSON.stringify({imgIsArray:Array.isArray(${JSON.stringify(IMGS)}), mainImgType:Array.isArray(f.mainImage)?'array':typeof f.mainImage,
|
||||
n:Array.isArray(f.mainImage)?f.mainImage.length:'?', opts:a.skuAttrs&&a.skuAttrs[0]?a.skuAttrs[0].options.length:0});})()`);
|
||||
console.log('填充:', r);
|
||||
await wait(3500);
|
||||
// 找外包装形状并选“长方体”
|
||||
await c.evalJS(`document.body.click()`); await wait(600);
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const s=it.querySelector('.ant-select-selector'); 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)]);})()`)));
|
||||
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[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=>/长方体/.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 'picked';})()`);
|
||||
console.log('外包装形状:', pk);
|
||||
await wait(2000);
|
||||
// 保存并抓错误
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(10000);
|
||||
const err = await c.evalJS(`(()=>{const ex=[...document.querySelectorAll('.ant-form-item-explain-error')].map(e=>(e.innerText||'').trim()).filter(Boolean);
|
||||
const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
|
||||
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
|
||||
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
|
||||
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
|
||||
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean);
|
||||
return JSON.stringify({ex:[...new Set(ex)].slice(0,12), red:[...new Set(red)].slice(0,14), toasts:[...new Set(toasts)].slice(0,3)});})()`);
|
||||
console.log('校验:', String(err).slice(0,1000));
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,19 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const out={};
|
||||
for(const n of [...p._s.keys()].filter(k=>/temu/i.test(k))){
|
||||
const s=p._s.get(n).$state; const ks=[];
|
||||
const walk=(x,path,d)=>{ if(d>2||!x||typeof x!=='object')return;
|
||||
if(Array.isArray(x)) return;
|
||||
for(const k of Object.keys(x)){ if(/img|image|pic|photo|material/i.test(k)) ks.push(path+'.'+k+'='+JSON.stringify(x[k]).slice(0,40));
|
||||
else walk(x[k],path+'.'+k,d+1); } };
|
||||
walk(s,n,0); if(ks.length) out[n]=ks.slice(0,8);
|
||||
}
|
||||
// 外包装类型 是不是 input
|
||||
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 info=it?{html:it.innerHTML.slice(0,360)}:{};
|
||||
return JSON.stringify({out, info},null,1);})()`);
|
||||
console.log(String(o).slice(0,1800));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,52 @@
|
||||
import { newTarget, CDP } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const SHOP='①Alimingpro';
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const x of tl.filter(y=>y.type==='page'&&/temu/i.test(y.url))) await fetch(`http://127.0.0.1:9223/json/close/${x.id}`).catch(()=>{});
|
||||
await wait(900);
|
||||
const nt = await newTarget('about:blank');
|
||||
const c = await CDP.connect(nt.webSocketDebuggerUrl);
|
||||
await c.send('Page.enable'); await c.send('Runtime.enable');
|
||||
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/temu/add'});
|
||||
await wait(22000);
|
||||
console.log('url:', await c.evalJS('location.href'));
|
||||
// 店铺选择器
|
||||
const sp = 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||document).querySelector('.ant-select-selector')||[...document.querySelectorAll('.ant-select-selector')].filter(e=>e.offsetParent!==null)[0];
|
||||
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)]);})()`)));
|
||||
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:sp[0],y:sp[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=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
|
||||
if(!hit)return'no-hit:'+[...dd.querySelectorAll('.ant-select-item-option')].slice(0,4).map(o=>o.innerText.trim()).join(',');
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'ok';})()`);
|
||||
console.log('选店铺:', pk);
|
||||
await wait(9000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const stores=[...p._s.keys()].filter(k=>/temu|pddkj/i.test(k));
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,24).padEnd(25)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,24);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const dump={};
|
||||
for(const n of stores){ try{ const s=p._s.get(n).$state; const o2={};
|
||||
for(const k of Object.keys(s)){ const v=s[k];
|
||||
if(v===null||['string','number','boolean'].includes(typeof v)){ if(v!==''&&v!==null&&String(v).length<60) o2[k]=String(v); }
|
||||
else if(v&&typeof v==='object'&&!Array.isArray(v)){ for(const k2 of Object.keys(v)){ const v2=v[k2];
|
||||
if(v2===null||['string','number','boolean'].includes(typeof v2)){ if(v2!==''&&v2!==null&&String(v2).length<50) o2[k+'.'+k2]=String(v2); } } } }
|
||||
dump[n]=o2; }catch(e){ dump[n]='ERR'; } }
|
||||
return JSON.stringify({stores, items, dump},null,1);})()`);
|
||||
fs.writeFileSync('/tmp/temu_map.json', String(o));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('stores:', j.stores.join(', '));
|
||||
console.log('--- form ---'); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('--- store 关键字段 ---');
|
||||
for(const [n,v] of Object.entries(j.dump)){ const hit=Object.entries(v||{}).filter(([k])=>/shop|site|currency|cat|brand|price|weight/i.test(k));
|
||||
if(hit.length) console.log(' ##',n,hit.map(([k,val])=>k+'='+val).join(' | ').slice(0,300)); }
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,49 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const CAT = process.argv[2] || '发夹';
|
||||
const dd = () => `[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop()`;
|
||||
// 滚回顶部
|
||||
await c.evalJS(`(()=>{const d=${dd()}; if(!d)return 0; const rc=d.querySelector('.rc-virtual-list-holder')||d; rc.scrollTop=0; return rc.scrollTop;})()`);
|
||||
await wait(1200);
|
||||
let res='nf';
|
||||
for(let i=0;i<120;i++){
|
||||
const r = await c.evalJS(`(()=>{const d=${dd()}; if(!d)return'no-dd';
|
||||
const target=${JSON.stringify(CAT)};
|
||||
const hit=[...d.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'){res='picked';break;}
|
||||
if(r==='no-dd'){res='no-dd';break;}
|
||||
await c.evalJS(`(()=>{const d=${dd()}; if(!d)return 0; const rc=d.querySelector('.rc-virtual-list-holder')||d; rc.scrollTop=rc.scrollTop+400; return rc.scrollTop;})()`);
|
||||
await wait(230);
|
||||
}
|
||||
console.log('类目:', res, CAT);
|
||||
if(res!=='picked') process.exit(1);
|
||||
await wait(14000);
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
|
||||
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const req=!!x.querySelector('.ant-form-item-required');
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
return (req?'* ':' ')+lab.slice(0,22).padEnd(23)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,22);
|
||||
}).filter(x=>x.trim().length>4);
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
const cut=(x,d=0)=>(d>3?'…':(Array.isArray(x)?(x.length?'['+x.length+'] '+JSON.stringify(x[0]).slice(0,260):'[]'):(x&&typeof x==='object'?Object.fromEntries(Object.entries(x).map(([k,v])=>[k,cut(v,d+1)])):x)));
|
||||
const dump={};
|
||||
for(const n of ['temuAddProductStore','temuAddSkuDataStore','temuAddSkuAttrStore','temuAddBasicStore','temuAddAttrStore'])
|
||||
{ try{dump[n]=cut(JSON.parse(JSON.stringify(p._s.get(n).$state)));}catch(e){dump[n]='ERR';} }
|
||||
const b=p._s.get('temuAddBasicStore').$state;
|
||||
return JSON.stringify({items, tables, dump, catId:b.formState.categoryId, path:b.dataState.categoryPathName},null,1);})()`);
|
||||
import('fs').then(fs=>fs.writeFileSync('/tmp/temu_deep.json', String(o)));
|
||||
const j=JSON.parse(String(o));
|
||||
console.log('catId:', j.catId, '| path:', j.path);
|
||||
console.log('=== form ==='); j.items.forEach(x=>console.log(' '+x));
|
||||
console.log('=== tables ==='); console.log(JSON.stringify(j.tables));
|
||||
console.log('=== stores ===');
|
||||
for(const [n,v] of Object.entries(j.dump)) console.log(' ##',n,JSON.stringify(v).slice(0,500));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,30 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
await c.evalJS(`document.body.click()`); await wait(700);
|
||||
const pos = 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||'');});
|
||||
if(!it)return'[]'; const s=it.querySelector('.ant-select-selector'); 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)]);})()`)));
|
||||
console.log('坐标:', JSON.stringify(pos));
|
||||
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1});
|
||||
await wait(3000);
|
||||
const o = 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));})()`);
|
||||
console.log('外包装类型选项:', String(o).slice(0,400));
|
||||
const pk = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')]; if(!its.length)return'no-opt';
|
||||
const hit=its.find(o=>/袋|Box|盒|Bag/i.test(o.innerText||''))||its[0];
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked:'+(hit.innerText||'').trim();})()`);
|
||||
console.log('选择:', pk);
|
||||
await wait(2000);
|
||||
// 素材图 形状
|
||||
const o2 = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const f=p._s.get('temuAddProductStore').$state.formState;
|
||||
return JSON.stringify({matType:Array.isArray(f.materialImage)?'array':typeof f.materialImage,
|
||||
mat:String(JSON.stringify(f.materialImage)).slice(0,220), mainN:Array.isArray(f.mainImage)?f.mainImage.length:'?',
|
||||
pkg:(()=>{const q=p._s.get('temuAddOtherStore').$state; return JSON.stringify(q.packageFormState).slice(0,200);})()});})()`);
|
||||
console.log('字段形状:', String(o2).slice(0,600));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,13 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const req=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null&&e.querySelector('.ant-form-item-required'));
|
||||
const empty=[], filled=[];
|
||||
req.forEach(x=>{ const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
|
||||
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
|
||||
const v=sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''));
|
||||
(v?filled:empty).push(lab+' ['+(ta?'ta':(sel?'select':(inp?'input':'-')))+']='+String(v).slice(0,16)); });
|
||||
const expl=[...document.querySelectorAll('.ant-form-item-explain-error')].map(e=>(e.innerText||'').trim()).filter(Boolean);
|
||||
return JSON.stringify({empty, filled, expl:[...new Set(expl)].slice(0,10)},null,1);})()`);
|
||||
console.log(String(o).slice(0,1600));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,38 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const reqs=[];
|
||||
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
|
||||
if(/temu/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
|
||||
await c.send('Network.enable').catch(()=>{});
|
||||
const IMGS=['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg',
|
||||
'https://cbu01.alicdn.com/img/ibank/O1CN01wCkR921xkoEgfOWb0_!!2222444614399-0-cib.jpg'];
|
||||
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const f=p._s.get('temuAddProductStore').$state.formState;
|
||||
const imgs=${JSON.stringify(IMGS)};
|
||||
const mk=(u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800});
|
||||
if(Array.isArray(f.mainImage)) f.mainImage=imgs.map(mk); else f.mainImage=JSON.stringify(imgs.map(mk));
|
||||
f.materialImage=imgs.slice(0,3).map(mk);
|
||||
const q=p._s.get('temuAddOtherStore').$state;
|
||||
if(q.packageFormState){ q.packageFormState.packageImages=imgs.slice(0,3).map(mk); q.packageFormState.packageShape=1; q.packageFormState.packageType=0; }
|
||||
return JSON.stringify({main:Array.isArray(f.mainImage)?f.mainImage.length:'str', mat:Array.isArray(f.materialImage)?f.materialImage.length:'obj',
|
||||
pkg:(q.packageFormState?q.packageFormState.packageImages.length:'-')});})()`);
|
||||
console.log('填充:', r);
|
||||
await wait(3500);
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(11000);
|
||||
const err = await c.evalJS(`(()=>{const ex=[...document.querySelectorAll('.ant-form-item-explain-error')].map(e=>(e.innerText||'').trim()).filter(Boolean);
|
||||
const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
|
||||
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
|
||||
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
|
||||
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
|
||||
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean);
|
||||
return JSON.stringify({ex:[...new Set(ex)].slice(0,10), red:[...new Set(red)].slice(0,12), toasts:[...new Set(toasts)].slice(0,2)});})()`);
|
||||
console.log('校验:', String(err).slice(0,800));
|
||||
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,20 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
await c.evalJS(`document.body.click()`); await wait(600);
|
||||
const pos = JSON.parse(String(await c.evalJS(`(()=>{
|
||||
const boxes=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/颜色/.test(e.innerText||'')&&e.querySelector('input[type=search]')&&e.querySelectorAll('*').length<300);
|
||||
const box=boxes[boxes.length-1]; if(!box)return'[]';
|
||||
const s=box.closest('.ant-select')||box.querySelector('.ant-select')||box;
|
||||
const sel=s.querySelector('.ant-select-selector')||s;
|
||||
sel.scrollIntoView({block:'center'}); const r=sel.getBoundingClientRect();
|
||||
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
console.log('坐标:', JSON.stringify(pos));
|
||||
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1}); }
|
||||
await wait(3000);
|
||||
const o = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!dd)return'no-dd';
|
||||
const opts=[...dd.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').trim());
|
||||
return JSON.stringify({n:opts.length, first:opts.slice(0,10), txt:(dd.innerText||'').replace(/\\s+/g,' ').slice(0,180)});})()`);
|
||||
console.log('下拉:', String(o).slice(0,600));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,12 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const d=JSON.parse(JSON.stringify(p._s.get('temuAddSkuDataStore').$state));
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('temuAddSkuAttrStore').$state));
|
||||
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
|
||||
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
|
||||
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
|
||||
return JSON.stringify({tables, skuN:(d.formState.skuDataList||[]).length, row0:(d.formState.skuDataList||[])[0],
|
||||
attrs:(a.formState.skuAttrs||[]).map(x=>({name:x.parentSpecName, n:(x.options||[]).length}))},null,1);})()`);
|
||||
console.log(String(o).slice(0,1400));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,39 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
// 1. 主规格/变种区结构
|
||||
const o1 = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const a=JSON.parse(JSON.stringify(p._s.get('temuAddSkuAttrStore').$state));
|
||||
const at=(a.formState.skuAttrs||[])[0]||{};
|
||||
return JSON.stringify({attrKeys:Object.keys(at), attr:{type:at.type,parentSpecId:at.parentSpecId,parentSpecName:at.parentSpecName,
|
||||
optCount:(at.options||[]).length, opt0:(at.options||[])[0], vals:at.values||at.checked||null},
|
||||
customs:(a.dataState.customThemeOptions||[]).slice(0,2)});})()`);
|
||||
console.log('变种属性:', String(o1).slice(0,900));
|
||||
// 2. 页面里变种可点元素
|
||||
const o2 = await c.evalJS(`(()=>{const boxes=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/颜色|Color/.test(e.innerText||'')&&e.querySelectorAll('*').length<200);
|
||||
const box=boxes[boxes.length-1]; if(!box)return'nf';
|
||||
const opts=[...box.querySelectorAll('.ant-tag,.ant-checkbox-wrapper,[class*=option],[class*=spec]')].filter(e=>e.offsetParent!==null).slice(0,8)
|
||||
.map(e=>({tag:e.tagName,cls:String(e.className||'').slice(0,45),t:(e.innerText||'').trim().slice(0,14)}));
|
||||
const inputs=[...box.querySelectorAll('input')].slice(0,4).map(i=>({ph:i.placeholder||'',cls:String(i.className||'').slice(0,40)}));
|
||||
return JSON.stringify({opts, inputs});})()`);
|
||||
console.log('变种区:', String(o2).slice(0,800));
|
||||
// 3. 外包装形状/类型 的选项
|
||||
await c.evalJS(`document.body.click()`); await wait(700);
|
||||
for (const lab of ['外包装形状','外包装类型']) {
|
||||
const pos = 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&&new RegExp(${JSON.stringify(lab)}).test(l.innerText||'');});
|
||||
if(!it)return'[]'; const s=it.querySelector('.ant-select-selector'); 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)]);})()`)));
|
||||
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1}); await wait(2500); }
|
||||
const o = 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,12));})()`);
|
||||
console.log(lab+':', String(o).slice(0,300));
|
||||
await c.evalJS(`document.body.click()`); await wait(700);
|
||||
}
|
||||
// 4. 图片字段类型
|
||||
const o4 = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const f=p._s.get('temuAddProductStore').$state.formState;
|
||||
return JSON.stringify({mainImageType:typeof f.mainImage, mainImage:String(f.mainImage).slice(0,60)});})()`);
|
||||
console.log('图片字段:', String(o4).slice(0,300));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,11 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{
|
||||
const boxes=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/颜色/.test(e.innerText||'')&&e.querySelectorAll('input').length>0&&e.querySelectorAll('*').length<300);
|
||||
const box=boxes[boxes.length-1]; if(!box)return'nf';
|
||||
const ins=[...box.querySelectorAll('input')].map(i=>({ph:i.placeholder||'',type:i.type,cls:String(i.className||'').slice(0,45)}));
|
||||
const btns=[...box.querySelectorAll('button,a,span')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim()).slice(0,10).map(e=>({tag:e.tagName,t:(e.innerText||'').trim().slice(0,14)}));
|
||||
return JSON.stringify({txt:(box.innerText||'').replace(/\\s+/g,' ').slice(0,260), ins, btns,
|
||||
html:box.innerHTML.slice(0,400)},null,1);})()`);
|
||||
console.log(String(o).slice(0,1500));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,11 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
const o = await c.evalJS(`(()=>{
|
||||
// 找含“颜色”且含“添加/请选择”的容器
|
||||
const cands=[...document.querySelectorAll('div')].filter(e=>e.offsetParent!==null&&/颜色/.test(e.innerText||'')&&e.querySelectorAll('input').length>=1&&e.querySelectorAll('*').length<400);
|
||||
const box=cands[cands.length-1]; if(!box)return'nf';
|
||||
const ins=[...box.querySelectorAll('input')].filter(i=>i.offsetParent!==null).map(i=>({type:i.type,ph:i.placeholder||'',cls:String(i.className||'').slice(0,50),dis:i.disabled}));
|
||||
const btns=[...box.querySelectorAll('button')].filter(e=>e.offsetParent!==null).map(b=>({t:(b.innerText||'').trim().slice(0,12),dis:b.disabled,cls:String(b.className||'').slice(0,40)}));
|
||||
return JSON.stringify({n:cands.length, txt:(box.innerText||'').replace(/\\s+/g,' ').slice(0,320), ins, btns},null,1);})()`);
|
||||
console.log(String(o).slice(0,1500));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"548459586741": {
|
||||
"ok": false,
|
||||
"cat": ""
|
||||
},
|
||||
"764423265986": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Peralatan Dapur(厨房用品)/Perkakas & Alat Dapur(厨房器具)/Termometer Dapur(厨房温度计)"
|
||||
},
|
||||
"685456757807": {
|
||||
"ok": false,
|
||||
"cat": ""
|
||||
},
|
||||
"735147938123": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Cekak Rambut(发带发箍)"
|
||||
},
|
||||
"610164857063": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Cekak Rambut(发带发箍)"
|
||||
},
|
||||
"676236596821": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪)"
|
||||
},
|
||||
"708420302844": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kemas & Aksesori Kostum(平价饰品)/Rantai Kunci(钥匙扣)"
|
||||
},
|
||||
"866537106657": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Bayi & Bersalin(母婴用品)/Penjagaan & Kesihatan Bayi(婴儿护理与健康)/Lampin(尿布)/Aksesori Lampin Kain(布尿布配件)/P"
|
||||
},
|
||||
"547183997437": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/14]:Aksesori & Hasil Barang Kemas(珠宝与衍生品)/Ambar(琥珀)/Rantai & Loket Ambar(琥珀项链&吊坠) | 尝试: 项链→picked[0/14]:Aksesori & Hasil Barang Kemas(珠宝"
|
||||
},
|
||||
"657617635303": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Rumah(家居收纳)/Kotak & Tong Penyimpanan(收纳盒) | 尝试: 收纳盒→picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Ruma"
|
||||
},
|
||||
"641673262387": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Bagasi & Beg(箱包)/Beg Bagasi & Perjalanan(旅行箱包)/Pengaturan Perjalanan(旅行收纳袋) | 尝试: 首饰袋→no-result / 首饰盒→no-result / 收纳袋→picked[0/1]:Bagasi & Beg(箱包)/Beg Bagasi & Per"
|
||||
},
|
||||
"953869908708": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 窗贴→no-result / 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"703822717997": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/19]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Alatan Penulisan & Pembet | 尝试: 笔夹→no-result / 笔夹→no-result / 文具→picked[0/19]:Komputer & Peralatan Pejabat(电脑办"
|
||||
},
|
||||
"863524134410": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"694637537727": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Bagasi & Beg(箱包)/Beg Fungsian(功能箱包)/Beg Pendingin(保温袋) | 尝试: 保温袋→picked[0/1]:Bagasi & Beg(箱包)/Beg Fungsian(功能箱"
|
||||
},
|
||||
"623011262210": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 窗贴→no-result / 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"836512561982": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Kecantikan & Penjagaan Diri(美妆个护)/Mekap(美妆)/Alatan Solekan(化妆工具)/Beg dan Bekas Solek(化妆包及化妆盒) | 尝试: 化妆包→picked[0/2]:Kecantikan & Penjagaan Diri(美妆个护)"
|
||||
},
|
||||
"714492799386": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Kecantikan & Penjagaan Diri(美妆个护)/Mekap(美妆)/Alatan Solekan(化妆工具)/Beg dan Bekas Solek(化妆包及化妆盒) | 尝试: 化妆包→picked[0/2]:Kecantikan & Penjagaan Diri(美妆个护)"
|
||||
},
|
||||
"591994902720": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/19]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Alatan Penulisan & Pembet | 尝试: 笔袋→no-result / 笔袋→no-result / 文具→picked[0/19]:Komputer & Peralatan Pejabat(电脑办"
|
||||
},
|
||||
"853414558425": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Telefon & Elektronik(手机与数码)/Aksesori Telefon(手机配件)/Tali & Hiasan Telefon(手机挂绳与挂件) | 尝试: 手机挂绳→picked[0/1]:Telefon & Elektronik(手机与数码)/Akses"
|
||||
},
|
||||
"997450831819": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"639409307567": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 墙贴→no-result / 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"902440558099": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fesyen Bayi(婴儿时尚配件)/Aksesori Rambut Bayi(婴儿发饰) | 尝试: 发圈→no-result / 发饰→picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fe"
|
||||
},
|
||||
"823838532886": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Bagasi & Beg(箱包)/Beg Lelaki(男包)/Dompet(钱包) | 尝试: 卡套→no-result / 卡包→no-result / 卡套→no-result / 钱包→picked[0/2]:Bagasi & Beg(箱包)/Beg Lelaki(男包)/D"
|
||||
},
|
||||
"698782391248": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/4]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Label, Pembahagi Indeks & | 尝试: 贴纸→picked[0/4]:Komputer & Peralatan Pejabat(电脑办公"
|
||||
},
|
||||
"707826905566": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/2]:Bagasi & Beg(箱包)/Beg Lelaki(男包)/Dompet(钱包) | 尝试: 卡包→no-result / 卡包→no-result / 卡套→no-result / 钱包→picked[0/2]:Bagasi & Beg(箱包)/Beg Lelaki(男包)/D"
|
||||
},
|
||||
"1027332215847": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Rumah(家居收纳)/Kotak & Tong Penyimpanan(收纳盒) | 尝试: 收纳盒→picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Ruma"
|
||||
},
|
||||
"682195499823": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/19]:Komputer & Peralatan Pejabat(电脑办公)/Alat Tulis & Bekalan Pejabat(办公文具用品)/Alatan Penulisan & Pembet | 尝试: 笔袋→no-result / 笔袋→no-result / 文具→picked[0/19]:Komputer & Peralatan Pejabat(电脑办"
|
||||
},
|
||||
"823560564174": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fesyen Bayi(婴儿时尚配件)/Aksesori Rambut Bayi(婴儿发饰) | 尝试: 发圈→no-result / 发饰→picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fe"
|
||||
},
|
||||
"739147684759": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Bayi & Bersalin(母婴用品)/Penjagaan & Kesihatan Bayi(婴儿护理与健康)/Lampin(尿布)/Aksesori Lampin Kain(布尿布配件)/P | 尝试: 纽扣→picked[0/1]:Bayi & Bersalin(母婴用品)/Penjagaan &"
|
||||
},
|
||||
"621300323670": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/78]:Komputer & Peralatan Pejabat(电脑办公)/Peralatan Pejabat(办公设备)/Alat Ganti Peralatan Pejabat(办公设备零部件) | 尝试: 胶带座→no-result / 胶带座→no-result / 办公→picked[0/78]:Komputer & Peralatan Pejabat(电脑办"
|
||||
},
|
||||
"896469995553": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kemas & Aksesori Kostum(平价饰品)/Rantai Kunci(钥匙扣) | 尝试: 钥匙扣→picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kema"
|
||||
},
|
||||
"944470567338": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"787261097583": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"833753630925": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Cekak Rambut(发带发箍) | 尝试: 发箍→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"719391062058": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Rumah(家居收纳)/Kotak & Tong Penyimpanan(收纳盒) | 尝试: 收纳盒→picked[0/3]:Bekalan Rumah(居家日用)/Pengatur Ruma"
|
||||
},
|
||||
"857781294719": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"863384503032": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"774494945453": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kemas & Aksesori Kostum(平价饰品)/Rantai Kunci(钥匙扣) | 尝试: 钥匙扣→picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kema"
|
||||
},
|
||||
"750271307455": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kemas & Aksesori Kostum(平价饰品)/Rantai Kunci(钥匙扣) | 尝试: 钥匙扣→picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kema"
|
||||
},
|
||||
"726442972676": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"775057912118": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kemas & Aksesori Kostum(平价饰品)/Rantai Kunci(钥匙扣) | 尝试: 钥匙扣→picked[0/1]:Aksesori Fesyen(时尚配件)/Barang Kema"
|
||||
},
|
||||
"893592264148": {
|
||||
"ok": true,
|
||||
"cat": "picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Rambut(发饰)/Klip Rambut & Pin Rambut(发夹发簪) | 尝试: 发夹→picked[0/1]:Aksesori Fesyen(时尚配件)/Aksesori Ra"
|
||||
},
|
||||
"607330049419": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fesyen Bayi(婴儿时尚配件)/Aksesori Rambut Bayi(婴儿发饰) | 尝试: 发圈→no-result / 发饰→picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fe"
|
||||
},
|
||||
"854003999648": {
|
||||
"ok": true,
|
||||
"cat": "picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fesyen Bayi(婴儿时尚配件)/Aksesori Rambut Bayi(婴儿发饰) | 尝试: 发圈→no-result / 发饰→picked[1/6]:Bayi & Bersalin(母婴用品)/Aksesori Fe"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
{"n":184,"items":[{"id":"184807703510005077","name":"Pure Natural Cotton Yarn for Baby Knitting - Soft 16-Ply","shop":5880976,"cat":951816,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510004697","name":"6.5cm Colorful Plastic Party Whistles for Kids, Blow Out Whistle Toys for Birthday Sports Games","shop":5880976,"cat":604390,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510004269","name":"High Elastic Wide Hair Ties Set of 5, Soft No-Break Hair Bands","shop":5880976,"cat":962696,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510004059","name":"50 Pcs Elastic Thin Hair Ties for Women Girls Daily Use","shop":5880976,"cat":962696,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510003789","name":"Cute Plaid Hair Clips & Bands for Kids, Bright Yellow Children's Hair Accessories","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510003519","name":"Stainless Steel Keychain with English Letter, Polished Metal Key Holder for Car Bag","shop":5880976,"cat":907656,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510003301","name":"Cute Cartoon Animal Mini Hair Clips for Bangs and Small Strands","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510003143","name":"Romantic Heart Keychain for Couples Silver Plated Valentine Gift","shop":5880976,"cat":907656,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510002979","name":"Stainless Steel Cat Keychain with Fish Charm Cute Bag Ornament","shop":5880976,"cat":907656,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510002811","name":"Elegant Red Rose Crab Hair Clip for Women Stylish Accessory","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510002613","name":"Cute Bunny Ear Hair Clips for Kids with Ribbon Bow, 2 Pcs","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510002419","name":"Clear Cosmetic Organizer with 6 Drawers for Desk Storage","shop":5880976,"cat":600621,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510002205","name":"Wide Plaid Fabric Headband for Women - Comfortable Hair Accessory","shop":5880976,"cat":605271,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510001565","name":"Transparent Crab Hair Clips with Hearts, Clear Acrylic Set","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510000821","name":"Korean Style Plaid Triangle Hair Clip with Decorative Button","shop":5880976,"cat":908552,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510000487","name":"Angel Wings & Stars Keychain Y2K Retro Bag Charm Cute Accessory","shop":5880976,"cat":907656,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703510000053","name":"Round Transparent Desktop Tape Dispenser with Smooth Cutter","shop":5880976,"cat":987656,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509999761","name":"Metallic Rectangular Coat Buttons with Leather Cover for Jackets","shop":5880976,"cat":1944336,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509999371","name":"Cute Heart Hair Ties Set, Soft Elastic Bands for Buns & Ponytails, Colorful Hair Accessories","shop":5880976,"cat":962696,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509998877","name":"96-Cell Brush Organizer Case for Watercolor & Gouache","shop":5880976,"cat":603002,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509998697","name":"Silver Matte Tin Photo Box with Clear Window for Cards","shop":5880976,"cat":600621,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509998203","name":"Ultra-Slim Card Holder Wallet for Men Women, Compact Cash & Card Pouch","shop":5880976,"cat":601430,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509997509","name":"Boho Style White PVC Laptop Stickers 10cm Round Decal","shop":5880976,"cat":988296,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509997369","name":"Cute Cartoon Cat Key Card Holder for Metro Office & Bag","shop":5880976,"cat":601430,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509996723","name":"Large Beige Fabric Hair Tie with Pearl for Buns and Low Ponytails","shop":5880976,"cat":962696,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509996281","name":"3D Glowing Butterfly Wall Stickers, Magnetic Light Up Decals","shop":5880976,"cat":988296,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509995631","name":"Matte PET Emoji Stickers for Goodnotes & Scrapbooking, 10 Sheets Waterproof","shop":5880976,"cat":988296,"catPath":"","img":1,"vars":0,"price":"","weight":""},{"id":"184807703509995245","Line truncated
|
||||
@@ -0,0 +1,211 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
|
||||
/* ── TikTok (AutoMavenShop_MY) 建品配方 ──
|
||||
node tkbatch.mjs <oid> [catKeyword] [variantValue]
|
||||
流程:选店铺 → 分类弹窗 → 标题/卖点/关键词/描述 → 变种属性 → SKU价+库存
|
||||
→ 重量/尺寸 → 仓库 → 图片(外链) → 来源URL → 保存 → 回读 */
|
||||
|
||||
const OID = process.argv[2];
|
||||
const CAT_KW = process.argv[3] || '';
|
||||
const VAR_VAL = process.argv[4] || 'Default';
|
||||
if (!OID) { console.log('用法: node tkbatch.mjs <oid> [catKeyword] [variantValue]'); process.exit(1); }
|
||||
|
||||
const IN = JSON.parse(fs.readFileSync('/tmp/tk_input.json', 'utf8'))[OID];
|
||||
if (!IN) { console.log('无输入数据', OID); process.exit(1); }
|
||||
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const log = (...a) => console.log(' ', ...a);
|
||||
const DIMS = (() => { const W = Math.max(100, Math.round(IN.weight * 1.3));
|
||||
return { L: Math.max(60, Math.min(400, W)), Wi: Math.max(40, Math.min(300, Math.round(W * 0.6))), H: Math.max(10, Math.min(200, Math.round(W * 0.2))) }; })();
|
||||
|
||||
// 0. 清理编辑页
|
||||
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' && /tiktok/i.test(x.url))) { await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {}); k++; }
|
||||
if (k) { log(`清理 tiktok 页 ${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/tiktokProduct/add' });
|
||||
await wait(20000);
|
||||
|
||||
const cur = async () => String(await c.evalJS(`(()=>{const s=[...document.querySelectorAll('.ant-select')].filter(e=>e.offsetParent!==null)[0];
|
||||
return s?((s.querySelector('.ant-select-selection-item')||{}).innerText||''):'';})()`));
|
||||
|
||||
// 1. 选店铺 AutoMavenShop_MY
|
||||
async function pickShop() {
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
const pos = JSON.parse(String(await c.evalJS(`(()=>{const s=[...document.querySelectorAll('.ant-select')].filter(e=>e.offsetParent!==null)[0];
|
||||
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)]);})()`)));
|
||||
if (!pos.length) return 'no-select';
|
||||
for (const type of ['mousePressed', 'mouseReleased']) await c.send('Input.dispatchMouseEvent', { type, x: pos[0], y: pos[1], button: 'left', clickCount: 1 });
|
||||
await wait(1800);
|
||||
await c.evalJS(`(()=>{const inp=document.querySelector('.ant-select-selection-search-input')||document.querySelector('.ant-select input');
|
||||
if(!inp)return 0; inp.focus();
|
||||
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(inp,'AutoMaven');
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); return 1;})()`);
|
||||
await wait(2200);
|
||||
const r = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
|
||||
if(!dd.length)return'no-dd'; const d=dd[dd.length-1];
|
||||
const hit=[...d.querySelectorAll('.ant-select-item-option')].find(o=>/AutoMavenShop_MY/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 'picked';})()`);
|
||||
await wait(6000);
|
||||
const s = await cur();
|
||||
if (/AutoMavenShop_MY/i.test(s)) return 'ok:' + s;
|
||||
log(' 店铺重试', attempt + 1, r, JSON.stringify(s));
|
||||
}
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
// 2. 分类
|
||||
async function pickCategory(kw) {
|
||||
await c.evalJS(`(()=>{document.querySelectorAll('.ant-select-dropdown').forEach(d=>{if(d.offsetParent!==null)d.style.display='none';});
|
||||
document.body.dispatchEvent(new MouseEvent('mousedown',{bubbles:true})); document.body.click(); return 1;})()`);
|
||||
await wait(1000);
|
||||
const open = 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[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'ok';})()`);
|
||||
if (open === 'nf') return 'no-btn';
|
||||
await wait(3500);
|
||||
const r = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const inp=m.querySelector('input[placeholder="搜索分类名称"]'); if(!inp)return'no-input';
|
||||
inp.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(inp,${JSON.stringify(kw)});
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
const b=[...m.querySelectorAll('button')].find(x=>/^\\s*搜索\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'searched';})()`);
|
||||
if (r !== 'searched') return r;
|
||||
await wait(4000);
|
||||
const pick = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const its=[...m.querySelectorAll('.search-result-item')];
|
||||
if(!its.length)return'no-result';
|
||||
const BAD=/二手|Barang Terpakai|Second|Used|Pre-?owned|Refurb/i;
|
||||
const texts=its.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
let idx=texts.findIndex(t=>!BAD.test(t));
|
||||
if(idx<0) idx=0;
|
||||
const hit=its[idx];
|
||||
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'picked['+idx+'/'+texts.length+']:'+texts[idx];})()`);
|
||||
await wait(4500);
|
||||
return pick;
|
||||
}
|
||||
|
||||
// 2b. 多关键词回退 + 失败即中止
|
||||
async function pickCategoryAny(kws) {
|
||||
const tried = [];
|
||||
for (const kw of kws) {
|
||||
const r = String(await pickCategory(kw));
|
||||
tried.push(kw + '\u2192' + r.slice(0, 45));
|
||||
if (!/no-result|no-input|no-modal|no-btn/.test(r)) return { r, tried };
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(m){const x=m.querySelector('.ant-modal-close'); if(x&&x.offsetParent!==null)x.click();} return 1;})()`);
|
||||
await wait(1500);
|
||||
}
|
||||
return { r: 'ALL-FAILED', tried };
|
||||
}
|
||||
|
||||
// 3. 直填 store
|
||||
const setStore = (js) => c.evalJS(js);
|
||||
|
||||
log('=== TikTok 建品 ===', OID, IN.title.slice(0, 50));
|
||||
log('1) 店铺:', await pickShop());
|
||||
|
||||
const kw = CAT_KW || IN.cat_kw || IN.title.split(/[ ,]+/).slice(0, 2).join(' ');
|
||||
const catRes = await pickCategoryAny([kw].concat(IN.cat_fb || []));
|
||||
log('2) 分类:', SAN(String(catRes.r)).slice(0, 110), '| 尝试:', catRes.tried.join(' / '));
|
||||
if (/ALL-FAILED|no-result|no-input|no-btn/.test(String(catRes.r))) { log('\u274c 分类未选中,跳过保存'); await c.shot('/tmp/tk_fail_' + OID + '.jpeg').catch(() => {}); process.exit(2); }
|
||||
|
||||
// 4. 文案
|
||||
const desc = IN.bullets.map(b => '• ' + b).join('<br>') + '<br><br>Keywords: ' + (IN.keywords || '');
|
||||
const wr = await setStore(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const f=p._s.get('tiktokGlobalAddProductStore').$state.formState;
|
||||
f.productName=${JSON.stringify(IN.title)};
|
||||
f.keyProductFeatures=${JSON.stringify(IN.bullets.slice(0, 5))};
|
||||
f.searchTerms=${JSON.stringify((IN.keywords || '').split(',').map(s => s.trim()).filter(Boolean).slice(0, 10))};
|
||||
f.description='<style>img{display: block}p{margin-block-start: 0; margin-block-end: 0}</style>'+${JSON.stringify(desc)};
|
||||
return 'ok';})()`);
|
||||
log('3) 文案:', wr, '| 标长', IN.title.length);
|
||||
|
||||
// 5. 变种属性
|
||||
const vr = await c.evalJS(`(()=>{
|
||||
const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/^\\s*变种属性\\s*$/.test((e.innerText||'').trim())).pop();
|
||||
if(!h)return'no-sec'; let box=h;
|
||||
for(let i=0;i<6&&box.parentElement;i++){ box=box.parentElement; if(box.querySelector('input[class*="!w-200"]'))break; }
|
||||
const inp=box.querySelector('input[class*="!w-200"]'); if(!inp)return'no-inp';
|
||||
inp.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(inp,${JSON.stringify(VAR_VAL)});
|
||||
inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
inp.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,which:13,bubbles:true}));
|
||||
const btn=[...box.querySelectorAll('button,a,span')].find(e=>/^\\s*(添加|Add)\\s*$/.test(e.innerText||''));
|
||||
if(btn) for(const ev of ['mousedown','mouseup','click']) btn.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
return 'ok';})()`);
|
||||
log('4) 变种属性:', vr, VAR_VAL);
|
||||
await wait(3500);
|
||||
|
||||
// 6. SKU 价格/库存
|
||||
const SKU = 'AM-' + OID;
|
||||
const sr = await c.evalJS(`(()=>{
|
||||
const setV=(el,v)=>{ Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,String(v));
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); };
|
||||
const tbl=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).find(t=>[...t.querySelectorAll('th')].some(h=>/本地展示价/.test(h.innerText||'')));
|
||||
if(!tbl)return'no-table';
|
||||
const ths=[...tbl.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
|
||||
const rows=[...tbl.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length);
|
||||
const done=[];
|
||||
rows.forEach((tr,ri)=>{ [...tr.querySelectorAll('td')].forEach((td,ci)=>{ const name=ths[ci]||''; const ins=[...td.querySelectorAll('input')];
|
||||
if(/^SKU/i.test(name)&&ins[0]&&!ins[0].value){ setV(ins[0],${JSON.stringify(SKU)}+(rows.length>1?('-'+ri):'')); done.push('sku'); }
|
||||
if(/本地展示价/.test(name)&&ins[0]&&!ins[0].value){ setV(ins[0],${JSON.stringify(String(IN.price_myr))}); done.push('price'); }
|
||||
if(/^库存/.test(name)&&ins[0]&&!ins[0].value){ setV(ins[0],'999'); done.push('stock'); } }); });
|
||||
return 'rows'+rows.length+':'+[...new Set(done)].join(',');})()`);
|
||||
log('5) SKU:', sr, '| MYR', IN.price_myr);
|
||||
await wait(2000);
|
||||
|
||||
// 7. 重量 + 尺寸
|
||||
const setItem = (rx, sel, 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||'');});
|
||||
const el=it?it.querySelector(${JSON.stringify(sel)}):null; if(!el)return'nf';
|
||||
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';})()`);
|
||||
log('6) 重量:', await setItem('重量', 'input:not([type=hidden])', IN.weight));
|
||||
const dim = 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||'');});
|
||||
if(!it)return'nf'; const ins=[...it.querySelectorAll('input')];
|
||||
const setV=(el,v)=>{ Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,String(v));
|
||||
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); };
|
||||
const L=ins.find(i=>i.placeholder==='长')||ins[0], W2=ins.find(i=>i.placeholder==='宽')||ins[1], H=ins.find(i=>i.placeholder==='高')||ins[2];
|
||||
if(L)setV(L,${DIMS.L}); if(W2)setV(W2,${DIMS.Wi}); if(H)setV(H,${DIMS.H});
|
||||
return 'ok:'+ins.length;})()`);
|
||||
log('7) 尺寸:', dim, JSON.stringify(DIMS));
|
||||
|
||||
// 8. 图片
|
||||
const ir = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const st=p._s.get('tiktokGlobalImgStore').$state;
|
||||
const urls=${JSON.stringify(IN.imgs)};
|
||||
st.formState.mainImage=urls.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800,checked:true}));
|
||||
return 'imgs:'+st.formState.mainImage.length;})()`);
|
||||
log('8) 图片:', ir, '(共', IN.imgs.length, ')');
|
||||
await wait(2500);
|
||||
|
||||
// 9. 来源URL
|
||||
log('9) 来源URL:', await setItem('来源URL', 'input:not([type=hidden])', 'https://detail.1688.com/offer/' + OID + '.html'));
|
||||
await wait(1200);
|
||||
|
||||
// 10. 保存
|
||||
const before = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
|
||||
const b=p._s.get('tiktokGlobalAddBasicStore').$state;
|
||||
return JSON.stringify({shopId:b.formState.shopId, cat:b.formState.categoryId, site:b.site});})()`);
|
||||
log('10) 保存前状态:', before);
|
||||
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('11) 保存点击:', sv);
|
||||
await wait(9000);
|
||||
const msg = await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null)
|
||||
.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,160)).filter(Boolean);
|
||||
return JSON.stringify({url:location.href, msgs:ms.slice(0,4)});})()`);
|
||||
log('12) 结果:', SAN(String(msg)).slice(0, 400));
|
||||
await c.shot('/tmp/tk_pilot.jpeg').catch(() => {});
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,47 @@
|
||||
6980309334003
|
||||
6980309334010
|
||||
6980309334027
|
||||
6980309334034
|
||||
6980309334041
|
||||
6980309334058
|
||||
6980309334065
|
||||
6980309334072
|
||||
6980309334089
|
||||
6980309334096
|
||||
6980309334102
|
||||
6980309334119
|
||||
6980309334126
|
||||
6980309334133
|
||||
6980309334140
|
||||
6980309334157
|
||||
6980309334164
|
||||
6980309334171
|
||||
6980309334188
|
||||
6980309334195
|
||||
6980309334201
|
||||
6980309334218
|
||||
6980309334225
|
||||
6980309334232
|
||||
6980309334249
|
||||
6980309334256
|
||||
6980309334263
|
||||
6980309334270
|
||||
6980309334287
|
||||
6980309334294
|
||||
6980309334300
|
||||
6980309334317
|
||||
6980309334324
|
||||
6980309334331
|
||||
6980309334348
|
||||
6980309334355
|
||||
6980309334362
|
||||
6980309334379
|
||||
6980309334386
|
||||
6980309334393
|
||||
6980309334409
|
||||
6980309334416
|
||||
6980309334423
|
||||
6980309334430
|
||||
6980309334447
|
||||
6980309334454
|
||||
6980309334461
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"items": [
|
||||
"* 店铺账号 [select] Snowmountain",
|
||||
"* 新版产品分类 [select] 发夹、别针和夹子(Hair Barret",
|
||||
" 是否为单品 [input] false",
|
||||
" 店小秘分类 [-] ",
|
||||
" 来源URL [input] ",
|
||||
" [input] ",
|
||||
"* Product Name (产品名称) [input] ",
|
||||
"* SKU [input] ",
|
||||
"* Product Identifiers (产品标识符) [select] UPC",
|
||||
"* 产品属性 [ta] Gram",
|
||||
"* Brand(品牌) [input] ",
|
||||
" Small Parts Warning Code(小零件警告代码) [input] 2 - Choking hazard c",
|
||||
" Net Content(净内容) [select] Gram",
|
||||
" Multipack Quantity(多包装数量) [input] 1",
|
||||
" Material(材料) [input] rhinestone",
|
||||
" Label Image(标签图像) [ta] ",
|
||||
" Label Image URL(标签图片URL) [ta] ",
|
||||
" Label Image Contains(标签图像包含) [select] ",
|
||||
" Is Prop 65 Warning Required(需要65号提案警告吗?) [select] No",
|
||||
" Has Written Warranty(已书面保修) [select] No",
|
||||
" Count Per Pack(每包计数) [input] 1",
|
||||
" Condition(条件) [select] New(New)",
|
||||
" Color Category(颜色类别) [input] ",
|
||||
" Color(颜色) [ta] ",
|
||||
" Assembled Product Width(组装产品宽度) [select] in",
|
||||
" Assembled Product Depth(组装产品深度) [select] in",
|
||||
" Age Group(年龄组) [input] Newborn",
|
||||
" Country of Origin - Substantial Transformation(原产国-实质性转变) [input] ",
|
||||
" Automate Pricing(自动定价) [ta] ",
|
||||
" Minimum Seller Allowed Price(最低卖家允许价格) [input] ",
|
||||
" Maximum Seller Allowed Price(卖方允许的最高价格) [input] ",
|
||||
" Repricer Strategy(重新定价策略) [ta] ",
|
||||
" External Product Identifier(外部产品标识符) [ta] ",
|
||||
" External Product ID Type(外部产品ID类型) [select] ",
|
||||
" External Product ID(外部产品ID) [ta] ",
|
||||
" Product Package Dimensions and Weight(产品包装尺寸及重量) [input] ",
|
||||
" Product Package Dimensions Depth (in)(产品包装尺寸深度(英寸)) [input] ",
|
||||
" Product Package Weight (lbs)(产品包装重量(磅)) [input] ",
|
||||
" Product Package Dimensions Height (in)(产品包装尺寸 高度(英寸)) [input] ",
|
||||
" Product Package Dimensions Width (in)(产品包装尺寸 宽度(英寸)) [input] ",
|
||||
" Site Start Date(网站开始日期) [input] ",
|
||||
" Site End Date(网站结束日期) [input] ",
|
||||
" State Restrictions(州限制) [ta] ",
|
||||
" State Restrictions Reason(国家限制原因) [select] ",
|
||||
" ZIP Codes(邮政编码) [ta] ",
|
||||
" States(各州) [select] ",
|
||||
" Academic Institution(学术机构) [ta] ",
|
||||
" Character(性格) [input] ",
|
||||
" Character Group(字符组) [input] ",
|
||||
" Collection(收藏) [ta] ",
|
||||
" Total Count(总计数) [input] ",
|
||||
" Designer(设计师) [ta] ",
|
||||
" Embellishment Type(装饰类型) [input] ",
|
||||
" Closure Type(闭合类型) [input] Clip",
|
||||
" Brand License(品牌授权) [input] ",
|
||||
" Hair Barrette, Pin & Clip Type(发夹、别针和夹子类型) [input] Tiara Hairpin",
|
||||
" Retail Packaging(零售包装) [input] ",
|
||||
" Is Primary Variant(是主要变体) [input] ",
|
||||
" Manufacturer Name(制造商名称) [ta] ",
|
||||
" Manufacturer Part Number(制造商零件编号) [ta] ",
|
||||
" Model Number(型号) [ta] ",
|
||||
" Net Content Statement(净内容声明) [ta] ",
|
||||
" Occasion(场合) [input] ",
|
||||
" Pattern(图案) [input] ",
|
||||
" Number of Pieces(件数) [input] ",
|
||||
" Product Line(产品线) [input] ",
|
||||
" California Prop 65 Warning Text(加州65号提案警告文本) [ta] ",
|
||||
" Required Storage Condition(所需存储条件) [input] Ambient Storage",
|
||||
" Season(季节) [input] Winter",
|
||||
" Shape(形状) [ta] ",
|
||||
" Size(尺寸) [ta] ",
|
||||
" Size Descriptor(大小描述符) [input] ",
|
||||
" Sports League(体育联盟) [input] ",
|
||||
" Sports Team(运动队) [input] ",
|
||||
" Style(风格) [input] ",
|
||||
" Theme(主题) [ta] ",
|
||||
" Third Party Accreditation Symbol on Product Package Code(产品包装代码上的第三方认可符号) [input] ",
|
||||
" Warranty Text(保修文本) [ta] ",
|
||||
" Warranty URL(保修网址) [input] ",
|
||||
"* 价格 [input] ",
|
||||
"* 库存 [input] ",
|
||||
"* 运输重量 [input] ",
|
||||
" MSRP [input] ",
|
||||
"* 产品图片 [-] ",
|
||||
"* 产品描述 [ta] ",
|
||||
"* key feature [ta] ",
|
||||
" Fulfillment Lag Time (Lag Time) [input] ",
|
||||
" Must ship alone? (单独运输) [input] ",
|
||||
" Ships in Original Packaging (原装运输) [input] ",
|
||||
" Product is or Contains this Battery Type(电子元器件类型) [input] ",
|
||||
" Product is or Contains a Chemical, Aerosol or Pesticide?(是否包含化学品) [input] ",
|
||||
" Product is or Contains an Electronic Component?(是否包含电子元器件) [input] "
|
||||
],
|
||||
"tables": []
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
[
|
||||
"眼镜盒(Eyeglass Cases)",
|
||||
"缝纫别针(Sewing Pins)",
|
||||
"文件文件夹(File Folders)",
|
||||
"钱夹(Money Clips)",
|
||||
"自行车瓶笼(Bicycle Bottle Cages)",
|
||||
"肥皂盘和持有人(Soap Dishes & Holders)",
|
||||
"时尚头带(Fashion Headbands)",
|
||||
"鞋带(Shoelaces)",
|
||||
"化妆包(Makeup Bags)",
|
||||
"举重手套(Weight Lifting Gloves)",
|
||||
"剪贴簿装饰(Scrapbooking Embellishments)",
|
||||
"自行车后视镜(Bicycle Mirrors)",
|
||||
"桌面管理器(Desktop Organizers)",
|
||||
"行李标签(Luggage Tags)",
|
||||
"汽车装饰内饰五金配件(Automotive Decorative Interior Hardware & Accessories)",
|
||||
"零钱包(Coin Purses)",
|
||||
"汽车组织者(Automotive Organizers)",
|
||||
"牙刷套(Toothbrush Covers)",
|
||||
"墙贴和贴纸(Wall Decals & Stickers)",
|
||||
"钩环紧固件(Hook & Loop Fasteners)",
|
||||
"窗膜(Window Film)",
|
||||
"自行车制动器组(Bicycle Brake Sets)",
|
||||
"标签(Labels)",
|
||||
"黑头和白头去除工具(Blackhead & Whitehead Remover Tools)",
|
||||
"无焰蜡烛(Flameless Candles)",
|
||||
"礼品袋(Gift Bags)",
|
||||
"跳绳(Jump Ropes)",
|
||||
"冰箱储物箱、搁板和储物篮(Refrigerator Bins, Shelves & Baskets)",
|
||||
"可重复使用的环保袋(Reusable Eco Bags)",
|
||||
"首饰盒(Jewelry Boxes)",
|
||||
"耳环(Earrings)",
|
||||
"派对横幅(Party Banners)",
|
||||
"应急准备工具包(Emergency Preparedness Kits)",
|
||||
"手套和连指手套(Gloves & Mittens)",
|
||||
"别针和胸针(Pins & Brooches)",
|
||||
"急救包(First Aid Kits)",
|
||||
"数学工具包(Math Tool Kits)",
|
||||
"风铃(Wind Chimes)",
|
||||
"护照持有人(Passport Holders)",
|
||||
"铅笔盒(Pencil Boxes)",
|
||||
"标尺(Rulers)",
|
||||
"书签(Bookmarks)",
|
||||
"浴室贴花(Bath Appliques)",
|
||||
"花园雕像(Garden Statues)",
|
||||
"浇水喷嘴(Watering Nozzles)",
|
||||
"袜子(Socks)",
|
||||
"隐形眼镜盒(Contact Lens Cases)",
|
||||
"缝纫紧固件(Sewing Fasteners)",
|
||||
"摩托车配件(Motorcycle Accessories)",
|
||||
"汽车反光镜(Automotive Reflectors)",
|
||||
"耳机盒(Headphone Cases)",
|
||||
"洗漱用具包(Toiletry Kits & Bags)",
|
||||
"手提包挂钩衣架(Handbag Hook Hangers)",
|
||||
"铅笔(Pencils)",
|
||||
"刷刷(Basting Brushes)",
|
||||
"按摩器(Massagers)",
|
||||
"篮子(Baskets)",
|
||||
"荧光笔(Highlighters)",
|
||||
"计算机机架和支架(Computer Racks & Mounts)",
|
||||
"熨烫垫(Ironing Mats)",
|
||||
"游泳袋(Swimming Gear Bags)",
|
||||
"手袋(Handbags)",
|
||||
"空白识别徽章(Blank Identification Badges)",
|
||||
"新奇和堵嘴玩具(Novelty & Gag Toys)",
|
||||
"葡萄酒手提袋、包和背包(Wine Totes, Bags & Backpacks)",
|
||||
"隐形眼镜清洁系统(Contact Lens Cleaning Systems)",
|
||||
"化妆刷和涂抹器(Makeup Brushes & Applicators)",
|
||||
"发夹、别针和夹子(Hair Barrettes, Pins & Clips)",
|
||||
"手机的皮肤(Skins for Cell Phones)",
|
||||
"贴纸(Stickers)",
|
||||
"自行车车架包(Bicycle Frame Bags)",
|
||||
"标记(Markers)",
|
||||
"卷发器(Hair Rollers)",
|
||||
"自行车座杆夹具(Bicycle Seatpost Clamps)",
|
||||
"墙纸(Wallpaper)",
|
||||
"便当盒(Bento Boxes)",
|
||||
"化妆组织者(Makeup Organizers)",
|
||||
"硬件挂钩(Hardware Hooks)",
|
||||
"储物箱和储物箱(Storage Chests & Boxes)",
|
||||
"自行车铃铛和喇叭(Bicycle Bells & Horns)",
|
||||
"汽车贴花和贴纸(Automotive Decals & Stickers)",
|
||||
"酒塞和软木塞(Wine Stoppers & Corks)",
|
||||
"腰包(Fanny Packs)",
|
||||
"手提包整理包和插页(Handbag Organizers & Inserts)",
|
||||
"药丸组织者(Pill Organizers)",
|
||||
"运动装备包(Sports Equipment Bags)",
|
||||
"浴帽(Shower Caps)",
|
||||
"面部镜子(Face Mirrors)",
|
||||
"钱包(Wallets)",
|
||||
"钢笔(Pens)",
|
||||
"鼻子和耳朵修剪器(Nose & Ear Trimmers)",
|
||||
"卷发器(Hair Curling Irons)",
|
||||
"钥匙扣(Keychains)",
|
||||
"壁橱收纳盒(Closet Organizers)",
|
||||
"项链吊坠(Necklace Pendants)",
|
||||
"项链(Necklaces)",
|
||||
"气球(Balloons)",
|
||||
"漏斗和漏斗组(Funnels & Funnel Sets)",
|
||||
"铅笔盒(Pencil Cases)",
|
||||
"医用袋(Medical Bags)",
|
||||
"键盘保护器(Keyboard Protectors)",
|
||||
"鞋袋(Shoe Bags)",
|
||||
"铅笔和钢笔橡皮擦(Pencil & Pen Erasers)",
|
||||
"排水蛇(Drain Snakes)",
|
||||
"学校和办公用纸(School & Office Paper)",
|
||||
"牙刷架(Toothbrush Holders)",
|
||||
"行李带(Luggage Straps)",
|
||||
"包装组织者,立方体和文件夹(Packing Organizers, Cubes & Folders)",
|
||||
"艺术画笔(Art Brushes)",
|
||||
"眼影(Eye Shadow)",
|
||||
"自行车灯(Bicycle Lights)",
|
||||
"书挡(Bookends)",
|
||||
"游戏附加组件(Game Add-On)",
|
||||
"蜡笔(Crayons)",
|
||||
"设备盖(Appliance Covers)",
|
||||
"写作笔记本和素描本(Writing Notebooks & Sketch Books)",
|
||||
"食品储存罐和容器(Food Storage Jars & Containers)",
|
||||
"背包(Backpacks)",
|
||||
"小雕像和小装饰品(Figurines & Knick-Knacks)",
|
||||
"自行车拨链器(Bicycle Derailleurs)",
|
||||
"手动卷笔刀(Manual Pencil Sharpeners)",
|
||||
"花盆和花盆(Plant Pots & Planters)",
|
||||
"公文包(Briefcases)",
|
||||
"行李袋(Duffel Bags)",
|
||||
"帽子(Hats)",
|
||||
"面部清洁刷(Facial Cleansing Brushes)",
|
||||
"头发刷子(Hair Brushes)",
|
||||
"自行车曲柄(Bicycle Cranksets)",
|
||||
"装饰品(Ornaments)",
|
||||
"包包饰物(Bag Charms)",
|
||||
"自行车工具(Bicycle Tools)",
|
||||
"睫毛夹(Eyelash Curlers)",
|
||||
"文具及文具套装(Stationery & Stationery Sets)",
|
||||
"发梳(Hair Combs)",
|
||||
"可重复使用的午餐袋和盒子(Reusable Lunch Bags & Boxes)",
|
||||
"信使包和单肩包(Messenger & Shoulder Bags)",
|
||||
"便条卡(Note Cards)",
|
||||
"腕带(Wristbands)",
|
||||
"铅笔握把(Pencil Grips)",
|
||||
"车把胶带(Handlebar Tape)",
|
||||
"绑腿(Gaiters)",
|
||||
"摩托车零件(Motorcycle Parts)",
|
||||
"明信片(Postcards)",
|
||||
"牙签(Toothpicks)",
|
||||
"自行车挡泥板(Bicycle Fenders)",
|
||||
"名片夹(Business Card Holders)",
|
||||
"其他化妆刷和工具(Other Makeup Brushes & Tools)",
|
||||
"其他发型造型工具和产品(Other Hair Styling Tools & Products)",
|
||||
"其他派对用品(Other Party Supplies)",
|
||||
"其他手机配件(Other Cell Phone Accessories)",
|
||||
"Other Bicycle Parts(Other Bicycle Parts)",
|
||||
"其他旅行配件(Other Travel Accessories)"
|
||||
]
|
||||
@@ -0,0 +1,227 @@
|
||||
{
|
||||
"1027332215847": {
|
||||
"cat": "picked:储物箱和储物箱(Storage",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"547183997437": {
|
||||
"cat": "picked:项链吊坠(Necklace",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"548459586741": {
|
||||
"cat": "picked:其他派对用品(Other",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"591994902720": {
|
||||
"cat": "picked:铅笔盒(Pencil",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"610164857063": {
|
||||
"cat": "picked:时尚头带(Fashion",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"621300323670": {
|
||||
"cat": "picked:桌面管理器(Desktop",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"623011262210": {
|
||||
"cat": "picked:墙贴和贴纸(Wall",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"627879474686": {
|
||||
"cat": "picked:袜子(Socks)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"639409307567": {
|
||||
"cat": "picked:墙贴和贴纸(Wall",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"641673262387": {
|
||||
"cat": "picked:首饰盒(Jewelry",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"657617635303": {
|
||||
"cat": "picked:桌面管理器(Desktop",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"661190598139": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"676236596821": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"682195499823": {
|
||||
"cat": "picked:铅笔盒(Pencil",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"685456757807": {
|
||||
"cat": "picked:缝纫紧固件(Sewing",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"694637537727": {
|
||||
"cat": "picked:可重复使用的午餐袋和盒子(Reusable",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"698782391248": {
|
||||
"cat": "picked:贴纸(Stickers)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"703822717997": {
|
||||
"cat": "picked:桌面管理器(Desktop",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"707826905566": {
|
||||
"cat": "picked:钱包(Wallets)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"708420302844": {
|
||||
"cat": "picked:钥匙扣(Keychains)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"714492799386": {
|
||||
"cat": "picked:化妆包(Makeup",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"719391062058": {
|
||||
"cat": "picked:化妆组织者(Makeup",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"726442972676": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"735147938123": {
|
||||
"cat": "picked:时尚头带(Fashion",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"739147684759": {
|
||||
"cat": "picked:缝纫紧固件(Sewing",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"750271307455": {
|
||||
"cat": "picked:钥匙扣(Keychains)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"774494945453": {
|
||||
"cat": "picked:钥匙扣(Keychains)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"775057912118": {
|
||||
"cat": "picked:钥匙扣(Keychains)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"787261097583": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"823560564174": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"823838532886": {
|
||||
"cat": "picked:名片夹(Business",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"833753630925": {
|
||||
"cat": "picked:时尚头带(Fashion",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"836512561982": {
|
||||
"cat": "picked:化妆包(Makeup",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"853414558425": {
|
||||
"cat": "picked:其他手机配件(Other",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"854003999648": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"857781294719": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"863384503032": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"863524134410": {
|
||||
"cat": "picked:贴纸(Stickers)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"866537106657": {
|
||||
"cat": "picked:缝纫紧固件(Sewing",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"893592264148": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"896469995553": {
|
||||
"cat": "picked:钥匙扣(Keychains)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"902440558099": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"944470567338": {
|
||||
"cat": "picked:发夹、别针和夹子(Hair",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"953869908708": {
|
||||
"cat": "picked:墙贴和贴纸(Wall",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
},
|
||||
"997450831819": {
|
||||
"cat": "picked:贴纸(Stickers)",
|
||||
"err": "仍报错的字段: []",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"997450831819": "184807701434436987",
|
||||
"953869908708": "184807701434436937",
|
||||
"944470567338": "184807701434436423",
|
||||
"902440558099": "184807701434436399",
|
||||
"896469995553": "184807701434436361",
|
||||
"893592264148": "184807701434436319",
|
||||
"866537106657": "184807701434436311",
|
||||
"863524134410": "184807701434436289",
|
||||
"863384503032": "184807701434436279",
|
||||
"857781294719": "184807701434436257",
|
||||
"854003999648": "184807701434436227",
|
||||
"853414558425": "184807701434436183",
|
||||
"836512561982": "184807701434436131",
|
||||
"833753630925": "184807701434435981",
|
||||
"823838532886": "184807701434435937",
|
||||
"823560564174": "184807701434435823",
|
||||
"787261097583": "184807701434435821",
|
||||
"775057912118": "184807701434435805",
|
||||
"774494945453": "184807701434435785",
|
||||
"750271307455": "184807701434435757",
|
||||
"739147684759": "184807701434435749",
|
||||
"735147938123": "184807701434435731",
|
||||
"726442972676": "184807701434435725",
|
||||
"719391062058": "184807701434435723",
|
||||
"714492799386": "184807701434435703",
|
||||
"708420302844": "184807701434435695",
|
||||
"707826905566": "184807701434435659",
|
||||
"703822717997": "184807701434435649",
|
||||
"698782391248": "184807701434435563",
|
||||
"694637537727": "184807701434435549",
|
||||
"685456757807": "184807701434435539",
|
||||
"682195499823": "184807701434435519",
|
||||
"676236596821": "184807701434435509",
|
||||
"661190598139": "184807701434435491",
|
||||
"657617635303": "184807701434435461",
|
||||
"641673262387": "184807701434435433",
|
||||
"639409307567": "184807701434435427",
|
||||
"627879474686": "184807701434435419",
|
||||
"623011262210": "184807701434435411",
|
||||
"621300323670": "184807701434435409",
|
||||
"610164857063": "184807701434435397",
|
||||
"591994902720": "184807701434435375",
|
||||
"548459586741": "184807701434435371",
|
||||
"547183997437": "184807701434435369",
|
||||
"1027332215847": "184807701434434765",
|
||||
"607330049419": "184807701434434763"
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"stores": [
|
||||
"walmartAddStore",
|
||||
"walmartBasicStore",
|
||||
"walmartProductStore",
|
||||
"walmartAttrStore",
|
||||
"walmartImgStore",
|
||||
"walmartDescStore",
|
||||
"walmartPackStore",
|
||||
"walmartSupplementStore",
|
||||
"walmartAddVariationStore",
|
||||
"walmartDxmInfoStore",
|
||||
"walmartAddThemeStore",
|
||||
"walmartPriceShippingStore"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
"lab": "店铺账号",
|
||||
"req": true,
|
||||
"kind": "select",
|
||||
"val": "Snowmountain"
|
||||
},
|
||||
{
|
||||
"lab": "新版产品分类",
|
||||
"req": true,
|
||||
"kind": "input",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "是否为单品",
|
||||
"req": false,
|
||||
"kind": "input",
|
||||
"val": "false"
|
||||
},
|
||||
{
|
||||
"lab": "店小秘分类",
|
||||
"req": false,
|
||||
"kind": "-",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "来源URL",
|
||||
"req": false,
|
||||
"kind": "input",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "产品属性",
|
||||
"req": false,
|
||||
"kind": "-",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "单位数量单价",
|
||||
"req": false,
|
||||
"kind": "input",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "库存",
|
||||
"req": true,
|
||||
"kind": "input",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "产品图片",
|
||||
"req": true,
|
||||
"kind": "-",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "产品描述",
|
||||
"req": true,
|
||||
"kind": "textarea",
|
||||
"val": ""
|
||||
},
|
||||
{
|
||||
"lab": "key feature",
|
||||
"req": true,
|
||||
"kind": "textarea",
|
||||
"val": ""
|
||||
}
|
||||
],
|
||||
"dump": {
|
||||
"walmartAddStore": {
|
||||
"sharedData.isTemplateQuoting": "false",
|
||||
"sharedData.isVariationTemplateEditing": "false",
|
||||
"sharedData.pageType": "add",
|
||||
"attrsInfo.hasPpuQuantity": "false",
|
||||
"defaultAttrsInfo.hasPpuQuantity": "false"
|
||||
},
|
||||
"walmartBasicStore": {
|
||||
"formState.shopId": "7661631",
|
||||
"formState.hasVariant": "false"
|
||||
},
|
||||
"walmartProductStore": {
|
||||
"formState.productIdType": "UPC",
|
||||
"dataState.skuChecked": "false",
|
||||
"dataState.productIdChecked": "false"
|
||||
},
|
||||
"walmartAttrStore": {
|
||||
"formState.isNoBrand": "false"
|
||||
},
|
||||
"walmartImgStore": {},
|
||||
"walmartDescStore": {},
|
||||
"walmartPackStore": {},
|
||||
"walmartSupplementStore": {},
|
||||
"walmartAddVariationStore": {
|
||||
"fieldProductIdType": "UPC",
|
||||
"baseState.skuChecked": "false",
|
||||
"baseState.upcChecked": "false"
|
||||
},
|
||||
"walmartDxmInfoStore": {},
|
||||
"walmartAddThemeStore": {},
|
||||
"walmartPriceShippingStore": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
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);
|
||||
@@ -0,0 +1,278 @@
|
||||
{
|
||||
"1027332215847": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 4",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"547183997437": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"548459586741": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"591994902720": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"607330049419": {
|
||||
"brand": "brand 现值: Unbranded",
|
||||
"imgs": "补后图片数: 4",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"610164857063": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"621300323670": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"623011262210": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"627879474686": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"639409307567": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"641673262387": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"657617635303": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"661190598139": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"676236596821": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"682195499823": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"685456757807": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"694637537727": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"698782391248": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"703822717997": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"707826905566": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"708420302844": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"714492799386": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"719391062058": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"726442972676": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"735147938123": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"739147684759": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"750271307455": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 5",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"774494945453": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"775057912118": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"787261097583": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"823560564174": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"823838532886": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"833753630925": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"836512561982": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"853414558425": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"854003999648": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"857781294719": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"863384503032": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"863524134410": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"866537106657": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"893592264148": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"896469995553": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"902440558099": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"944470567338": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"953869908708": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
},
|
||||
"997450831819": {
|
||||
"brand": "brand 现值: Generic",
|
||||
"imgs": "补后图片数: 6",
|
||||
"save": "保存: 成功",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
/* 给已存在的 Walmart 产品补:无品牌 + 网络图片 */
|
||||
const PID = process.argv[2];
|
||||
const OID = process.argv[3];
|
||||
const IN = JSON.parse(fs.readFileSync('/tmp/wm_input.json','utf8'))[OID];
|
||||
if(!PID||!IN){ console.log('用法: node wmfixone.mjs <pid> <oid>'); process.exit(1); }
|
||||
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();
|
||||
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(()=>{});
|
||||
await wait(1000);
|
||||
const t=await (await import('/tmp/cdp3.mjs')).newTarget('about:blank');
|
||||
const {CDP}=await import('/tmp/cdp3.mjs');
|
||||
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/edit?id=${PID}`});
|
||||
await wait(24000);
|
||||
// 1. 无品牌
|
||||
const br=await c.evalJS(`(()=>{const lab=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||||
.find(x=>{const l=x.querySelector('label'); return l&&/Brand/.test(l.innerText||'');});
|
||||
if(!lab)return'nf';
|
||||
const inp=lab.querySelector('input[name=brand]');
|
||||
if(inp&&inp.value&&inp.value!=='')return 'already:'+inp.value;
|
||||
const cb=[...lab.querySelectorAll('.ant-checkbox-wrapper')].find(e=>/无品牌/.test(e.innerText||''));
|
||||
if(!cb)return'no-cb';
|
||||
for(const ev of ['mousedown','mouseup','click']) cb.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
|
||||
const i2=cb.querySelector('input[type=checkbox]'); if(i2) i2.click();
|
||||
return 'clicked';})()`);
|
||||
log('无品牌:',br);
|
||||
await wait(2000);
|
||||
const bv=await c.evalJS(`(()=>{const lab=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
|
||||
.find(x=>{const l=x.querySelector('label'); return l&&/Brand/.test(l.innerText||'');});
|
||||
const inp=lab?lab.querySelector('input[name=brand]'):null; return inp?inp.value:'?';})()`);
|
||||
log('brand 现值:', String(bv));
|
||||
// 2. 图片(若已有则跳过)
|
||||
const cur=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 m=(it?it.innerText:'').match(/已经选用了\\s*(\\d+)\\s*张/); return m?Number(m[1]):-1;})()`);
|
||||
log('已有图片数:', String(cur));
|
||||
if (Number(cur) < 4) {
|
||||
const pos=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 b=[...it.querySelectorAll('button')].find(e=>/选择图片/.test(e.innerText||''));
|
||||
b.scrollIntoView({block:'center'}); const r=b.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:pos[0],y:pos[1]}); await wait(1500);
|
||||
await c.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:pos[0]+2,y:pos[1]+2}); await wait(2200);
|
||||
const hit=JSON.parse(String(await c.evalJS(`(()=>{const d=[...document.querySelectorAll('.ant-dropdown')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!d)return'[]'; const it=[...d.querySelectorAll('li,.ant-dropdown-menu-item')].find(e=>/网络图片/.test(e.innerText||''));
|
||||
if(!it)return'[]'; const r=it.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
|
||||
if(hit.length){ await c.send('Input.dispatchMouseEvent',{type:'mouseMoved',x:hit[0],y:hit[1]}); await wait(300);
|
||||
for(const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:hit[0],y:hit[1],button:'left',clickCount:1}); }
|
||||
await wait(4000);
|
||||
const f=await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return'no-modal'; const ta=m.querySelector('textarea'); if(!ta)return'no-ta';
|
||||
ta.focus(); Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype,'value').set.call(ta, ${JSON.stringify((IN.imgs||[]).join('\n'))});
|
||||
ta.dispatchEvent(new Event('input',{bubbles:true})); ta.dispatchEvent(new Event('change',{bubbles:true}));
|
||||
return 'n='+ta.value.split('\\n').filter(Boolean).length;})()`);
|
||||
log('填URL:', f);
|
||||
await wait(1200);
|
||||
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
|
||||
if(!m)return 0; const b=[...m.querySelectorAll('button')].find(x=>/^\\s*添加\\s*$/.test(x.innerText||''));
|
||||
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(15000);
|
||||
}
|
||||
const after=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 m=(it?it.innerText:'').match(/已经选用了\\s*(\\d+)\\s*张/); return m?Number(m[1]):-1;})()`);
|
||||
log('补后图片数:', String(after));
|
||||
// 3. 保存
|
||||
await c.evalJS(`(()=>{const b=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null&&/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
const el=b[b.length-1]; el.scrollIntoView({block:'center'});
|
||||
for(const ev of ['mousedown','mouseup','click']) el.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
let ok=false;
|
||||
for(let i=0;i<16;i++){ await wait(800);
|
||||
const m=await c.evalJS(`(()=>[...document.querySelectorAll('.ant-modal,.ant-message-notice')].some(e=>e.offsetParent!==null&&/成功/.test(e.innerText||'')))()`);
|
||||
if(m){ ok=true; break; } }
|
||||
log('保存:', ok?'成功':'未见成功提示');
|
||||
process.exit(ok?0:3);
|
||||
@@ -0,0 +1,433 @@
|
||||
{
|
||||
"548459586741": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kw": "whistle",
|
||||
"time": "2026-09-19T09:43:14.224Z"
|
||||
},
|
||||
"627879474686": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kw": "socks",
|
||||
"time": "2026-09-19T09:49:34.908Z"
|
||||
},
|
||||
"735147938123": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"headband",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T09:58:13.215Z"
|
||||
},
|
||||
"764423265986": {
|
||||
"ok": false,
|
||||
"msg": "[]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"thermometer",
|
||||
"hygrometer"
|
||||
],
|
||||
"time": "2026-09-20T07:43:15.046Z"
|
||||
},
|
||||
"610164857063": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"headband",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:02:44.735Z"
|
||||
},
|
||||
"676236596821": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:04:28.203Z"
|
||||
},
|
||||
"866537106657": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"button"
|
||||
],
|
||||
"time": "2026-09-19T10:06:11.668Z"
|
||||
},
|
||||
"863524134410": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:07:55.135Z"
|
||||
},
|
||||
"623011262210": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:09:38.601Z"
|
||||
},
|
||||
"714492799386": {
|
||||
"ok": false,
|
||||
"msg": "分类失败:makeup bag → noresult [modal未关/无cat]"
|
||||
},
|
||||
"997450831819": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:12:19.863Z"
|
||||
},
|
||||
"639409307567": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:14:03.296Z"
|
||||
},
|
||||
"698782391248": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:15:46.706Z"
|
||||
},
|
||||
"707826905566": {
|
||||
"ok": false,
|
||||
"msg": "[]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"card holder",
|
||||
"wallet"
|
||||
],
|
||||
"time": "2026-09-20T07:53:22.225Z"
|
||||
},
|
||||
"1027332215847": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 4,
|
||||
"kws": [
|
||||
"box"
|
||||
],
|
||||
"time": "2026-09-19T10:18:27.975Z"
|
||||
},
|
||||
"682195499823": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"pencil case",
|
||||
"brush"
|
||||
],
|
||||
"time": "2026-09-20T07:56:07.816Z"
|
||||
},
|
||||
"739147684759": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"button"
|
||||
],
|
||||
"time": "2026-09-19T10:21:09.254Z"
|
||||
},
|
||||
"944470567338": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:22:52.766Z"
|
||||
},
|
||||
"787261097583": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:24:36.330Z"
|
||||
},
|
||||
"833753630925": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"headband",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:26:19.820Z"
|
||||
},
|
||||
"863384503032": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:28:03.320Z"
|
||||
},
|
||||
"774494945453": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T10:29:46.680Z"
|
||||
},
|
||||
"661190598139": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"hair",
|
||||
"hairband"
|
||||
],
|
||||
"time": "2026-09-19T10:31:30.243Z"
|
||||
},
|
||||
"685456757807": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"yarn"
|
||||
],
|
||||
"time": "2026-09-19T10:33:13.720Z"
|
||||
},
|
||||
"708420302844": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T10:34:57.167Z"
|
||||
},
|
||||
"547183997437": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"necklace"
|
||||
],
|
||||
"time": "2026-09-19T10:36:40.675Z"
|
||||
},
|
||||
"657617635303": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:38:24.097Z"
|
||||
},
|
||||
"641673262387": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"earring"
|
||||
],
|
||||
"time": "2026-09-19T10:40:07.524Z"
|
||||
},
|
||||
"953869908708": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"sticker"
|
||||
],
|
||||
"time": "2026-09-19T10:41:50.993Z"
|
||||
},
|
||||
"703822717997": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T10:43:34.366Z"
|
||||
},
|
||||
"694637537727": {
|
||||
"ok": false,
|
||||
"msg": "分类失败:insulated bag → noresult [modal未关/无cat]"
|
||||
},
|
||||
"836512561982": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"box"
|
||||
],
|
||||
"time": "2026-09-19T10:46:15.578Z"
|
||||
},
|
||||
"591994902720": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"pencil case",
|
||||
"pen bag"
|
||||
],
|
||||
"time": "2026-09-20T07:50:36.285Z"
|
||||
},
|
||||
"853414558425": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T10:48:56.820Z"
|
||||
},
|
||||
"902440558099": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"hair",
|
||||
"hairband"
|
||||
],
|
||||
"time": "2026-09-19T10:50:40.305Z"
|
||||
},
|
||||
"823838532886": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"card"
|
||||
],
|
||||
"time": "2026-09-19T10:52:23.793Z"
|
||||
},
|
||||
"823560564174": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"hair",
|
||||
"hairband"
|
||||
],
|
||||
"time": "2026-09-19T10:54:07.265Z"
|
||||
},
|
||||
"621300323670": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"tape"
|
||||
],
|
||||
"time": "2026-09-19T10:55:50.760Z"
|
||||
},
|
||||
"896469995553": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T10:57:34.189Z"
|
||||
},
|
||||
"719391062058": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"storage box",
|
||||
"organizer"
|
||||
],
|
||||
"time": "2026-09-20T07:58:53.640Z"
|
||||
},
|
||||
"857781294719": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T11:00:15.538Z"
|
||||
},
|
||||
"750271307455": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 5,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T11:01:58.924Z"
|
||||
},
|
||||
"726442972676": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T11:03:42.512Z"
|
||||
},
|
||||
"775057912118": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"keychain"
|
||||
],
|
||||
"time": "2026-09-19T11:05:25.954Z"
|
||||
},
|
||||
"893592264148": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"clip",
|
||||
"hair"
|
||||
],
|
||||
"time": "2026-09-19T11:07:09.457Z"
|
||||
},
|
||||
"607330049419": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"hair",
|
||||
"hairband"
|
||||
],
|
||||
"time": "2026-09-19T11:08:53.317Z"
|
||||
},
|
||||
"854003999648": {
|
||||
"ok": true,
|
||||
"msg": "[\"消息提示 您的产品已保存到「 待发布」,请在待发布页完成发布! 创建新产品继续编辑\"]",
|
||||
"imgs": 6,
|
||||
"kws": [
|
||||
"hair",
|
||||
"hairband"
|
||||
],
|
||||
"time": "2026-09-19T11:10:36.759Z"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[{"oid":"548459586741","batch":"OK","found":2,"id":"184807708921479685","cat":"202213618","subj":"6.5cm Colorful Plastic Party Whistles fo"},{"oid":"661190598139","batch":"OK","found":1,"id":"184807708921552911","cat":"201223336","subj":"Retro Hair Tie with Crystal Pendant for "},{"oid":"764423265986","batch":"FAIL","found":0,"id":"","cat":"","subj":""},{"oid":"685456757807","batch":"OK","found":1,"id":"184807708921553779","cat":"200001531","subj":"Pure Natural Cotton Yarn for Baby Knitti"},{"oid":"735147938123","batch":"OK","found":1,"id":"184807708921513553","cat":"201303801","subj":"Wide Denim Retro Hair Band for Women Col"},{"oid":"627879474686","batch":"OK","found":1,"id":"184807708921502753","cat":"202220055","subj":"Cotton Cartoon Print High Socks for Girl"},{"oid":"610164857063","batch":"OK","found":1,"id":"184807708921526779","cat":"201303801","subj":"Breathable Sports Headband Non-Slip Runn"},{"oid":"676236596821","batch":"OK","found":1,"id":"184807708921530261","cat":"200352146","subj":"Cute Cartoon Baby Hair Clips Set, 3 Pcs "},{"oid":"708420302844","batch":"OK","found":1,"id":"184807708921554161","cat":"200387151","subj":"Brass Keychain with 12 Zodiac Signs & 5 "},{"oid":"866537106657","batch":"OK","found":1,"id":"184807708921535221","cat":"200127142","subj":"Butterfly Resin Buttons for Sweaters Coa"},{"oid":"547183997437","batch":"OK","found":1,"id":"184807708921555383","cat":"200001699","subj":"Cute Horse Pendant Necklace Set Silver J"},{"oid":"657617635303","batch":"OK","found":1,"id":"184807708921556577","cat":"200352146","subj":"Clear Magnetic Paperclip Box with Pastel"},{"oid":"641673262387","batch":"OK","found":1,"id":"184807708921557607","cat":"200001691","subj":"Organza Jewelry Pouch Bag with Drawstrin"},{"oid":"953869908708","batch":"OK","found":1,"id":"184807708921558109","cat":"200003295","subj":"Transparent Window Stickers Christmas Sn"},{"oid":"703822717997","batch":"OK","found":1,"id":"184807708921558801","cat":"200352146","subj":"Stainless Steel Spring Clip for Notebook"},{"oid":"863524134410","batch":"OK","found":1,"id":"184807708921536363","cat":"200003295","subj":"50pcs Pink Aesthetic Vinyl Stickers for "},{"oid":"694637537727","batch":"FAIL","found":0,"id":"","cat":"","subj":""},{"oid":"623011262210","batch":"OK","found":1,"id":"184807708921537457","cat":"200003295","subj":"Transparent Snowflake Window Stickers fo"},{"oid":"836512561982","batch":"OK","found":1,"id":"184807708921559613","cat":"37042004","subj":"Cute Plush Velvet Cosmetic Case with Bea"},{"oid":"714492799386","batch":"FAIL","found":0,"id":"","cat":"","subj":""},{"oid":"591994902720","batch":"OK","found":1,"id":"184807708922339527","cat":"211113","subj":"Cute Cat Pencil Case with Moving Ears an"},{"oid":"853414558425","batch":"OK","found":1,"id":"184807708921560431","cat":"200387151","subj":"Soft Silicone Heart Phone Bracelet Keych"},{"oid":"997450831819","batch":"OK","found":1,"id":"184807708921538449","cat":"200003295","subj":"Matte PET Emoji Stickers for Goodnotes &"},{"oid":"639409307567","batch":"OK","found":1,"id":"184807708921540367","cat":"200003295","subj":"3D Glowing Butterfly Wall Stickers, Magn"},{"oid":"902440558099","batch":"OK","found":1,"id":"184807708921561153","cat":"201223336","subj":"Large Beige Fabric Hair Tie with Pearl f"},{"oid":"823838532886","batch":"OK","found":1,"id":"184807708921562235","cat":"200364150","subj":"Cute Cartoon Cat Key Card Holder for Met"},{"oid":"698782391248","batch":"OK","found":1,"id":"184807708921542239","cat":"200003295","subj":"Boho Style White PVC Laptop Stickers 10c"},{"oid":"707826905566","batch":"FAIL","found":0,"id":"","cat":"","subj":""},{"oid":"1027332215847","batch":"OK","found":1,"id":"184807708921544281","cat":"37042004","subj":"Silver Matte Tin Photo Box with Clear Wi"},{"oid":"682195499823","batch":"OK","found":1,"id":"184807708922342261","cat":"211113","subj":"96-Cell Brush Organizer Case for Waterco"},{"oid":"823560564174","batch":"OK","found":1,"id":"184807708921564695","cat":"201223336","subj":"Cute Heart Hair Ties Set, Soft Elastic B"},{"oid":"739147684759","batch":"OK","found":1,"id":"184807708921545827","cat":"200127142","subj":"Metallic Rectangular Coat Buttons with L"},{"oid":"621300323670","batch":"OK","found":1,"id":"184807708921568715","cat":"40512","subj":"Round Transparent Desktop Tape Dispenser"},{"oid":"601964534137","batch":"-","found":0,"id":"","cat":"","subj":""},{"oid":"896469995553","batch":"OK","found":1,"id":"184807708921569227","cat":"200387151","subj":"Angel Wings & Stars Keychain Y2K Retro B"},{"oid":"944470567338","batch":"OK","found":1,"id":"184807708921546567","cat":"200352146","subj":"Korean Style Plaid Triangle Hair Clip wi"},{"oid":"787261097583","batch":"OK","found":1,"id":"184807708921548291","cat":"200352146","subj":"Transparent Crab Hair Clips with Hearts,"},{"oid":"833753630925","batch":"OK","found":1,"id":"184807708921549201","cat":"201303801","subj":"Wide Plaid Fabric Headband for Women - C"},{"oid":"719391062Line truncated
|
||||
@@ -0,0 +1,256 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
|
||||
/* 速卖通批量建品(全自动):分类走 UI 弹窗搜索,其余按已验证配方 */
|
||||
const SHOP = 4024039;
|
||||
const FREIGHT_NAME = process.argv[2] || 'MY beauty 30g+1美金';
|
||||
const ONLY = process.argv[3];
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const log = (...a) => console.log(' ', ...a);
|
||||
|
||||
const EN = JSON.parse(fs.readFileSync('/tmp/en_all.json', 'utf8'));
|
||||
const CLEAN = JSON.parse(fs.readFileSync('/tmp/wm_clean.json', 'utf8'));
|
||||
const ALL = JSON.parse(fs.readFileSync('/tmp/all48.json', 'utf8'));
|
||||
const KW_FB = {};
|
||||
const DONE_F = '/tmp/smt_batch_done.json';
|
||||
const DONE = fs.existsSync(DONE_F) ? JSON.parse(fs.readFileSync(DONE_F, 'utf8')) : {};
|
||||
|
||||
// 分类关键词:从英文标题里取最能定位品类的词
|
||||
function catKeywords(oid) {
|
||||
const t = (EN[oid] || {}).title || '';
|
||||
const CAND = [
|
||||
[/whistle/i, ['whistle']],
|
||||
[/hair tie|scrunchie|hair ring|ponytail|elastic/i, ['hair', 'hairband']],
|
||||
[/hair clip|barrette|hairpin|clip/i, ['clip', 'hair']],
|
||||
[/headband|hair band/i, ['headband', 'hair']],
|
||||
[/keychain|key ring|key holder/i, ['keychain']],
|
||||
[/sticker|decal/i, ['sticker']],
|
||||
[/thermometer|hygrometer/i, ['thermometer']],
|
||||
[/button/i, ['button']],
|
||||
[/yarn|knitting/i, ['yarn']],
|
||||
[/sock/i, ['socks']],
|
||||
[/necklace|pendant/i, ['necklace']],
|
||||
[/bracelet/i, ['bracelet']],
|
||||
[/earring/i, ['earring']],
|
||||
[/card holder|card case|badge/i, ['card']],
|
||||
[/organizer|storage/i, ['organizer']],
|
||||
[/box|case|holder|container/i, ['box']],
|
||||
[/bag|pouch/i, ['bag']],
|
||||
[/comb|brush/i, ['comb']],
|
||||
[/mirror/i, ['mirror']],
|
||||
[/tape dispenser/i, ['tape']],
|
||||
[/hair dryer/i, ['hair dryer']],
|
||||
];
|
||||
for (const [rx, ks] of CAND) if (rx.test(t)) return ks;
|
||||
return [(t.split(/\s+/)[0] || 'gift')];
|
||||
}
|
||||
|
||||
const oids = ONLY ? [ONLY] : Object.keys(ALL).filter(o => !DONE[o] && (CLEAN[o] || []).length >= 1);
|
||||
log(`待建商品: ${oids.length} | 已有记录: ${Object.keys(DONE).length}`);
|
||||
|
||||
for (const oid of oids) {
|
||||
const en = EN[oid], imgs = CLEAN[oid] || [];
|
||||
let kws = catKeywords(oid);
|
||||
if (!en || !imgs.length) { log(`⚠ ${oid} 缺文案或图片,跳过`); continue; }
|
||||
log('');
|
||||
log(`═══ ${oid} | 图${imgs.length} 张 | 关键词候选 ${JSON.stringify(kws)} ═══`);
|
||||
|
||||
// 清掉旧标签
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x => x.type === 'page' && /\/web\/smt\/add/.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {});
|
||||
await wait(900);
|
||||
|
||||
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('Network.enable');
|
||||
let posted = false;
|
||||
c.on(m => { if (m.method === 'Network.requestWillBeSent') { const r = m.params.request;
|
||||
if (r.method === 'POST' && /\/api\/smtProduct\/add\.json/.test(r.url)) posted = true; } });
|
||||
|
||||
try {
|
||||
await c.send('Page.navigate', { url: 'https://www.dianxiaomi.com/web/smt/add' });
|
||||
await wait(32000);
|
||||
|
||||
await c.evalJS(`(function(){
|
||||
window.__f=function(rx,val,sel){ sel=sel||'input';
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; var lb=it.querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf(rx)<0) continue;
|
||||
var el=it.querySelector(sel); if(!el) return 'nf';
|
||||
var proto=el.tagName==='TEXTAREA'?window.HTMLTextAreaElement.prototype:window.HTMLInputElement.prototype;
|
||||
Object.getOwnPropertyDescriptor(proto,'value').set.call(el,String(val));
|
||||
['input','change','blur'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});
|
||||
return 'ok'; }
|
||||
return 'noitem'; };
|
||||
window.__openSel=function(rx){
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; var lb=it.querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf(rx)<0) continue;
|
||||
var sel=it.querySelector('.ant-select'); if(!sel) return 'nosel';
|
||||
var tg=sel.querySelector('.ant-select-selector')||sel; tg.scrollIntoView({block:'center'});
|
||||
['mousedown','mouseup','click'].forEach(function(ev){tg.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'opened'; }
|
||||
return 'noitem'; };
|
||||
window.__pick=function(txt){ var dds=[].slice.call(document.querySelectorAll('.ant-select-dropdown')).filter(function(e){return e.offsetParent!==null;});
|
||||
if(!dds.length)return 'nodrop'; var os=[].slice.call(dds[dds.length-1].querySelectorAll('.ant-select-item-option')); if(!os.length)return 'noopt';
|
||||
var o=txt? os.filter(function(x){return (x.innerText||'').trim()===txt;})[0] : os[0]; if(!o)o=os[0];
|
||||
['mouseenter','mouseover','mousedown','mouseup','click'].forEach(function(ev){o.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'picked:'+(o.innerText||'').trim().slice(0,26); };
|
||||
return 'ready';})()`);
|
||||
|
||||
// ① 店铺 + 模板
|
||||
await c.evalJS(`(async function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
var b=p._s.get('smtAddBasicStore');
|
||||
try{await b.updateShop(${SHOP});}catch(e){} await new Promise(z=>setTimeout(z,2500));
|
||||
try{await b.updateShopIdFun(${SHOP});}catch(e){} await new Promise(z=>setTimeout(z,3000)); return 1;})()`, true);
|
||||
const tplN = await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
return (p._s.get('smtAddBasicStore').$state.dataState.freightTemplateList||[]).length;})()`);
|
||||
log(` ① 店铺 ok,运费模板 ${tplN} 个`);
|
||||
|
||||
// ② 分类(UI 弹窗,多关键词重试 + 校验关闭)
|
||||
let catOk = false, catInfo = '';
|
||||
if (process.env.SMT_KW) { kws = process.env.SMT_KW.split(',').filter(Boolean); log(' 关键词覆盖:', kws.join('/')); } else { kws = kws.concat(KW_FB[oid] || []); }
|
||||
for (const kw of kws) {
|
||||
await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;});
|
||||
ms.forEach(function(m){var cb=m.querySelector('.ant-modal-close'); if(cb)cb.click();}); return 1;})()`);
|
||||
await wait(1200);
|
||||
await c.evalJS(`(function(){var bs=[].slice.call(document.querySelectorAll('button')).filter(function(e){return e.offsetParent!==null&&/选择分类/.test(e.innerText||'');});
|
||||
if(bs.length){bs[0].scrollIntoView({block:'center'});['mousedown','mouseup','click'].forEach(function(ev){bs[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});} return 1;})()`);
|
||||
await wait(4200);
|
||||
const n0 = await c.evalJS(`(function(){return [].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}).length;})()`);
|
||||
if (!n0) { catInfo = 'modal未开'; continue; }
|
||||
await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}); if(!ms.length)return 0;
|
||||
var m=ms[ms.length-1]; var el=m.querySelector('input[name="searchCategory"]'); if(!el)return 0;
|
||||
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,${JSON.stringify(kw)});
|
||||
['input','change'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});
|
||||
var b=[].slice.call(m.querySelectorAll('button')).filter(function(x){return /搜索/.test(x.innerText||'');}); if(b.length)b[0].click(); return 1;})()`);
|
||||
await wait(5500);
|
||||
const picked = await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}); if(!ms.length)return 'nomodal';
|
||||
var m=ms[ms.length-1]; var its=[].slice.call(m.querySelectorAll('.search-result-item'));
|
||||
if(!its.length) return 'noresult';
|
||||
var best=null,bs=-999;
|
||||
var TITLE=${JSON.stringify((EN[oid]||{}).title||'')};
|
||||
var BAD=/慈善|Благотвор|二手|Second-?hand|Used|Refurb|Sexual|情趣|Dewasa/i;
|
||||
var KID=/Baby|Kids|Kanak|婴儿|儿童|Infant|Toddler/i;
|
||||
var AUTO=/Automobile|Automotive|汽车|Motosikal/i;
|
||||
var ISK=KID.test(TITLE), ISA=AUTO.test(TITLE);
|
||||
var KW=${JSON.stringify(kw)}.toLowerCase();
|
||||
its.forEach(function(it){ var tx=(it.innerText||'').replace(/\\s+/g,' ').trim(); var s=0;
|
||||
if(BAD.test(tx)) { s-=200; }
|
||||
if(!ISK && KID.test(tx)) s-=60;
|
||||
if(!ISA && AUTO.test(tx)) s-=60;
|
||||
var segs=tx.split('/'); var lastSeg=(segs[segs.length-1]||'').replace(/\(.*?\)/g,'').trim().toLowerCase();
|
||||
if(KW && lastSeg.indexOf(KW)>=0) s+=40;
|
||||
if(/小百货|配饰|饰品|发饰|家居|住宅|文具|玩具|运动|美容|健康/i.test(tx)) s+=12;
|
||||
s -= Math.min(tx.length/80,5);
|
||||
if(s>bs){bs=s;best=it;} });
|
||||
if(!best) return 'noresult';
|
||||
best.scrollIntoView({block:'center'});
|
||||
['mousedown','mouseup','click'].forEach(function(ev){best.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'picked:'+(best.innerText||'').replace(/\\s+/g,' ').trim().slice(0,60);})()`);
|
||||
await wait(7000);
|
||||
const left = await c.evalJS(`(function(){return [].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null && (e.innerText||'').indexOf('选择类目')>=0;}).length;})()`);
|
||||
const cid = await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
return String(p._s.get('smtAddBasicStore').$state.formState.categoryId||'');})()`);
|
||||
catInfo = kw + ' → ' + SAN(String(picked)).slice(0,60);
|
||||
if (!left && cid) { catOk = true; break; }
|
||||
catInfo += ' [modal未关/无cat]';
|
||||
}
|
||||
log(` ② 分类${catOk ? '✅' : '❌'}: ${catInfo.slice(0, 100)}`);
|
||||
if (!catOk) { log(' 分类失败,跳过该商品'); DONE[oid] = { ok:false, msg:'分类失败:'+catInfo.slice(0,60) }; fs.writeFileSync(DONE_F, JSON.stringify(DONE,null,1)); c.close(); await wait(1000); continue; }
|
||||
|
||||
// ③ 文本 + 尺寸
|
||||
await c.evalJS(`(function(){
|
||||
window.__f('产品标题',${JSON.stringify(en.title)},'input:not([type=hidden])');
|
||||
window.__f('零售价格','1.99'); window.__f('货值','0.50'); window.__f('库存数量','100');
|
||||
window.__f('重量(kg)','0.02'); window.__f('包装后重量','0.02'); return 1;})()`);
|
||||
await c.evalJS(`(function(){
|
||||
var set=function(el,v){Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,v);
|
||||
['input','change','blur'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});};
|
||||
[['长','10'],['宽','5'],['高','3']].forEach(function(p){ var els=[].slice.call(document.querySelectorAll('input')).filter(function(e){return e.offsetParent!==null&&(e.placeholder||'')===p[0];});
|
||||
if(els.length) set(els[els.length-1],p[1]); });
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var lb=its[i].querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf('包装后尺寸')<0) continue;
|
||||
var ins=[].slice.call(its[i].querySelectorAll('input')); ['10','5','3'].forEach(function(v,k){ if(ins[k]) set(ins[k],v); }); }
|
||||
return 1;})()`);
|
||||
await wait(2000);
|
||||
log(' ③ 文本+尺寸 ok');
|
||||
|
||||
// ④ 模板下拉
|
||||
await c.evalJS(`window.__openSel('运费模板')`); await wait(3000);
|
||||
let fr = await c.evalJS(`window.__pick(${JSON.stringify(FREIGHT_NAME)})`);
|
||||
if (/noopt|nodrop/.test(String(fr))) fr = await c.evalJS(`window.__pick()`);
|
||||
log(` ④ 运费模板: ${SAN(String(fr))}`);
|
||||
await wait(2500);
|
||||
await c.evalJS(`window.__openSel('服务模板')`); await wait(3000);
|
||||
log(` 服务模板: ${SAN(String(await c.evalJS(`window.__pick()`)))}`);
|
||||
await wait(2500);
|
||||
await c.evalJS(`window.__openSel('是否原箱')`); await wait(2800);
|
||||
log(` 是否原箱: ${SAN(String(await c.evalJS(`window.__pick('否')`)))}`);
|
||||
await wait(2200);
|
||||
|
||||
// ⑤ 含税
|
||||
await c.evalJS(`(function(){var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; if((it.innerText||'').indexOf('报价是否含关税')<0) continue;
|
||||
var ws=[].slice.call(it.querySelectorAll('.ant-radio-wrapper'));
|
||||
for(var j=0;j<ws.length;j++){ if((ws[j].innerText||'').indexOf('不含关税')>=0){ var inp=ws[j].querySelector('input'); if(inp&&!inp.checked)inp.click(); return 1; } } }
|
||||
return 0;})()`);
|
||||
await wait(1800);
|
||||
|
||||
// ⑥ 物流属性(直接赋值,不依赖 UI 点击)
|
||||
await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
p._s.get('smtAddProductStore').$state.formState.specialProductTypeList=['1983471271:2025103']; return 1;})()`);
|
||||
await wait(1500);
|
||||
log(' ⑤ 含税+物流属性 ok');
|
||||
|
||||
// ⑦ 属性(含 check_box 归一化)
|
||||
await c.evalJS(`(function(){
|
||||
var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
var at=p._s.get('smtAddAttrStore'); var a=p._s.get('smtAddStore').$state.attrsInfo.attrsList||[];
|
||||
var want={'品牌':'201512802','产地':'9441741844','化学品':'23399591357'};
|
||||
var d=at.$state.dynamicFormData||{};
|
||||
for(var i=0;i<a.length;i++){ var nm=a[i].nameZh||''; var key=String(a[i].arrtNameId);
|
||||
for(var k in want){ if(nm.indexOf(k)>=0) d[key]='history-'+want[k]; } }
|
||||
var ra=at.$state.renderAttrs||[];
|
||||
for(var j=0;j<ra.length;j++){ var x=ra[j]; var kk=String(x.arrtNameId); var st=String(x.attributeShowTypeValue||'');
|
||||
var needArr=(st==='check_box'||st==='multiSelect'||st==='selectPercent');
|
||||
if(needArr && d[kk]!==undefined && !Array.isArray(d[kk])) d[kk]=[d[kk]];
|
||||
if(!needArr && Array.isArray(d[kk])) d[kk]=d[kk][0]||''; }
|
||||
at.$state.dynamicFormData=d; return 1;})()`);
|
||||
await wait(1500);
|
||||
|
||||
// ⑧ 描述
|
||||
await c.evalJS(`(function(){
|
||||
try{ if(typeof CKEDITOR==='undefined'||!CKEDITOR.instances) return 0;
|
||||
var n=Object.keys(CKEDITOR.instances)[0]; var ed=CKEDITOR.instances[n];
|
||||
ed.setData(${JSON.stringify('<p>' + en.bullets.map(b => '• ' + b).join('<br/>') + '</p><p><strong>Keywords:</strong> ' + en.keywords + '</p>')});
|
||||
ed.fire('change'); ed.fire('blur'); ed.updateElement(); return 1; }catch(e){ return 0; }})()`);
|
||||
await wait(2000);
|
||||
|
||||
// ⑨ 图片(直接赋值,外部 URL 可用)
|
||||
const imgData = imgs.slice(0, 6).map((u, i) => ({ id: String(i + 1), url: u, name: 'img' + i + '.jpg', width: 800, height: 800, imgId: String(1000 + i), checked: false }));
|
||||
await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
p._s.get('smtAddProductStore').$state.formState.imageList=${JSON.stringify(imgData)}; return 1;})()`);
|
||||
await wait(3000);
|
||||
log(` ⑥ 描述+图片(${imgData.length}张) ok`);
|
||||
|
||||
// ⑩ 保存
|
||||
await c.evalJS(`(function(){var bs=[].slice.call(document.querySelectorAll('button')).filter(function(e){return e.offsetParent!==null&&/^\\s*保存\\s*$/.test((e.innerText||'').trim());});
|
||||
if(bs.length) bs[0].click(); return 1;})()`);
|
||||
await wait(17000);
|
||||
const msg = String(await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-modal,.ant-message-notice')).filter(function(e){return e.offsetParent!==null;}).map(function(e){return (e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,120);}))`));
|
||||
const ok = /已保存到/.test(msg);
|
||||
log(` ${ok ? '✅' : '❌'} 保存: ${SAN(msg).slice(0, 110)} | POST=${posted}`);
|
||||
DONE[oid] = { ok, msg: SAN(msg).slice(0, 100), imgs: imgData.length, kws, time: new Date().toISOString() };
|
||||
fs.writeFileSync(DONE_F, JSON.stringify(DONE, null, 1));
|
||||
} catch (e) {
|
||||
log(` ❌ 异常: ${String(e).slice(0, 90)}`);
|
||||
DONE[oid] = { ok: false, err: String(e).slice(0, 90) };
|
||||
fs.writeFileSync(DONE_F, JSON.stringify(DONE, null, 1));
|
||||
}
|
||||
try { c.close(); } catch (e) {}
|
||||
await wait(1200);
|
||||
}
|
||||
log('');
|
||||
log(`全部结束。成功 ${Object.values(DONE).filter(x => x.ok).length} / ${Object.keys(DONE).length}`);
|
||||
@@ -0,0 +1,47 @@
|
||||
# 店小秘编辑器「保存静默拦截」诊断法
|
||||
|
||||
## 症状
|
||||
点「保存」→ **不发任何 POST**、**没有任何界面提示**、**无 `.ant-form-item-has-error`**。
|
||||
四个平台(速卖通/SHEIN/Temu)独立复现同一现象。
|
||||
|
||||
## 根因
|
||||
校验确实失败了,但报错被 **`MYJ.message.error` 吞掉**——它只在屏幕角落闪一下,
|
||||
而且有些报错是**层层递进**的(修完 A 才显示 B),所以肉眼和 DOM 扫描都抓不到。
|
||||
|
||||
## 诊断步骤
|
||||
1. 打开目标平台的建品页,填完所有能填的字段
|
||||
2. **在点保存之前**注入钩子,劫持消息库:
|
||||
|
||||
```js
|
||||
window.__cap=[];
|
||||
function wrap(obj,name,label){
|
||||
const o=obj[name];
|
||||
obj[name]=function(){
|
||||
window.__cap.push(label+': '+[...arguments].map(a=>typeof a==='string'?a:JSON.stringify(a)).join(' '));
|
||||
return o.apply(obj,arguments);
|
||||
};
|
||||
}
|
||||
['error','warning','info','success'].forEach(k=>wrap(MYJ.message,k,'MYJ.'+k));
|
||||
['error','warning','info','success'].forEach(k=>wrap(antd.message,k,'antd.'+k));
|
||||
```
|
||||
|
||||
3. 点「保存」,等 9 秒
|
||||
4. 读 `window.__cap` —— 被吞掉的真实报错就在里面
|
||||
|
||||
## 已定位的结果
|
||||
| 平台 | 被吞掉的报错 | 性质 |
|
||||
|---|---|---|
|
||||
| 速卖通 | `商品说明书 → 请上传文件!` | **类目相关**的资质文件要求(Pet Medical 等类目强制) |
|
||||
| SHEIN | `有必选规格未选择,请完善后再提交` | 类目定义的必选规格未选全 |
|
||||
| Temu | `产地选择中国时,省份必填` → `产品变种不能为空!` | **条件必填**(选中国才出现省份)+ 变种 |
|
||||
|
||||
## 修复要点
|
||||
- **速卖通**:换一个不要求资质文件的类目即可(已验证:温度计从 Pet Medical 换到
|
||||
`Tools/仪器仪表/温湿度` 后立刻存进去)
|
||||
- **SHEIN**:需把类目要求的**全部**规格(不止主规格颜色)都选中
|
||||
- **Temu**:省份必须走 **UI 选择**(直接改 `region2Id` store 无效),且必须生成变种行
|
||||
|
||||
## 教训
|
||||
`必填项扫描`(找 `.ant-form-item-required` 且为空)**不够**——
|
||||
条件必填字段(Temu 省份)和类目相关的文件字段(速卖通说明书)都不在这个扫描范围内。
|
||||
以后遇到「点了没反应」,第一步就该挂这个钩子。
|
||||
@@ -0,0 +1,34 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
|
||||
console.log('url:', await c.evalJS('location.href'));
|
||||
// 探测可用的消息库
|
||||
const probe = await c.evalJS(`(()=>{const keys=['MYJ','Vue','antd','$message','ELEMENT'];
|
||||
const found={};
|
||||
keys.forEach(k=>{ try{ found[k]=typeof window[k]; }catch(e){ found[k]='err'; } });
|
||||
return JSON.stringify(found);})()`);
|
||||
console.log('全局对象:', String(probe));
|
||||
// 埋钩子(尽量广)
|
||||
const hook = await c.evalJS(`(()=>{window.__cap=[]; let n=0;
|
||||
function wrap(obj,name,label){ try{ if(!obj||!obj[name])return; const o=obj[name];
|
||||
obj[name]=function(){ try{ window.__cap.push(label+': '+Array.prototype.slice.call(arguments).map(function(a){return typeof a==='string'?a:JSON.stringify(a);}).join(' ').slice(0,200)); }catch(e){}
|
||||
return o.apply(obj,arguments); }; n++; }catch(e){} }
|
||||
try{ if(window.MYJ&&window.MYJ.message){ ['error','warning','info','success'].forEach(k=>wrap(window.MYJ.message,k,'MYJ.'+k)); } }catch(e){}
|
||||
try{ if(window.antd&&window.antd.message){ ['error','warning','info','success'].forEach(k=>wrap(window.antd.message,k,'antd.'+k)); } }catch(e){}
|
||||
try{ if(window.$message){ ['error','warning','info','success'].forEach(k=>wrap(window.$message,k,'$message.'+k)); } }catch(e){}
|
||||
return 'wrapped '+n;})()`);
|
||||
console.log('钩子:', String(hook));
|
||||
// 点保存
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(9000);
|
||||
const cap = await c.evalJS(`JSON.stringify(window.__cap||[])`);
|
||||
console.log('★ 捕获:', String(cap).slice(0,600));
|
||||
const errs = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-explain-error')).map(function(e){
|
||||
var p=e.closest?e.closest('.ant-form-item'):null; var l=p?(p.querySelector('label')||{}).innerText||'':'';
|
||||
return l.replace(/\\s+/g,' ').trim().slice(0,30)+' → '+(e.innerText||'').trim();}).filter(Boolean))`);
|
||||
console.log('行内错误:', String(errs).slice(0,400));
|
||||
const errItems = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-has-error')).map(function(x){var l=x.querySelector('label');return l?(l.innerText||'').replace(/\\s+/g,' ').trim().slice(0,36):'?';}))`);
|
||||
console.log('出错字段:', String(errItems).slice(0,300));
|
||||
process.exit(0);
|
||||
@@ -0,0 +1,283 @@
|
||||
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
|
||||
import fs from 'fs';
|
||||
|
||||
/* 速卖通批量建品(全自动):分类走 UI 弹窗搜索,其余按已验证配方 */
|
||||
const SHOP = 4024039;
|
||||
const FREIGHT_NAME = process.argv[2] || 'MY beauty 30g+1美金';
|
||||
const ONLY = process.argv[3];
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const log = (...a) => console.log(' ', ...a);
|
||||
|
||||
const EN = JSON.parse(fs.readFileSync('/tmp/en_all.json', 'utf8'));
|
||||
const CLEAN = JSON.parse(fs.readFileSync('/tmp/wm_clean.json', 'utf8'));
|
||||
const ALL = JSON.parse(fs.readFileSync('/tmp/all48.json', 'utf8'));
|
||||
const KW_FB = {};
|
||||
const DONE_F = '/tmp/smt_batch_done.json';
|
||||
const DONE = fs.existsSync(DONE_F) ? JSON.parse(fs.readFileSync(DONE_F, 'utf8')) : {};
|
||||
|
||||
// 分类关键词:从英文标题里取最能定位品类的词
|
||||
function catKeywords(oid) {
|
||||
const t = (EN[oid] || {}).title || '';
|
||||
const CAND = [
|
||||
[/whistle/i, ['whistle']],
|
||||
[/hair tie|scrunchie|hair ring|ponytail|elastic/i, ['hair', 'hairband']],
|
||||
[/hair clip|barrette|hairpin|clip/i, ['clip', 'hair']],
|
||||
[/headband|hair band/i, ['headband', 'hair']],
|
||||
[/keychain|key ring|key holder/i, ['keychain']],
|
||||
[/sticker|decal/i, ['sticker']],
|
||||
[/thermometer|hygrometer/i, ['thermometer']],
|
||||
[/button/i, ['button']],
|
||||
[/yarn|knitting/i, ['yarn']],
|
||||
[/sock/i, ['socks']],
|
||||
[/necklace|pendant/i, ['necklace']],
|
||||
[/bracelet/i, ['bracelet']],
|
||||
[/earring/i, ['earring']],
|
||||
[/card holder|card case|badge/i, ['card']],
|
||||
[/organizer|storage/i, ['organizer']],
|
||||
[/box|case|holder|container/i, ['box']],
|
||||
[/bag|pouch/i, ['bag']],
|
||||
[/comb|brush/i, ['comb']],
|
||||
[/mirror/i, ['mirror']],
|
||||
[/tape dispenser/i, ['tape']],
|
||||
[/hair dryer/i, ['hair dryer']],
|
||||
];
|
||||
for (const [rx, ks] of CAND) if (rx.test(t)) return ks;
|
||||
return [(t.split(/\s+/)[0] || 'gift')];
|
||||
}
|
||||
|
||||
const oids = ONLY ? [ONLY] : Object.keys(ALL).filter(o => !DONE[o] && (CLEAN[o] || []).length >= 1);
|
||||
log(`待建商品: ${oids.length} | 已有记录: ${Object.keys(DONE).length}`);
|
||||
|
||||
for (const oid of oids) {
|
||||
const en = EN[oid], imgs = CLEAN[oid] || [];
|
||||
let kws = catKeywords(oid);
|
||||
if (!en || !imgs.length) { log(`⚠ ${oid} 缺文案或图片,跳过`); continue; }
|
||||
log('');
|
||||
log(`═══ ${oid} | 图${imgs.length} 张 | 关键词候选 ${JSON.stringify(kws)} ═══`);
|
||||
|
||||
// 清掉旧标签
|
||||
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
|
||||
for (const t of tl.filter(x => x.type === 'page' && /\/web\/smt\/add/.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(() => {});
|
||||
await wait(900);
|
||||
|
||||
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('Network.enable');
|
||||
let posted = false;
|
||||
c.on(m => { if (m.method === 'Network.requestWillBeSent') { const r = m.params.request;
|
||||
if (r.method === 'POST' && /\/api\/smtProduct\/add\.json/.test(r.url)) posted = true; } });
|
||||
|
||||
try {
|
||||
await c.send('Page.navigate', { url: 'https://www.dianxiaomi.com/web/smt/add' });
|
||||
await wait(32000);
|
||||
|
||||
await c.evalJS(`(function(){
|
||||
window.__f=function(rx,val,sel){ sel=sel||'input';
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; var lb=it.querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf(rx)<0) continue;
|
||||
var el=it.querySelector(sel); if(!el) return 'nf';
|
||||
var proto=el.tagName==='TEXTAREA'?window.HTMLTextAreaElement.prototype:window.HTMLInputElement.prototype;
|
||||
Object.getOwnPropertyDescriptor(proto,'value').set.call(el,String(val));
|
||||
['input','change','blur'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});
|
||||
return 'ok'; }
|
||||
return 'noitem'; };
|
||||
window.__openSel=function(rx){
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; var lb=it.querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf(rx)<0) continue;
|
||||
var sel=it.querySelector('.ant-select'); if(!sel) return 'nosel';
|
||||
var tg=sel.querySelector('.ant-select-selector')||sel; tg.scrollIntoView({block:'center'});
|
||||
['mousedown','mouseup','click'].forEach(function(ev){tg.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'opened'; }
|
||||
return 'noitem'; };
|
||||
window.__pick=function(txt){ var dds=[].slice.call(document.querySelectorAll('.ant-select-dropdown')).filter(function(e){return e.offsetParent!==null;});
|
||||
if(!dds.length)return 'nodrop'; var os=[].slice.call(dds[dds.length-1].querySelectorAll('.ant-select-item-option')); if(!os.length)return 'noopt';
|
||||
var o=txt? os.filter(function(x){return (x.innerText||'').trim()===txt;})[0] : os[0]; if(!o)o=os[0];
|
||||
['mouseenter','mouseover','mousedown','mouseup','click'].forEach(function(ev){o.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'picked:'+(o.innerText||'').trim().slice(0,26); };
|
||||
return 'ready';})()`);
|
||||
|
||||
// ① 店铺 + 模板
|
||||
await c.evalJS(`(async function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
var b=p._s.get('smtAddBasicStore');
|
||||
try{await b.updateShop(${SHOP});}catch(e){} await new Promise(z=>setTimeout(z,2500));
|
||||
try{await b.updateShopIdFun(${SHOP});}catch(e){} await new Promise(z=>setTimeout(z,3000)); return 1;})()`, true);
|
||||
const tplN = await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
return (p._s.get('smtAddBasicStore').$state.dataState.freightTemplateList||[]).length;})()`);
|
||||
log(` ① 店铺 ok,运费模板 ${tplN} 个`);
|
||||
|
||||
// ② 分类(UI 弹窗,多关键词重试 + 校验关闭)
|
||||
let catOk = false, catInfo = '';
|
||||
if (process.env.SMT_KW) { kws = process.env.SMT_KW.split(',').filter(Boolean); log(' 关键词覆盖:', kws.join('/')); } else { kws = kws.concat(KW_FB[oid] || []); }
|
||||
for (const kw of kws) {
|
||||
await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;});
|
||||
ms.forEach(function(m){var cb=m.querySelector('.ant-modal-close'); if(cb)cb.click();}); return 1;})()`);
|
||||
await wait(1200);
|
||||
await c.evalJS(`(function(){var bs=[].slice.call(document.querySelectorAll('button')).filter(function(e){return e.offsetParent!==null&&/选择分类/.test(e.innerText||'');});
|
||||
if(bs.length){bs[0].scrollIntoView({block:'center'});['mousedown','mouseup','click'].forEach(function(ev){bs[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});} return 1;})()`);
|
||||
await wait(4200);
|
||||
const n0 = await c.evalJS(`(function(){return [].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}).length;})()`);
|
||||
if (!n0) { catInfo = 'modal未开'; continue; }
|
||||
await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}); if(!ms.length)return 0;
|
||||
var m=ms[ms.length-1]; var el=m.querySelector('input[name="searchCategory"]'); if(!el)return 0;
|
||||
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,${JSON.stringify(kw)});
|
||||
['input','change'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});
|
||||
var b=[].slice.call(m.querySelectorAll('button')).filter(function(x){return /搜索/.test(x.innerText||'');}); if(b.length)b[0].click(); return 1;})()`);
|
||||
await wait(5500);
|
||||
const picked = await c.evalJS(`(function(){var ms=[].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null;}); if(!ms.length)return 'nomodal';
|
||||
var m=ms[ms.length-1]; var its=[].slice.call(m.querySelectorAll('.search-result-item'));
|
||||
if(!its.length) return 'noresult';
|
||||
var best=null,bs=-999;
|
||||
var TITLE=${JSON.stringify((EN[oid]||{}).title||'')};
|
||||
var BAD=/慈善|Благотвор|二手|Second-?hand|Used|Refurb|Sexual|情趣|Dewasa/i;
|
||||
var KID=/Baby|Kids|Kanak|婴儿|儿童|Infant|Toddler/i;
|
||||
var AUTO=/Automobile|Automotive|汽车|Motosikal/i;
|
||||
var ISK=KID.test(TITLE), ISA=AUTO.test(TITLE);
|
||||
var KW=${JSON.stringify(kw)}.toLowerCase();
|
||||
its.forEach(function(it){ var tx=(it.innerText||'').replace(/\\s+/g,' ').trim(); var s=0;
|
||||
if(BAD.test(tx)) { s-=200; }
|
||||
if(!ISK && KID.test(tx)) s-=60;
|
||||
if(!ISA && AUTO.test(tx)) s-=60;
|
||||
var segs=tx.split('/'); var lastSeg=(segs[segs.length-1]||'').replace(/\(.*?\)/g,'').trim().toLowerCase();
|
||||
if(KW && lastSeg.indexOf(KW)>=0) s+=40;
|
||||
if(/小百货|配饰|饰品|发饰|家居|住宅|文具|玩具|运动|美容|健康/i.test(tx)) s+=12;
|
||||
s -= Math.min(tx.length/80,5);
|
||||
if(s>bs){bs=s;best=it;} });
|
||||
if(!best) return 'noresult';
|
||||
best.scrollIntoView({block:'center'});
|
||||
['mousedown','mouseup','click'].forEach(function(ev){best.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));});
|
||||
return 'picked:'+(best.innerText||'').replace(/\\s+/g,' ').trim().slice(0,60);})()`);
|
||||
await wait(7000);
|
||||
const left = await c.evalJS(`(function(){return [].slice.call(document.querySelectorAll('.ant-modal')).filter(function(e){return e.offsetParent!==null && (e.innerText||'').indexOf('选择类目')>=0;}).length;})()`);
|
||||
const cid = await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
return String(p._s.get('smtAddBasicStore').$state.formState.categoryId||'');})()`);
|
||||
catInfo = kw + ' → ' + SAN(String(picked)).slice(0,60);
|
||||
if (!left && cid) { catOk = true; break; }
|
||||
catInfo += ' [modal未关/无cat]';
|
||||
}
|
||||
log(` ② 分类${catOk ? '✅' : '❌'}: ${catInfo.slice(0, 100)}`);
|
||||
if (!catOk) { log(' 分类失败,跳过该商品'); DONE[oid] = { ok:false, msg:'分类失败:'+catInfo.slice(0,60) }; fs.writeFileSync(DONE_F, JSON.stringify(DONE,null,1)); c.close(); await wait(1000); continue; }
|
||||
|
||||
// ③ 文本 + 尺寸
|
||||
await c.evalJS(`(function(){
|
||||
window.__f('产品标题',${JSON.stringify(en.title)},'input:not([type=hidden])');
|
||||
window.__f('零售价格','1.99'); window.__f('货值','0.50'); window.__f('库存数量','100');
|
||||
window.__f('重量(kg)','0.02'); window.__f('包装后重量','0.02'); return 1;})()`);
|
||||
await c.evalJS(`(function(){
|
||||
var set=function(el,v){Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,v);
|
||||
['input','change','blur'].forEach(function(ev){el.dispatchEvent(new Event(ev,{bubbles:true}));});};
|
||||
[['长','10'],['宽','5'],['高','3']].forEach(function(p){ var els=[].slice.call(document.querySelectorAll('input')).filter(function(e){return e.offsetParent!==null&&(e.placeholder||'')===p[0];});
|
||||
if(els.length) set(els[els.length-1],p[1]); });
|
||||
var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var lb=its[i].querySelector('label'); if(!lb) continue;
|
||||
if((lb.innerText||'').indexOf('包装后尺寸')<0) continue;
|
||||
var ins=[].slice.call(its[i].querySelectorAll('input')); ['10','5','3'].forEach(function(v,k){ if(ins[k]) set(ins[k],v); }); }
|
||||
return 1;})()`);
|
||||
await wait(2000);
|
||||
log(' ③ 文本+尺寸 ok');
|
||||
|
||||
// ④ 模板下拉
|
||||
await c.evalJS(`window.__openSel('运费模板')`); await wait(3000);
|
||||
let fr = await c.evalJS(`window.__pick(${JSON.stringify(FREIGHT_NAME)})`);
|
||||
if (/noopt|nodrop/.test(String(fr))) fr = await c.evalJS(`window.__pick()`);
|
||||
log(` ④ 运费模板: ${SAN(String(fr))}`);
|
||||
await wait(2500);
|
||||
await c.evalJS(`window.__openSel('服务模板')`); await wait(3000);
|
||||
log(` 服务模板: ${SAN(String(await c.evalJS(`window.__pick()`)))}`);
|
||||
await wait(2500);
|
||||
await c.evalJS(`window.__openSel('是否原箱')`); await wait(2800);
|
||||
log(` 是否原箱: ${SAN(String(await c.evalJS(`window.__pick('否')`)))}`);
|
||||
await wait(2200);
|
||||
|
||||
// ⑤ 含税
|
||||
await c.evalJS(`(function(){var its=[].slice.call(document.querySelectorAll('.ant-form-item'));
|
||||
for(var i=0;i<its.length;i++){ var it=its[i]; if((it.innerText||'').indexOf('报价是否含关税')<0) continue;
|
||||
var ws=[].slice.call(it.querySelectorAll('.ant-radio-wrapper'));
|
||||
for(var j=0;j<ws.length;j++){ if((ws[j].innerText||'').indexOf('不含关税')>=0){ var inp=ws[j].querySelector('input'); if(inp&&!inp.checked)inp.click(); return 1; } } }
|
||||
return 0;})()`);
|
||||
await wait(1800);
|
||||
|
||||
// ⑥ 物流属性(直接赋值,不依赖 UI 点击)
|
||||
await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
p._s.get('smtAddProductStore').$state.formState.specialProductTypeList=['1983471271:2025103']; return 1;})()`);
|
||||
await wait(1500);
|
||||
log(' ⑤ 含税+物流属性 ok');
|
||||
|
||||
// ⑦ 属性(含 check_box 归一化)
|
||||
await c.evalJS(`(function(){
|
||||
var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
var at=p._s.get('smtAddAttrStore'); var a=p._s.get('smtAddStore').$state.attrsInfo.attrsList||[];
|
||||
var want={'品牌':'201512802','产地':'9441741844','化学品':'23399591357'};
|
||||
var d=at.$state.dynamicFormData||{};
|
||||
for(var i=0;i<a.length;i++){ var nm=a[i].nameZh||''; var key=String(a[i].arrtNameId);
|
||||
for(var k in want){ if(nm.indexOf(k)>=0) d[key]='history-'+want[k]; } }
|
||||
var ra=at.$state.renderAttrs||[];
|
||||
for(var j=0;j<ra.length;j++){ var x=ra[j]; var kk=String(x.arrtNameId); var st=String(x.attributeShowTypeValue||'');
|
||||
var needArr=(st==='check_box'||st==='multiSelect'||st==='selectPercent');
|
||||
if(needArr && d[kk]!==undefined && !Array.isArray(d[kk])) d[kk]=[d[kk]];
|
||||
if(!needArr && Array.isArray(d[kk])) d[kk]=d[kk][0]||''; }
|
||||
at.$state.dynamicFormData=d; return 1;})()`);
|
||||
await wait(1500);
|
||||
|
||||
// ⑧ 描述
|
||||
await c.evalJS(`(function(){
|
||||
try{ if(typeof CKEDITOR==='undefined'||!CKEDITOR.instances) return 0;
|
||||
var n=Object.keys(CKEDITOR.instances)[0]; var ed=CKEDITOR.instances[n];
|
||||
ed.setData(${JSON.stringify('<p>' + en.bullets.map(b => '• ' + b).join('<br/>') + '</p><p><strong>Keywords:</strong> ' + en.keywords + '</p>')});
|
||||
ed.fire('change'); ed.fire('blur'); ed.updateElement(); return 1; }catch(e){ return 0; }})()`);
|
||||
await wait(2000);
|
||||
|
||||
// ⑨ 图片(直接赋值,外部 URL 可用)
|
||||
const imgData = imgs.slice(0, 6).map((u, i) => ({ id: String(i + 1), url: u, name: 'img' + i + '.jpg', width: 800, height: 800, imgId: String(1000 + i), checked: false }));
|
||||
await c.evalJS(`(function(){var el=document.querySelector('#app');var p=el.__vue_app__.config.globalProperties.$pinia;
|
||||
p._s.get('smtAddProductStore').$state.formState.imageList=${JSON.stringify(imgData)}; return 1;})()`);
|
||||
await wait(3000);
|
||||
log(` ⑥ 描述+图片(${imgData.length}张) ok`);
|
||||
|
||||
// ⑩ 保存(调试版:埋钩子)
|
||||
const hook = await c.evalJS(`(function(){
|
||||
window.__cap=[];
|
||||
try{
|
||||
var M = window.MYJ && window.MYJ.message;
|
||||
if(M){ ['error','warning','info','success'].forEach(function(k){
|
||||
var orig=M[k];
|
||||
M[k]=function(){ try{ window.__cap.push(k+': '+Array.prototype.slice.call(arguments).map(function(a){return typeof a==='string'?a:JSON.stringify(a);}).join(' ').slice(0,200)); }catch(e){}
|
||||
return orig.apply(M, arguments); };
|
||||
}); return 'hooked MYJ.message'; }
|
||||
return 'no MYJ';
|
||||
}catch(e){ return 'ERR '+e.message; }})()`);
|
||||
log(' 钩子:', hook);
|
||||
const btns = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('button')).filter(function(e){return e.offsetParent!==null;}).map(function(b){
|
||||
var r=b.getBoundingClientRect(); return {t:(b.innerText||'').replace(/\\s+/g,' ').trim().slice(0,14), y:Math.round(r.y), dis:b.disabled, cls:String(b.className||'').slice(0,34)};}).filter(function(x){return x.t;}))`);
|
||||
log(' 按钮清单:', SAN(String(btns)).slice(0,420));
|
||||
await c.evalJS(`(function(){var bs=[].slice.call(document.querySelectorAll('button')).filter(function(e){return e.offsetParent!==null&&/^\\s*保存\\s*$/.test((e.innerText||'').trim());});
|
||||
if(bs.length) bs[0].click(); return 1;})()`);
|
||||
await wait(9000);
|
||||
const cap = await c.evalJS(`JSON.stringify(window.__cap||[])`);
|
||||
log(' ★ 被捕获的错误消息:', SAN(String(cap)).slice(0,500));
|
||||
const errs = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-explain-error')).map(function(e){
|
||||
var t=(e.innerText||'').trim(); if(!t) return null;
|
||||
var p=e.closest? e.closest('.ant-form-item') : null;
|
||||
var lab=p? (p.querySelector('label')||{}).innerText||'' : '';
|
||||
return lab.replace(/\\s+/g,' ').trim().slice(0,40)+' → '+t;}).filter(Boolean))`);
|
||||
log(' 行内校验错误(带字段):', SAN(String(errs)).slice(0,400));
|
||||
const hasErrItems = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-has-error')).map(function(x){
|
||||
var l=x.querySelector('label'); return l?(l.innerText||'').replace(/\\s+/g,' ').trim().slice(0,44):'?';}))`);
|
||||
log(' 出错字段列表:', SAN(String(hasErrItems)).slice(0,400));
|
||||
await wait(8000);
|
||||
const msg = String(await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-modal,.ant-message-notice')).filter(function(e){return e.offsetParent!==null;}).map(function(e){return (e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,120);}))`));
|
||||
const ok = /已保存到/.test(msg);
|
||||
log(` ${ok ? '✅' : '❌'} 保存: ${SAN(msg).slice(0, 110)} | POST=${posted}`);
|
||||
DONE[oid] = { ok, msg: SAN(msg).slice(0, 100), imgs: imgData.length, kws, time: new Date().toISOString() };
|
||||
fs.writeFileSync(DONE_F, JSON.stringify(DONE, null, 1));
|
||||
} catch (e) {
|
||||
log(` ❌ 异常: ${String(e).slice(0, 90)}`);
|
||||
DONE[oid] = { ok: false, err: String(e).slice(0, 90) };
|
||||
fs.writeFileSync(DONE_F, JSON.stringify(DONE, null, 1));
|
||||
}
|
||||
try { c.close(); } catch (e) {}
|
||||
await wait(1200);
|
||||
}
|
||||
log('');
|
||||
log(`全部结束。成功 ${Object.values(DONE).filter(x => x.ok).length} / ${Object.keys(DONE).length}`);
|
||||
@@ -0,0 +1,23 @@
|
||||
import { attach } from '/tmp/cdp3.mjs';
|
||||
const wait = ms => new Promise(z => setTimeout(z, ms));
|
||||
const c = await attach('/web/temu/add') || await attach('dianxiaomi.com');
|
||||
console.log('url:', await c.evalJS('location.href'));
|
||||
const hook = await c.evalJS(`(()=>{window.__cap=[]; let n=0;
|
||||
function wrap(obj,name,label){ try{ if(!obj||!obj[name])return; const o=obj[name];
|
||||
obj[name]=function(){ try{ window.__cap.push(label+': '+Array.prototype.slice.call(arguments).map(function(a){return typeof a==='string'?a:JSON.stringify(a);}).join(' ').slice(0,200)); }catch(e){}
|
||||
return o.apply(obj,arguments); }; n++; }catch(e){} }
|
||||
try{ if(window.MYJ&&window.MYJ.message){ ['error','warning','info'].forEach(k=>wrap(window.MYJ.message,k,'MYJ.'+k)); } }catch(e){}
|
||||
try{ if(window.antd&&window.antd.message){ ['error','warning','info'].forEach(k=>wrap(window.antd.message,k,'antd.'+k)); } }catch(e){}
|
||||
return 'wrapped '+n;})()`);
|
||||
console.log('钩子:', String(hook));
|
||||
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
|
||||
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
|
||||
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
|
||||
await wait(9000);
|
||||
const cap = await c.evalJS(`JSON.stringify(window.__cap||[])`);
|
||||
console.log('★ 捕获:', String(cap).slice(0,600));
|
||||
const errItems = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-has-error')).map(function(x){var l=x.querySelector('label');return l?(l.innerText||'').replace(/\\s+/g,' ').trim().slice(0,36):'?';}))`);
|
||||
console.log('出错字段:', String(errItems).slice(0,300));
|
||||
const ex = await c.evalJS(`JSON.stringify([].slice.call(document.querySelectorAll('.ant-form-item-explain-error')).map(e=>{var p=e.closest?e.closest('.ant-form-item'):null;var l=p?(p.querySelector('label')||{}).innerText||'':'';return l.replace(/\\s+/g,' ').trim().slice(0,30)+' → '+(e.innerText||'').trim();}).filter(Boolean))`);
|
||||
console.log('行内错误:', String(ex).slice(0,400));
|
||||
process.exit(0);
|
||||
Reference in new issue
Block a user