tiktok-batch-budget-updatelisted
Install: claude install-skill njh20030605-code/claude-skills
# TikTok 广告组批量改预算
按用户给的「广告组ID 新预算」清单,在已打开的「修改预算」弹窗里批量写入新日预算/总预算。
## 前提
用户已经在 `ads.tiktok.com/i18n/manage/adgroup` 勾选了广告组并打开了「修改预算」弹窗。
如果弹��没开:先截图确认列表页状态,让用户勾选并点开弹窗,不要自己去猜着勾选广告组。
用户清单的典型格式(制表符或空格分隔,无表头):
```
7664548737817054485 60
7642356927774739733 20
```
左边是广告组ID,右边是新预算(单位就是弹窗里显示的币种,通常 USD)。
## 关键 DOM 事实(踩过的坑,别重新试)
1. **弹窗主体不在 `.vi-dialog` 里**。`.vi-dialog.bulk-adjust-dialog` 只有 header 和 footer;
表格在独立节点 `.bulk-adjust-form-table`。对着 `[role="dialog"]` 查 `tr` 会返回 0 行。
2. **行**:`.bulk-adjust-form-table .vi-table__body-wrapper tbody tr`
单元格顺序:`[0]` 广告组名称、`[1]` 总消耗、`[2]` 当前预算、`[3]` 新预算输入框。
3. **广告组ID = 名称的数字后缀**,正则 `/(\d{15,})\s*$/`。
例:`0721-withmayanh-White Essence-BC-护肤人群-7664548737817054485`。
表格里没有独立的 ID 列,只能这样取。
4. **输入框在两层 shadow DOM 里**:
`[x-name="x-input-number"]` → shadowRoot → `x-input-*` → shadowRoot → `<input>`。
直接 `row.querySelector('input')` 永远是 null。用 `references/set_budget.js` 里的 `deepInput()` 递归穿透。
5. **写值必须用 native setter + composed 事件**:
```js
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
inp.focus(); setter.call(inp, '60');
inp.dispatchEvent(new Event('input',{bubbles:true,composed:true}));
inp.dispatchEvent(new Event('change',{bubbles:true,composed:true}));
inp.blur();
```
少了 `composed:true` 事件跨不出 shadow 边界,组件收不到。
6. **`已修改 N 个广告组` 计数在同一次 JS 调用里读是旧值**(Vue 还没重渲染)。
必须**另起一次 `javascript_tool` 调用**去读,否则会误判成失败。
## 执行流程
1. **先只读一次**,把 15 行左右的 `[行号, 广告组ID, 当前预算]` 全部 dump