TikTok 广告组批量改预算
按用户给的「广告组ID 新预算」清单,在已打开的「修改预算」弹窗里批量写入新日预算/总预算。
前提
用户已经在 ads.tiktok.com/i18n/manage/adgroup 勾选了广告组并打开了「修改预算」弹窗。
如果弹窗没开:先截图确认列表页状态,让用户勾选并点开弹窗,不要自己去猜着勾选广告组。
用户清单的典型格式(制表符或空格分隔,无表头):
7664548737817054485 60
7642356927774739733 20
左边是广告组ID,右边是新预算(单位就是弹窗里显示的币种,通常 USD)。
关键 DOM 事实(踩过的坑,别重新试)
- 弹窗主体不在
.vi-dialog里。.vi-dialog.bulk-adjust-dialog只有 header 和 footer; 表格在独立节点.bulk-adjust-form-table。对着[role="dialog"]查tr会返回 0 行。 - 行:
.bulk-adjust-form-table .vi-table__body-wrapper tbody tr单元格顺序:[0]广告组名称、[1]总消耗、[2]当前预算、[3]新预算输入框。 - 广告组ID = 名称的数字后缀,正则
/(\d{15,})\s*$/。 例:0721-withmayanh-White Essence-BC-护肤人群-7664548737817054485。 表格里没有独立的 ID 列,只能这样取。 - 输入框在两层 shadow DOM 里:
[x-name="x-input-number"]→ shadowRoot →x-input-*→ shadowRoot →<input>。 直接row.querySelector('input')永远是 null。用references/set_budget.js里的deepInput()递归穿透。 - 写值必须用 native setter + composed 事件:
少了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 边界,组件收不到。 已修改 N 个广告组计数在同一次 JS 调用里读是旧值(Vue 还没重渲染)。 必须另起一次javascript_tool调用去读,否则会误判成失败。
执行流程
- 先只读一次,把 15 行左右的
[行号, 广告组ID, 当前预算]全部 dump 出来,不写任何值。 - 在本地把清单和页面行对上,分成三类:
- 需要改的(当前值 ≠ 目标值)→ 写入
- 已经等于目标值 → 留空跳过,不要写重复值(留空 = 不改,写值会让"已修改"数虚高)
- 弹窗里被勾选、但不在用户清单里的行 → 留空,并且必须在回复里单独警告用户
- 用户清单里有、但弹窗里找不到的 ID → 也要单独报出来(可能没勾选,或落在「无法修改」tab)
- 一次 JS 调用写完所有需要改的行(
references/set_budget.js),返回逐行日志。 - 另起一次 JS 调用回读所有 input 的 value +
已修改 N 个广告组计数,确认 N == 需要改的行数。 - 截图确认弹窗状态。
- 列出对照表(ID / 当前 / 新)让用户确认,然后才点「提交」。 提交是不可逆操作,永远不要自动点。
Tab 说明
弹窗有三个 tab:日预算(默认)、总预算、无法修改。
用户没特别说就是日预算。要改总预算先切 tab 再重新 dump 行。
「无法修改」tab 里的广告组是平台不允许改的(预算类型不符、状态限制等),如果用户清单里的 ID 出现在这里,直接告诉用户改不了,别硬试。
输出格式
给用户的回复里包含:
- 需要改动的表格:
广告组ID | 当前 | 新预算 - 已达标跳过的 ID 列表(一行带过即可)
- ⚠️ 异常项:勾选了但不在清单的、清单里但没找到的
- 最后一句:问是否点「提交」