新版表头(订单列表导出)
{{ s.name }}
自定义表头(订单自定义导出)
{{ s.name }}
已支持(抖店「订单自定义导出」108 列)
{{ s.name }}
未支持(请走「订单原始表」)
{{ s.name }}
{{ s.name }}
这个店导不出这份 108 列的格式 —— 请到「订单原始表」看它的订单(导入也会被后端拒绝)
打开该店账单下载页
未配置账单下载页(店铺管理里设置)
期初单价
出入库原始表
库存月快照
采购原始表(已停用)
全部
快递账单
{{ s }}
异常费用
{{ s }}
⚠ 加收>{{ (meta.wh_freight||{}).surcharge_std || 0.4 }}
只看当前生效价
全部档位
账期
{{ p === '' ? '(空)' : p }}
搜索
重置
{ raw.filters = f; loadRaw(1); }">
新增
导入
由聚水潭 API 同步维护 · 手工增改已禁用
工作簿「地区运费」sheet 的费率卡:省份 × 重量档单价 + 首重/续重 + 特殊类型;调价改 WH_WUHAN_RATES / WH_WUHAN_RATES_SPECIAL 常量,核对与展示同一真源
费率卡(工作簿「地区运费」sheet,调价改 WH_WUHAN_RATES 常量)
这里只是基础资料;按这张卡逐行核对账单的结果在「武汉(百侣)运费表 → 核对」tab
特殊类型(橙色四列):物流公司为
{{ ((wwc.data || {}).special_carriers || []).join(' 或 ') }} 且收货省为
宁夏 / 青海 时,0.5/1/2/3kg 四档改按橙色价核;没有首重/续重,>3kg 仍不参与核对。
其余承运商(含圆通)一律走左侧正常卡。
{{ t[1] }} {{ cf.libPubs[t[0]] }}
已选 {{ cf.libSel.length }} 条
批量标已发布
批量标已投放
批量删除
取消选择
cf.libSel = v ? (cf.lib||[]).map(x=>x.id) : []">
成品
来源切片
商品
谁在说
字面扫雷
投放
创建人 · 创建
没有符合条件的成品。清掉筛选
这一档是空的 —— 换个「字面扫雷」档看看。
还没有标过「已发布」的成品 —— 在列表「投放」那一列点「发」。
还没有标过「已投放」的成品 —— 在列表「投放」那一列点「投」。
成品库还是空的。
两条路:去「案例库」挑一条对标点「复刻」,
一键把整条逐段换成你的品;或去「切片库」打开一条切片,
把已经复刻好的段用「存到成片」挑几段拼成一条。
成品不会自己产生 —— 只有在案例库点「复刻」或在切片详情点「存到成片」才会进来。
有 {{ rp.failedScenes.length }} 段没能生成,这条脚本是不完整的:
{{ i?'、':'' }}第 {{ f.seq }} 段「{{ f.name }}」({{ f.err }})
—— 回案例里对这几段单独复刻,或整条重来。
原切片
▶
{{ cfDur(rp.origin.duration) }}
{{ cfPlatCn(rp.origin.platform) }}
{{ rp.origin.author || '—' }}
粉丝 {{ cfNum(rp.origin.author_fans) }} · 时长 {{ cfDur(rp.origin.duration) }}
发布 {{ (rp.origin.publish_at || '—').slice(0,10) }}
对标原片,只读。成品是照它的结构写的。
在切片库打开 ↗
整片信息
选了哪几段{{ cfPickedLabel || '—' }}
谁在说{{ (rp.batch && rp.batch.persona) || '—' }}
段数{{ rp.scripts.length }} 段 · 源切片共 {{ rp.srcTotal }} 段
说给{{ (rp.batch && rp.batch.audience) || '—' }}
总时长{{ cfTotalDur }}
存入{{ cfBjTime((rp.batch&&rp.batch.created_at)||'') }} · {{ rp.batch.created_by }}
对标原片的数据
{{ cfBjTime(rp.origin.stat_at) }} 抓的快照,不会刷新
{{ s.k }}{{ s.src==='huitun' ? s.v : cfNum(s.v) }}
热词
{{ h }}
销售额与销量是灰豚预估的区间,不是抖音官方数据;抖音接口不给播放量,所以不列。
源切片里没被选进来的段 · 还在切片库,想补就回去存一次
{{ cfNo(s.no) }} {{ s.title }}
{{ s.why }}
源切片的段全都进来了。
这条成品没有标题候选,列表里用商品名代替。
旧版工作台批次与段级草稿都没有。
已自定义名称「{{ rp.batch.name }}」,选标题不再改名字。
改回跟着标题走
回车保存 · Esc 取消 · 清空=删掉这条
取消
改好了
{{ t.kind }}{{ t.text }}
改一下
照的是对标这条 · 只读
{{ rp.origin.title || '—' }}
{{ t }}
裂变草稿
{{ cf.fis.drafts.length }} 套待处理
第 {{ i+1 }} 套
{{ cfFisFirstLine(d) }}
{{ d.n_hits ? (d.n_hits + ' 待改') : '干净' }}
缺 {{ d.n_missing }} 段
🔓 {{ d.n_lock_missing }}
还没上架,只有你看得到;上架后才进裂变库
逐段成品
每段标着它在源切片里是第几段;用的都是复刻的最新一版
全部按官方话术替换
{{ cfNo(i+1) }}
{{ sc.skeleton_name || ('第 ' + (i+1) + ' 段') }}
{{ (sc.src.t_start||0).toFixed(1) }}—{{ (sc.src.t_end||0).toFixed(1) }}s
源切片第 {{ cfNo(sc.src.seq) }} 段
复制本段
时间码和段落结构不变,改完会重新扫一遍卡审词。
{{ h.pattern }}
{{ h.vio_type }}
→
{{ h.replacement || '(词库没给替换话术)' }}
替换
对标原句 · 只读
{{ sc.src.text || '(这一段原片没有台词)' }}
结构和节奏照抄这一段,只换了台词里的品和说法。
这条成品还没有脚本。
多半是生成时整条都失败了——回案例库对这条重新复刻一次。
共 {{ cfProductRows.length }} 个商品
新增商品
{{ rowIndex + 1 }}
e.target.style.display='none'">
—
{{ record.name }}
打开 ↗
—
齐了
缺 {{ cfProductMissing(record).length }} 项
有卖点
{{ bjTime(record.updated_at) }}
还没有商品。复刻时要选商品——把基础信息填全,复刻出来的话才有依据。
商品库
{{ rp.pe.row.name || '新增商品' }}
资料齐了
缺:{{ cfProductMissing(rp.pe.row).join('、') }}
保存
页面预览
可滑动查看全部内容 · 点图放大
主图 {{ cfProdMedia.main.length }} 张
e.target.style.display='none'">
详情长图({{ cfProdMedia.n }} 张拼成一张 {{ cfProdMedia.size }})
e.target.style.display='none'">
还没有图。
填好左边的商品链接,
点「按链接取图文」,主图与详情长图会出现在这里。
灵感详情
{{ cf.id.row.task_id ? '已开拆' : '未拆解' }}
解析出 {{ cfInspUrlN }} 条链接。存进来会顺带把原片下到服务器(后台自动,不用管)——
不占拆解额度(不调任何模型,只花带宽),想拆再点「开拆」。
还没抓到封面
{{ cf.prev.title }}
{{ cf.prev.meta }}
去原片
重试下载
正在存到服务器(排队中),一会儿刷新就能在这里播。
正在存到服务器…
没能存进服务器:{{ cf.prev.mediaErr }}
服务器上还没有这条的原片,先看封面。
{{ cfRdTitle() }}
{{ cf.rd.forTitles ? '照着原标题与 tag 的写法,换成新商品'
: (cf.rd.seg ? '只改这一段的台词,其余段不动' : '结构和节奏照抄,只换台词') }}
取案例中…
裂变这条成品
圈住不能动的,其余的换个说法
取成品中…
裂变草稿
{{ (cf.fis.drafts||[]).length }} 套待处理
看过没问题再上架;上架后才进裂变库
谁在说
人设决定整篇的口气、口头禅和禁区。选错了,台词会整篇不像人说的。
+ 新建人设 ↗
只有五段文案进模型
使用次数、创建人、占比、依据都不进 —— 不然脚本里会冒出
「我这个人设已经用过 6 次了」「我们 84% 的用户都是妈妈」这种话。
{{ p.name }}
{{ p.size_label }}
已选
{{ p.who || '—' }}
{{ p.speak_to || '—' }}
{{ p.pain || '—' }}
{{ p.path || '—' }}
{{ p.taboo || '—' }}
{{ x }}
正在看历史版本「{{ cf.aud.verLabel }}」
回到当前版
{{ cf.aud.groups.length }} 个维度 · TGI 榜只列占比 ≥ {{ (cf.aud.floor*100).toFixed(0) }}% 的标签
历史版本 {{ cf.aud.versions.length }}
导入画像
还没有画像数据。
导入平台导出的人群画像(xlsx 或 csv)
(四列:标签类型 / 标签 / 占比 / TGI)。导入后这里按维度呈现,
再去「人设」把它收敛成能写进脚本的结论。
{{ g.dim }}
{{ g.is_exclusive ? '互斥(占比=构成比)' : '多选(占比=覆盖率,不可相加)' }}
{{ g.n_tags }} 个标签
无数据(原表这一维度是空的)
按占比(量在哪)
{{ t.tag }}
{{ (t.share*100).toFixed(1) }}%
按 TGI(比大盘高多少倍)
{{ t.tag }}
{{ (t.tgi/100).toFixed(1) }}×
占比 {{ (t.share*100).toFixed(1) }}%
没有占比 ≥ {{ (cf.aud.floor*100).toFixed(0) }}% 的标签(TGI 榜刻意设了门槛:
不设的话「占比 0.01% 而 TGI 46000」这种噪声会排在最前面)
{ cf.aud.up.err=''; cf.aud.up.file = f[0]; }">
拖拽 xlsx / csv 到此处,或点击选择文件
四列:标签类型 / 标签 / 占比 / TGI
{{ cf.aud.up.file.name }}
重选
{{ cf.aud.up.err }}
存成新的一版:同一个店铺再导会新建一版并设为当前,旧版留着,
可以在「历史版本」里回看、也可以切回去。每个店铺最多留 {{ cf.aud.verKeep }} 版,
超出会清掉最旧的(会在回执里说明)。
互斥/多选由占比合计自动判定,不需要你标注。
每次导入存成一版。当前版就是画像页与人设页正在用的那一份;
点「查看」只是临时看看,点「设为当前」才真的切过去。
当前版不能删——删了画像页会整页空白,而数据其实还在别的版本里。
{{ record.label || ('#' + record.id) }}
当前
正在查看
{{ record.n_dims }}(互斥 {{ record.n_excl }})
{{ record.src_name || '—' }}
{{ (record.created_at || '').slice(5, 16) }}
{{ record.created_by || '—' }}
查看
设为当前
删除
全部 {{ cf.personas.length }}
{{ x }} {{ cf.personas.filter(p => p.shop === x).length }}
cf.pf = f">
清空
只有五段文案会进模型,占比与依据不进
{ cf.psf = e.sf; cf.psd = e.sd; }">
← 看画像数据
新增人设
还没有人设。
先在「人群画像」导入数据,
再把它收敛成 2~3 个能写进脚本的人设。
{{ p.name }}
{{ p.size_label }}
{{ p.shop }}
已停用
✦ 提炼
✦ 衍生
{{ p.who || '—' }}
{{ p.speak_to || '—' }}
{{ p.pain || '—' }}
{{ p.path || '—' }}
{{ p.taboo || '—' }}
依据{{ p.evidence }}
(只给人看,不进模型)
下面这五段是会进模型的部分,写成"对着人说话"的口吻;上面的占比与最下面的依据不进。
粘进来一大段也没关系,点右边让 AI 收成五段。
✦ AI 提炼
提炼草稿
还没填进表单,也还没保存。下面是模型收拾过的版本,
对着上面原来的内容看一眼——点「用这一版」会把上面那五段替换掉,之后还要点确定才存。
{{ cf.pe2.draft.name || '—' }}
{{ cf.pe2.draft.who || '—' }}
{{ cf.pe2.draft.speak_to || '—' }}
{{ cf.pe2.draft.pain || '—' }}
{{ cf.pe2.draft.path || '—' }}
{{ cf.pe2.draft.taboo || '—' }}
源人设{{ cf.dv.src.name }}
{{ cf.dv.src.who }}
生成的是草稿,不会自动存。看过之后勾中哪几个,再点下面的「存选中的」。
只有源人设那五段文案会喂给模型,画像里的占比与 TGI 一个数都不进。
还没生成。填好方向(或者不填)点上面那个按钮。
{{ it.who || '—' }}
{{ it.speak_to || '—' }}
{{ it.pain || '—' }}
{{ it.path || '—' }}
{{ it.taboo || '—' }}
全部({{ cfSayTotalAll }})
{{
c.cat || '没设类目' }}({{ c.n }})
这一档是标的时候那条复刻还没设商品类目的。没类目的说法对所有类目的复刻
都会递给模型;划到某个类目之后,就只在那个类目的复刻上生效。
所以只有确定它只适用于某个类目时才去划,不确定就留在这里。
运营在复刻产出上标「好」或「不好」,那句话就落到这里。复刻某个类目的台词时,
只取那个类目的说法 —— 认可与否掉各取最新 {{ cf.say.max.good }} 条,
认可的当参考、否掉的当禁止使用。一条就生效,不用攒。
这里列的是全部类目,切上面的 tab 看单个类目实际会带哪几条。
运营在复刻产出上标「好」或「不好」,那句话就落到这里。复刻这个类目的台词时,
认可与否掉各取最新 {{ cf.say.max.good }} 条递给模型 ——
认可的当参考、否掉的当禁止使用。一条就生效,不用攒。
{{ (cf.say.page-1)*cf.say.size + rowIndex + 1 }}
{{
record.verdict === 'good' ? '认可' : '否掉' }}
{{ record.t }}
{{
record.pre }}{{
record.hit }}{{ record.post }}
这一段整条被标 —— 上面就是全文
来源那条复刻已不在,看不到原段落了
正文后来改过,标不出位置了:{{ record.body }}
{{ record.scene_name || '这条复刻' }}
来源已不在
{{
record.product_name }}
{{ record.who || '—' }}
{{ (record.created_at || '').slice(5, 16) }}
已停用
进提示词
排队中
cfSayPatch(record, {is_active: v?1:0})">
类目
这个类目还没有攒到说法 —— 去复刻产出上选中一段字,点「好」或「不好」。
「{{ cf.sayCat.t }}」
挂在哪个类目下,就只在那个类目的复刻上递给模型。
不划=对所有类目都生效。
(不划,对所有类目生效)
{{ c.cat }}
段落类型({{ zc.zones.length }})
骨架分组({{ zc.groups.length }})
修辞手法({{ zc.skills.length }})
{{ zc.kindCn[k] || k }}({{ zc.enums.filter(e => e.kind === k).length }})
zc.filters = f">
清空
{ zc.sf = e.sf; zc.sd = e.sd; }">
新增
⚠️ 有 {{ zc.orphan.length }} 个类型挂在不存在的分组上:{{ zc.orphan.join('、') }}
—— 它们进提示词时会缺掉组名(「先判组再判类」那句话对它们落空),请改到一个现有分组下。
取数据中…
{{ rowIndex + 1 }}
{{ record.name }}
{{ cfZcGrpName(record.grp) }}
{{ record.category }}
通用
是
—
—
{{ record.used }} 次
未用到
{{ rowIndex + 1 }}
{{ record.name }}
{{ record.code }}
{{ z }}
(还没有类挂在这个组下)
{{ rowIndex + 1 }}
{{ record.name }}
—
{{ record.used }} 次
未用到
{{ rowIndex + 1 }}
{{ record.name }}
名字改不了 —— 它就是存量切片里存着的那个值,改了历史段落全部对不上。
要换叫法:停用这一条,再新建一个。
{{ g.name }}({{ g.code }})
提示词里是「先判组,再判类」—— 挂错组会让模型在错误的候选集里挑。
这一栏是 27 类里最值钱的部分:一半以上是从实测错判里长出来的。
只写「是什么」而不写「不是什么」,相邻的两类模型分不开。
「痛点兑现」这条校验就是看整条切片里有没有一段属于这类。
改它会让用到这一类的 {{ zc.ed.row.used }} 段切片重算兑现结论
资质、背书这类不算 —— 它们证明的是可信,不是怎么解决。
现在 27 类全部留空。等别的品类进来、发现某一类只对功效品成立时,
再把它标上 —— 标了之后它只对该类目的切片进提示词。
组标识改不了 —— 底下的类挂在它上面,代码也拿它派生骨架
(钩子→放大痛点→证明有效→促成交)。
{{ zc.kindCn[k] || k }}
维度是写死的,只能改它们底下的值。加一个新维度要同时改提示词、
改落库的列、改页面 —— 不是配置能覆盖的事,所以这里不让填别的。
{{ cfBanCatName(c) }}({{ rp.bans.filter(b=>cfBanInTab(b,c)).length }})
rp.banf = f">
清空
{ rp.bansf = e.sf; rp.bansd = e.sd; }">
变更记录
测试
新增词条
这一档是还没划类目的词,多半是在切片详情里选中一段话直接加进来的。
没划类目的词对所有稿子都参与扫雷;划到某个类目之后,就只在那个类目的稿子上扫。
所以只有确定它只适用于某个类目时才去划,不确定就留在这里。
已选 {{ rp.banSel.length }} 条
批量划到类目
取消选择
✓ 未命中({{ rp.quickRes.checked }} 条词表)
✗ 命中:{{ h.pattern }}
{{ rowIndex + 1 }}
{{ record.word }}
{{ c }}
全类目通用
{{ record.replacement }}
{{ p }}
(仅提示,不参与扫雷)
内置词表
cfPatchRow('banword', record, {is_active: v?1:0})">
类目
谁、什么时候、把哪个词加进来或改成了什么。
只看这一条词。看全部 ↗
最近 200 条。
取记录中…
还没有记录。词库的增改从 2026-08-04 起留痕,更早的查不到。
{{ r.action }}
{{ r.target }}
{{ r.who }}
{{ r.at }}
{{ d.to }}
{{ d.from }} → {{ d.to }}
一个词可以同时属于多个类目,勾中哪几个就在哪几个类目的稿子上参与扫雷。
一个都不勾=全类目通用,对所有稿子都扫。
{{ c }}
还没有任何类目。先在某个词的「编辑」里填一个类目名,它就会出现在这里。
{{ v }}
全部{{ cf.hlLib.all_n }}
{{ k
}}{{ cf.hlLib.counts[k] || 0 }}
已选 {{ cf.hlSel.length }} 条
批量移出高光库
移出 ≠ 删除 —— 切片、类型、备注都还在
取消选择
取数据中…
库里还没有画面
自动划出来的不会自己进来。拆解时机器按台词划好了区间,
但"这个词说出口的那几秒"不等于"这几秒画面好" —— 哪条值得留下只有人看得出来。
去切片库打开一条,
在「高光画面」那一栏点想留的那几张的星。
当前有 {{ cf.hlLib.pending }} 条自动划的躺在各条切片的工作区里,等你挑
各条切片的工作区里也还没有自动划出来的 —— 先去拆解一条片子。
没有符合筛选条件的 —— 库里一共 {{ cf.hlLib.all_n }} 条,
清掉筛选看全部。
cf.hlSel = v ? (cf.hlLib.rows||[]).map(x=>x.id) : []">
高光词
时长
画面类型
来源切片
存入人 · 存入
cfTogglePick(cf.hlSel, h.id, v)">
e.target.style.display='none'">
▶
{{ h.word }}
整段划的
{{ cfTs(h.t_start) }}—{{ cfTs(h.t_end) }}
{{ (h.t_end - h.t_start).toFixed(1) }}s
cfHlSetKind(h, k)">
{{ h.kind }}AI
标个类型 ›
{{ k }}
配置表里一个启用的类型都没有
清掉类型
{{ h.kind }}
未标类型
e.target.style.display='none'">
片
▶
{{ h.case_title || '(来源切片已删)' }}
{{ h.saved_by }}{{ (h.updated_at||'').slice(5,10) }}
—
移出
{{ t[1] }} {{ cf.fisScans[t[0]] }}
只看这条成品的裂变:{{ cf.fisSrcName || cf.fq.src.slice(0,8) }}
搜索
重置
去成品库裂变一条
已选 {{ cf.fisSel.length }} 条
批量删除
取消选择
cf.fisSel = v ? (cf.fisRows||[]).map(x=>x.id) : []">
脚本
源成品
商品
谁在说
字面扫雷
创建人 · 创建
没有符合条件的裂变脚本。清掉筛选
这一档是空的 —— 当前没有扫雷命中的裂变脚本。
这一档是空的 —— 现有裂变脚本都还有字面扫雷要改。
裂变库还是空的。
去「成品库」打开一条成品,点右上角「裂变」:
用鼠标圈住不能动的那几句,选要不要换商品/人设,一次裂出几套;
看过没问题再上架,才会进到这里。
这里每一条都是人点头上架过的 —— 还没上架的草稿在它源成品那一页的底部。
‹ {{ cfBackName(cf.fisFrom, 'cf_fission') }}
{{ cfTitleOf(cf.fis.one) }}
裂变 · {{ cfFisSegN(cf.fis.one) }} 段
{{ cf.fis.one.n_hits ? ('扫雷 ' + cf.fis.one.n_hits + ' 处待改') : '扫雷干净' }}
分段
通篇对照
缺 {{ cf.fis.one.n_missing }} 段
{{ cf.fis.one.n_lock_missing ? ('🔓 锁定被改 ' + cf.fis.one.n_lock_missing + ' 处') : ('🔒 锁定 ' + cf.fis.one.n_locks + ' 处完好') }}
导出 txt
回源成品再裂一批
复制整篇台词
它从哪来
商品{{ cf.fis.one.product_name || '—' }}
换了品
与源相同
谁在说{{ cf.fis.one.persona || '—' }}
换了人设
与源相同
裂变方向{{ cf.fis.one.fission_brief || '(没填,就是换个说法)' }}
同批共 {{ (cf.fis.one.sibling||{}).n || 1 }} 套,上架 {{ (cf.fis.one.sibling||{}).shelved || 0 }} 套
当时锁住了什么 · 共 {{ cf.fis.one.n_locks }} 处
{{ cfNo(i+1) }}
整段
{{ cfNo(i+1) }}
{{ k.t }}
逐字比对:{{ cf.fis.one.n_lock_missing ? ('有 ' + cf.fis.one.n_lock_missing + ' 处被改动了(红色那几条)') : '全部完好 ✓' }}
就地改台词时若动到这几处,页面会提醒但不拦。
逐段对照
左边是裂变出来的,右边是源成品同一段
裂变台词
{{ ((sc.scan&&sc.scan.hits)||[]).length ? (((sc.scan&&sc.scan.hits)||[]).length + ' 处待改') : '干净' }}
{{ (sc.body||'').length }} 字(源 {{ sc.src_len }} 字)
改完会重新扫一遍卡审词;动到锁定内容的话上面的标会变。
锁定的内容被改动了:{{ j?'、':'' }}「{{ t }}」
源成品这一段 · 只读
蓝底是圈住不许动的部分,两边应当逐字一样。
这一套裂变还没有内容。
粘贴任意脚本(含达人交回来的稿子)→ 字面扫雷即时、句式复核走 LLM
{{ c }}
字面扫雷
句式复核(LLM)
✓ 字面扫雷通过({{ rp.checkRes.checked }} 条词表 · {{ rp.checkRes.chars }} 字)
恐吓/怂恿等句式级违规请再点「句式复核」
✗ 命中 {{ rp.checkRes.hits.length }} 处
| 命中词 | 违规类型 | 替换话术 |
| {{ h.pattern }} |
{{ h.vio_type }} |
{{ h.replacement }} |
{{ r['违规类型'] }}「{{ r['句子'] }}」→ {{ r['替换建议'] }}
句式复核:未发现八类违规
{{ t[1] }} {{
cf.rk.stats[t[0]] }}
已选 {{ cf.rk.sel.length }} 条
一次最多 {{ cf.linksMax }} 条,去掉几个再存
存进灵感库
取消选择
排名
商品
店铺
短视频用户支付金额
点击次数
成交件数
带货视频数
带货短视频
{{ r.rank }}
{{ cfRankAccts.length > 1 ? (r.acct_name || '自家') : '自家' }}
{{ r.product_name || '—' }}
商品页 ↗
{{ r.shop_name || '—' }}
{{ cfRankMoney(r.pay_amt_txt) }}
{{ r.is_exact ? '精确' : '区间' }}
{{ cfRankNum(r.click_txt) }}
{{ cfRankNum(r.combo_txt) }}
{{ cfRankNum(r.vcnt_txt) }}
{{ (r.videos || []).length }} 条 {{
cf.rk.open[r.id] ? '▲' : '▼' }}
cfTogglePick(cf.rk.sel, v.vid, on)">
{{ v.seq }}
{{ v.title || '(没有标题)' }}
{{ v.author ? ('@' + v.author) : '—' }}
{{ v.publish_at ? String(v.publish_at).slice(5, 10) : '—' }}
打开 ↗
看档案 →
存灵感
这一行没有视频
{{
cf.rkCates.length ? '这个类目还没有快照。点右上「立即抓一次」,约 45 秒。'
: '还没有在跟的类目。先去「榜单类目」加一个。' }}
{{
cf.rkHasCookie ? '罗盘 Cookie 已配置' : '还没配罗盘 Cookie' }}
去「集成 · API → API Key 配置 → 抖音电商罗盘」粘贴整串 Cookie(从 DevTools 请求头复制,document.cookie 取不到)
自动更新
每天 {{ cf.rkCronAt || '—' }} 各抓一份 · 下次 {{ cfRankNextRun() }}
已停用 —— 今晚一个类目都不会抓,只能手动点「抓一次」
加一个类目
类目数据源账号窗口状态
上次抓取上次入库
{{ c.cate_path || '(没记路径)' }}
{{ c.category_id }}
{{ c.acct_name || '—' }}
取不到 Cookie
{{ cfRankWin(c.win) }}
不进候选
{{ cfRankStatusCn(c) }}
{{ c.last_run_at ? cfShortTime(c.last_run_at) : '—' }}
{{ c.last_status === 'ok'
? (c.last_products + ' / ' + c.last_videos) : '—' }}
抓一次
{{ c.is_active ? '停用' : '启用' }}
删除
还没有在跟的类目。点右上「加一个类目」。
有 {{ cfRankFailed().length }} 个类目上次没抓成:
{{ c.cate_path || c.category_id }}{{ c.last_err || '(没记原因)' }}
每天 {{ cf.rkCronAt || '—' }}
自动把启用中的类目各抓一份(一个类目约 45 秒)。
自动更新已停用 ——
下面这些类目今晚一个都不会抓,只能逐个点「抓一次」。
「上次入库」是 商品数 / 视频数。
停用只是不再抓,历史快照照常看得见;删除会连带删掉它的全部历史快照,
而罗盘的历史取不回来 —— 删了就真没了。
{{ a.name }}{{ a.is_enabled ? '' : '(已停用)' }}
类目树、以及"哪些商品给精确数",都是这个店铺决定的。
换店铺会重新拉一棵树。店铺名与罗盘 Cookie 在「集成·API → API Key 配置 → 抖音店铺」配。
先在上面选一个账号,类目树是按账号拉的。
类目树是实时从罗盘拉的(当前店铺:{{ cfRankCateAcctName() }})。选到最细那一级——
越细榜单越贴题(TOP200 是按所选层级算的)。
{{ t[1] }} {{
cf.cCounts[t[0] || 'all'] || 0 }}
已选 {{ cf.cSel.length }} 条
批量存进灵感库
批量跳过
批量撤销跳过
取消选择
cf.cSel = v ? (cf.cand||[]).map(x=>x.id) : []">
选中本页全部
cf.cSel = v ? (cf.cand||[]).map(x=>x.id) : []">
标题 · 来源商品作者 · 粉丝
点赞评论收藏分享
上榜 · 回捞状态
没有符合筛选条件的候选。清掉筛选
待看的都看完了 🎉 明天 06:00 会再回捞一批。
还没跳过任何一条 —— 在待看那一档点「跳过」的会落到这里,随时能撤回。
还没有归档的候选。存进灵感库或跳过超过 7 天的会自动归到这里。
候选库还是空的。
它不用人添 —— 每天 06:00 从「抖音榜单」里按规则回捞一批进来,
你只要决定存进灵感库还是跳过。没有的话先去「榜单类目」加一个在跟的类目。
cfTogglePick(cf.cSel, r.id, v)">
e.target.style.visibility='hidden'">
▶
{{ cfDur(r.duration) }}
{{ r.title || '(没取到标题)' }}
{{ cfSrcKindCn(r.src_kind) }}
{{ r.src_acct }}
{{ r.src_shop }}{{ r.src_label }}
cfFpGo(r, k)">
{{ cfSrcPlatCn(r, 'item') }} ↗
发布 {{ String(r.publish_at).slice(0, 10) }}
{{ r.author ? ('@' + r.author) : '—' }}
粉丝 {{ cfW(r.author_fans) }}
{{ cfW(r.stat_digg) }}
{{ cfW(r.stat_comment) }}
{{ cfW(r.stat_collect) }}
{{ cfW(r.stat_share) }}
今天首上榜
首上榜 {{ r.first_rank_day.slice(5) }}
—
{{ cfShortTime(r.created_at) }}
已跳过
待看
撤销
存
跳过
cfTogglePick(cf.cSel, r.id, v)">
![]()
e.target.style.visibility='hidden'">
{{ cfDur(r.duration) }}
{{ r.video_url ? '▶' : '◻' }}
{{ cfSrcKindCn(r.src_kind) }}
@{{ r.author }}
{{ r.skipped_at ? '已跳过' : '待看' }}
{{ r.title || '(没取到标题)' }}
{{ cfW(r.stat_digg) }} 赞 · {{ cfW(r.stat_comment) }} 评 ·
{{ cfW(r.stat_collect) }} 藏 · {{ cfW(r.stat_share) }} 享
{{ cfSrcLine(r) }}
候选详情
已跳过
待看
⛔ 取数队列已停摆
{{ cf.huHalted }}
按上面这句处理掉之后,点下面的「重试」或任一条的「获取」即可恢复。
在此期间新排的队一条都不会被取。
⚠️ 灰豚数据桥装了两处
{{ cf.huMulti }}
{{ t[1] }} {{
cf.iCounts[t[0] || 'all'] }}
{{ k.cn }}
{{ a }} 的榜
清空
卡片
列表
{{ cf.huFailed }} 条没取到灰豚 · 重试
存灵感
已选 {{ cf.iSel.length }} 条
批量开拆
批量打标
批量删除
取消选择
开拆会按「存灵感」弹窗里选的拆法跑(当前:{{
cf.up.kind==='lines' ? '只提台词' : '五轨拆解' }})
cf.iSel = v ? (cf.insp||[]).map(x=>x.id) : []">
标题{{ cfSortMark('title') }}
作者 · 粉丝{{ cfSortMark('author') }}
发布{{ cfSortMark('pub') }}
时长{{ cfSortMark('dur') }}
点赞{{ cfSortMark('hot') }}
评论{{ cfSortMark('cmt') }}
备注
存的人 · 存入{{ cfSortMark('new') }}
状态
cfTogglePick(cf.iSel, r.id, v)">
e.target.style.visibility='hidden'">
▶
{{ r.title || '(没取到标题)' }}
{{ cfSrcKindCn(r.src_kind) }}
{{ r.src_acct }}{{ cfSrcLine(r) }}
cfFpGo(r, k)">
{{ t }}×
cfSaveTags(r, ts, 'insp')">
{{ cfSrcPlatCn(r) }} ↗
{{ r.author ? ('@' + r.author) : '—' }}
粉丝 {{ cfW(r.author_fans) }}
{{ r.publish_at ? String(r.publish_at).slice(0,10) : '—' }}
{{ r.duration ? cfDur(r.duration) : '—' }}
{{ r.stat_at ? cfW(r.stat_digg) : '—' }}
{{ r.stat_at ? cfW(r.stat_comment) : '—' }}
{{ r.note || '—' }}
{{ r.created_by || '—' }}
{{ cfShortTime(r.created_at) }}
{{
r.task_id ? '已开拆' : '未拆解' }}
灰豚 ✕
灰豚…
开拆
删除
没有符合筛选条件的灵感。清空筛选
还没存过灵感。刷到好片子就贴一条链接进来,回头再决定拆哪些。
cfTogglePick(cf.iSel, r.id, v)">
![]()
e.target.style.visibility='hidden'">
{{ cfDur(r.duration) }}
{{ r.video_url ? '▶' : '◻' }}
缓存中
{{ cfSrcPlatCn({source_note: r.url}) }}
@{{ r.author }}
已开拆
{{ r.title || '(没取到标题)' }}
存入时 {{ cfW(r.stat_digg) }} 赞 · {{ cfW(r.stat_comment) }} 评
(元信息没抓到,只存了链接)
备注:{{ r.note }}
{{ t[1] }} {{
cf.counts[t[0] || 'all'] }}
已选 {{ cf.sel.length }} 条
批量打标
批量删除
取消选择
cf.sel = v ? (cf.tasks||[]).map(x=>x.id) : []">
切片名{{ cfSortTaskMark('title') }}
时长{{ cfSortTaskMark('duration') }}
状态
拆解规模
创建人 · 创建{{ cfSortTaskMark('created_at') }}
拆解人 · 拆解{{ cfSortTaskMark('cut_at') }}
cfTogglePick(cf.sel, r.id, v)">
e.target.style.visibility='hidden'">
▶
脚本
台词异常
{{ r.title || '(抓取中…)' }}
{{ cfSrcKindCn(r.src_kind) }}
{{ r.src_acct }}{{ cfSrcLine(r) }}
cfFpGo(r, k)">
{{ t }}×
cfSaveTags(r, ts, 'task')">
{{ cfSrcPlatCn(r) }} ↗
{{ r.duration ? r.duration.toFixed(1) + 's' : '—' }}
{{ cfStatusText(r) }}
{{ r.progress||0 }}%
—
{{ r.n_scene || 1 }} 段 · 台词{{ cfLinesSrcCnOf(r) }}
场景 {{ r.n_scene }} · 花字 {{ r.n_ov }} · 音效 {{ r.n_sfx }}
{{ r.created_by || '—' }}
{{ cfShortTime(r.created_at) }}
{{ r.cut_by || '—' }}
{{ cfShortTime(r.cut_at) }}
{{
r.saved ? '移出案例库' : '存案例' }}
重拆
删除
没有符合筛选条件的任务。清空筛选
切片库还是空的。上传一条对标视频或粘一个链接,几分钟后回来看五轨拆解结果。
cfTogglePick(cf.sel, r.id, v)">
![]()
e.target.style.visibility='hidden'">
{{ cfDur(r.duration) }}
▶
{{ r.kind==='lines' ? '只提台词' : '五轨' }}
{{
cfStatusText(r) }}
{{ r.title || '(抓取中…)' }}
场景 {{ r.n_scene || 0 }} · 花字 {{ r.n_ov || 0 }} · 音效 {{ r.n_sfx || 0 }}
{{ t }}
{{ cf.up.kind==='lines' ? '提取视频脚本' : '新建拆解' }}
拆完落在切片库,值不值得留由你决定
1视频从哪来
粘链接
上传 mp4
{{ i+1 }}
{{ l.url }}
{{ l.cn }}
{{ cf.up.err }}
链接过期 / 私密 / 需要登录都会抓不到 —— 那时切到「上传 mp4」把片子传上来,
结果完全一样。不做去水印:画面上的平台水印与账号角标会在花字识别时自动排除,不进图鉴。
把 mp4 拖进来,或点这里选文件
单个不超过 {{ cf.maxMb }}MB · 竖屏 9:16 效果最好 · 任务名取文件名
{{ cf.up.file.name }}
{{ (cf.up.file.size/1048576).toFixed(1) }} MB
重选
2拆到什么程度
五轨拆解2~4 分钟
台词 · 场景画面 · 花字贴纸 · 音效 · 运营维度
只提台词7~9 秒
只要一整篇口播文字,不分段、不看画面
台词从哪来
{{ o.t }}
{{ cfLinesSrcTip }}
3怎么叫它都可以不填
{{ cfUpLinks.length }} 条各建一个任务,任务名自动取视频标题。要单独改名就等抓完在切片库改。
任务名
来源备注 · 只给自己看
{{ cfUpSummary }}
{{ cf.up.kind==='lines'
? '抽音频 → 台词 → 入库,每条约 1 分钟,完成后落在切片库'
: '切镜 / 抽音频 → 场景 / 台词 / 花字 / 音效 → 汇总,每条 2~4 分钟,完成后落在切片库' }}
取消
{{ cfUpOkText }}
{{ p.shop_name }} · {{ p.name }}
千播出单按播放 ≥ {{ cf.frMeta.cpmMinPlay || 10000 }} 的示例算;
样本不足 {{ cf.frMeta.cpmMinN || 3 }} 条的显示「——」
cf.framef = f">
清空
{ cf.framesf = e.sf; cf.framesd = e.sd; }">
框架
{{ cf.frames.length }}
未归类
{{ cf.frUnsorted }}
战绩导入
另有 {{ cf.frHidden }} 个框架属于别的类目(本商品是「{{ cf.frDocCat.split('>').pop().trim() }}」){{ cf.frAllCats ? ',正一并显示' : ',已折起' }}
{{
cf.frAllCats ? '只看本类目' : '显示全部类目' }}
框架(开场型 → 说服路径)
类目本商品全部
千播出单销售额
{{ cf.frOpen===f.id ? '▾' : '▸' }}
{{ f.code }}
{{ f.hook_cn }}
→
{{ f.pivot_cn }}
{{ f.category }}
全类目
{{ f.n_prod }} 条
{{ f.n_all }}
{{ f.cpm.toFixed(2) }}
——
{{ cfYuan(f.sale_c) }}
{{ f.note }}
本商品 {{ f.n_prod }} 条 · 全部 {{ f.n_all }} 条
主场景分布
{{ cf.frAgg.agg_n }} 条示例{{ cf.frAgg.agg_cut ? '(只取销售额前 500 条)' : '' }}
{{ sc.cn }} {{ sc.n }}
七拍命中率
命中率低的那一拍就是这个框架现在的短板
{{ b.cn }}{{ b.pct }}%
七拍命中率
这个类目还没有节奏判词表,算不出来(判词是按类目分的)
示例(达人 · 口播开头)场景
播放销量销售额千播出单
{{ cf.frExOpen===e.id ? '▾' : '▸' }}
{{ e.author || '未知达人' }}
已拆解
仅文案
{{ e.body.slice(0, 46) }}…
{{ e.scene_cn || '—' }}
{{ e.play ? e.play.toLocaleString() : '——' }}
{{ e.perf_at ? e.qty : '——' }}
{{ e.perf_at ? cfYuan(e.sale_c) : '——' }}
{{ e.cpm.toFixed(2) }}
——
完整口播
{{ e.body }}
七拍命中
{{ b.cn }}
{{ b.words.length ? b.words.join(' / ') : '—' }}
#{{ t }}
战绩
播放量{{ e.perf_at ? e.play.toLocaleString() : '——' }}
销量{{ e.perf_at ? e.qty.toLocaleString() : '——' }}
销售额{{ e.perf_at ? cfYuan(e.sale_c) : '——' }}
千播出单{{
(e.cpm !== null && e.cpm !== undefined) ? e.cpm.toFixed(2) : '——' }}
这个商品在这个框架上还没有示例 —— 去未归类里挑,或者从切片归入
这个类目下还没有框架 —— 上面那 {{ cf.frHidden }} 个属于别的类目,
可以「显示全部类目」看看有没有能借用的
还没有框架
已选 {{ cf.frSel.length }} 条
{{ f.code }} · {{ f.hook_cn }} + {{ f.pivot_cn }}
归入
取消选择
归入不改口播,只改归属;改错了随时挪回来
cf.frSel = v ? cf.frUn.map(x => x.id) : []">
示例(达人 · 口播开头)场景
播放销量销售额千播出单
cfTogglePick(cf.frSel, e.id, v)">
{{ e.author || '未知达人' }}
{{ e.body.slice(0, 52) }}…
{{ e.scene_cn || '—' }}
{{ e.play ? e.play.toLocaleString() : '——' }}
{{ e.perf_at ? e.qty : '——' }}
{{ e.perf_at ? cfYuan(e.sale_c) : '——' }}
{{ e.cpm.toFixed(2) }}
——
没有未归类的示例
把平台导出的 xlsx 传进来
需要这几列:视频链接 · 播放量 · 销量 · 销售额(认常见别名,列序随意、多几列没关系)
视频链接里那串数字就是关联键;对不上的行不会建示例,只报出来
导入台账
文件导入时刻文件行数
匹配上更新对不上操作人
{{ l.filename }}
{{ (l.created_at || '').slice(0, 16) }}
{{ l.rows }}
{{ l.matched }}
{{ l.updated }}
{{ l.missed }}
{{ l.created_by || '—' }}
还没有导过战绩
本商品战绩覆盖
缺战绩的示例在列表里显示「——」而不是 0 ——
「没导过」和「真的一单没出」是两回事
重导会按视频号覆盖同一条的数字,幂等 —— 同一份文件导两遍结果一样。
匹配不上的行不会新建示例:示例只能从切片归入,或从未归类里挑。
口径
千播出单 = 销量 ÷ 播放量 × 1000,实时算不落库
框架默认按销售额排,但挑框架要看千播出单 ——
销售额是达人的分,千播出单才是脚本的分
{{ t[1] }}
已选 {{ cf.caseSel.length }} 条
批量移出案例库
批量存进案例库
取消选择
移出不删数据 —— 案例与它的五轨照常留着,只是不在这一页列出
cf.caseSel = v ? (cf.cases||[]).map(x=>x.id) : []">
标题类型判断时长
拆解规模存入人 · 存入
cfTogglePick(cf.caseSel, c.id, v)">
e.target.style.visibility='hidden'">▶
脚本{{ c.title || '未命名案例' }}
cfFpGo(c, k)">
{{ t }}↑
{{ cfSrcPlatCn(c) }} ↗
{{ c.type_judgement || '—' }}
{{ c.duration ? cfDur(c.duration) : '—' }}
{{ c.scene_count }} 段 · 只有台词轨
场景 {{ c.scene_count }} · 花字 {{ c.overlay_count }} · 音效 {{ c.sfx_count }}
{{ c.saved_by || '—' }}
{{ cfShortTime(c.created_at) }}
复刻
移出
![]()
e.target.style.visibility='hidden'">
★
{{ cfDur(c.duration) }}
▶
脚本
{{ c.type_judgement }}
· {{ c.persona }}
{{ c.title || '未命名案例' }}
{{ c.scene_count }} 段 · 只有台词轨
场景 {{ c.scene_count }} · 花字 {{ c.overlay_count }} · 音效 {{ c.sfx_count }}
拆过的都已经存进案例库了。
没有符合条件的案例。清空筛选
案例库还是空的。
案例库放的是你挑过、准备照着做的对标——去「切片库」里,
在觉得好的那条上点「存进案例库」。拆过但没存的,点上面的「拆过但没存」还能找回来。
{{ cf.zoom.sub }}
成品库存的是自家品的成品脚本。只有复刻过的段能进;每段一律取最新那一版,成品里不留版本历史。
取消勾选的段里有 {{ cfFilmLoseCount }} 段在成品里改过台词,覆盖后这几段的改动会丢。
{{ cf.fp.err }}
cf.fp.pick[s.scene_id] = v">
{{ cfNo(s.no) }}
{{ s.title }}
{{ (s.t_start||0).toFixed(1) }}—{{ (s.t_end||0).toFixed(1) }}s
{{ s.ver }}
{{ s.n_hits ? (s.n_hits + ' 处待改') : '干净' }}
还没复刻 · 先在这一段点「复刻这段」并保留一版
{{ s.text }}
商品 {{ s.product || '—' }} · 谁在说 {{ s.persona || '—' }}
这条切片还没有段。
这条还没有复刻过的段。先在某一段点「复刻这段」,生成并保留一版,才能存成品。
成品会记住是从哪条切片、哪几段、哪一版来的。
取消
{{ cfFilmPickCount ? ((cf.fp.filmId ? '覆盖这条成品(' : '存进成品库(') + cfFilmPickCount + ' 段)') : '至少选一段' }}
这条切片是旧框架拆的,没有整片档案与段落类型。
重新拆解一次就有了(会重新花一次模型调用的钱,所以不自动跑)。
{{ d.t }}
分段
通篇对照
已复刻 {{ cfDraftCount }} 段
已保存 {{ cf.savedAt }}
复制台词
重拆
★ 存入案例库
移出案例库
存到成片
本案例有 {{ cf.mediaMissing }} 个媒体文件缺失(切片/截图/音效),页面上会显示成空白——
多半是上一次「重新拆解」跑到一半被打断。点右上角「重新拆解整条」重建即可。
本次拆解有 {{ cfTrackErrors.length }} 条轨失败,对应内容是「缺的」不是「没有」:
{{ i?'、':'' }}{{ e.label }}({{ e.msg }})
—— 修好后点「重新拆解整条」。
{{ cf.linesWarn }}
{{ cf.splitNote }}
![]()
e.target.style.display='none'">
{{ String(i+1).padStart(2,'0') }}
{{ n.name || '未命名' }}
草稿
转化点
{{ ((n.t_end||0)-(n.t_start||0)).toFixed(1) }}s
标题与 tag
{{ cf.case.title || '(没有抓到原标题)' }}
#{{ t }}
没有原 tag
还没复刻
{{ cfTtHits ? ('扫雷 ' + cfTtHits + ' 处待改') : '扫雷干净' }}
商品 {{ cfTt.product || '—' }} · 人设 {{ cfTt.persona }} · {{ cfBjTime(cfTt.at) }}
{{ cfTt.fold ? '展开' : '收起' }}
{{ cfTt.titles.length ? '重新复刻' : '复刻标题与 tag' }}
⋯
复制标题与 tag
清空这份复刻(可以再复刻一次)
标题 · 抓取原文
{{ cf.case.title || '(没有抓到原标题)' }}
原 tag
#{{ t }}
—
复刻
还没做。会拿商品、人设和左边这条原标题一起改写,出一组候选标题和 tag。
复刻后
{{
cfTtHits ? ('扫雷 ' + cfTtHits + ' 处') : '扫雷干净' }}
商品 {{ cfTt.product || '—' }} · 人设 {{ cfTt.persona }} · {{ cfBjTime(cfTt.at) }}
复制
再来一版
删这版
‹
v{{ cfTt.cur + 1 }} / 共 {{ cfTt.n_vers }} 版
›
上一版原样留着,可以来回切着比
{{ cfNo(i+1) }}
{{ (t.text||'').length }} 字
×
{{ cf.tt.open
? '收起' : ('还有 ' + (cfTt.titles.length - 4) + ' 条 · 全部展开') }}
{{ String(i+1).padStart(2,'0') }}
{{ s.name || '未命名' }}
{{ cfTs(s.t_start) }}—{{ cfTs(s.t_end) }}
{{ cfSegRole(s) }}
{{ cfTeaser(s) }}
修辞手法
{{ k }}
草稿 {{ s.repros.length }} 版
展开
{{ String(i+1).padStart(2,'0') }}
{{ s.name || '未命名场景' }}
{{ (s.t_start||0).toFixed(1) }}—{{ (s.t_end||0).toFixed(1) }}s
▶
{{ cfSegGrp(s) }}
{ s.zone = v || ''; cfSaveScene(s, 'zone'); }">
{{ o.grp_cn }} · {{ o.name }}
{{ cfSegRole(s) }}
修辞手法
{{ k }}
×
cfSkillAdd(s, v)">
{{ o }}
复刻这段
没有这一段的切片
只提台词的任务不切片
点右上角「重新拆解」可以补上
{{ s.screen_lines || '这一段画面上没有字' }}
{{ s.visual_lines }}
复刻后
{{
(r.scan||{}).ok ? '扫雷干净' : ('扫雷 ' + (((r.scan||{}).hits)||[]).length + ' 处') }}
{{ (r.body||'').length }} 字(原 {{ cfReproBase(s) }} 字)
高光词 {{ cfLockMiss(r, s).length }} 处没保住
商品 {{ r.product_name || '—' }} · 说给 {{ r.audience_name }} · 谁在说 {{ r.persona_name }}
已保存 {{ r._savedAt }}
复制
再来一版
删除
反馈 {{ (r.marks||[]).length }} 条
下次「再来一版」会带上还生效的那 {{ cfFbLive(r).length }} 条
{{ m.alive===false ? '⊘' : (m.verdict==='good' ? '▲' : '▼') }}
{{ m.t }}
整条
{{ m.reason_cn }}
正文后来改过,这条不再生效
✕
{{ h.pattern }}
字面命中,改完自动重扫;句式复核在「脚本自查」
![]()
e.target.style.display='none'">
✕
{{ cfTs(x.t_start) }}—{{ cfTs(x.t_end) }}
{{ (x.t_end - x.t_start).toFixed(1) }}s
{{ x.word || '(整段里的一截)' }}
{{ x.kind
}}AI{{
x.note ? ' · ' + x.note : '' }}
调区间 · 标类型 ›
未标类型
贴纸 / 花字这一段没有,画面上只有原生字幕
+ 补一条
音效无点缀音效(原声),只有说话声和环境声
+ 补一条
加载中或暂无场景…
上游调用
{{ cf.apilog.total }} 次 · 成功 {{ cf.apilog.ok }}
· 失败 {{ cf.apilog.failed }}
· {{ (cf.apilog.msTotal / 1000).toFixed(1) }} 秒 · {{ cf.apilog.tokens }} tokens
{{ cf.apilog.loaded ? '刷新' : '查看' }}
这条任务没有调用留档。留档是 2026-08-13 才上线的,在那之前拆的任务不会有
—— 不是没调用过,是当时没记。重新拆解一次就会有了。
{{ r.ok ? '成功' : '失败' }}
{{ r.stage }}
{{ r.model }}
{{ r.http_status || '—' }}
{{ (r.ms / 1000).toFixed(1) }}s
{{ r.usage.total_tokens }} tok
{{ m.type }} {{ (m.bytes / 1048576).toFixed(1) }}MB
{{ (r.created_at || '').slice(5, 19) }}
{{ cf.apilog.open[r.id] ? '收起' : '展开' }}
提示词
{{ r.req_text }}{{ r.req_text_more ? ('\n…(还有 ' + r.req_text_more + ' 字,点「看全文」)') : '' }}
{{ r.ok ? '响应' : '错误' }}
{{ r.ok ? r.resp_text : r.err }}{{ r.ok && r.resp_text_more ? ('\n…(还有 ' + r.resp_text_more + ' 字,点「看全文」)') : '' }}
看全文
模型换代改这里即可,不需要改代码(本轮踩过"选完就下架")
保存
调用链路
| 步骤 | 模型 | 喂给模型的东西 | 调用次数 | 提示词 |
| ① 场景拆解 | {{ cf.set.values.scene_model }} |
整段视频(480p 副本,base64 内联)+ ffmpeg 算出的候选边界 | 1 次 |
看/改 ↓ |
| ② 台词 | {{ cf.set.values.asr_model }} |
每个场景各自切出来的音频片段(物理隔离,串不了段) |
N 次(N=场景数 ≤5) |
看/改 ↓ |
| ③ 花字/贴纸 | {{ cf.set.values.huazi_model }} |
整段视频(同 ①) | 1 次 |
看/改 ↓ |
| ④ 音效判型 | {{ cf.set.values.sfx_model }} |
整段视频 + ffmpeg 信号法定位出的时刻表 | 1 次 |
看/改 ↓ |
| ⑤ 花字解读 | {{ cf.set.values.scene_model }} |
花字清单 + 整片台词(纯文本,不看画面) | 1 次 | — |
| 纯代码,不调模型 | ffmpeg |
切镜 · 场景切片 · 抽音频 · 裁花字横条 · 音效信号定位(高通+逐窗RMS+峰均比)· 波形/频谱图 |
台词只能用 omni 系——实测把字幕带裁掉后,qwen3.7/3.8 的 9 个关键词只剩 1~2 个(它们读的是画面上烧的字幕),
omni 保持 6~7/9。六个模型都自报"听得见",这条结论只能靠对照组拿到。
网络(TikTok 抓取需要)
抖音走本机解析服务,不需要代理;TikTok 从这台机器直连 tikwm 是 403(Cloudflare 拦大陆 IP),
必须配代理。只支持 http 代理(socks5 要 PySocks,不在镜像依赖里)——clash 一般同时开
http(7890) 与 socks5(7891),填 http 那个。
测试是真的去拿一次 TikTok 解析结果,不是只看端口通不通:代理挂掉是静默的,
"节点 delay 通"≠"能拿到数据"。
设为高光词
词、解读、画面一次做完
{{ cf.kwSet.word }}
{{ cfTs(cf.kwSet.t.t_start) }}–{{ cfTs(cf.kwSet.t.t_end) }}
定不到时刻
为什么这么写选填;以后在「为什么这么写」那一栏还能改
顺手划一条高光画面
{{ cfTs(cf.kwSet.shot[0]) }}–{{ cfTs(cf.kwSet.shot[1]) }}
起止取自{{ cf.kwSet.shot[2] === 'cut' ? '镜头切点' : '这个词说出口的那几秒' }},存下来是一段独立文件
起止按这个词说出口的那几秒算
这几个字在台词里定不到时刻(这条切片是本功能上线前拆的,或者词与台词对不上),
所以这次只存词和解读。要划画面得先「重新拆解」一次。
‹ {{ cfBackName('cf_hl', 'cf_hl') }}
{{ cf.hlv.row.word || '整段里的一截' }}
{{ cf.hlv.row.kind }}AI
未标类型
{{ cfTs(cf.hlv.row.t_start) }}—{{ cfTs(cf.hlv.row.t_end)
}} {{ ((cf.hlv.row.t_end - cf.hlv.row.t_start) || 0).toFixed(1) }}s
★ 移出高光库
去切片详情看全片 →
取数据中…
取不到:{{ cf.hlv.err }}
这一段画面
备注{{ cf.hlv.row.note }}
—
存入{{ cf.hlv.row.saved_by }}
—
起止取自{{ cfHlAnchorCn(cf.hlv.row.anchor) }}
类型来自AI 标的
{{ cf.hlv.row.kind_by }}
—
在原片的哪儿
![]()
e.target.style.visibility='hidden'">
段 {{ cfNo(cf.hlv.scene.seq || 0) }} {{ cf.hlv.scene.name }}
{{ cfTs(cf.hlv.scene.t_start) }}—{{ cfTs(cf.hlv.scene.t_end) }}
本段功能 · {{ cf.hlv.scene.stage }}
「{{ cf.hlv.scene.lines }}」
这一段没有台词
这一条不挂在任何一段上(多半是重新拆解之后段落变了)
原片
{{ (cf.hlv.case || {}).title || '(来源切片已删)' }}
{{ cfHlvSpec() }}
判型
{{ cf.hlv.case.type_judgement
}} · 人设 {{ cf.hlv.case.persona }}
—
作者
{{ cf.hlv.src.author || '—' }} · {{ cfNum(cf.hlv.src.author_fans) }} 粉
发布
{{ (cf.hlv.src.publish_at || '').slice(0,10) || '—' }}
取数于 {{ (cf.hlv.src.stat_at || '').slice(0,16) }} —— 是那一刻的快照,不是现在的数
{{ cf.hlv.srcWhy || '没有原片战绩' }} 显示「—」不显示 0。
划一条高光画面
选这几秒,起个名字
cf.hlAdd.t0 = v"
@update:t1="v => cf.hlAdd.t1 = v">
名字是以后在高光库里认出它的唯一线索 ——
「产品特写 3」这种编号式的,翻库时等于没写。选填,不填就记成「整段里的一截」。
{{ cf.hl.id ? '这一条高光画面' : '划高光画面' }}
{{ cf.hl.id
? '调区间 / 标类型 / 写备注' : '给这个高光词划出它对应的那几秒' }}
cfHlSetT('t0', v)" @update:t1="v => cfHlSetT('t1', v)">
{{ cf.hl.word }}
整段
{{ cf.hl.why || '(这一条不是为某个词划的)' }}
段 {{ cfNo((cf.hl.seg.seq||0)) }}
{{ cf.hl.seg.name }}
{{ cfTs(cf.hl.seg.t_start) }}—{{ cfTs(cf.hl.seg.t_end) }}
改完保存会按新区间重切一段文件,旧那段随之删掉。
这一条在别处的引用(高光库、复刻带出来的备注)跟着走,不用重新挑一遍。
起止已经落在「{{ cf.hl.word }}」这几个字上
(取自原片逐词时间轴)。但那是"嘴说这句话"的时刻,不是"画面好看"的时刻 ——
反应镜头常压在话说完之后,下面条子上拖一下看看。
默认给的是这一整段的范围。这条切片没有逐词时间轴
(本功能上线前拆的、或当时回落了旧链),机器说不出这几个字在第几秒 ——
在下面的条子上拖一下。
这是什么画面选一个,以后在库里按它筛
{{ k }}
备注选填,复刻时会一起带出来
这条片在讲什么
商品、说给谁、什么场合 —— 读这条切片的前提
{{ w.cn }}
01
这条片在讲什么
{{ w.cn }}
收起 ▲
商品
{{ f.cn }}
{{ f.v }}
台词里没有
目标用户
{{ f.cn }}
{{ f.v }}
台词里没有
用户层级
cfDosSave('level', v)">
{{ o }}
依据「{{ cf.case.level_why }}」
台词里引不出依据,按最宽的一档算
使用场景
{{ f.cn }}
{{ f.v }}
台词里没有
用户痛点 · 只写一个
{{ cf.case.pain_point }}
台词里没有
产品怎么解决 · 只写作用机制
{{ cf.case.how_solve }}
台词里没有
这条视频的档案
原片数据与归属 —— 这条为什么值得拆
正在取…
标签 · 归属
{{ t }}
还没打标签 —— 去切片库那一行点「+ 加标签」
上下游
取不到:{{ cf.archIn.err }}
视频档案
从候选到裂变,这条视频经过的每一步
给选中的 {{ cfBatchTagIds.length }} 条加标签,是追加,已经有的标签不会被清掉。
多个标签用空格或逗号分开,每条最多 10 个。
搜索
重置
{ videos.filters = f; loadVideos(1); }">
不自动
每6小时
每12小时
每天
每3天
每7天
添加视频
批量导入
刷新所选
自动取新:每小时把状态是「待刷新」的视频取一批(每轮最多
{{ videoSettings.backfill_max }} 条)。
现在还有 {{ videoSettings.pending }} 条待刷新。
现在没有待刷新的。
取数失败的(视频已删或设为私密)不会自动重试,要在这里勾选后手动刷新。
toggleTracking(record, v)">
各外部接口的取数任务状态;密钥只存容器环境变量,页面不做配置
刷新状态
{{ it.name }}
{{ it.key_configured ? '已配置密钥' : '免费回退中' }}
{{ it.provider }}
{{ it.api_host }}
已配置 {{ it.key_masked }}(来源:{{ it.key_source }})
未配置,走免费回退(生产会被限流,建议配置)
{{ fmt(it.total) }}
{{ fmt(it.refreshed) }} / {{ fmt(it.pending) }} / {{ fmt(it.failed) }}
{{ it.last_run || '从未' }}
{{ it.task_label || '立即刷新全部' }}
{{ it.task_hint }}
配置 API 密钥(仅管理员)
保存密钥
清除
保存后立即生效并可点上方刷新验证;密钥掩码存储、绝不明文显示。到 rapidapi.com 订阅 tiktok-scraper7 免费档拿 key。
{{ s.name }}
{{ shopPrefixNote(ff.shop) }}
rvTab.fund_final = (v === 'review')">
资金月度账单表
待核对{{ rvBadge(ff.checks).n }}
月度账单(账期)
{{ o }} 账单
导出 xlsx
{{ s.name }}
{{ shopPrefixNote(fb.shop) }}
rvTab['fund_bills'] = (v === 'review')">
资金汇总表
待核对{{ rvBadge(fb.checks).n }}
选择方式
月度
季度
半年度
年度
{{ o.label }}
{{ fbScope }}
导出 xlsx
{{ s.name }}
{{ shopPrefixNote(tc.shop) }}
rvTab['fund_talent'] = (v === 'review')">
达人佣金表
待核对{{ rvBadge(tc.checks).n }}
月度账单(账期)
{{ o }} 账单
搜索
重置
导出 xlsx
{{ ({ pay_due: '订单实付应结', commission: '佣金' })[k] }}
{{ s.name }}
{{ shopPrefixNote(sf.shop) }}
rvTab['settle_final'] = (v === 'review')">
订单结算汇总表
待核对{{ rvBadge(sf.checks).n }}
账期
全部
{{ m }}
搜索
重置
导出 xlsx
{{ s.name }}
{{ dyfShopNote }}
{{ l }}{{ unmDomBadge('dy_cost').open }}{{ rvBadge(dyf.checks).n }}
账期
全部
{{ m }}
出单渠道
全部
{{ c }}
搜索
重置
导出 xlsx
{{ l }}{{ unmDomBadge('dy_cost').open }}{{ rvBadge(dyc.checks).n }}
账期
全部
{{ m }}
刷新
导出 xlsx
共 {{ dyc.total }} 单
有 {{ dyc.costDup.length }} 个订单在两个仓的成本报表里都出现了(已按登记顺序取第一个,未相加):
{{ dyc.costDup.slice(0,3).map(d => d[0] + '(' + d[1] + ' / ' + d[2] + ')').join('、') }}
{{ c[1] }}
这个店铺在该账期没有订单 —— 先到「订单自定义原始表」或「订单原始表」导入数据
rvTab[active] = (v === 'review')">
月度账单表
待核对{{ rvBadge(mf.checks).n }}
月度账单(账期)
{{ o }} 账单
导出 xlsx
选择方式
rvTab[active] = (v === 'review')">
汇总表
待核对{{ rvBadge(mfb.checks).n }}
月度
季度
半年度
年度
{{ o.label }}
{{ mfbScope }}
导出 xlsx
rvTab[active] = (v === 'review')">
订单结算汇总表
待核对{{ rvBadge(sr.checks).n }}
账期
全部
{{ m }}
搜索
重置
导出 xlsx
rvTab[active] = (v === 'review')">
{{ active==='wh_by_final' ? '百优仓成本汇总表' : '百侣仓成本汇总表' }}
待核对{{ rvBadge(wf.checks).n }}
发货月
全部
{{ m }}
{{ s }}
{{ w }}
搜索
重置
⚠ 缺成本价 {{ Object.keys(wf.unpriced).length }} 货品 · 去补录
导出 xlsx
{{ l }}{{ rvBadge(wff.checks).n }}{{ unmDomBadge('wh_freight').open }}
账单月
全部
{{ m }}
归属
全部
{{ m }}
{{ s }}
搜索
重置
导出
账单月
全部
{{ m }}
根据 界面运费最终表 按归属月统计
账单月
全部
{{ m }}
{{ s }}
导出
账单月
全部
{{ m }}
显示已归档({{ wfcArchivedN() }})
查看计费规则 →
① 费率核对(附件1:应收=首重3/3.6元,每+1kg加1元)
已核 {{ fmt(wfc.rate.checked) }}
通过 {{ fmt(wfc.rate.passed) }}
不符 {{ fmt(wfc.rate.mismatch_n) }}
无计费重量 {{ fmt(wfc.rate.no_weight_n) }}
已归档 {{ fmt(wfc.rate.archived_n) }}
零费行(免费补发)跳过 {{ wfc.rate.zero_fee }} 笔
|加收合计 {{ fmt(wfc.rate.surcharge.total) }} 元(每单 {{ wfc.rate.surcharge.std }} 元 × {{ fmt(wfc.rate.surcharge.std_rows) }} 单)
⚠ 加收异常 {{ wfc.rate.surcharge.odd_rows }} 单
判定无差异({{ wfcSelRows('rate').length }})
wfc.sel.rate = k">
标过,但数据后来变了 · {{ record.ack.note }}
—
撤销
判定无差异
✓ 全部通过(另有 {{ fmt(wfc.rate.archived_n) }} 条已人工归档)
② 异常费核对(退货/拦截/改地址:发时收多少,退就收多少)
已核 {{ fmt(wfc.refund.checked) }}
通过 {{ fmt(wfc.refund.passed) }}
金额不符 {{ fmt(wfc.refund.mismatch_n) }}
原发未找到 {{ fmt(wfc.refund.notfound_n) }}
已归档 {{ fmt(wfc.refund.archived_n) }}
未收费(0元)行跳过 {{ wfc.refund.zero_fee }} 笔
归档({{ wfcSelRows('refund').length }})
「原发运单未找到」的 {{ fmt(wfc.refund.notfound_n) }} 条:原发多半在更早的账期,
先看能不能补导那个月的运费账单(补完自动匹配、不用归档);确认拿不到那份账单,再归档并写明。
wfc.sel.refund = k">
标过,但数据后来变了 · {{ record.ack.note }}
—
撤销
归档
✓ 全部通过(另有 {{ fmt(wfc.refund.archived_n) }} 条已人工归档)
③ 我司快递核对(运费账单里的快递单号应能在订单原始表/样品表查到,查不到=跳出来手工核对)
已核运单 {{ fmt(wfc.ours.checked) }}
订单可认领 {{ fmt(wfc.ours.matched) }}
认领不到 {{ fmt(wfc.ours.miss_n) }}
已归档 {{ fmt(wfc.ours.archived_n) }}
(列表展示前 200 条)
归档({{ wfcSelRows('ours').length }})
wfc.sel.ours = k">
标过,但数据后来变了 · {{ record.ack.note }}
—
撤销
归档
✓ 账单全部运单都能在订单侧认领(另有 {{ fmt(wfc.ours.archived_n) }} 条已人工归档)
④ 已人工归档(谁标的 / 什么结论 / 现在还成不成立)
共 {{ fmt(wffAckList.rows.length) }} 条
数据变了、已失效 {{ wffAckList.stale }}
问题已自己消失 {{ wffAckList.gone }}
归档不改任何金额,只是让这一条不再跳出来;数据一变会自动重新提醒
{{ wfcKindCn((record.brief || {}).kind || String(record.oid || '').split('|')[0]) }}
仍成立
已失效
已无需
撤销
{{ wfcKindCn(wffAck.kind) }} · 共 {{ wffAck.rows.length }} 条
{{ r.waybill_no }}
{{ r.oid || '(无订单号)' }}
{{ r.why }}
…… 另有 {{ wffAck.rows.length - 20 }} 条
核对结论(必填)
归档之后这几条不再计入上面的不符计数,一分钱都不会变(三块核对本来就不参与金额计算)。
但只要这一条的数据变了(重量/金额/原发合计任一改变),归档会自动失效、重新提醒。
系统核对所依据的计费口径(与实际核对计算同一套参数)
参与费率核对的来源:{{ wfc.rules.couriers.join(' / ') }}
偏远地区:{{ wfc.rules.remote_provinces.join(' / ') }} = {{ wfc.rules.remote_rate }} 元/公斤
应收快递费阶梯(附件1;计费重量向上取整到公斤)
↑ 5 公斤以上以此类推:普通=公斤数+2 或 +2.6 元;偏远=公斤数 × {{ wfc.rules.remote_rate }} 元
{{ l }}{{ unmDomBadge('wh_product').open }}{{ rvBadge(wpf.checks).n }}
发货月
全部
{{ m }}
按产品
按店铺
未匹配 {{ fmt(wpfUnmatched.cost) }} 元(占 {{ wpfUnmatched.pct }}%)——不计入任何店铺,各店成本相应偏低 · 点此处理
⚠ 缺成本价 {{ Object.keys(wpf.unpriced).length }} 货品 · 去补录
导出 xlsx
{{ l }}{{ rvBadge(wwf.checks).n }}{{ unmDomBadge('wh_product').open }}
账期
{{ m }}
归属店铺
{{ sname }}
查询
导出 xlsx
账单月
{{ m }}
「税金 2%」只有 {{ (wwm.taxMonths || []).join('、') || '(没有)' }} 还有 ——
{{ wwm.taxedMonths.join('、') }} 起已折进逐单运费合计
镜像人工「月成本,快递费明细」表(理赔 {{ fmt(wwm.claim_n) }} 条)
新增人工项
导出 xlsx
「订单成本明细」的运费合计 = 运费 × (1 + 税率)。税率按月生效:
每一条运费账单行按它自己的账单月取「生效月 ≤ 该月」的最新一档。
没有档的月份税率就是 0(不是回落一个内置值)—— 2026-06 及更早刻意没有档,
那些月份已经对过账、逐格不变。
还没有任何一档 —— 所有月份的运费合计都不含税。
账单月
{{ m }}
口径勾稽:{{ wwr.data.bad ? ('异常 ' + wwr.data.bad + ' 项') : '应对平的项全部对平' }}
费率核对:相符 {{ fmt(wwc.data.rate_ok) }} / 不符 {{ fmt(wwc.data.rate_mis_rows) }} 行
服务费核对(≤1kg {{ wwc.data.svc[0] }} / 以上 {{ wwc.data.svc[1] }}):相符 {{ fmt(wwc.data.svc_ok) }} / 不符 {{ fmt(wwc.data.svc_mis_rows) }} 行
差额合计 {{ fmt(wwc.data.rate_diff) }} 元
差异只暴露不改数
① 口径勾稽
「应对平」的项差额必须为 0(≠0 即真错);「口径差」的项是设计上的差异,原因写在最后一列
② 费率卡核对差异(按原因汇总)
差额=账单实收−应收(负=少收);费率卡本身在「武汉(百侣)运费账单 → 地区运费规则」tab。
特殊价仅限 {{ (wwc.data.special_carriers || []).join(' / ') }} + 宁夏/青海,其余承运商一律按正常卡核——
宁夏/青海的其它承运商若按特殊价收费,即下表 R1
③ 差异明细(逐单 + 具体原因)
{{ s.reason }}({{ s.rows }} 行)
当前 {{ fmt(wwcDetRows.length) }} 行
页面仅显示前 {{ fmt(wwc.data.rows.length) }} 行 / 共 {{ fmt(wwc.data.rows_total) }} 行,完整清单请导出 xlsx
导出差异明细
{{ l }}
待核对{{ rvBadge(wgf.checks).n }}
账单月
{{ m }}
查询
导出 xlsx
{{ wgfReconTitle }}
新增调整项
编辑
删除
{{ l }}{{ rvBadge(wwb.checks).n }}{{ unmDomBadge('wh_product').open }}
选择方式
月度
季度
半年度
年度
{{ o.label }}
{{ wwbScope }}
新增人工项
导出 xlsx
rvTab[active] = (v === 'review')">
账期汇总表
待核对{{ rvBadge(byb.checks).n }}
选择方式
月度
季度
半年度
年度
{{ o.label }}
{{ bybScope }}
导出 xlsx
本次不产出「实际应付」——所选账期按自然月不连续,或区间最早那个账单月没有登记
「上月剩余应付」。结转是逐月的,断一期就推不出区间末的应付余额。详见「待核对」。
成本与运费各行不受影响,照常可用。
ⓘ {{ byb.costDiffNote }}
{{ l }}{{ unmDomBadge('by_product').open }}
待核对{{ rvBadge(by.checks).n }}
账单月
{{ m }}
{{ m }}
上月对比基准 {{ byp.prevMonth }}
查询
导出 xlsx
ⓘ {{ by.costDiffNote }}
有 {{ by.billOnly.orders }} 单({{ fmt(by.billOnly.cost) }} 元)只在「百优仓账单」里、
出库产品明细里没有——这部分钱既进不了「未匹配处理」tab(工作台按产品明细诊断),
也无法人工指定归属。请补导该账单月的「百优出库产品明细」,补完自动消。
编辑
删除
只看有差异
默认按 |差异| 降序,无上月数据的行排最后
{{ l }}{{ jmTodoN }}
待核对{{ rvBadge(jm.checks).n }}
账单月
{{ m }}
{ if (v) { jm.diff = ''; jm.flag = ''; } loadJm(1); }">
占位单号并组
编码不一致
多笔都记了
单价差
{{ k }}
暴露桶:{{ jm.flag }}
查询
导出 xlsx
{{ c[1] }}
{{ record.oid_show || record.oid }}
补发
已归 {{ record.home_period }} · {{ yuan(record.cost_home, 2) }}
{{ yuan(record.cost, 2) }}
需核对
{{ record.why }}
取整差
已核对
撤销
标记已失效
重新核对
标已核对
—
订单号
{{ jmAck.oid }}
{{ jmAck.why }}
核对结论(必填)
标记之后这一单不再计入「待核对」。
但只要这一单的数据变了(成因/结算金额/产品成本/流水行数任一改变),标记会自动失效、重新提醒。
刷新
🟡 补数据可消 {{ jmTodo.data.length }} 项
🔴 要改代码 {{ jmTodo.fix.length }} 单 / {{ fmt(jmTodo.fixAmt) }} 元
🟠 待人工核对 {{ jmTodo.ack.length }} 单 / {{ fmt(jmTodo.ackAmt) }} 元
已核对 …{{ (jmAckList.rows || []).length }} 条
核对结论存在独立的表里,重导账单不会丢;数据一变标记自动失效
🟡 补齐数据即可自动消(不需要人工处置)
补完下次打开这一页就没有了,不用回来点任何按钮
✓ 没有「补数据即可消」的项
🔴 要改代码才消得掉(人工标记对它无效)
这几单我们确实把成本算少了,出路是先定口径再改算法 ——
所以这一区没有「标已核对」按钮:标掉只是把一个真的少算永久静音
✓ 没有这一类
🟠 要人看一眼给个结论
我们算的是对的(或只是人工侧与价目表对不上)——
看过之后写下结论,这一条就不再提醒;数据后来变了会自动重新亮起来
标已核对
—
✓ 没有待人工核对的单
⚪ 已人工核对(谁标的 · 什么结论 · 还成不成立)
三个月后要答得出这一单当初为什么被放过
生效中
已失效
问题已消失
撤销
—
还没有人工核对过任何一单
库存最终表
待补成本价
({{ vnwf.pending.length }})
待核对{{ rvBadge(vnwf.checks).n }}
账期
{{ m }}
查询
{{ vnwf.pending.length }} 行待补成本价 · 货值少算 ≥{{ fmt(vnwf.pending_miss) }} 元(另 {{ vnwf.pending_unknown }} 行估不出) · 点此补录
⚠ SKU 白名单未维护 · 当前显示全部
共 {{ (vnwf.rows || []).length }} 行 · 货值合计 {{ fmt(vnwf.sum_value) }} 元
导出 xlsx
期初
入库
出库
退库
调整
新增次品
期末
成本价
货值
{{
record.cost_src === '待补' ? '—' : record.cost }}
{{ record.cost_src }}
库存最终表
待补成本价
({{ vnwf.pending.length }})
这些行系统算不出成本价(只能人工给),暂时货值按 0 计 —
页面总货值偏低,少算 ≥{{ fmt(vnwf.pending_miss) }} 元(另 {{ vnwf.pending_unknown }} 行连参考价都没有、估不出,
所以真实缺口只会更大)。
价从哪来 / 怎么填
导出待补清单
{{ l }}{{ unmDomBadge('tkvn_settle').open }}
待核对{{ rvBadge(tk.checks).n }}
账期
{{ m }}
汇率 {{ Number(tk.rate.rate).toFixed(2) }}({{ tk.rate.from }} ~ {{ tk.rate.to }} 的 {{ tk.rate.n }} 笔付款平均)
汇率算不出({{ tk.rate && tk.rate.from ? tk.rate.from + ' ~ ' + tk.rate.to : '本账期' }} 窗口内没有付款/钱包记录)
仅订单仅样品
{{ x }}
只看需核对
| 项 目 | 行次 |
{{ (tk.period||'').slice(5) }}月 | 公式 / 取数说明 |
| {{ r.label }} |
{{ r.no || '' }} |
tkSetInput(r.item, v)">
{{ tkNum(r.value, r.unit) }}
|
|
| 项 目 |
订单(越南盾) | 订单(人民币) |
样品(越南盾) | 样品(人民币) |
合计(越南盾) | 合计(人民币) |
| {{ r.label }}
|
{{ r.vnd ? tkInt(r.o) : tkNum(r.o) }} |
{{ tkNum(r.oc) }} |
{{ r.vnd ? tkInt(r.s) : tkNum(r.s) }} |
{{ tkNum(r.sc) }} |
{{ r.vnd ? tkInt(r.a) : tkNum(r.a) }} |
{{ tkNum(r.ac) }} |
| 科目 \ 结算月 |
{{ m }}(₫) | {{ m }}(元) |
合计(₫) | 合计(元) |
| {{ r.label }}
|
{{ tkInt((r.cells[m]||{}).vnd) }} |
{{ tkNum((r.cells[m]||{}).cny) }} |
{{ tkInt(r.total.vnd) }} |
{{ tkNum(r.total.cny) }} |
{{ l }}{{ unmDomBadge('tkth_settle').open }}
待核对{{ rvBadge(tth.checks).n }}
账期
{{ m }}
汇率 {{ Number(tth.rate.rate).toFixed(2) }}({{ tth.rate.from }} ~ {{ tth.rate.to }} 的 {{ tth.rate.n }} 笔付款平均)
汇率算不出({{ tth.rate && tth.rate.from ? tth.rate.from + ' ~ ' + tth.rate.to : '本账期' }} 窗口内没有付款/钱包记录)
仅订单仅样品
{{ x }}
只看需核对
| 项 目 | 行次 |
{{ (tth.period||'').slice(5) }}月 | 公式 / 取数说明 |
| {{ r.label }} |
{{ r.no || '' }} |
tthSetInput(r.item, v)">
{{ tthNum(r.value, r.unit) }}
|
|
| 项 目 |
订单(泰铢) | 订单(人民币) |
样品(泰铢) | 样品(人民币) |
合计(泰铢) | 合计(人民币) |
| {{ r.label }}
|
{{ tthNum(r.o) }} |
{{ tthNum(r.oc) }} |
{{ tthNum(r.s) }} |
{{ tthNum(r.sc) }} |
{{ tthNum(r.a) }} |
{{ tthNum(r.ac) }} |
| 科目 \ 结算月 |
{{ m }}(฿) | {{ m }}(元) |
合计(฿) | 合计(元) |
| {{ r.label }}
|
{{ tthNum((r.cells[m]||{}).thb) }} |
{{ tthNum((r.cells[m]||{}).cny) }} |
{{ tthNum(r.total.thb) }} |
{{ tthNum(r.total.cny) }} |
{{ l }}{{ rvBadge(sp.pl.checks).n }}
账期
{{ m }}
汇率 {{ (sp.rate.rate_mil||0)/1000 }}(越南盾 ÷ 汇率 = 人民币)
未登记 · 用内置期初值
({{ sp.rate.eff_month }} 起生效那一档)
{{ spChkText(sp.pl.checks) }}
| 项 目 | 行次 |
{{ (sp.period||'').slice(5) }}月 | 公式 / 取数说明 |
| {{ r.label }} |
{{ r.no || '' }} |
spSetInput(r.item, v)">
{{ spNum(r.value, r.unit) }}
|
|
账期
{{ m }}
共 {{ sp.settle.n }} 行结算明细,按【订单成立时间】归月(本表天然跨月,账期只用于取汇率档)
汇率 {{ (sp.rate.rate_mil||0)/1000 }}
⚠ 各组成项之和与「拨款金额」差
{{ spInt(sp.settle.alloc_check.diff) }} ₫。{{ sp.settle.alloc_check.why }}
详见「待核对」
| 项目 |
{{ (m||'').slice(5) }}月 |
合计 |
公式 / 取数说明 |
| 越南盾 | 金额 |
越南盾 | 金额 |
| {{ r.label }} |
{{ spInt((r.cells[m]||{}).vnd) }} |
{{ spNum((r.cells[m]||{}).cny) }} |
{{ spInt(r.total.vnd) }} |
{{ spNum(r.total.cny) }} |
|
账期
{{ m }}
汇率 {{ (sp.rate.rate_mil||0)/1000 }}
| 项目 | 越南盾 |
金额 | 公式 / 取数说明 |
| {{ r.label }}派生 |
{{ spInt(r.vnd) }} | {{ spNum(r.cny) }} |
|
| {{ sp.ads.total.label }} |
{{ spInt(sp.ads.total.vnd) }} |
{{ spNum(sp.ads.total.cny) }} |
|
账期
{{ m }}
下单日期
{{ o }}
只看需核对
{{ spChkText(sp.final.checks) }}
成本口径差额 {{ spNum(sp.final.cost_diff.diff) }} 元
—— 本表成本合计 {{ spNum(sp.final.cost_diff.order_side) }},总表「产品成本」{{ spNum(sp.final.cost_diff.pl_side) }}。
{{ sp.final.cost_diff.note }}
|
{{ c[1] }}
|
| {{ (r.order_time||'').slice(0,16) }} |
{{ r.oid }} | {{ r.status_cn }} |
{{ spInt(r.sale) }} |
{{ spNum(r.cost) }} |
{{ r.paid == null ? '——' : spInt(r.paid) }} |
{{ r.alloc == null ? '——' : spInt(r.alloc) }} |
需人工核对
已核对
spSetNote(r.oid, v)">
{{ r.note }}
确认已核对
撤销
|
按月生效:算某个账期时取「生效月 ≤ 该账期」的最新一档;留空=期初档,对所有更早的月生效
| 生效月 | 汇率 |
备注 | 最后修改 |
操作 |
| {{ r.eff_month || '期初档' }} | {{ (r.rate_mil||0)/1000 }} |
{{ r.note }} | {{ r.updated_at }} |
删除 |
|
还没登记任何一档,当前用的是内置期初值 {{ (sp.fx.seed_mil||0)/1000 }} |
{{ l }}{{ vf.unm.length || (vf.unmatched.ret_n + vf.unmatched.out_n) }}{{ rvBadge(vf.checks).n }}
账期
{{ m }}
当期单价
快递 {{ (vf.rate.ship_mil||0)/1000 }} 元/单 · 大货物流 {{ (vf.rate.bulk_mil||0)/1000 }} 元/件 · 包装盒 {{ (vf.rate.box_mil||0)/1000 }} 元/单
未登记 · 用内置期初值
({{ vf.rate.eff_month }} 起生效那一档)
⚠ 挂账 {{ vf.unmatched.ret_n + vf.unmatched.out_n }} 单 · 未计入任何店铺
⚠ {{ vf.conflicts.length }} 条人工归属与订单表不一致
人工录入{{ vf.adjRows.length }}
改单价
导出 xlsx
退货快递费
出库快递费
退货数量
退货金额
出库数量
出库金额
大货物流费
包装盒
{{ record.cost }}
{{ record.cost_src }}
为什么
{{ record.sku_id }} 这个月出库 {{ fmt(Math.abs(record.out_qty)) }} 件,
系统算不出它的成本价:
{{ record.why }}
三个单价按月生效:算某个账期时取「生效月 ≤ 该账期」的最新一档,留空的那档=期初档
费率表还没有任何一档,当前报表用的是代码内置的期初值
(快递 {{ (vf.rateSeed.ship_mil||0)/1000 }} / 大货物流 {{ (vf.rateSeed.bulk_mil||0)/1000 }} / 包装盒 {{ (vf.rateSeed.box_mil||0)/1000 }} 元)。
登记一档就会接管它。
账期
{{ m }}
这些行认领不到归属 —— 钱挂账、不计入任何店铺与月份。
先去导对应月份的订单原始表即可自动消掉;确实补不到的才在这里人工指定。
线上单号为空的行不在这里——它们已按规则计入「{{ vf.emptyOidShop || 'tiktok' }}·当期」。
共 {{ vf.unmTotal }} 行 · 只列出前 {{ vf.unmMax }} 行
{{ record.cause==='need_order' ? '补数据即可消' : '只能人工指定' }}
{{ record.cause_label }}
指定归属
已人工指定(撤销也会留痕 —— 硬删后无法解释成本为何变过又变回来)
撤销
{{ l }}({{ thwf.pending.length }})({{ thwf.unlisted.n }}){{ rvBadge(thwf.checks).n }}
账期
{{ m }}
查询
{{ thwf.pending.length }} 行待补成本价 · 这些行货值按 0 计、总货值偏低 · 点此补录
⚠ SKU 白名单未维护 · 当前显示全部
⚠ {{ thwf.unlisted.n }} 个编码有流水却不在册 —— 它们的货(合计 {{ thwf.unlisted.end_qty }} 件 /
约 {{ fmt(thwf.unlisted.value_c4 / 10000) }} 元)不在本表里 · 点此处置
共 {{ (thwf.rows || []).length }} 行 · 货值合计 {{ fmt(thwf.total) }} 元
导出 xlsx
期初
入库
出库
退库
次品
期末
成本价
货值
待补
{{ fmt(record.cost) }}
—
{{ fmt(record.value) }}
{{ l }}({{ thwf.pending.length }})({{ thwf.unlisted.n }})
这些行成本价取不到,货值按 0 计、总货值偏低;到「SKU 与成本价」页补录即可消(口径见页头 ⓘ)
导出待补清单
将影响 {{ thwPfx.nos.length }} 个出库单号下的所有行。
按单号整单改 —— 同一单的多行是一次出库的不同货品,劈到两个月的话报表上看不出来。
会把这些单号的账期还原成按「创建时间」自动推导的那个月,并清掉「人工」标记。
只作用于改过的行;没改过的不受影响。
账期决定这批货算进哪个月的报表,改动会记进操作日志;改过的行在账期列上带「人工」标记,
原推导值留着、随时可改回。
{{ l }}({{ thwf.pending.length }})({{ thwf.unlisted.n }})
账期
{{ m }}
查询
这些编码有流水却不在册,本表数量按选定账期算(口径见页头 ⓘ)
共 {{ (thwUnl.rows || []).length }} 个 · 期末 {{ thwUnl.end_qty }} 件 ·
约 {{ fmt(thwUnl.value_c4 / 10000) }} 元
加入在册({{ thwUnl.sel.length }})
清空选择
待补
{{ fmt(record.cost_c4 / 10000) }}
—
{{ fmt(record.value_c4 / 10000) }}
加入在册
{{ l }}{{ thwfee.pend.length }}{{ rvBadge(thwfee.checks).n }}
账期
{{ m }}
当期单价
退货快递 {{ (thwfee.rate.ret_ship_mil||0)/1000 }} · 出库快递 {{ (thwfee.rate.ship_mil||0)/1000 }} 元/单 · 大货物流 {{ (thwfee.rate.bulk_mil||0)/1000 }} 元/件 · 包装盒 {{ (thwfee.rate.box_mil||0)/1000 }} 元/单
未登记 · 用内置期初值
({{ thwfee.rate.eff_month }} 起生效那一档)
⚠ 挂账 {{ thwfee.unmatched.orders }} 单 / {{ thwfee.unmatched.qty }} 件 · 未计入任何店铺与月份
店铺
下单月
退货单数
退货快递费
出库单数
出库单数补差
出库单数补差金额
出库快递费
退货数量
退货金额
出库数量
出库金额
大货物流费
包装盒
{{ record.out_orders_adj ? (record.out_orders_adj > 0 ? '+' : '') + record.out_orders_adj : '—' }}
补差超出,按 0 计费
{{ record.out_note }}
{{ record.out_orders_adj_c4 ? fmt(record.out_orders_adj_c4 / 10000) : '—' }}
{{ record.out_amt_note }}
账期
{{ m }}
查询
按产品
按店铺
共 {{ (thwProdViewRows || []).length }} 行
导出 xlsx
{{ thwfee.prodDim === 'shop' ? '归属店铺' : '店铺' }}
单价(含税)
来源
销售出库数量
销售出库金额
退货入库数量
退货入库金额
待补
{{ fmt(record.cost_c4 / 10000) }}
账期
{{ m }}
按面单打印时间统计每天的去重订单数(口径见页头 ⓘ)
共 {{ (thwfee.daily.rows || []).length }} 天 · 合计 {{ thwfee.daily.total }} 单
导出 xlsx
四个单价按月生效:取「生效月 ≤ 该账期」的最新一档,留空那档=期初档;改价不影响历史月份
{{ record.eff_month }}
(期初档)
{{ (record.ret_ship_mil / 1000).toFixed(3) }}
{{ (record.ship_mil / 1000).toFixed(3) }}
{{ (record.bulk_mil / 1000).toFixed(3) }}
{{ (record.box_mil / 1000).toFixed(3) }}
费率表还没有任何一档,当前报表用的是代码内置的期初值
(退货快递 {{ (thwfee.rateSeed.ret_ship_mil||0)/1000 }} / 出库快递 {{ (thwfee.rateSeed.ship_mil||0)/1000 }} /
大货物流 {{ (thwfee.rateSeed.bulk_mil||0)/1000 }} / 包装盒 {{ (thwfee.rateSeed.box_mil||0)/1000 }} 元)。
登记一档就会接管它。
账期
{{ m }}
发货订单原始表
出入库明细原始表
补导订单表即可消
只能人工指定
查询
关键词超过 {{ thwfee.kwMax }} 个,只用了前 {{ thwfee.kwMax }} 个
共 {{ (thwfee.pend || []).length }} 行 · 挂账不计入任何店铺与月份
批量指定归属({{ thwfee.sel.length }})
清空选择
导出 xlsx
(空 · 按备注归属)
{{ record.match_key }}
(单号与备注都空)
{{ record.why === 'need_order' ? '补导订单表即可消' : '只能人工指定' }}
{{ fmt(record.amt_c4 / 10000) }}
指定归属
已指定的归属(人工改动台账)
这是本页唯一「人可以直接改报表数字」的通道;标可删除的说明补齐数据后已能自动匹配,覆盖已多余
{{ bjTime(record.at) }}
生效中
可删除
撤销
还没有人工指定过归属
在册
已摘掉
只看待补成本价
查询
新增
共 {{ (thwsku.rows || []).length }} 个商品编码
导出 xlsx
未录
{{ record.begin }}
待补
{{ fmt(record.cost / 10000) }}
—
{{ m.period }}:{{ fmt(m.cost / 10000) }} 次品 {{ m.defect }}
{{ record.active ? '在册' : '已摘掉' }}
编辑
删除
摘掉后报表不再出这个品(不硬删,留痕可查)
这三笔都是人工填的,系统算不出来。填在「店铺 × 订单月」这一个格子上。
退货快递费=退货单数 × 单价。它不是流水里的退货行数:流水记的是"退回来的货品",
单数记的是"退回来的包裹",一个包裹可能装多个货品。
出库单数是系统按【泰国仓发货订单原始表·订单编号】去重数出来的,这一格是补它的差,
可正可负;出库快递费与包装盒都按「出库单数 + 补差」算。
这一笔只做登记:不进出库快递费、不进包装盒、不进大货物流费,
也不进「TikTok 泰国财务报表」的成本四项。表格底部那一行是本列自己的合计。
来源:{{ thwfee.attrRow.src_label }} ·
这一行没有单号,按备注原文归属
单号:{{ thwfee.attrRow.match_key || '(空)' }}
备注:{{ thwfee.attrRow.note }}
这个键下面还有别的行:本次会一起归走 {{ thwAttrImpact.n }} 行 ·
{{ thwAttrImpact.qty }} 件 · {{ fmt(thwAttrImpact.amt) }} 元。
清单按商品编码分行显示各自的数量金额,而归属键是共用的。
选中 {{ (thwfee.sel || []).length }} 行,去重后 {{ thwAttrBatchPairs.length }} 个归属键
(同一个键的多行共用一条归属;没有单号的行按备注原文成键,
所以备注相同的几行会一起归走)。这批会一起归到下面填的店铺与月份,理由也共用一条。
其中 {{ thwAttrBatchNeedOrder }} 个是「补导订单表即可消」——
它们的正解是把那个月的订单原始表导进来,补完会自动归位。
人工指定会把它们永久钉在你填的这个月上,之后订单表补齐了也不会自动纠正。
{{ s }}
搜索
只看待补成本价的
({{ vnws.need_n }})
共 {{ (vnws.rows || []).length }} 行
新增
库存最终表
期初库存
待核对{{ rvBadge(invf.checks).n }}
账期
{{ m }}
⚠ 本月有 {{ invf.unlisted_n }} 个未登记货品发生流水 · 不计入本表 · 点此去登记
⚠ {{ invf.negative.length }} 个货品期末为负 —— 缺期初库存或缺入库单据
共 {{ (invf.rows || []).length }} 个货品
导出 xlsx
期初
{{ invColTitle(0) }}
{{ invColTitle(1) }}
{{ invColTitle(2) }}
{{ invColTitle(3) }}
{{ invColTitle(4) }}
{{ invColTitle(5) }}
{{ invColTitle(6) }}
期末
{{ l }}{{ unmDomBadge(unm.domain).open }}{{ rvBadge(wff.checks).n }}{{ rvBadge(wwf.checks).n }}{{ rvBadge(wwb.checks).n }}
待核对{{ rvBadge(by.checks).n }}
处置类型
{{ unmDomLabel(d) }}{{ unmDomBadge(d).open }}
刷新
🟡 补数据可消 …{{ unmNeedData.length }} 组 / {{ fmt(unmD.data_cost || 0) }} {{ unmUnit }}
🔴 需人工 …{{ fmt(unmD.total_rows || 0) }} 行 / {{ fmt(unmMoneyTotal) }} {{ unmUnit }}
已指定归属 …{{ unm.overrides.length }} 条
🆕 较上次查看新增 {{ unm.newSince }} 条
指定的归属存在独立的覆盖表里,重导账单不会丢
🟡 补齐订单数据即可自动消(不需要人工归属)
本类不存在「补数据即可消」——
平台只给了总费用、没拆任何费用明细,补多少数据、加多少列都补不上,
只能在下面那一区人工指定归属科目
按订单号号段与订单表已覆盖的范围比 ——
TikTok 订单号内含时间戳、单调递增,故能逐单指名该补哪个方向的月份;
号段落在已覆盖区间内却查不到的,补文件也消不掉,会归到下面那一区
按「品名的历史归属店铺」+「该店订单表已覆盖的下单月」推断该补什么——
订单表按下单月归档,故这里比对的是下单月而不是发货月
另有 {{ fmt(unmD.truncated_data) }} 组未展示(导出 xlsx 可看全量;处理完本批刷新即补上)
—— 本类没有这一档
✓ 没有「补数据即可消」的未匹配
🟠 兜底归属复核(系统按商品名猜的,可改)
{{ fmt(unmD.review_rows || 0) }} 单 / {{ fmt(unmD.review_cost || 0) }} {{ unmUnit }}
备注只说「样品/寄样」而商品名认不出任何品牌 → 已按兜底店铺计入。
这部分钱已经算进某家店了(不是挂账),所以不计入上面的未匹配金额;不对就在这里改。
已选 {{ unm.rvSel.length }} 单
批量指定归属
另有 {{ fmt(unmD.review_truncated) }} 单未展示(处理完本批刷新即补上)
unm.rvSel = k">
🔴 需人工指定归属
共 …{{ unmFiltered ? '已筛出' : '共' }} {{ fmt(unmManualRows.length) }} 行 / {{ fmt(unmFilteredTotal) }} {{ unmUnit }}
清空列筛选
全选{{ unmFiltered ? '筛选结果' : '' }}({{ fmt(unmManualRows.length) }} 行)
已选 {{ unm.sel.length }} 行
批量指定归属
导出 xlsx
另有 {{ fmt(unmD.truncated) }} 行未展示(导出 xlsx 可看全量;处理完本批刷新即补上)
各列表头 ⏷ 可筛选/排序(筛选与排序在已加载的前 500 单内进行);改筛选会清空已勾选,防隐藏行被一并归属
{{ c.cause }}:{{ fmt(c.rows) }} 行 / {{ fmt(c.fee != null ? c.fee : c.cost) }} {{ unmUnit }}
unm.sel = k">
当前列筛选下没有行——
清空列筛选(共 {{ fmt((unmD.need_manual || []).length) }} 行)
✓ 没有需要人工归属的行
已指定的归属(人工改动台账)
这是系统里唯一「人可以直接改报表数字」的通道——金额影响列记录每次改动挪动了多少钱;
标「可删除」的说明补了数据后已能自动匹配,覆盖已多余
撤销
—
尚未指定过任何归属
消除历史(最近 {{ unm.ledger.resolved.length }} 条)
未匹配台账:每单记首次出现时间与消除方式(补数据自动消 / 人工指定 / 原始行删除)——
成本"变过又变回来"从此有痕可查;侧栏徽标与挂账天数同源于此
人工改钱台账 · 共 {{ alog.total }} 条
凡是人直接改过报表数字的都在这儿:人工指定归属、手工调整行、改成本价、改快递费率。
每条记「谁、什么时候、把什么改成了什么、理由、影响多少钱」
{{ s.label }}({{ s.n }})
查询
🩺 匹配健康度
核对中(需遍历各店守恒/成本表交叉核对,稍候)…
🩺 匹配健康度
异常 {{ unm.health.bad }}
警告 {{ unm.health.warn }}
计算于 {{ unm.health.age_sec > 60 ? Math.floor(unm.health.age_sec / 60) + ' 分钟前' : '刚刚' }}
({{ unm.health.computed_at }})
重算
系统里还有一批跨表匹配,失配后会自动降级换个规则继续算、页面上看不出来;这里把它们的实况列出来
导入台账
「未匹配」最常见的上游成因是该月订单表被静默剔了一半——
归属过滤剔除、零额守卫拦截、覆盖重导清库此前只在弹窗闪一次,现在留档可查
刷新台账
暂无导入记录(本功能 2026-07-26 上线,只记录此后的导入)
同一货品调价 = 新增一条「生效起始月」记录,旧月份自动沿用旧价;三张成本报表实时按发货月取档(发货时间空回退下单月)
新增价格
看哪天的价
{{ m || '期初档' }}
{{ rt.blocks.length }} 个价格方案
{{ rt.changed }} 个价格在这天之后还改过
新增方案
快递公司
{{ rt.asOfApplied || '期初' }} 当天生效的价格
重量向上取整;超 3kg 按 首重 + 续重 × 重量取整 算。
点表头=配这个方案管哪些快递、哪些省;点价格=改价 / 看历史。
· 角标 = 这个格子有历史价。灰格 = 该省不在这个方案里。
{{ b.name }} 配置
服务费规则
与省份无关,只看重量;核对时逐单比对账单「服务费」列
{{ x.label }}:{{ x.price == null ? '未配置' : fmt(x.price) }}
({{ x.eff_date || '期初' }} 起)
{{ rt.quote ? (rt.quote.ok ? ('¥' + fmt(rt.quote.total)) : '缺价') : '—' }}
{{ (rt.quote || {}).why || '选好四项自动试算' }}
{{ t.n }} {{ tal.stats[t.k] || 0 }}
查询
清空
排名达人主营类目带货商家 / TOP 商品
粉丝 · 信用分
近 7 天结算近 30 天结算件单价
判定
{{ r.rank || ((tal.page-1)*tal.size + i + 1) }}
{{ (r.nickname || '?').slice(0,1) }}
{{ r.nickname || '—' }}
{{ r.shop_name }}
抖音号 {{ r.aweme_id }} ↗
{{
r.inst_name }}
{{ tal.srcCn[k] || k }}
{{ lb.t }}
{{ c }}
—
{{ sp.name }}
—
{{ talNum(r.fans) }}
{{ r.level || '—' }} · 信用 {{ r.credit }}
{{ talMetricCn(r, 'gmv7') || (r.gmv_set ? talGmv(r.gmv7_lo, r.gmv7_hi) : '—') }}
{{ talGmvNote(r, r.gmv7_cnt, 'gmv7') }}
{{ talMetricCn(r, 'gmv30') || (r.gmv_set ? talGmv(r.gmv30_lo, r.gmv30_hi) : '—') }}
{{ talStale(r) }}
{{ talGmvNote(r, r.gmv30_cnt, 'gmv30') }}
{{ r.price_bin || '—' }}
区间
{{ talVdCn(r) }}
查看原因/规则
重新查询
没有符合筛选条件的达人。清掉筛选
这一天还没取过数。
去「取数配置」建一条规则,或点右上「立即取一次」(一条规则约 40 秒)。
这一天没有人达标 —— 口径是近 7 天 > {{ tal.th && tal.th.d7 }}
且 近 30 天 > {{ tal.th && tal.th.d30 }},两个都取区间下界。
这一天没有人达标。全部店铺按各自阈值独立判定。
这一天没有剔掉的店播号。判据是昵称命中「店播词库」里的词。
当前业务筛选没有达人。
{{ talShopUnlinked }} 个还没关联精选联盟
去配置店铺 →
{{
talCfgWindowReady ? '取数窗口已就绪' : '取数窗口未就绪' }}
{{ talBridgeCn }}
查看补数日清
查看补数记录
自动更新
每天 {{ tal.cfg.cronAt || '—' }} 各取一份
已停用 —— 今晚一条规则都不会取,只能手动点「取一次」
加一条取数规则
按店判定与刷新店播词库也按店独立
{{ sh.label }}配置 v{{ (tal.cfg.configs[String(sh.id)]||{}).revision || 1 }}
{{ tal.cfg.configs[String(sh.id)].explanation.logic }}
来源取数{{ tal.cfg.configs[String(sh.id)].stages.run.status==='ok' ? '完成' : '未完成' }};
整链完成
整链待补 {{ tal.cfg.configs[String(sh.id)].stages.facts.pending }} 人详情 / 已建联快照
{{ tal.cfg.configs[String(sh.id)].stages.run.why }}
插件定点唤起
每天 {{ (tal.cfg.wakeByShop[String(sh.id)]||{}).wake_at }} 唤起一次
未启用(不会自动打开该店浏览器)
上次:{{ talWakeStateCn((tal.cfg.wakeByShop[String(sh.id)]||{}).last_state) }}{{ (tal.cfg.wakeByShop[String(sh.id)]||{}).last_message ? ' · ' + (tal.cfg.wakeByShop[String(sh.id)]||{}).last_message : '' }}
说明
{{ tal.cfg.configs[String(sh.id)].explanation.fresh }}
{{ tal.cfg.configs[String(sh.id)].explanation.word }}
{{ tal.cfg.configs[String(sh.id)].explanation.coop }}
{{ tal.cfg.configs[String(sh.id)].explanation.source }}
编辑阈值 / 刷新期
立即唤起插件
确认罗盘店铺 · {{ cand.worker_fingerprint }}
取数规则店铺状态
上次取数上次入库
{{ c.content_label }}{{ c.sub_labels ? '(' + c.sub_labels + ')' : '(全部)' }}
· {{ talPotCn(c.potential_label) }}
{{ c.compass_browser_ok ? '罗盘浏览器' : c.acct_name }}{{ c.sub_labels ? ' · ' + c.sub_labels : '' }}
{{ c.shop_name || '未指定' }}
取数窗口已就绪
取数窗口未就绪
{{ talCfgCn(c) }}
状态:{{ talCompassStateCn(c) }}
{{ c.last_run_at ? c.last_run_at.slice(5, 16) : '—' }}
{{ c.last_status === 'ok' ? (c.last_n + ' 人') : '—' }}
取一次
{{ c.is_active ? '停用' : '启用' }}
删除
这家店还没有取数规则。点右上「加一条取数规则」。
有 {{ talCfgFailed.length }} 条规则上次没取成:
{{ c.content_label }} · {{ talPotCn(c.potential_label) }} · {{ c.acct_name }}{{ c.last_err || '(没记原因)' }}
{{ sh.label }}
内容标签
{{ talFilterAt }}
{{
talFilterTree.length ? '重取' : '取一次' }}
{{ c.display }}
子标签
罗盘那边一个不选是 0 条,不是全部 ——
2026-08-29 实测:留空 0 人、只给大类名 0 人、给三个子标签 400 人。
所以默认全勾。
这几个是从罗盘缓存的({{
talFilterAt || '还没取过' }})—— 每天取数时顺手更一遍,
也可以点上面「重取」当场取。
i
全选
{{ x }}
这个大类下没读到子标签 —— 多半是缓存那份过期了,点上面「重取」。
{{ cn }}
选两个在罗盘那边会变成「且」而且不报错 —— 要「或」就建两条规则
{{ tal.cfg.policy.shop_name }}
保存只用已经落库的结算额与联系方式重判,不会重新请求平台;未取到或过期仍是等待事实。
每日精选任务:
{{ t.shop_name }} · {{ talDailyTaskCn(t) }}
重试
{{ t.n }} {{ tal.stats[t.k] || 0 }}
查询
清空
排名达人主营类目带货商家 / TOP 商品
粉丝 · 信用分
近 7 天结算近 30 天结算件单价
判定
上榜日期操作
{{ r.rank || ((tal.page-1)*tal.size + i + 1) }}
{{ (r.nickname || '?').slice(0,1) }}
{{ r.nickname || '—' }}
{{ r.shop_name }}
抖音号 {{ r.aweme_id }} ↗
{{
r.inst_name }}
{{ tal.srcCn[k] || k }}
{{ lb.t }}
{{ c }}
—
{{ sp.name }}
—
{{ talNum(r.fans) }}
{{ r.level || '—' }} · 信用 {{ r.credit }}
{{ talMetricCn(r, 'gmv7') || (r.gmv_set ? talGmv(r.gmv7_lo, r.gmv7_hi) : '—') }}
{{ talGmvNote(r, r.gmv7_cnt, 'gmv7') }}
{{ talMetricCn(r, 'gmv30') || (r.gmv_set ? talGmv(r.gmv30_lo, r.gmv30_hi) : '—') }}
{{ talStale(r) }}
{{ talGmvNote(r, r.gmv30_cnt, 'gmv30') }}
{{ r.price_bin || '—' }}
区间
{{ talVdCn(r) }}
查看原因/规则
{{ r.first_pass || '—' }}
进库 {{ r.first_seen.slice(5) }}
重新查询
恢复
—
没有符合筛选条件的达人。清掉筛选
这一天还没取过数 —— 名单是从「短视频达人库」那一批人里判出来的。
去「取数配置」建一条规则,或在达人库点「立即取一次」。
这一天没有人达标 —— 口径是近 7 天 > {{ tal.th && tal.th.d7 }}
且 近 30 天 > {{ tal.th && tal.th.d30 }},两个都取区间下界。
换个快照日、或到「全部名单」看看差多少。
这一天没有人达标。全部店铺按各自阈值独立判定。
当前业务筛选没有达人。
待建联 {{ tal.pk.stats.open || 0 }}
已移出 {{ tal.pk.stats.closed || 0 }}
达人主营类目带货商家 / TOP 商品
粉丝 · 信用分近 7 天结算近 30 天结算
件单价标记于平台状态操作
{{ (r.nickname || '?').slice(0,1) }}
{{ r.nickname || '—' }}
{{ r.shop_name }}
抖音号 {{ r.aweme_id }} ↗
{{ r.inst_name }}
{{ lb.t }}
{{ c }}
—
{{ sp.name }}
—
{{ talNum(r.fans) }}
{{ r.level || '—' }} · 信用 {{ r.credit }}
{{ talMetricCn(r, 'gmv7') || (r.gmv_set ? talGmv(r.gmv7_lo, r.gmv7_hi) : '—') }}
{{ talGmvNote(r, r.gmv7_cnt, 'gmv7') }}
{{ talMetricCn(r, 'gmv30') || (r.gmv_set ? talGmv(r.gmv30_lo, r.gmv30_hi) : '—') }}
{{ talStale(r) }}
{{ talGmvNote(r, r.gmv30_cnt, 'gmv30') }}
{{ r.price_bin || '—' }}
区间
{{ (r.picked_at || '').slice(5, 10) }} {{ (r.picked_at || '').slice(11, 16) }}
{{ talPickBy(r.picked_by) }}
{{ tal.pk.platCn[r.plat_state] || '无变化' }}
{{ r.closed_at ? (r.closed_at.slice(5,10) + ' ' + r.closed_why) : (r.plat_at.slice(5,10) + ' 检出') }}
精选联盟 ↗
抖音 ↗
移出
正在加载待建联清单…
没有符合当前筛选条件的达人。清掉筛选
还没有移出过任何人 —— 在「待建联」那一档点「移出」的会落到这里。
还没有人 —— 达标的人会自动进这里(已经在精选联盟建过联的先剔掉)。
达标名单那边如果也是空的,先去「取数配置」跑一轮。
历史详情补数日清{{ tal.ops.business_date || '当前业务日' }} · 已排程才占用额度;“唤起”与“已完成查询”不是同一件事刷新
本日剩余额度 = 每日上限 − 今日已占;尚可安全新排 = min(本日剩余额度,尚未入队的安全候选)。两者必须分开看:额度还有但没有安全对象时,系统不会为了凑满额度重查失败或终态达人。
正在加载今日额度与回执…
没有可展示的启用店铺。
{{ s.shop_name }}{{ talOpsStateCn(s) }}
本日剩余额度{{ s.remaining_today }}/ 每日上限 {{ s.daily_limit }}
{{ talOpsCapacityWhy(s) }}
{{ s.scheduled_today }}今日已占
{{ s.pending_today }}待查询
{{ s.succeeded_today }}已完成
{{ s.failed_today }}失败
恢复回执:{{ s.resume_state }}{{ s.resume_message ? ' · ' + s.resume_message : '' }}
级别店铺当前告警下一步
{{ a.severity }}{{ talOpsShopName(a.shop_id) }}{{ a.message }}{{ a.next_action }}
当前没有可确定的商务补数告警。
补数执行记录补数明细与插件台账分开阅读;“已唤起”不冒充查询完成刷新
近 7 天近 30 天近 90 天
{{ tal.hist.total }} 条记录{{ (tal.hist.stats||{}).pending || 0 }} 待查询{{ (tal.hist.stats||{}).succeeded || 0 }} 已完成{{ ((tal.hist.stats||{}).failed || 0) + ((tal.hist.stats||{}).expired || 0) }} 失败/超时
正在读取补数任务与受限回执…
所选范围内没有历史补数记录。
达人 / 店铺排程与执行状态查询结果业务判定
{{ r.nickname || ('达人 ' + r.aweme_id) }}{{ r.shop_name }} · 抖音号 {{ r.aweme_id }}粉丝 {{ talNum(r.fans) }}
业务日 {{ r.business_date }}排程:{{ r.created_at || '—' }}领取:{{ r.started_at }}回执:{{ r.finished_at }}
{{ r.status_cn }}尝试 {{ r.attempt_count }} / {{ r.max_attempts }}{{ r.error_code }}
近 7 天 {{ talGmv(r.gmv7_lo,r.gmv7_hi) }}近 30 天 {{ talGmv(r.gmv30_lo,r.gmv30_hi) }}联系方式:{{ talContactCn(r) }}{{ talHistSnapshotCn(r) }}
{{ r.status==='succeeded' ? '未保留可展示字段' : '等待回执或无可信详情' }}{{ talHistSnapshotCn(r) }}
{{ talVerdictCn(r.verdict) }}{{ r.verdict_why || '—' }}
查看详情
插件台账按店隔离;请先在上方选择一家店铺。
仅 {{ talHistShopName(tal.hist.shop) }};当前待执行是实时快照,唤起事件不计入详情成功数。
正在读取插件侧台账…
当前没有待领取或执行中的其它插件任务。
{{ job.plugin }}排队:{{ job.queued_at || '—' }}{{ job.category }} · {{ job.n }} 项{{ job.query }}{{ job.status }}{{ job.result || '—' }}
{{ group.day }}
{{ ev.at || '—' }}完成:{{ ev.finished_at }}{{ ev.category }}{{ ev.query }}{{ ev.status }}{{ ev.result || '—' }}
近 {{ tal.hist.days }} 天没有其它插件执行记录。
全部 {{ coop.stats.all || 0 }}
{{ coop.statusCn[String(b)] || b }} {{ coop.stats[String(b)] || 0 }}
查询
有联系方式
没有
{{ o.cn || ('未知来源(' + o.v + ')') }}
含已离场 {{ coop.stats.gone || 0 }}
清空
达人标签添加来源合作状态
寄样商品件数{{ coop.windowCn }}
挂车商品数{{ coop.windowCn }}
成交商品件数{{ coop.windowCn }}
支付订单数{{ coop.windowCn }}
支付金额{{ coop.windowCn }}
预估佣金{{ coop.windowCn }}
结算订单数{{ coop.windowCn }}
预估结算金额{{ coop.windowCn }}
跟进人操作
{{ (r.nickname || '?').slice(0,1) }}
{{ r.nickname || '—' }}
抖音号 {{ r.aweme_id }} ↗
LV{{ r.level }}
{{ c }}
{{ r.city }}
有联系方式
在本店候选池
已离场
{{ r.remark || '未设置' }}
{{ coopSrcCn(r.source) }}
{{ coop.statusCn[String(r.coop_status)] || r.coop_status }}
{{ coopSubCn(r) }}
久未出单
{{ r.send_sample_cnt }}
{{ r.bind_cart_product_cnt }}
{{ r.pay_product_cnt }}
{{ r.pay_order_cnt }}
{{ coopMoney(r.pay_gmv_c) }}
{{ coopMoney(r.est_cos_fee_c) }}
{{ r.settled_order_cnt }}
{{ coopMoney(r.est_settled_gmv_c) }}
{{ r.follower_name || '—' }}
去精选联盟 ↗
没有符合筛选条件的达人。清掉筛选
还没有同步过 —— 点右上角「同步」,装着「精选联盟数据桥」插件的那台机器
会去把「我的达人库」的待出单 / 已出单两桶抓回来(约 2 分钟)。
这一档下面一个人都没有(上次同步:{{ coop.lastAt }})。
添加
改完到「达标名单」点「重新判定」全量重跑
词状态加的人 / 时间
{{ w.word }}
{{
w.is_active ? '启用中' : '已停用' }}
{{ w.created_by || '—' }}
{{ (w.created_at || '').slice(0, 16) }}
保存
取消
编辑
{{
w.is_active ? '停用' : '启用' }}
删除
{{ talWordShopName ? ('「' + talWordShopName + '」还没有店播词') : '请选择店铺' }}
达人名称含这些词之一即剔除,落「已剔除」桶、不进判定。
停用只是不参与判定,那一行还在。
删除会在当前店铺永久隐藏这个词,系统重启后也不会重新出现;其他店铺不受影响。
达人 / 店铺
{{ tal.judge.row.nickname || '—' }} · {{ tal.judge.row.shop_name }}
抖音号 {{ tal.judge.row.aweme_id }}
当前判定
{{ talVdCn(tal.judge.row) }}
{{ tal.judge.row.verdict_why || '暂无判定原因' }}
本店核验
{{ talFactCn(tal.judge.row) }} · {{ talEligibilityCn(tal.judge.row) }}
{{ tal.judge.row.eligibility_why || '暂无核验说明' }}
详情任务
{{ (tal.judge.row.business_status||{}).label || '状态异常,请刷新' }}
{{ tal.judge.row.detail_state_why || '暂无详情任务状态' }}
自动更新
{{ talJudgeAutoCn(tal.judge.row) }}
本店阈值
近 7 天下界 ≥ {{ talThresholdCn(tal.judge.row, 'd7') }};
近 30 天下界 ≥ {{ talThresholdCn(tal.judge.row, 'd30') }}
命中规则
{{ talSourceRuleCn(sr) }}
尚未核对到本店当前取数规则来源。每日精选只是平台推荐,
不等于按本店规则筛选。
← 返回题库
{{ training.examEditor.row.id ? '编辑 BL 自建试卷' : '新增 BL 自建试卷' }}
{{ training.examEditor.preview ? '员工作答预览' : '试卷配置草稿' }}
DAY {{ it.day_no }} · {{ it.title }}
支持单选、多选、判断题;多选题按正确选项完全一致得分
+ 添加题目
{{ String(qi+1).padStart(2,'0') }}第 {{ qi+1 }} 题删除
单选题多选题判断题
{{ trainingOptionLetter(oi) }} · {{ option }}
{{ trainingOptionLetter(oi) }}删除
+ 添加选项
{{ img.label || img.src.split('/').pop() }}移除 trainingExamUploadImage(q,files)">+ 添加图片
BL 新人培训{{ training.examEditor.row.title || '未命名试卷' }}
{{ trainingExamItemTitle(training.examEditor.row.item_key) }}
{{ training.examEditor.row.questions.length }} 题{{ training.examEditor.row.pass_score }} 分及格{{ training.examEditor.row.duration_minutes }} 分钟最多 {{ training.examEditor.row.max_attempts }} 次
{{ qi+1 }}. {{ q.prompt }}{{ q.type==='multiple'?'多选':q.type==='judgment'?'判断':'单选' }} · {{ q.points }} 权重
{{ trainingOptionLetter(oi) }}{{ option }}
{{ trainingViewMeta().title }}
{{ trainingViewMeta().desc }}
飞书培训机器人
群内触达 · 飞书身份授权 · BL 事项提交、作答与审核闭环
{{ training.loadErr ? '连接状态未知' : (training.overview.feishu && training.overview.feishu.running ? '长连接运行中' : (training.overview.feishu && training.overview.feishu.configured ? '长连接未运行' : '待配置飞书凭证')) }}
刷新发送待发卡片
{{ training.overview.outbox_pending || 0 }}待发卡片
{{ training.overview.outbox_dead || 0 }}已停手待重投
{{ training.overview.profile_pending || 0 }}身份资料待重试
{{ training.overview.active_enrollments || 0 }}培训中
{{ training.overview.tasks || 0 }}任务总数
有 {{ training.overview.outbox_dead }} 张卡片已经停手不再重试。
它们连着试了很多次都没发出去(多半是飞书凭证、权限或可用范围没配好)。
按上面「连接配置」那几行核对完,再点右边「重新投递」放回队列。
连接配置
飞书应用与回调密钥保存在服务器环境变量中,此处仅显示脱敏状态。
App ID{{ training.overview.feishu && training.overview.feishu.app_id_masked || '未配置' }}
业务公网地址{{ training.overview.feishu && training.overview.feishu.public_base_url || '未配置' }}
OAuth 回调地址{{ training.overview.feishu && training.overview.feishu.oauth_callback_url || '未配置' }}
签名密钥{{ training.overview.feishu && training.overview.feishu.link_secret_configured ? '已配置' : '未配置' }}
应开通权限(后台核对){{ p }}
应订阅事件(后台核对){{ e }}
最近错误:{{ training.overview.feishu.last_error }}
当前没有需要处理的连接错误。
运行策略
群内触达与导师通知保存后 worker 下一轮处理即生效,无需重新发布培训计划。
自动处理待发卡片关闭后卡片保留在队列,只能点击“发送待发卡片”人工发送。
导师私聊通知员工开始培训、提交待审核事项或考试达线时通知已绑定导师。
群内结果通知将员工提交、考试成绩和导师验收结论发回原培训群。
无需验收完成时也通知导师员工在群卡片上一键确认完成「无需审核」的事项时,私聊告诉导师一声。默认关闭——这一档本来就不需要导师做任何事。
最近由 {{ training.botSettings.updated_by || '管理员' }} 修改于 {{ bjTime(training.botSettings.updated_at) }}
保存运行策略
{{ training.overview.pending_claims || 0 }}待确认身份
{{ training.overview.active_enrollments || 0 }}培训中
{{ training.overview.tasks || 0 }}已生成任务
{{ training.plan.items.length || 0 }}计划事项
{{ training.exams.length || 0 }}当前试卷
{{ (training.reviews.length || 0) + (training.taskReviews.length || 0) }}待审核
机器人自动接入
把“BL 新人培训”机器人加入飞书群机器人收到进群事件后自动读取群名称、登记群聊并发送身份认领卡,不需要填写 Chat ID。
首次启用还需在飞书开发者后台订阅“机器人进群”和“机器人被移出群”事件,并发布应用版本。
扫描飞书群
导师飞书身份
下列人员来自 BL 用户配置。每位导师用自己的 BL 账号登录并绑定对应飞书,绑定后即可接收私聊提醒。没有本模块权限的同事,登录后点右上角头像 →「绑定我的飞书」同样可以绑。
{{ m.display_name || m.username }}BL 账号:{{ m.username }}
{{ m.bound_at ? '已绑定飞书' : '待绑定' }}
{{ m.bound_at ? '重新绑定我的飞书' : '绑定我的飞书' }}
请该用户登录后绑定
已发现飞书培训群
{{ training.groups.length }} 个
{{ g.chat_name }}{{ g.chat_fingerprint }}
{{ g.status==='authorized'?'等待员工认领':g.status==='active'?'培训中':g.status==='completed'?'培训已完成':'机器人已移出' }}
{{ g.start_date }}
◎还没有发现培训群将机器人加入一个飞书群后,通常十几秒内会出现在这里。
还没有任何导师绑定飞书,下面的「导师」暂时一个都选不了
请让要当导师的同事用他自己的 BL 账号登录,点右上角头像 → 绑定我的飞书,走完飞书授权即可。绑定入口对所有登录用户开放,不需要新人培训的模块权限。
培训群飞书员工档案姓名开始日导师(BL 用户)操作
{{ c.chat_name }}{{ c.claimed_at ? bjTime(c.claimed_at) : '刚刚认领' }}{{ c.feishu_name || '飞书资料待解析' }}{{ c.employee_no || '无工号' }} · {{ c.operator_fingerprint }}
{{ m.display_name || m.username }}{{ m.bound_at?' · 已绑定':' · 未绑定飞书' }}
核对并确认
◎暂无待确认身份员工本人点击群内认领卡后,会在这里等待人事核对。
员工培训群开始日计划版本{{ training.view==='confirmations'?'确认进度':'培训进度' }}操作
{{ e.employee_name }}{{ e.mentor_username || '未填导师' }}{{ e.chat_name }}{{ e.start_date }}V{{ e.plan_version }}{{ e.completed || 0 }}/{{ e.tasks || 0 }}{{ training.view==='confirmations'?'已完成事项':'七天任务' }}
{{ training.view==='confirmations'?'查看确认清单':'查看详情' }}发送到期事项
◫{{ training.view==='archive'?'还没有培训档案':'当前没有进行中的培训' }}人事确认飞书身份后,系统会按计划版本生成七天任务。
普通事项待验收 · {{ training.taskReviews.length }}
员工培训事项员工提交附件审核说明操作
{{ r.employee_name }}{{ r.chat_name }} · 第 {{ r.revision }} 次{{ r.item_title }}{{ r.pass_standard }}{{ r.summary || '员工已确认完成' }}打开成果链接 ↗{{ f.name }}无附件通过退回
✓暂无普通事项待验收员工提交需要人工验收的事项后,会出现在这里。
考试答卷待审核 · {{ training.reviews.length }}
员工试卷成绩作答审核说明操作
{{ r.employee_name }}{{ r.chat_name }}{{ r.exam_title }}{{ r.score }} 分第 {{ r.attempt_no }} 次错 {{ r.wrong.length }} 题通过退回
✓暂无待审核答卷达到及格线的答卷会进入这里,导师结论会自动回到原培训群。
D{{ training.day }}
{{ trainingDayMeta(training.day).title }}
{{ trainingDayMeta(training.day).goal }}
{{ trainingDayItems(training.day).length }}培训事项
{{ trainingDayNeedsReview(training.day) }}需导师验收
{{ training.plan.is_draft ? ('草稿 V' + training.plan.version + ' · 未发布') : ('已发布 V' + training.plan.version) }}
新增培训事项
{{ training.plan.is_draft ? ('发布 V' + training.plan.version) : '发布' }}({{ trainingPlanWarnCount }} 处提醒)
{{ String(idx+1).padStart(2,'0') }}
{{ x.title }}
{{ x.is_enabled ? '已发布' : '草稿' }} · 计划 V{{ x.version }}{{ x.questions.length }}题目
{{ x.pass_score }}及格分
{{ x.duration_minutes }}分钟
{{ x.max_attempts }}最多次数
关联:{{ x.item_title }}
◇还没有 BL 自建试卷新增后关联一个培训事项,与计划草稿一起发布。
{{ training.archive.enrollment.employee_name }}{{ training.archive.enrollment.chat_name }} · {{ training.archive.enrollment.start_date }} 开始 · 计划 V{{ training.archive.enrollment.plan_version }}
发到群里({{ trainingSelTasks.length }})
同步计划最新内容({{ trainingSyncTasks.length }})
勾上要发的事项 —— 已经发过的会在群里再发一张新卡,做完的发不了。
这个人有 {{ trainingDiffCount }} 条与计划不一样
培训日培训事项任务状态当前分数提交与审核记录
training.archive.sel[t.id] = v">
DAY {{ t.day_no }}
{{ t.snapshot.title }}{{ t.due_date }} · {{ trainingModeText(trainingItemMode(t.snapshot)) }}计划已改:{{ training.archive.diff[t.id].diff.join('、') }}
{{ trainingTaskStatus(t.status) }}
{{ t.score === null ? '—' : (t.score + ' 分') }}
{{ a.exam_title }} · 第 {{ a.attempt_no }} 次 · {{ a.score }} 分
{{ trainingReviewStatus(a.review_status) }} · {{ a.reviewed_by }} · {{ a.review_note }}
错题:{{ a.wrong.map(w => w.position).join('、') }}
第 {{ s.revision }} 次提交 · {{ s.status==='approved'?'验收通过':s.status==='rejected'?'已退回':'等待验收' }}
{{ bjTime(s.submitted_at) }} · {{ s.reviewed_by }} · {{ s.review_note }}
{{ s.summary }}成果链接 ↗{{ f.name }}
暂无提交记录
没有培训任务。
← 返回计划
{{ training.editor.row.id ? '编辑培训事项' : '新增培训事项' }}
{{ trainingEditorPlanText }}
对应课件
放了 {{ trainingMaterialImageCount }} 张图:飞书卡片最多放 {{ trainingCardImageMax }} 张,请删掉多出来的再保存。
考核
{{ trainingEditorExam() ? trainingEditorExam().title : '这条事项还没挂试卷' }}
{{ trainingEditorExam().is_enabled ? '已发布' : '还是草稿(发布计划前要先发布试卷,否则整份计划发不出去)' }}
· {{ (trainingEditorExam().questions || []).length }} 道题 · {{ trainingEditorExam().pass_score }} 分及格
· 最多 {{ trainingEditorExam().max_attempts }} 次
⚠️ {{ trainingEditorExamWarn() }}
{{ trainingEditorExam() ? '编辑这份试卷' : '新建试卷' }}
DAY {{ x.day_no }} · {{ x.item_title }}|{{ x.title }}
关联所选试卷
一份试卷只能关联一条事项;关联已有试卷会从它原来的事项移到这一条,不会复制或覆盖。
考核要求(员工在卡片上看到,可留空)
卡片按钮
{{ trainingPreviewButton }}
无
{{ trainingPreviewActionText }}
负责人
{{ m.display_name || m.username }} · {{ m.username }}
卡片预览按左边五项自动生成
{{ training.editor.row.title || '培训事项名称' }}
{{ trainingPreviewSubtitle }}
{{ trainingPreviewInstruction }}
考核
{{ trainingPreviewStandard }}
{{ trainingPreviewButton }}
{{ trainingPreviewButton ? '点完:' : '没有按钮:' }}{{ trainingPreviewActionText }}
发一张测试卡给我{{ trainingPreviewTestHint }}
发送
链接名称(员工在卡片上看到的字,可留空)
链接地址
{{ training.editor.linkDlg.err }}
有 {{ training.publishConfirm.warnings.length }} 处要先知道(不拦住发布):
DAY {{ w.day_no }} · {{ w.message }}
这条计划里选「在线考试」的事项都挂好试卷了。
发布后员工端按 V{{ training.publishConfirm.version }} 发卡片,已经发过的不重发。
想让改动作到已经在跑的人身上,去「全部培训档案」里点「同步计划最新内容」。
现在没有正在进行的场次
没有场次就一条都不发 ——
插件装在谁的电脑上就能进任何直播间,「什么时候发、往哪儿发」只能由一场直播回答
+ 新建场次
{{ live.sess.last ? '现在没有开着的场次' : '还没有开过场' }}
一场 = 一次直播,有开始有结束。开了它,选中的号才会收到发言指令;
结束之后这一场的账就封口了,进「场次记录」。
新建时要填三样:发到哪个直播间、
用哪几个号、
按什么顺序发。
+ 新建场次
还没开的一份
{{ live.sess.draft.name || '(还没起名)' }}
直播间 {{ live.sess.draft.room_id }} ·
{{ (live.sess.draft.seats || []).length }} 个号 ·
序列 {{ (live.sess.draft.seq || []).length }} 项
继续配
直接开播
丢掉
新建时可以从三种起点开始 —— 都能改,开播之后也能改
最常用的那条:昨天怎么发的,今天照着来。
跑过一场之后这一格才会出现。
照「{{ live.sess.last.name }}」再开一场
什么都没有的时候从这儿开始。配好之后可以顺手存成一个方案,下次直接复用。
{{
live.sess.cur.state_cn }}
{{ live.sess.cur.name }}
已跑 {{ liveElapsed }} · {{
(live.sess.cur.started_cn || '').slice(11, 16) }} 开始 · 改过 {{ live.sess.cur.n_edit || 0 }} 次
直播间
{{ live.sess.cur.room_name }} · {{ live.sess.cur.room_id }} ↗{{ live.sess.cur.room_id }}
小窗
{{
live.sess.cur.pause_cn }}
一键账号重启
改这一场
暂停发送
恢复发送
结束这一场
停发中:{{ live.sess.cur.pause_cn || '—' }}
另有 {{ live.sess.cur.outside_online.length }} 个号此刻在线,但不在这一场里:
{{
(i ? '、' : '') + (o.nickname || o.uid)
}}(还没授权)。
如果你刚在浏览器里换过登录,那就是它 —— 换号会得到一行
新席位,
旧号不会再上线,这一场不会自己恢复。
让「{{
(live.sess.cur.outside_online[0] || {}).nickname
|| (live.sess.cur.outside_online[0] || {}).uid }}」接替
成因消失它会自己恢复(插件下一次来问时就会算)。这一场没有结束,账也没封口。
到了这一场设的计划结束时刻。点「恢复发送」会把那个时刻清掉再继续 ——
否则下一次心跳立刻又停。
连着 {{ live.limits.fail_abort || '几' }} 次派活都没发出去,已自动停手。
它不会自己恢复 —— 发送失败时输入框是刻意不清的(清框会把编辑器
弄到"按钮恒亮而框是空的"),所以不去清一次的话,下一次必然又撞上同一件事。
先去那台机器的直播间页面把评论输入框清空,再点「恢复发送」(点它会
把失败计数一起清零)。下面每个号的「连着没发出去」写着是谁卡住的。
人点的停 —— 只能人点回来。
{{ live.sess.stat.sent || 0 }}
/ {{ live.sess.stat.cap }} 句
句 · 不限
{{
(live.sess.stat.n_grp_runs || 0) + (live.sess.stat.n_single || 0) }} 次派发 · 序列 {{
live.sess.stat.n_sys || 0 }} · 自动 {{ live.sess.stat.n_trig || 0 }} · 手动 {{
live.sess.stat.n_fire || 0 }} · 还可发 {{
Math.max(0, (live.sess.stat.cap || 0) - (live.sess.stat.sent || 0)) }} 句
{{ live.sess.stat.online || 0 }}
在线{{
liveSeatsDown.length }}掉线
{{
live.roles.send }} {{ live.sess.stat.send_online || 0 }} · {{ live.roles.watch }} {{
live.sess.stat.watch_online || 0 }} · 都在线
掉线:{{
liveSeatsDown.map(x => x.nickname || x.uid).join(' · ') }}
让别的号接替 →
{{
(live.sess.trigger || {}).n_replied || 0 }}/ 命中 {{
(live.sess.trigger || {}).n_hit || 0 }} 条评论
{{
(live.sess.trigger || {}).n_noreply
? (live.sess.trigger.n_noreply + ' 次命中了没回 · ' + liveTrigWhy)
: ((live.sess.trigger || {}).n_rules ? ((live.sess.trigger.n_rules) + ' 条规则开着') : '还没建触发规则') }}
触发总开关关着 · 观众评论不会自动回复
{{
live.sess.stat.n_fire || 0 }}次
{{
live.sess.fire_cool_left
? ('冷却中,还有 ' + live.sess.fire_cool_left + ' 秒能再点')
: '可立即发送' }}
发送序列
共 {{ liveBoardSeq.length }} 项 · 排完点「存下来」
共 {{ liveSeqPlan.total || 0 }} 项 · 每次从当前可发送项里随机抽取 · 多项可选时不连续重复同一项 ·
发送间隔 {{ liveSeqPlan.iv_cn || '' }}{{ liveSeqPlan.jitter_pct ? ' ±' + liveSeqPlan.jitter_pct + '%' : '' }}
共 {{ liveSeqPlan.total || 0 }} 项 ·
这一轮还有 {{ liveBoardSeq.length }} 项没发完 · 走完回到第 1 项 ·
预计时刻按序列间隔 {{ liveSeqPlan.iv_cn || '' }}{{ liveSeqPlan.jitter_pct ? ' ±' + liveSeqPlan.jitter_pct + '%' : '' }} 估,抖动会前后差一两分钟
{{
live.sess.seqOnly ? '只看序列(插队的 ' + liveSeqPlan.extras_n + ' 条已隐藏)' : '只看序列' }}
就地排序列
改完点「存下来」·
下一轮生效,正在发的那一组不打断
算了
存下来
#发什么
{{ live.sess.seqEdit ? '' : ((live.sess.cur || {}).seq_mode === 'random' ? '发送用户' : '预计谁发') }}
{{ live.sess.seqEdit ? '' : ((live.sess.cur || {}).seq_mode === 'random' ? '发送时机' : '预计发送') }}
{{ live.sess.seqEdit ? '排序' : '状态' }}
{{ live.sess.seqEdit ? (i + 1)
: (it.seq == null ? '—' : (it.seq || (i + 1))) }}
{{
it.name || it.code || '—' }}
{{
(live.meta.seq_srcs || {})[it.src] || it.src }}
{{
(live.meta.seq_kinds || {})[it.kind] || it.kind }}
{{ liveSeqMeta(it) }}
{{ k + 1 }}. {{ ln.content }} · {{
ln.sent_at_cn }} 已发
{{ it.broken }}
{{ it.gap_prev_cn }}
「{{
it.trig_by.nick }}」说「{{ it.trig_by.content }}」 · 规则「{{ it.trig_by.rule }}」 · 从 {{ it.pick_n }} 组里随机挑一组
{{
(live.sess.cur || {}).seat_mode === 'random' ? '均匀轮转' : '届时轮换' }}
{{ it.seat_nick }}
实发
轮到它
你指定的
暂时没人能发
—
{{ it.started_cn }} 开始
约 {{ it.finish_cn }} 发完
约 {{ it.eta_cn }}
{{ it.eta_lo_cn }}~{{ it.eta_hi_cn }} 之间
↑
↓
×
正在发 · {{ it.n_sent }}/{{ it.n_total }}
等着回 · {{ it.in_sec }} 秒后
排着 · {{ it.expire_sec }} 秒内没人领就作废
冷却中 · {{ Math.ceil(it.cool_left / 60) }} 分后
本轮候选
下一项
排队中
这一场的序列是空的 —— 一条都不会发。点「就地排序列」从库里加几项。
这一轮已发过 {{ liveSeqPlan.done_n }} 项({{
liveSeqPlan.done_last.name }} {{
liveSeqPlan.done_last.at_cn }}),不再列在上面;第 {{
liveSeqPlan.total }} 项发完绕回第 1 项开始下一轮。发过的全在下面「本场发过的」里
{{ live.ed.tab === 'group' ? '话术组' : '话术' }}
已排
加进序列
{{ r.content || r.name }}
{{
live.ed.tab === 'group'
? (r.n_live + '/' + r.n_lines + ' 句能发 · 隔 ' + r.gap_sec + ' 秒')
: ((live.cats || {})[r.cat] || r.cat || '')
+ (r.n_var ? (' 另 ' + r.n_var + ' 个说法') : '') }}
{{ liveBanCn(r.ban_hit) }}
{{
liveSeqCount(live.ed.tab === 'group' ? 'group' : 'phrase', r.id)
? ('已排 ' + liveSeqCount(live.ed.tab === 'group' ? 'group' : 'phrase',
r.id) + ' 次') : '' }}
加
{{ live.ed.kw ? '这个词没搜到 —— 清掉搜索看全部'
: (live.ed.tab === 'group'
? '还没有话术组。点上面「新建一个话术组」,或去「配置 → 话术组」建'
: '还没有能排的话术。点上面「新写一条话术」,或去「配置 → 话术库」加') }}
本场发过的
一行一次派发(一组算一次,单条也算一次)·
{{ (live.sess.dispatches || []).length }} 次 · {{ live.sess.stat.sent || 0 }} 句 · 点开看每一句
触发来的 {{ liveDispTrigN }} 次
{{ live.sess.allOpen ? '全部收起' : '全部展开' }}
时间发了什么
哪个号句数设定间隔实际间隔
状态
{{ (d.at || '').slice(11, 19) || '—' }}
{{ d.name || d.code || '—' }}
{{
(live.meta.seq_srcs || {})[d.trig ? 'trig' : (d.fire ? 'fire' : 'system')]
|| (d.trig ? 'trig' : (d.fire ? 'fire' : 'system')) }}
{{
(live.meta.seq_kinds || {})[d.kind] || d.kind }}
{{ d.code }}
「{{
d.trig_by.nick }}」说「{{ d.trig_by.content }}」 · 规则「{{ d.trig_by.rule }}」
{{ d.err }}
{{ d.seat_nick || d.seat_uid || '—' }}
{{ d.kind === 'group' ? (d.n_sent + '/' + d.n) : '1' }}
{{ d.gap_set_cn }}
{{ d.real_gap_cn }}
{{ d.state_cn }}
{{ (ln.sent_at_cn || '').slice(11, 19) || '—' }}
{{ ln.seq }}. {{ ln.text }}
{{ ln.err }}
{{ ln.gap_prev == null ? (ln.sent_at_cn ? '' : '等着')
: ('距上一句 ' + ln.gap_prev + ' 秒') }}
{{ ln.state_cn }}
这一场还没有发过 ——
这一场停着({{
live.sess.cur.pause_cn || '已暂停' }})。
序列是空的,点「改这一场」排几项进去。
这一场里还没有在线的{{ live.roles.send }},
去「账号席位」把要发言的号开成{{ live.roles.send }},并确认它选进了这一场。
{{ liveBlockCn }}
正在跑、{{ live.roles.send }}也在线,等下一个发送点。
设定间隔:组行是组内每句之间的秒数,单条行是序列里与上一项的间隔(含抖动)·
实际间隔:组行按组内相邻两句反推,单条行是距上一条真实发出的时间 ·
蓝色「触发」那几行下面写着是谁的哪条评论引出来的
用哪个号发
谁先来谁发 · 默认 · {{
liveSendSeats.filter(x => x.can_send).length }} 个能发
{{ s.nickname || liveUidShort(s.uid) }}
· {{ s.can_send ? '能发' : (s.block_cn || '发不了')
}} {{ s.wait_sec }} 秒
「还要等 N 秒」是同号最短间隔那道闸 —— 手点也不绕它。
指定了它,这一下会排着、等到点再发
{{ g.name }}
{{ g.n_live }} 句 · 隔 {{
g.gap_sec }} 秒
{{
live.sess.fire_cool_left ? ('冷却 ' + live.sess.fire_cool_left + ' 秒')
: ((live.sess.grp_cool || {})[g.id]
? ('这组刚发过 · ' + Math.ceil((live.sess.grp_cool || {})[g.id] / 60) + ' 分后')
: ('约 ' + g.span_sec + ' 秒')) }}
发
冷却是手点那一档({{ (live.sess.plan || {}).fire_cooldown_sec || 60 }} 秒)——
主播喊一次上链接、十几秒没动静就再点一次,是最容易把号打进限流的那条路
这一场的席位
选了 {{ (live.sess.seats || []).length }} 个 ·
{{ live.sess.stat.online || 0 }} 个在线
恢复全部异常
改
{{
s.nickname || liveUidShort(s.uid) }}
{{ live.roles[s.role] || s.role }} · {{
liveAgo(s.seen_ago, '没心跳') }} · 插件 {{
s.ext_ver }} · {{
liveAgo(s.work_ago) }}回传过 · {{
liveAgo(s.send_ago) }}发过一条
{{
s.state === 'unknown' ? '监听席掉线 · 评论触发与公屏确认都停了' : '监听中 · 读公屏' }}
停在别的直播间({{ s.room_id || '还没读到' }})
{{
s.can_send ? '能发' : (s.tripped ? '停发' : (s.block_cn || '发不了'))
}} {{ s.wait_sec }} 秒
人工恢复
远程重开
{{ s.listen ? '停止监听' : '设为监听' }}
轮到它
连着 {{ s.fail_run }}/{{ s.fail_abort }} 次没发出去{{
s.tripped ? ' · 已停发这个号' : '' }}
这一场一个席位都没选 —— 点「改这一场」把要发言的号选进来。
公屏评论
{{ (live.sess.listeners || []).length
? (live.sess.listeners.map(l => l.nickname || l.uid).join('、') + ' 在读')
: '没人在读' }} · 只留 24 小时
触发规则
最近 {{ liveCmt.n || 0 }} 条
回了 {{ liveCmt.n_replied || 0 }}
没回 {{ liveCmt.n_noreply || 0 }}
{{ c.nick || '(没名字)' }} · {{ c.at_cn }}
我们自己的号
主播
排除名单
回了{{
c.hit.grp ? ('「' + c.hit.grp + '」') : '' }}
等着回
{{ c.hit.state_cn }}
{{ c.text }}
↳ 命中「{{
c.hit.rule }}」,但{{ c.hit.why_cn }}
没有在读公屏的号 ——
给上面那张卡里的一个号点「设为监听」,评论与触发才有数。
这一场还没有触发过自动回复。
没有「命中了却没回」的 —— 闸没拦下过任何一条。
在读了,但还没有人在公屏上说话。
正在取这一场的状态…
{{ live.sess.cur && (live.sess.cur.room_name || live.sess.cur.name)
|| '当前直播' }} · {{ live.player.room }}
正在接入直播画面…
{{ live.player.error }}
在任何电脑登录这个 BL 账号都能操作本场绑定的设备;
设备令牌、浏览器登录态、IP 和机器配置不会显示。
刷新
远程操作边界:这里只能执行“远程重开窗口”,用于恢复掉线、错房或连续发送失败的席位。
它会关闭并重新打开对应 BitBrowser 窗口,等新的插件心跳和当前直播间核验通过才算恢复成功。
正在核对当前场次的设备绑定…
现在没有进行中的直播场次,因此没有可控制的设备。
{{ live.dc.session.state_cn || live.dc.session.state }}
{{ live.dc.session.name || '当前直播' }}
直播间 {{ live.dc.session.room_id || '—' }}
显示的是这一场选中的号所绑定的窗口,不是全公司的设备清单。
{{ m.label }}
{{ m.online ? '设备在线' : '设备心跳过期' }}
设备助手 {{ m.helper_ver }}
最近心跳 {{ m.last_seen_cn || '还没有' }}
{{ p.seat.nickname || p.seat.uid || '未识别账号' }}
{{ live.roles[p.seat.role] || p.seat.role || '—' }}
窗口:{{ p.status || '设备尚未回报窗口状态' }} · 直播探测 {{ p.broadcast }} · 插件 {{ p.runtime_ver }}
当前可发送
{{ p.seat.block_cn || p.seat.block }}
设备已取消托管
远程重开窗口
{{ p.seat.role === 'send' ? '当前不需要远程恢复' : ('当前不是「' + (live.roles[p.seat.role] || p.seat.role || '—') + '」,不能远程重开') }}
本场还没有任何席位与 BL 设备助手托管窗口完成可核验绑定。
暂时不能远程控制的席位系统没有猜测目标窗口
{{ s.nickname || s.uid }}{{ s.block_cn || s.block }}{{ s.error }}
插件装在哪台机器上、打开了直播间,这里就会多一行。能不能发言要在这里显式开。
装插件
刷新
号抖音 uid机器状态
直播间心跳 / 干活角色
{{ s.nickname || '—' }}
本场主播本人
{{ liveUidShort(s.uid) }}
兜底认出来的
来源不详
{{ s.install_label || s.machine || s.install_id }}
还没报过
掉登录了 · 最后一次读到{{ s.nickname || '不详' }}
没开着直播间
{{ s.probe_why }}
{{ liveSeatCn(s) }}
已退役{{ s.succ_by ? (',被「' + s.succ_by + '」接替') : '' }}
连着 {{
s.fail_run }}/{{ s.fail_abort }} 次没发出去{{ s.tripped ? ' · 已停发' : '' }}
{{ f.t }}
{{ s.room_id }}
—
{{
(s.live_on ? '在播' : '没在播') + ' · '
+ (live.st.sessRoom ? (s.room_ok ? '就是这一场那一间' : '不是这一场那一间')
: '还没开场') }}
{{ (s.last_seen_cn || '').slice(5, 16) || '—' }}
{{ (s.last_work_cn || '').slice(5, 16) || '还没发过' }}
{{ live.roles.send }}
{{ live.roles.watch }}
{{ live.roles.off }}
{{ s.listen ? '监听中' : '监听评论' }}
接替别的号
已清框,重新数
远程重开窗口
还没有任何席位 —— 那台电脑上装了插件、打开了直播间,这里才会多一行。
往哪儿发
现在有一场开着,只发:
{{ live.st.sessRoom }}
去开播台
上面每一行那句「就是/不是这一场那一间」就是拿它判的。要换直播间,
在开播台点「改配置」。
还没开场。往哪个直播间发,在开一场的时候填。
去开播台开一场
号码就是上面「直播间」列里的那一串(点一下可复制)。
没有开着的场次时一条都不会发 —— 往哪儿发由那一场说了算。
{{ live.roles.send }}
按计划替公司在公屏上发言。一个直播间同一时刻只有一条在飞,多个轮流上。
{{ live.roles.watch }}
不发言,只把公屏上看到的评论回传,用来确认"发出去的真的播出去了"。
这是唯一能回答"有没有被限流"的东西。
{{ live.roles.off }}
心跳照记(看得出这台机器还活着),但不发也不看。新出现的号默认就是这一档。
监听评论
给会发言的号加的开关:它也读公屏、也回传,不必单独配一个只看不发的号。
回执只认别的号看到的(自己看得见自己的评论,作不了证);观众说到触发规则里的关键词,就是它报上来的。
新增话术
全库重扫卡审
存下来那一刻按「卡审词表」扫一遍(直播自己的那张表,与内容工厂的无关);
那边加词改词会自动重扫全部话术,左边这个按钮留作兜底
编号分类内容
卡审状态
{{ w.code }}
{{ live.cats[w.cat] || w.cat }}
{{ w.content }}
属于话术组 {{ liveGrpName(w.grp_id) }} · 只在整组发,不参与单条轮播
另 {{ w.variants.length }} 个说法:{{ w.variants.join(' / ') }}
{{ liveBanCn(w) }}
{{
w.is_active ? '启用中' : '已停用' }}
改
{{ w.is_active ? '停用' : '启用' }}
还没有话术 —— 点左上「新增话术」加第一条。发送计划开着但一条话术都没有时,
实时看板会说「挑不出话术」。
运营总览只显示当前场次的确定性证据;历史终态不作为当前告警刷新
级别对象现状下一步
{{ a.severity }}{{ a.seat_uid || '传输层' }}{{ a.message }}{{ a.next_action }}
当前没有可确定的运行告警。
席位角色最近轮询 / 阻塞建议
{{ s.nickname || s.uid }}{{ s.role }}{{ s.block_cn || s.last_poll_at || '正常或等待业务条件' }}{{ s.next_action || '无需动作' }}
上个月
{{ live.cal.month }}
下个月
回到本月
+ 批量排期
到点自动开始;已有活动场次时不抢占。提前下播连续 10 分钟才结束
周{{ w }}
{{ r.run_from || '—' }}
{{ r.name }}
{{ r.room_name || r.room_id }} · {{ r.state_cn }}
{{ r.schedule_error }}
已下播,{{ Math.ceil(r.room_end_left / 60) }} 分钟后结束
复制到日期
新建话术组
导入
批量改节奏({{ live.gr.selected.length }})
导出 Excel
一组 = 一串按顺序连着发的句子。组里的句子不参与单条轮播 ——
要发它们,把这一组排进这一场的发送序列
liveGroupsSelect(g.id, v)">
{{ g.code }}
{{ g.name }}
隔 {{ g.gap_sec }} 秒{{ g.jitter_pct ? ' ±' + g.jitter_pct + '%' : '' }}
约 {{ g.span_sec }} 秒发完
{{ i + 1 }}. {{ w.content }}
停用中 · 这一组会跳过它
{{ liveBanCn(w) }} · 这一组会跳过它
{{
g.is_active ? '启用中' : '已停用' }}
立即发送
改
删
还没有话术组 —— 点左上「新建话术组」加第一个。
一组里的几句会按你设的秒数连着发完,适合逼单那种一串的话。
新增卡审词
只管直播话术,与内容工厂的卡审词库没有关系。加词、改词、停用、删除都会
自动把全部话术重扫一遍;「现在拦着」那一列按此刻的话术现算
词命中写法违规类型建议改法
现在拦着状态
{{ w.word }}
{{ w.note }}
{{ p }}
{{ w.vio_type || '—' }}
{{ w.replacement || '—' }}
{{ w.n_hit }} 句
{{
w.is_active ? '启用中' : '已停用' }}
改
{{ w.is_active ? '停用' : '启用' }}
删
这张表还是空的 —— 直播话术此刻一个词都不会被拦。
点左上「新增卡审词」加第一条;这里只管直播话术,不从内容工厂那边复制。
规则 {{ live.tg.rules.length }}
最近命中 {{ live.tg.hits.length }}
参数
新增规则
不监听名单 {{ live.tg.exclude.length }}
在读公屏的号:{{
live.tg.listeners.map(l => (l.nickname || l.uid) + (l.alive ? '' : '(掉线)')).join('、') }}
还没有号在读公屏 —— 去「账号席位」给一个会发言的号开「监听评论」
· 总开关关着
规则关键词(任一命中)回哪一组
规则冷却回过状态
还没有规则。一条规则 = 几个关键词(同义词并列)+ 一个话术组:观众说到其中任一个词,就回那一组。
{{ r.name }}
任意评论{{ k }}
随机 所有启用的组
随机 {{ (r.grp_names || []).join('、') }}
{{ r.grp_name || ('#' + r.grp_id) }} {{ r.grp_code }}
组不在了
{{ liveDur(r.cooldown_sec) }}
{{ r.n_hit }}
{{ r.is_active ? '启用' : '停用' }}
改
{{ r.is_active ? '停用' : '启用' }}
删
时间谁说的说了什么命中规则
结果说明
还没有命中。
{{ h.created_cn }}
{{ h.nick || '—' }}
{{ h.content }}
{{ liveTrigName(h.trigger_id) }}
{{
h.state_cn }}
{{ (h.grp_name ? ('回「' + h.grp_name + '」· ') : '') + (h.reply_cn || h.reason_cn || (h.due_cn ? ('计划 ' + h.due_cn + ' 回') : '')) }}
排除此人
总开关
liveTrigParam('trig_on', v ? 1 : 0)">
关掉后命中照记、只是不回(记录里写「总开关关着」)
回复延迟(秒)
liveTrigParam('trig_delay_lo', v)">
~
liveTrigParam('trig_delay_hi', v)">
看到评论之后隔多久回,区间里随机;秒回像机器人
两次触发回复的间隔(秒)
liveTrigParam('trig_gap_sec', v)">
每场触发上限
liveTrigParam('trig_cap_session', v)">
0 = 不限
同一观众冷却(秒)
liveTrigParam('trig_viewer_cooldown_sec', v)">
按评论者 id 算,同一个人这段时间里只回一次
触发回复和「立即逼单」一样绕过序列里项与项之间的间隔,但不绕同号最短间隔、同话术组冷却、在飞互斥、断路器;谁来发按轮替。
每条规则自己还有一个「规则冷却」(同一条规则多久之内不再回)。
每个发过评论的人都会把昵称和数字 ID记到下面;看到自家号,直接点「标为我们的号」,以后不记命中、不回。
触发规则
刷新
① 我们自己的号
能自动识别的席位会直接记下;没识别到不用找 ID,去下面按昵称点「标为我们的号」。
席位角色心跳自动识别的评论者 id
还没有席位。
{{ o.nickname || o.uid }}监听
{{ live.roles[o.role] || o.role }}
{{ o.alive ? '在线' : '掉线' }}
{{ o.commenter_id || '还没自动识别' }}
清掉
② 主播
按「未打码昵称 = 直播间名字」认,直播间名字由插件从页面上读。
这一场的直播间:{{ live.ig.anchor.room_name }} —— 这个昵称的评论不当观众。
没有开着的场次,或插件还没报直播间名字(1.12.1 起才报)。这时主播的评论会被当观众 —— 要挡就把它加进下面的排除名单。
③ 已标记不回复的账号
从下面最近评论过的人里标记;按 ID 认,改昵称也拦得住。
评论者 id昵称类型加入
还没有标记。从下面按昵称点一下就行。
{{ x.user_id }}{{ x.nick || '—' }}
{{
x.kind === 'ours' ? '我们的号' : '不回复' }}{{ x.created_by }}
取消
④ 最近 24 小时评论过的人
监听席读到的(评论只留 24 小时)。看到自己人或官方号在这儿,就地标上。
昵称评论者 id条数最近一条最近现在当
还没有读到过评论 —— 要有在线的监听席(或观察席)。
{{ c.nick || '—' }}
{{ c.user_id }}
{{ c.n }}
{{ c.last_content }}
{{ c.last_cn }}
{{
c.is_ours ? '自己人' : (c.is_anchor ? '主播' : (c.excluded ? '排除' : '观众')) }}
标为我们的号
不回复
取消标记
场次名
按今天自动起的,可以改。场次记录里全靠它认人
发到哪个直播间 *
整条 live.douyin.com 链接粘进来就行。
一场只发这一间 ——
填宽了就是用公司的号在别人直播间刷屏
用哪几个号 *
已授权 {{ (live.st.rows || []).filter(x => x.enabled && !x.is_anchor).length }} 个。
授权在「账号席位」 —— 这里只决定这一场用哪几个
谁读公屏
{{ liveSeatName(u) }}
×
+ {{ liveSeatName(u) }}
公屏评论由它读回来,观众评论触发才响得起来。
⚠️ 这是这个号的设置,别的场次里它也会读公屏。
还没人读公屏 ——
这一场不会有任何自动回复,开播台的「公屏评论」也会一直是空的。
不选也能开场,只是那半功能不生效。
运行时段
—
按北京时间。排期到结束时间会结束本场。
手动场次出了时段停发不结束,回到时段自动恢复。
两头都留空=全天
发送方式
均匀打散会先让当前可发的话术组、席位各走完一轮,再重新洗牌;整组连续发送,组内句序每次重洗
{{
live.ed.id ? '排期设置' : '多日排期' }}
留空开始日期=手动场次
{{ live.ed.id ? '排期日期' : '起止日期' }}
保存后一次生成 {{ liveScheduleRangeN }} 天;房间、席位、话术和节奏相同,之后可在日历里逐天修改
{{ live.ed.seq_mode === 'random' ? '均匀打散池' : '按什么顺序发' }}
{{ live.ed.seq.length }} 项 ·
{{ live.ed.seq_mode === 'random' ? '每轮各发一次 · 组内句序重洗' : '走完回到第 1 项' }} ·
话术和话术组同一层
{{ liveEdSpan || '还没排' }}
类型内容编号
{{ i + 1 }}
{{
(live.meta.seq_kinds || {})[it.kind] || it.kind }}
{{
it.name || it.code }}{{
it.kind === 'group'
? (' ' + it.n_live + ' 句 · 隔 ' + it.gap_sec + ' 秒 · 约 '
+ it.span_sec + ' 秒发完')
: (it.n_var ? (' 另 ' + it.n_var + ' 个说法') : '') }}
{{ k + 1 }}. {{ ln.content }}
{{ it.broken }}
{{
it.code || '—' }}
↑
↓
×
从右边的库里点「加」往这儿添 —— 同一条排两次是允许的,顺序是你定的
序列还是空的 —— 从右边的库里点「加」。空序列开不了场(开了也一条不发)。
话术
{{ r.content }}
{{ r.code }} · {{
live.cats[r.cat] || r.cat
}} · 另 {{ r.n_var }} 个说法
{{ r.is_active ? liveBanCn(r.ban_hit)
: '已停用' }}
加
已加 {{ liveSeqCount('phrase', r.id) }} 次
没有匹配「{{ live.ed.kw }}」的话术。
话术库是空的。
话术组
{{ r.name }}
{{ r.code }} · {{ r.n_live
}} / {{ r.n_lines }} 句 · 隔 {{ r.gap_sec }} 秒 · 约 {{
r.span_sec }} 秒发完
{{ k + 1 }}. {{ ln.content }} —— {{
liveBanCn(ln.ban_hit) }} —— 已停用
{{ r.is_active ? '一句能发的都没有' : '已停用' }}
加
已加 {{ liveSeqCount('group', r.id) }} 次
没有匹配「{{ live.ed.kw }}」的话术组。
还没有话术组 —— 逼单三连那种,去「话术组」建一个。
+ 在这儿新写一条
+ 去话术组建一个
{{ live.ed.tab === 'phrase'
? '写完直接进库,也进这一场' : '组要排几句、设间隔,在那一页建' }}
被卡审拦下的挑不进来 —— 它进了序列也会被跳过,去话术库改一下
节奏与额度
运行时段 {{
(live.ed.run_from || live.ed.run_to) ? ((live.ed.run_from || '00:00') + '—' + (live.ed.run_to || '24:00')) : '全天' }} ·
项与项之间 {{ live.ed.interval_sec }} 秒 ± {{ live.ed.jitter_pct }}%(话术组之间也按它)·
本场上限 {{ String(live.ed.cap || '').trim() === '' ? '不限' : (live.ed.cap + ' 条') }} ·
直播页随机刷新 {{ live.ed.page_refresh_min_min }}—{{ live.ed.page_refresh_max_min }} 分钟
· 全局:同号最短 {{ (live.sess.plan || {}).min_gap_sec || 20 }} 秒 ·
同组冷却 {{ Math.round(((live.sess.plan || {}).group_cooldown_sec || 0) / 60) }} 分
去运行参数改
评论触发{{
(live.sess.trigger || {}).on ? '开着' : '关着' }}
{{
(live.sess.trigger || {}).n_rules || 0 }} 条规则 ·
这一场还没人读公屏,开着也不会响
观众说到关键词会自动回一组话术
观众说什么都不会自动回
去触发规则改
(全局,不分场次)
{{
live.ed.rhythm ? '收起' : '展开改' }}
运行时段
—
北京时间。出了时段停发不结束,
回到时段自动恢复;两头留空=全天
多久发一项
秒 ·
% 抖动
抖动是为了不让节奏
看起来像机器。一项 = 序列里的一格(一句话术,或者一整组);
话术组之间也按它,起点是上一次派发结束(失败的也算)
直播页保活刷新
—
分钟(随机)
可填 10—120 分钟;仅防直播页长时间灰屏,
每轮从这个区间重新随机。正在发送话术时会等当前话术结束后再刷新
本场上限
按场算不按天算 —— 一天两场各算各的;留空=不限
「先建着」落在配置中,还不发;「建好就开播」直接进进行中,选中的号下一次心跳就开始发
存下去下一项就按新的走;已经排在队里的那一条不受影响
方案是开一场时的起点 —— 开播那一刻复制进那一场,之后两边各走各的
存成一个方案
取消
{{
live.ed.kind === 'preset' ? '存方案'
: ((live.ed.id && live.ed.state !== 'draft') ? '存下来' : '先建着') }}
建好就开播
{{ live.roles.send }}与{{ live.roles.watch }}都要选 ——
{{ live.roles.watch }}不发言,但没有它就没人从公屏上读回执,
「公屏确认」会一直是 0,而那与"全被限流了"长得一模一样。
{{
s.nickname || liveUidShort(s.uid) }}
{{ live.roles[s.role] || s.role }}
主播本人
没启用
一个席位都没有 —— 先把插件装到要发言的那台电脑上(配置 → 浏览器插件)。
主播本人的号选不了。没启用的号选进来也发不出去 ——
去「账号席位」把它开成{{ live.roles.send }}再回来。
新建方案
方案是开一场时的起点:
开播那一刻复制一份进那一场,之后两边各走各的 —— 改这里不影响已经开着的场次。
点「改」进的是和「新建场次」同一个构建器(独立页,不是抽屉)
名字直播间用几个号发送序列
节奏本场上限
{{ p.name }}
{{ p.note }}
{{ p.room_id }}
{{ (p.seats || []).length }} 个
话术 ×{{ (p.seq || []).filter(x => x.kind === 'phrase').length }}
话术组 ×{{
(p.seq || []).filter(x => x.kind === 'group').length }}
{{
(p.seq || []).map(x => x.name || x.code).join(' → ') || '序列是空的' }}
{{ p.interval_sec }} 秒一项
{{ p.cap }} 条
改
复制
照它开一场
删
还没有方案 —— 每天开的是同一套配置的话,存一份在这儿,以后一点就开。
今天有一场正在进行
去开播台
一场一行。结束之后账就封口了 ——
当时用的配置、序列、发了多少、公屏确认多少,都留在那一行里改不动
刷新
场次时段时长方案
发出公屏确认未广播怎么结束的
{{ r.name }}
业务日 {{ r.day || '—' }} · 直播间 {{ r.room_id }}
{{ (r.started_cn || '').slice(11, 16) || '—' }}
— {{ r.ended_cn ? r.ended_cn.slice(11, 16) : liveStateShort(r) }}
{{ liveDurCn(r.dur_sec) }}
{{ r.preset_name || '空白建的' }}
改过 {{ r.n_edit }} 次
{{ r.tally.out }}
{{ r.tally.broadcast }}
{{
Math.round(r.tally.broadcast / r.tally.out * 100) }}%
{{ r.tally.noack }}
—
{{ r.end_cn || r.pause_cn || r.state_cn }}
看这一场
还没有开过场 —— 去「开播台」开第一场。
「{{ liveWorstSess.name }}」的公屏确认率只有 {{ liveWorstRate }}%
这一场发出 {{ liveWorstSess.tally.out }} 条、确认 {{ liveWorstSess.tally.broadcast }} 条、
未广播 {{ liveWorstSess.tally.noack }} 条。
未广播是唯一能回答"有没有被限流"的信号 ——
但它也可能只是那一场没有在线的观察席。
点开看它发了什么 →
这 {{ liveCompareBase }} 场的公屏确认率都在 80% 以上
没有明显被限流的迹象。⚠️ 前提是那几场都有在线的观察席 ——
没人看的时候「确认」这个数说明不了任何事。
还没有够得着比较的场次
要两场以上、每场至少发出 5 条才比得出确认率的高低。
现在不下任何结论 —— 条数太少时那个百分比是噪音。
这一页一行是一场,「历史看板」一行是一天
一天开两场时,这一页看得出是两场、各自的账分得开;「历史看板」把它们
按业务日合起来算。要复盘某一场就来这儿,要看一天的总量去那边。
发出
{{ (live.sl.det.stat || {}).sent || 0 }}
/ 上限 {{ live.sl.det.stat.cap }}
公屏确认
{{ (live.sl.det.stat || {}).ack || 0 }}
/ {{ (live.sl.det.stat || {}).sent || 0 }}
未广播
{{
(live.sl.det.stat || {}).noack || 0 }}
按规则跳过
{{ (live.sl.det.stat || {}).skipped || 0 }}
{{
live.sl.det.skip_by.map(x => x.why_cn + ' ' + x.n).join(' · ') }}
当时的发送序列
{{ it.seq }}. {{
it.name || it.code }}
序列是空的
发了什么(最近 {{
(live.sl.det.rows || []).length }} 条)
时间哪个号发了什么来自状态
{{ (r.sent_at_cn || r.planned_at_cn || '').slice(11, 19) || '—' }}
{{ r.seat_nick || r.seat_uid || '—' }}
{{ r.sent_text || r.gen_text }}
{{ r.err }}
{{ r.from_cn || '—' }}
{{ liveStCn(r) }}
这一场一条都没发出去。
这一页是全局的下界,跨场累计。
停发 / 运行时段 / 发送间隔 / 本场上限都在
开播台这一场上改。
下面改过的值还没存,点「保存」才生效
保存
同号最短间隔(秒)
一个号两次开口的下界,按派活算(失败的也算)、跨场累计。
20 秒是唯一实测验过的那一档(2026-08-28,9/9 全部广播)。
更快的没测过 —— 要提速请先看几天可见率曲线,别先拍一个数。下界锁在 20。
同话术组冷却(秒)
同一个话术组多久之内不再发,不分哪个号发的、跨场累计。
序列轮到一个还在冷却的组就跳到下一项;一圈全在冷却就等最早解冷的那一组(开播台会写明)。
「立即逼单」也受它约束 —— 这组刚发过时那颗按钮直接禁掉并写明原因。
立即发送冷却(秒)
手点「立即发送」之后多久之内不许再点。
跨进程锁只保证不同时跑,不保证不连着跑,而对平台来说这两件事完全一样 ——
主播喊一次上链接、十几秒没看见动静就再点一次,是最容易把号打进限流的那条路。
这一页改的是全局下界,不是"这一场怎么播"
发什么、用哪几个号、往哪个直播间发、几点到几点、多久一项、本场上限,都在开播台的这一场上;
一条话术的几个说法轮着用,最久没用过的先上,不设冷却。
账号轮替是规则不是参数:能发的号里上一次开口最早的先上,30 秒没来领就顺延。
装在运营自己电脑的 Chrome 上。BL 决定什么时候发哪一句,插件只负责把字写进公屏的
输入框、按下发送 —— 抖音登录态一个字都不进服务器。
刷新
四步:拿包 → 装进 Chrome → 粘令牌 → 打开直播间
每台电脑各走一遍。装完之后,这台电脑上登录的抖音号会出现在「账号席位」里 ——
能不能发言要在那一页显式开。
① 拿包
插件清单取不到
{{ live.st.extErr || '多半是权限或网络,刷新一下再看' }}
插件包还没传上来
需要管理员传一版。
上传插件包
{{ live.st.ext.name }} v{{ live.st.ext.version }}
下载
上传新版
{{ if2ExtSize(live.st.ext.size) }} ·
{{ live.st.ext.username }} 于 {{ bjTimeShort(live.st.ext.created_at) }} 上传
{{ live.st.ext.note }}
{{ if2.extOpen[live.st.ext.ext_key] ? '▾' : '▸' }}
历史版本({{ live.st.ext.history.length }})—— 新版有问题可以退回去
v{{ h.version }}
{{ bjTimeShort(h.created_at) }}
{{ h.note }}
下载这一版
② 装进 Chrome
解压到一个以后不会删的目录(Chrome 记的是路径,挪了就失效)→
浏览器打开 chrome://extensions → 右上角打开「开发者模式」→
「加载已解压的扩展程序」→ 选那个目录。
更细的(含升级怎么办)在压缩包里的《安装说明.txt》。装完把下面那枚令牌粘进
插件的「选项」页。
③ 令牌
令牌列表取不到
{{ live.st.toksErr ||
'你可能只有查看权 —— 签发需要「直播」的编辑权' }}
还没签过
签一枚
已有 {{ live.st.toks.length }} 枚
管理 / 再签一枚
一枚就够,装几台粘几台。明文只在签发那一次显示,之后谁也读不出来 ——
丢了就吊销旧的再签一枚。⚠️ 令牌跟着签发人走:他被停用或调走部门时它当场失效。
⚠️ 这个数是你名下全部的推送令牌,不分用途(同一张表也给素材库、
灰豚那几条链用)—— 看得到数字不等于已经签过直播这一枚。
④ 装完确认
插件一次都没来问过。三种都长这样,在 BL 这边分不开:
① 那台电脑上没有开着直播间的标签页 —— 插件没有直播间就不联网,
这一种最常见;② 令牌还没粘进插件的「选项」页(填完要点保存);
③ 那台电脑上插件没启用 / 装完没点过 ↻。
把鼠标停在扩展图标上,它会直说是哪一种。
插件来问过(最后一次 {{ (live.st.agentSeenCn || '').slice(5, 16) || '刚刚' }}),
但一个席位都没落下 —— 多半是那台电脑上还没登录抖音
(读不到"我是谁"时插件不猜,宁可不上报)。
有席位但读不到房间号 —— 那台电脑的标签页不在直播间页面上
(地址要是 live.douyin.com/<数字>)。
已经有 {{ live.st.rows.length }} 个席位
接着去「账号席位」给它们开权限。
接下来
去账号席位开权限
去开播台开一场
往哪个直播间发是这一场的事,开一场的时候填;没开场一条都不发。
日期排了发出去
公屏确认按规则跳过失败
{{ h.day }}
{{ h.planned }}
{{ h.sent }}
{{ h.ack }}
{{ h.skipped }}
{{ h.failed }}
还没有任何发送记录。
「公屏确认」只统计观察席真的在公屏上看到的那些。那一天没有观察席在线时它恒为 0,
那不代表没播出去 —— 所以这里刻意不给百分比。
用户挂部门后按部门权限访问;未分部门的普通用户 = 无任何权限(须分配部门后才能使用,2026-07-21 安全加固);管理员不受限制
新增部门
{{ m.label }}·编辑
{{ m.label }}·查看
无任何页面权限
只改显示的名字:权限记的是内部标识,与名字是两回事,改名不会影响任何人的权限。留空=用默认名。
重置未保存的修改
保存并刷新页面
{{ g.def }}顶部 tab
{{ g.desc }}
最多 {{ mcfg.maxLen }} 个字。保存后页面会自动刷新一次——菜单结构在多处渲染,刷新是最省事也最不会出错的做法。
类型
{{ lb }}
刷新
{{ plog.note }}
{{ d.what }}
{{ d.flag }}
{{ d.plain }}
{{ d.from || '(空)' }}
→
{{ d.to || '(空)' }}
无实质变化(保存了一次但内容没变)
{{ plog.open[record._k] ? '收起' : '展开' }}
看全文
{{ plog.detail.row.at }} · {{ plog.detail.row.who || '—' }} ·
{{ plog.kinds[plog.detail.row.kind] || plog.detail.row.kind }} ·
{{ plog.detail.row.target || '—' }}
{{ d.what }}
{{ d.flag }}
{{ d.plain }}
{{ d.from || '(空)' }}
→
{{ d.to || '(空)' }}
备注{{ plog.detail.row.note }}
{{ w === 'old_json' ? '改之前(原始)' : '改之后(原始)' }}
{{ opRawJson(plog.detail.row, w) }}
同平台店铺共用一套账单格式与计算规则;账单数据源链接 = 该店在平台后台的账单下载页
显示归档主体({{ archiveShopCount }})
新增店铺
{{ record.source_url }}
未配置
{{ cnt(record.data_rows) }}
{{ record.is_active ? '启用' : '禁用' }}
—
{{ (mcpChannel && mcpChannel.cn) || '' }}通道
{{ (mcpChannel && mcpChannel.desc) || '' }}
使用说明
怎么接 四步,一次就好 —— 下面那段配置里的条目名与地址是这条通道自己的,别跟另一条混用
复制右边这段,粘进 WorkBuddy 的 mcp.json(别的 MCP 客户端同理)。
第一次连接时,浏览器会自动弹出一张授权页。
在那一页上用你自己的 BL 账号点一下「授权」—— 它拿到的权限就是你此刻的权限,一分不多。
想收回,随时到下面那张表里点吊销,立刻失效、不可恢复。
{{ mcpConf() }}
手工签静态令牌那条路已经关掉(2026-09-10)——
原先那种明文令牌不会过期、也不绑定客户端;而登录授权拿到的这枚跟着你的账号走:
你的权限变了它立刻跟着变,停用账号它当场失效。历史上签过的那些在下面照常看得见,也随时能吊销。
「技能包」那一页已经撤了
那套离线算账脚本 2026-09-10 整体退役。装过的人请把本机的
~/.workbuddy/skills/bl-finance-report/ 目录删掉 ——
留着它会接着按退役那天的口径算,而算出来的数看着完全正常,不会报错。
要算账走上面这条 MCP:报表由 BL 现算,口径永远是线上这一版。
已授权的应用 一行一个连过来的客户端。「现在能用」是按授权人此刻的部门权限实时算的,不是授权那天的快照
{{ record.channel_cn }}
旧口径
{{ m }}
当前无权限
{{ record.revoked ? '已吊销' : '有效' }}
{{ record.n_used }} 次
还没用过
{{ record.last_used_at }}
—
—
{{ (mcpChannel && mcpChannel.cn) || '' }}通道
契约指纹 {{ mcp.ct.contract }} —— 工具名 / 参数 / 说明变了它才变,改实现不算
这条通道给你开了 {{ (mcp.ct.tools || []).length }} 个技能,按你当前的部门权限算
使用说明
这条通道的边界 四条都是结构性的,不是设置项
只读助手改不了 BL 里的任何东西 —— 不能建档、不能刷新、不能改标记。要写还是回页面上点。
权限跟着你走能调什么,按授权人当前的部门权限每次现查。权限被摘掉,同一条连接当场就调不动。
给得出什么{{ (mcpChannel && mcpChannel.desc) || '' }}
每次都留痕谁在什么时候取走了什么,都记在「调用记录」页上,留档 90 天。
{{ t.name }}
{{ t.menu_cn }}
会写数据
{{ t.body || t.desc }}
参数
必填
说明
{{ p.name }}
必填选填
{{ p.desc || '—' }}
不用传参,直接调。
你当前的部门权限在这条通道上一个技能都用不了 ——
连上去也是空的。找管理员在「系统管理 → 部门管理」里补权限。
{{ (mcpChannel && mcpChannel.cn) || '' }}通道
{{ mcp.isAdmin
? '你是管理员,这里是全部人的调用'
: '只列你自己授权的那些连接的调用' }} —— 留档 90 天,含入参与返回行数
{{ record.tool }}
{{ record.method }}
失败
成功
这条通道还没有人调用过。接上之后,每一次取数都会记在这里。
注册邀请码找管理员要;新用户也可由此直接创建
新增用户
{{ record.role === 'admin' ? '管理员' : '普通用户' }}
{{ record.is_active ? '启用' : '禁用' }}
模板库
三种模板走三条完全不同的路,建的时候选错就用不上:背景图随原图一起喂给 AI,让它把图搬上去(批量任务的第二步 / 换背景图任务用);白底画布与氛围图都不进 AI,由页面直接合成——白底画布是垫在产品下面的底,氛围图是盖在商品图上面的装饰层。
{{ if2TplKindCn(k) }}
启用中已停用
新增模板
{{ if2TplHasQ() ? '没有符合条件的模板。换个搜索词或清掉筛选试试。' : '还没有模板。把你的品牌底板(空白的那张,只有品牌头和角标)传进来,新建任务时就能选它。' }}
{{ if2.tplSel[t.id] ? '✓' : '' }}
{{ t.name }}
{{ if2TplKindCn(t.kind) }}{{ t.kind === 'fontref' && t.lang ? ' · ' + if2LangCnOf(t.lang) : '' }}
{{ t.note }}
{{ t.used_by ? (t.used_by + ' 个任务在用') : '暂无任务使用' }}{{ t.is_active ? '' : ' · 已停用' }}
改名
{{ t.is_active ? '停用' : '启用' }}
{{ t.used_by ? ('仍要删(影响 ' + t.used_by + ' 个任务)') : '确认删除' }}
取消
删除
新增模板
底图请传空白底板(只有品牌头和角标、中间没有产品的那张)。
点击选择底图
拖入 / 粘贴也行
用途
给哪门语言用
同一门语言传了多张时,取排序最前的那张。传上来即接管——原先在「图工厂设置」里配的那张自动让位。
取消
{{ if2.tplBusy ? '上传中…' : '创建' }}
AI 生图
写一句话就出图。可以传参考图、在参考图上圈出要改的地方(选区只是给 AI 的引导,不保证选区外完全不动)。出来的图收进素材库,之后能挑进翻译任务。
历史记录 ↗
1. 模型
{{ aiGen.model || 'gpt-image-2' }}
2. 提示词
aiPhTo('ai', -1, t)">
插入引用
{图{{ i+1 }}}
{图{{ i+1 }}选区{{ k+1 }}}
3. 参考图(可选,最多 {{ aiGen.maxRefs }} 张)
✕
▢ 标注
图{{ i+1 }}{{ (aiGen.marks[i]||[]).length ? ' · ' + aiGen.marks[i].length + ' 选区' : '' }}
+
▤素材库
4. 尺寸 / 分辨率
分辨率
{{ r.toUpperCase() }}
比例
{{ r === 'auto' ? '自动' : r }}
{{ aiGen.busy ? '提交中…' : '✳ 生成图片' }}
↻ 重置
{{ aiGen.err }}
生成结果
🖼
生成结果将在此处显示
输入提示词并点「生成图片」开始创作
这里只显示本次生成的图;以前出的都在「历史记录」里
{{ a.error || '生成失败' }}
{{ a.status === 'queued' ? '排队中…' : '生成中…' }}
已等 {{ aiWait(a) }}
比平时久(一般 1~2 分钟)。上游忙的时候会这样,再等等或删掉重来一次。
{{ a.prompt }}
{{ a.size }} · {{ (a.resolution||'').toUpperCase() }} · {{ bjTimeShort(a.created_at) }}
{{ a.set_id ? '再收一次' : '收进素材库' }}
标注选区 — 图{{ aiGen.mk.i + 1 }}
▢ 方框选取
✎ 画笔涂抹
撤销
清除全部
{{ aiMkNo(k, aiGen.mk) }}
已标注 {{ aiMkRectN(aiGen.mk) }} 个选区{{ aiMkBrushN(aiGen.mk) ? ('(另有 ' + aiMkBrushN(aiGen.mk) + ' 笔涂抹,会合并成一块区域说给 AI)') : '' }} · 选区只是引导,不保证选区外完全不动
取消
确认标注
从素材库选参考图
已选 {{ aiGen.refs.length }} / {{ aiGen.maxRefs }} 张
完成
把这张图收进素材集
收进去之后它会出现在「加工产物」那一栏,可以直接挑进翻译任务。
批量生成
一次配好 N 条各自不同的提示词,挂着让它慢慢跑。四种玩法见新建页顶部的切换。要出一张就好,去单张生图。
{{ aiBat.picking ? '取消选择' : '选择' }}
刷新
+ 新建批量任务
批次总数
{{ aiBat.sum.batches || 0 }}
{{ aiBat.sum.rows || 0 }} 行
已完成
{{ aiBat.sum.done || 0 }}
成功率 {{ Math.round((aiBat.sum.rate || 0) * 100) }}%
进行中
{{ aiBat.sum.active || 0 }}
{{ (aiBat.sum.active||0) ? '涉及批次' : '都跑完了' }}
失败
{{ aiBat.sum.failed || 0 }}
{{ (aiBat.sum.failed||0) ? '行,点进去看原因' : (Object.keys(aiBat.sum||{}).length ? '无异常' : '——') }}
草稿
{{ aiBat.sum.drafts || 0 }}
{{ (aiBat.sum.drafts||0) ? '存着还没提交' : '无草稿' }}
{{ batAllSel() ? '取消全选' : '全选' }}
已选 {{ batSelIds().length }} 个
删除选中({{ batSelIds().length }})
🗂
{{ (aiBat.items||[]).length ? '这个筛选下没有批次' : '还没有批量任务' }}
{{ (aiBat.items||[]).length ? '换个筛选条件看看' : '点右上角「+ 新建批量任务」开始' }}
{{ batModeCn(b.mode) }}
{{ b.prompt || '(没有提示词)' }}
{{ batStCn(b.state) }}
{{ b.batch_no }}
{{ bjTimeShort(b.created_at) }}
{{ b.size }}
{{ (b.resolution||'').toUpperCase() }}
衍生自 {{ b.from_batch }}
{{ i+1 }}
{{ b.state==='draft' ? '草稿,还没提交' : (b.state==='active' ? '正在生成…' : '还没有出图') }}
{{ b.done }} / {{ b.total }} 完成 {{ b.failed }} 失败
接着编
复用
查看详情 →
{{ b.batch_no }}
{{ b.prompt || '(没有提示词)' }}
{{ batModeCn(b.mode) }}
{{ batStCn(b.state) }}
{{ b.done }}/{{ b.total }} ✕{{ b.failed }}
{{ aiBat.live ? aiBat.no : '新建批量任务' }}
四种玩法在下面切。不限条数——但一行约 95 秒,配多少条就要等多久,页面下方会实时算给你看。
已提交,跑完会自动出现在下面。这一批衍生自 {{ aiBat.from }}。可以关掉页面,回来再看。
{{ aiBat.err }}
玩法
{{ batModeCn(m) }}
▤ 模板灵感
{{ batModeHint() }}
模型
{{ aiBat.model }}
分辨率
{{ r.toUpperCase() }}
比例
{{ r === 'auto' ? '自动' : r }}
共用提示词模板 所有行都用这一段
aiPhTo('tpl', -1, t)">
插入槽位引用
{图{{ i+1 }}}
「图N」=第 N 个槽位那张图,AI 收到的就是 image N,指得准
槽位定义 每个槽位放一张图,下面每一行按这个次序填
{{ i+1 }}
✕
共 {{ batSlotFilled(i) }} 张
batSlotBulk(i, f)">
⇪ 批量上传到「图{{ i+1 }}」
+加位置
共用参考图 全批每一行都会带上这几张(最多 {{ aiBat.maxRefs }} 张)
✕
▢ 标注
共用图{{ i+1 }}{{ (aiBat.sharedMarks[i]||[]).length ? ' · ' + aiBat.sharedMarks[i].length + ' 选区' : '' }}
batPickFiles(-1, f)">
+
▤素材库
任务行
{{ aiBat.rows.length }} 行
✎ 全部再编辑
⋮⋮
{{ ri+1 }}
再编辑
✕
▢
{{ batRowMarks(ri)[si].length }}区
batPickFiles(ri, f, si)">
没放图
✕
▢
{{ batRowMarks(ri)[i].length }}区
aiPhTo('row', ri, t)">
插入引用
{图{{ i+1 }}}
{图{{ i+1 }}选区{{ k+1 }}}
用上面那句共用模板,这一行只换素材。
槽位只填了一半,这一行还不能跑。
→
✕
✓
{{ batRowLabel(ri) }}
{{ batRowSub(ri) }}
+ 添加一行
{{ aiBat.live ? '加的行会并进这一批一起跑' : '不限条数,但一行约 95 秒' }}
准备好 {{ batReady() }} / {{ batNewRows().length }} 行(新加的)
· {{ batBad() }} 行还不能跑
预计约 {{ batEta() }}(期间图工厂其它出图会排在后面)
存草稿
存成模板
{{ aiBat.busy ? '提交中…'
: ((aiBat.live ? '✳ 生成新任务 ' : '✳ 提交 ') + batReady() + ' 行') }}
模板灵感
把配好的一批(玩法、槽位、提示词、参考图、所有行)存成模板,下次一键套用。本部门共用。在批量生成页底部点「存成模板」即可新增。
▤
还没有模板
在「批量生成」里配好一批,点底部的「存成模板」,它就会出现在这儿
{{ t.name }}
{{ batModeCn((t.payload||{}).mode) }}
{{ ((t.payload||{}).rows||[]).length }} 行
· {{ (t.payload||{}).slots.length }} 个槽位
· {{ (t.payload||{}).size }} / {{ ((t.payload||{}).resolution||'').toUpperCase() }}
{{ batTplPrompt(t) || '(每行各写各的提示词)' }}
{{ t.username }} 于 {{ bjTimeShort(t.created_at) }}
套用
标注选区 — {{ aiBat.mk.r < 0 ? '共用图' : '图' }}{{ aiBat.mk.i + 1 }}
▢ 方框选取
✎ 画笔涂抹
撤销
清除全部
{{ aiMkNo(k, aiBat.mk) }}
已标注 {{ aiMkRectN(aiBat.mk) }} 个选区{{ aiMkBrushN(aiBat.mk) ? ('(另有 ' + aiMkBrushN(aiBat.mk) + ' 笔涂抹,会合并成一块区域说给 AI)') : '' }} · 选区只是引导,不保证选区外完全不动
取消
确定
还没有模板。配好一批之后点底部「存成模板」,下次就能一键套用
{{ t.name }}
{{ batModeCn((t.payload||{}).mode) }} · {{ ((t.payload||{}).rows||[]).length }} 行 · {{ t.username }} 于 {{ bjTimeShort(t.created_at) }}
{{ (t.payload||{}).prompt_template || '(每行各写各的提示词)' }}
套用
模板名
会把当前的玩法、槽位、提示词、参考图与所有行一起存下来,本部门都能套用。
{{ aiLbCur().ai_prompt || 'AI 生图' }}
{{ aiLb.idx + 1 }} / {{ aiLb.list.length }}
←
→
图{{ i+1 }}
参考图 {{ aiLb.refIdx + 1 }}
产物
{{ aiLbCur().ai_error || '生成失败' }}
还没有出图
加载原图…
{{ batRowStCn(aiLbCur().gen_status) }}
{{ aiBat.model || 'gpt-image-2' }}
{{ aiLbCur().ai_reso.toUpperCase() }}
{{ aiLbCur().ai_size }}
{{ bjTimeShort(aiLbCur().created_at) }}
{{ aiLbCur().ai_batch_no }}
{{ aiLbCur().ai_prompt || '(没有提示词)' }}
浏览器插件
装在你自己电脑的 Chrome 上,用来采集素材 / 处理 TikTok 视频。下载 zip → 解压 → 打开 chrome://extensions → 右上角开启「开发者模式」→ 点「加载已解压的扩展程序」选那个文件夹。更新时重复一遍即可,装过的会被覆盖。
+上传新版
还没有上传任何插件包。{{ isAdmin ? '点右上「上传新版」把打好的 zip 传上来。' : '让管理员传一个上来。' }}
{{ e.name }}
v{{ e.version }}
有更新
{{ e.note }}
{{ if2ExtSize(e.size) }} · {{ e.username }} 于 {{ bjTimeShort(e.created_at) }} 上传
下载最新版
{{ if2.extOpen[e.ext_key] ? '▾' : '▸' }} 历史版本({{ e.history.length }})—— 新版有问题可以退回去
v{{ h.version }}
{{ bjTimeShort(h.created_at) }}
{{ h.note }}
下载
删除
素材库
贴一个抖店商品链接,自动收下该商品的详情图与主图,按商品存成一个素材集,之后反复取用。
商品图
素材集
商品链接
TikTok 产品链接
图片
状态
更新时间
操作
还没有素材集。点右上「从商品链接导入」,粘贴一个抖店商品详情页链接。
无图
{{ s.title }}
{{ s.promotion_id }}
{{ s.n_images }} 张
{{ bjTimeShort(s.updated_at) }}
← 返回素材库
{{ mat.set ? mat.set.title : '' }}
改名
· {{ mat.set.shop }}
{{ matPlatCn(p) }}链接
{{ matPlatCn(p) }} ↗
{{ matLinkOf(p).item_id }}
复制
更换
删除
{{ mat.busy ? '获取中…' : '重新获取图片' }}
图由插件在商品页推送(服务端取不到)
未添加
+ 添加
{{ mat.busy ? '处理中…' : (matCanWrite(mat.tab) ? '+ 上传到' + matTabCn(mat.tab) : '+ 上传图片') }}
复制到{{ matTabCn(t) }}{{ matTabSel().length ? '('+matTabSel().length+')' : '' }}
{{ mat.sel.length ? '下载选中('+mat.sel.length+')' : '全部下载' }}
删除({{ mat.sel.length }})
送进翻译任务{{ mat.sel.length ? '('+mat.sel.length+')' : '' }}
主图没取到(详情图不受影响,已全部收下)。原因:{{ mat.set.main_error }}
主图要经取图边车(独立容器里跑的浏览器)才拿得到,边车镜像没建或起不来时就是这个状态。
可以在上方抖店链接那一行点「重新获取图片」重试,或直接用「上传图片」把主图传进来。
正在取图…{{ mat.set.import_msg ? '(' + mat.set.import_msg + ')' : '' }}
图片会陆续出现在下面,取完会自动拼出完整长图。
可以关掉这个页面——导入在服务器上跑,回头再进来看结果就行。
上次导入没跑完:{{ mat.set.import_msg }}
可以在上方抖店链接那一行点「重新获取图片」重试,已经收下的图不受影响。
完整长图没拼出来:{{ mat.set.long_error }}
素材本身都在,可以逐张切;或删掉几张过大的图后再导入一次。
本页 {{ matTabItems().length }} 张{{ matTabSel().length ? ',已选 '+matTabSel().length : '' }}
{{ matTabAllSel() ? '取消全选本页' : '全选本页' }}
其它标签页还选着 {{ matSelOther() }} 张 · 点此取消
点图放大;左上角方框勾选;也可以直接把图片拖进本页或 Ctrl+V 粘贴
这个素材集还没有图。可以「上传图片」、把图拖进来、Ctrl+V 粘贴,或「+ 添加商品链接」。
{{ sec.label }} {{ sec.items.length }}
长图与它切出来的段;送进翻译任务时别和原图重复选
从翻译任务回填的出图产物,不参与拼长图
▶
{{ matKindCn(im.kind) }}
{{ matPlatCn(im.platform) }}
{{ im.kind==='video' ? '视频' : (im.width + '×' + im.height) }}
切割
‹ 上一张
{{ mat.zoom+1 }} / {{ matZoomList().length }} · {{ matKindCn(matZoomImg().kind) }} · {{ matZoomImg().width }}×{{ matZoomImg().height }}
下一张 ›
-
{{ Math.round(mat.zoomScale*100) }}%
+
贴合
去切割
原尺寸打开
关闭 (Esc)
删除 {{ mat.sel.length }} 张素材图
只从本素材集移除,已经送进翻译任务的那些拷贝不受影响(进任务时是拷贝一份,不是引用)。
删除会记进操作日志。
{{ matLinkOf(mat.linkPlat) ? '更换' : '添加' }}{{ matPlatCn(mat.linkPlat) }}链接
取消
{{ mat.busy ? '导入中…' : '导入' }}
插件推送令牌
直播席位插件用这个专用令牌调 BL,不是你的登录账号 ——
它只能问「现在该发哪一句」和回报结果,动不了账单,丢了单独吊销即可。
一枚就够,装几台粘几台;明文只显示这一次,之后谁也读不出来。
⚠️ 令牌跟着签发人走:签发人被停用或调走部门时它当场失效 ——
这是刻意的(离职流程不该漏掉一条还在往公屏发言的通道)。
⚠️ 能不能发言不由令牌决定:插件装上去只让那个号在「账号席位」里
出现,发不发是你在那一页上点的。
淘宝商品页必须登录才看得到图(服务端和无头浏览器都会被挡在登录页外),所以淘宝素材由
浏览器插件在你已登录的页面里抓好、推给 BL。插件用这个专用令牌调接口——
不是你的登录账号:它只能往素材库推图,动不了账单,丢了单独吊销即可。
接口:POST /api/img/material/push,
请求头 X-Push-Token,
body:{platform, url, title, images:[{url, kind, data}]},
kind 取 main / sku / detail。
data = 图片二进制的 base64,强烈建议带上:淘宝有一类图从服务器侧取不到
(alicdn 恒返回 420,与请求头和并发都无关),而插件那边图早就加载好了,
直接推字节即可,BL 不再出网取第二遍。单张 ≤8MB、单次合计 ≤20MB。
不带 data 就退回 BL 自己下载,同一批可混用。
新令牌只显示这一次,请立刻复制保存
{{ mat.newTok }}
复制
全域令牌:不绑部门,能读写所有部门的灵感行——这是这套凭证里权限最大的一档,
只给「灰豚数据桥」插件用。它不能往素材库推图;签发人被降级或禁用后会立即失效。
还没有令牌
这台库里一共 {{ mat.tokTotal }} 枚令牌,这里只列了最新的 {{ mat.tokLimit }} 枚。
多出来的多半是席位令牌(seat:…,比特窗口每次被拉起时自动签一枚、上一枚当场作废)——
它们在「配置 → 机器与窗口」那一页按窗口看得到,不用在这里找。
{{ t.token_head }}…
{{ t.label }}
全域 · 灰豚桥
{{ t.group }}
未配部门
用过 {{ t.n_used }} 次{{ t.last_used_at ? ',最近 '+bjTimeShort(t.last_used_at) : '' }}
已吊销
吊销
从商品链接导入素材
商品链接(抖店 / 淘宝天猫)
提交后在服务器后台取图,可以直接关掉页面,回头再进素材集看结果。
同一个商品重复导入不会重复建集,也不会重复下载已有的图;取完自动拼出完整长图。
{{ mat.importErr }}
取消
{{ mat.busy ? '提交中…' : '开始导入' }}
画切割线({{ mat.cutImg.width }}×{{ mat.cutImg.height }})
鼠标移到哪儿,虚线就跟到哪儿——点一下把它定成一条切割线,点线上的 ✕ 删除。将切成 {{ mat.cutYs.length + 1 }} 段。原图保留,切出来的段作为新素材加入本集。
✨ 自动识别切割线
{{ mat.autoInfo }}
y={{ mat.hoverY }}
✕
y={{ y }}
清空线
取消
{{ mat.busy ? '切割中…' : '切成 '+(mat.cutYs.length+1)+' 段' }}
把 {{ mat.sel.length }} 张素材送进翻译任务
任务备注(可留空,任务号自动生成)
选一个已有任务
{{ matSelWarn() }}
素材会拷贝一份进任务(不是引用):之后在素材库改名或删除,不影响已在跑的任务。
进任务后是「已上传,还没开始识别」状态,需要在任务里点「开始识别翻译」。
{{ mat.jobErr }}
取消
{{ mat.busy ? '处理中…' : (mat.jobMode==='new' ? '新建任务并送入' : '送进任务') }}
把 {{ if2Backfillable().length }} 张成品图回填到素材集
选一个素材集
只回填已经做好的那些;进去后在素材集的「成品图」tab 里,不参与拼长图。
同一张回填两次不会重复。任务被删掉后成品图仍留在素材集里。
{{ if2.bfErr }}
从素材库导入到 {{ if2.job ? if2.job.job_no : '' }}
素材库还是空的,先去「素材库」导一个商品。
{{ s.title }}
{{ s.n_images }} 张
左边选一个素材集
共 {{ if2.matImgs.length }} 张(含加工产物 {{ if2.matImgs.filter(function(x){return x.kind==='long'||x.kind==='cut'}).length }} 张),已选 {{ if2.matSel.length }}
{{ if2.matSel.length===if2.matImgs.length && if2.matImgs.length ? '取消全选' : '全选' }}
{{ matKindCn(im.kind) }}
✓
{{ if2.matErr }}
取消
{{ if2.linkBusy ? '导入中…' : '导入 '+if2.matSel.length+' 张' }}
批量任务
一个任务 = 一批原图。新建后上传原图,系统识别翻译,你逐条确认再统一出图。也可以直接把一批图片拖进本页或 Ctrl+V 粘贴,自动新建任务。
{{ o }}
搜索
重置
共 {{ fmt(if2.total) }} 个任务
{{ record.job_no }}
{{ record.note }}
+{{ record.n_imgs-4 }}
{{ record.n_imgs }} 张 · {{ record.n_rows }} 条 · {{ if2LangCnOf(record.lang) }}
{{ if2TplOf(record.template_id).name }}
原图背景
{{ if2StatusText(record.status) }}
{{ record.n_done }} / {{ record.n_imgs }} 张已出图
{{ if2HasQ() ? '没有符合条件的任务。换个搜索词或清掉筛选试试。' : '还没有任务。点右上「新建批量任务」,上传一批要翻译的中文图。' }}
新建批量任务
底板模板(可空 —— 留空则保留原图自己的背景)
保留
原图背景
{{ t.name }}
目标语言
底板与语言会记住你这次的选择,下次新建自动带上(只记在这台电脑上)
{{ if2.job.job_no }} 不是批量翻译任务
它是「{{ if2ModeCn(if2.job.mode) }}」任务,请到对应的菜单里打开。
← 回批量任务列表
← 返回批量任务列表
{{ if2.job ? if2.job.job_no : '' }}
{{ if2HeadSub() }}
底板
{{ if2TplOf(if2.job && if2.job.template_id) ? if2TplOf(if2.job.template_id).name : '原图背景' }}
{{ if2TplStale() }} 张待重套底板
目标语言
目标语言 {{ if2LangCn() }}
成品比例
成品比例 {{ (if2.job && if2.job.ratio) || 'auto' }}
字体参考
无
本任务
换图
恢复默认
↓ 批量下载 {{ if2Done().length }} 张
+{{ if2.uploading ? '上传中…' : '添加原图' }}
+从素材库导入
↩ 回填成品图到素材集({{ if2Done().length }})
{{ if2AllImSel() ? '✓' : '' }}{{ if2AllImSel() ? '取消全选' : '全选' }}
已选 {{ if2SelIms().length }} 张:
{{ if2ExtractLabel() }}
AI 出图
删除
{{ if2Stale() ? '一键重新翻译改过的 ' + if2Stale() + ' 条' : '没有需要重译的行' }}
识别原文和译文都可以直接改;不需要处理的行点 ✕ 移除,不参与出图。可 Ctrl+V 直接粘贴图片。
原图
位置
识别原文
{{ if2LangCn() }}译文
回译中文
操作
① 译文图
{{ if2HasStep1() ? '② 套底板成品' : '套底板成品' }}
{{ if2.imSel[im.id] ? '✓' : '' }}
{{ im.name }}
{{ im.skip ? '已排除' : (im.phase==='new' ? '待开始识别' : (if2Pending(im) ? '识别翻译中' : (im.phase==='failed' ? '识别失败' : if2Rows(im).length + ' 条要改'))) }}
↺
删
⊕ 补识别
{{ im.phase==='translating' ? '正在翻译成' + if2LangCn() : '正在识别图上文字' }}
{{ im.phase==='translating' ? '逐条生成译文与回译中文,完成后出现在这里' : 'OCR 提取文案与位置,约 1 分钟' }}
{{ im.progress||0 }}%
识别翻译失败:{{ im.phase_error || '未知错误' }}
{{ if2FailHint(im,'extract') }}
重新识别
图上没有识别到中文文字
{{ r.keep ? '—' : (r.back || '—') }}
有 {{ if2EmptyN(im) }} 条没翻出来(已自动重试 5 轮)。现在出图,这几句会保持中文留在成品里。
全部重译
已移除 {{ if2RemovedN(im) }} 条,不参与出图
恢复
{{ im.gen_kept_src ? '原图' : ('v' + (im.gen_ver||1)) }}
对照 / 重新生成
图上没有可翻译的文字,已直接沿用原图
(没有调用 AI,未计费)
这张图上没有可翻译的文字,这一版是 AI 重画的
改用原图
{{ im.gen_error || '出图失败' }}
{{ if2FailHint(im,'gen') }}
稍后重新生成这张
{{ im.skip ? '已排除' : (im.phase==='new' || if2Pending(im) ? '等识别翻译完成' : (im.no_text ? '图上没有可翻译的文字,出图会直接沿用原图' : '还没生成')) }}
{{ im.gen_status==='success' ? '↻ 重新生成' : (['queued','generating'].includes(im.gen_status) ? '出图中…' : '生成这张') }}
v{{ im.bg_ver||1 }}
对照 / 重新生成
{{ im.bg_error || '合成失败' }}
{{ if2FailHint(im,'bg') }}
① 的译文图还在,可以重试这一步
{{ if2CanBg2(im) ? '等你点「换背景图」' : '先完成 ① 译文图' }}
{{ im.bg_status==='success' ? '↻ 重新套底板' : (['queued','generating'].includes(im.bg_status) ? '合成中…' : '套底板') }}
共 {{ if2TotalRows() }} 条译文 · {{ if2.images.length }} 张原图 · ① 译文图 {{ if2Done().length }} 张 · ② 套底板 {{ if2BgDone().length }} 张
{{ if2Stale() }} 条原文改过还没重新翻译
+ 继续添加原图
② 全部换背景图{{ if2Bgable2().length }} 张 · 约 {{ Math.max(2, if2Bgable2().length*2) }} 分钟
{{ if2GenAllText() }}{{ if2Genable().length }} 张 · 约 {{ Math.max(2, if2Genable().length*2) }} 分钟
先上传要投放的中文图
上传后点「开始识别翻译」,逐条确认译文,再 AI 生成图片。
加载中…
{{ if2.job.job_no }} 不是换背景图任务
它是「{{ if2ModeCn(if2.job.mode) }}」任务,请到对应的菜单里打开。
← 回换背景图列表
← 返回换背景图列表
{{ if2.job ? if2.job.job_no : '' }}
{{ if2.images.length }} 张原图 · 已换好 {{ if2BgDone().length }} 张 —— 本任务不翻译,图上的字一个都不动
背景图
{{ if2TplOf(if2.job && if2.job.template_id) ? if2TplOf(if2.job.template_id).name : '—' }}
{{ if2TplStale() }} 张待重做
↓ 批量下载 {{ if2BgDone().length }} 张
+添加原图
+ 从素材库导入
↩ 回填成品图到素材集({{ if2BgDone().length }})
{{ if2AllImSel() ? '✓' : '' }}{{ if2AllImSel() ? '取消全选' : '全选' }}
已选 {{ if2SelIms().length }} 张:
{{ if2Bgable2().length ? ('换背景图 ' + if2Bgable2().length + ' 张') : '没有待处理的图' }}
删除
每张约 1~2 分钟、按张计费。可 Ctrl+V 直接粘贴图片。
{{ if2.imSel[im.id] ? '✓' : '' }}
原图
{{ im.name }}
成品
v{{ im.bg_ver||1 }}
对照 / 重做
{{ im.bg_error || '失败了' }}
{{ if2FailHint(im,'bg') }}
原图还在,可以重试
{{ im.skip ? '已排除' : '等你点「换背景图」' }}
{{ im.bg_status==='success' ? '↻ 重做这张' : (['queued','generating'].includes(im.bg_status) ? '处理中…' : '换背景图') }}
删除
{{ if2.images.length }} 张原图 · 已换好 {{ if2BgDone().length }} 张
+ 继续添加原图
全部换背景图{{ if2Bgable2().length }} 张 · 约 {{ Math.max(2, if2Bgable2().length*2) }} 分钟
先把要换背景的图传进来
png / jpg / webp,单张 ≤ 30MB,可以一次传多张,也可以直接 Ctrl+V 粘贴。
传完点「换背景图」,AI 会把原图搬到你选的那张背景上——图上的文字一个都不会动。
加载中…
{{ if2BgOv().title }}
{{ if2BgOv().sub }}
{{ o }}
新建{{ if2BgOv().short }}任务
{{ if2MHasQ() ? '没有符合条件的任务' : '还没有' + if2BgOv().short + '任务' }}
{{ if2MHasQ() ? '换个搜索词或清掉筛选试试。' : if2BgOv().empty }}
{{ if2.mSel[j.id] ? '✓' : '' }}
还没有图
{{ j.job_no }}
{{ j.note || '(无备注)' }}
{{ j.n_imgs || 0 }} 张 · 已完成 {{ j.n_done || 0 }}
新建{{ if2BgOv().short }}任务
{{ if2BgOv().pick }}
模板库里还没有「{{ if2BgOv().kindCn }}」类型的模板。
先去模板库上传一张,类型选「{{ if2BgOv().kindCn }}」。
任务备注(可留空)
白底主图
一个任务就是一张白底图:上传产品图 → AI 抠出透明底 → 拖到品牌画布上配色排版 → 导出。要批量翻译中文详情图请去「批量任务」。
{{ o }}
新建白底图
{{ if2WHasQ() ? '没有符合条件的白底图。换个搜索词或清掉筛选试试。' : '还没有白底图。点右上「新建白底图」,选一张品牌画布就能开始。' }}
还没有图
{{ if2.wSel[w.id] ? '✓' : '' }}
{{ w.job_no }}
{{ w.canvas_name }}
{{ w.n_imgs }} 张素材 · {{ w.n_layers }} 个图层
{{ bjTimeShort(w.created_at) }}
确认删除
取消
删除
新建白底图
名称(可空)
品牌画布
还没有「白底画布」类型的模板 —— 先去模板库建一个。
← 返回氛围图列表
{{ if2.job ? if2.job.job_no : '' }}
{{ if2.images.length }} 张商品图 · 已贴好 {{ if2BgDone().length }} 张 —— 贴图不调 AI、不花钱;想微调进「对照 / 微调」交给 AI
氛围图
↓ 批量下载 {{ if2BgDone().length }} 张
+{{ if2.uploading ? '上传中…' : '添加原图' }}
+ 从素材库导入
↩ 回填成品图到素材集({{ if2BgDone().length }})
全部贴上去{{ if2OvTodo().length }} 张
成品
v{{ im.bg_ver||1 }}
对照 / 微调
{{ if2.cv.saving===im.id ? '正在贴…' : '等你点「贴氛围图」' }}
{{ im.bg_url ? '↻ 重贴' : '贴氛围图' }}
删除
先把要贴氛围图的商品图传进来
氛围图会铺满盖在每张商品图上,点一下就贴好,不调 AI、不花钱。
贴完想改,进「对照 / 微调」写一句提示词交给 AI。
← 返回白底主图列表
{{ if2.job ? if2.job.job_no : '' }}
{{ if2WSub() }}
{{ if2IsOv() ? '氛围图' : '画布' }}
{{ if2IsOv() ? '氛围图已丢失' : '底板已丢失' }}
{{ if2.job && if2.job.template ? if2.job.template.name : '—' }}
+{{ if2.uploading ? '上传中…' : '添加产品图' }}
↩ 回填成品到素材集({{ if2Backfillable().length }})
{{ if2.cv.saving ? '保存中…' : '保存成品' }}
↓ 下载
{{ if2.job.job_no }} 不是{{ if2IsOv() ? '氛围图' : '白底主图' }}任务
它是「{{ if2ModeCn(if2.job.mode) }}」任务,请到对应的菜单里打开。
← 回{{ if2IsOv() ? '氛围图' : '白底主图' }}列表
素材
抠好的产品是透明底。要把第二个产品也放进这张图,点它下面的「加到画布」。
{{ im.name }}
{{ ({none:'原图(未抠)',queued:'排队中',generating:'抠图中…',success:'已抠出透明底',failed:'抠图失败 · 仍可用原图'})[im.gen_status] }}
加到画布
{{ ({success:'重抠',failed:'重试',queued:'…',generating:'…'})[im.gen_status] || '抠图' }}
删
{{ im.gen_error }}
{{ if2FailHint(im,'gen') }}
裁剪中 · 拖白色方块收放取景框 · 压暗的部分会被裁掉 · 按 Esc 或再点一次「裁剪」退出
切割中 · 在图层上点一下落刀,当场分成两个图层(原地不动,可各自摆放)· 按 Esc 退出
点图层选中 · 拖动挪位置 · 拖右下角方块缩放 · 拖顶部圆点旋转(按住 Shift 吸 15°)· 滚轮也能缩放
T
{{ ly.type==='text' ? (ly.text || '(空文字)') : (if2IsOv() ? '氛围图' : '产品图') }}
↑
↓
✕
还没有图层
主题色(品牌头/角标)
色块取自产品本身的主要颜色
选中图层
在画布或上面的列表里点一个图层
对齐
{{ ({left:'左',center:'中',right:'右'})[a] }}
粗
{{ if2.cv.mode==='crop' ? '完成裁剪' : '裁剪' }}
{{ if2.cv.mode==='cut' ? '取消切割' : '一刀两断' }}
还原为整图
刀口
竖着切
横着切
选中后按 Delete / Backspace 也能删
已保存成品
{{ (if2Cv().versions||[]).length }} 个版本
↓ 下载
加载中…
这张白底图缺少画布
任务数据已经读到了({{ if2.images.length }} 张素材),但它没有画布,所以没法排版。
这通常是很早以前建的任务 —— 点下面重新加载,系统会自动补一张空画布。
重新加载
历史图片
所有出过的图都在这里。点图放大做原图对照,也可以重新生成或单张下载。
h.id),'imgs-all.zip')">↓ 批量下载 {{ if2OkList().length }} 张
⌕
{{ if2AllSel() ? '✓' : '' }}{{ if2AllSel() ? '取消全选' : '全选本页' }}
显示 {{ if2HisList().length }} 张
↓ 下载选中({{ if2SelIds().length }} 张)
正在出图,约 1~2 分钟
{{ if2.hisSel[h.id] ? '✓' : '' }}
{{ h.gen_status==='success' ? '已出图' : '出图中' }}
v{{ h.gen_ver }}
{{ h.name }}
{{ h.n_rows }} 条文案 · {{ if2LangCnOf(h.lang) }} · {{ bjTimeShort(h.created_at) }}
{{ if2HisHasJob(h) ? '放大对照' : '放大查看' }}
回所属任务
{{ if2.his.length ? '没有匹配的结果' : '还没有出过图' }}
{{ if2.his.length ? '换个关键字或筛选试试。' : '在批量任务里确认译文并生成,产物会归到这里。' }}
{{ if2LbCur().name }}
{{ if2LbGenText() }}
已重新生成 {{ if2LbVer()-1 }} 次(当前 v{{ if2LbVer() }})
←
{{ if2.lb.idx+1 }} / {{ if2.lb.list.length }}
→
✕
图 1 · {{ if2LbLeftTag() }}
{{ if2.lb.srcMarks.length ? '已标 ' + if2.lb.srcMarks.length + ' 个原图选区' : '' }}
{{ i+1 }}
图 1
{{ if2LbLeftTag() }} · 框写作 {图1选区N}
图 2 · {{ if2.lb.stage==='bg' ? ((if2IsBgOnly()||if2IsOv()) ? '成品' : '② 套底板成品') : ('产物 ' + (if2LbCur().lang||'').toUpperCase()) }}
{{ if2.lb.marks.length ? '已标 ' + if2.lb.marks.length + ' 个选区' : '' }}
‹
v{{ (if2LbShown()||{}).ver }}(历史版本)
›
{{ ['queued','generating'].includes(if2LbSt()) ? (if2.lb.stage==='bg' ? '正在套底板,约 1~2 分钟' : '正在出图,约 1~2 分钟') : (if2LbSt()==='failed' ? '上一次失败了' : '这张还没生成') }}
{{ i+1 }}
图 2
当前产物 · 框写作 {图2选区N}
(这一步的选区只用于「遮住」,不发给 AI)
(没引用它时不会发给 AI)
两侧都能拖框:左边原图=图 1,中间产物=图 2;框分别写作 {图1选区N}(橙)/ {图2选区N}(蓝),点芯片即可引用。这个叫法与 AI 实际收到的图序一一对应——它看到的就是 image 1 / image 2,所以指得准。
引用了 {图2选区N} 时会把上一版产物作为图 2 一并发过去,且这一次不带字体样板(要让"图 2"真的是图 2,就不能再插一张进去)。选区仍只是引导,不保证选区外完全不动;不标就是整张重画。
这一步不认选区:套底板/换背景是按模板整张重做的,AI 只收到你写的这段文字+固定的输入图。提示词里写 {图2选区N} 不会被替换,会原样发过去当噪声。
拖框在这里只服务下面那颗 「■ 遮住选中区域」——它逐行取选区左右的真实像素填过去,不过模型、秒出、不花钱,旁边一个像素不动。
本次发给 AI 的是固定的输入图(底图+底板,或成品一张),与你标了几个选区无关。想改哪块,用「遮住」;想整体重来,就在下面写清楚要什么。
本次会把图 1(原图)+ 图 2(当前产物)两张一起发给 AI,编号与上面一致。这一次不带字体样板——要让「图 2」真的是第 2 张,就不能再插一张进去。
本次只发图 1(原图)给 AI。要让它看当前产物,在提示词里引用 {图2选区N}。
⛶ 打开产物工作台
已标 {{ if2.lb.srcMarks.length }} + {{ if2.lb.marks.length }} 个选区
清除选区
{图1选区{{ i+1 }}}
{图2选区{{ i+1 }}}
{{ if2LbBtnText() }}
↓ 下载这张{{ if2LbIsOld() ? '(v' + (if2LbShown()||{}).ver + ')' : '' }}
产物工作台
{{ (if2LbCur()||{}).name }}
{{ if2.lb.stage==='bg' ? '成品' : ('产物 ' + ((if2LbCur()||{}).lang||'').toUpperCase()) }}
‹
v{{ (if2LbShown()||{}).ver }}
›
✕
{{ if2.lb.refFold ? '▸' : '◂ 折叠' }}
图 1 · {{ if2LbLeftTag() }}
{{ i+1 }}
在这里拖框 → {图1选区N}。选区只是给 AI 的引导、不保证精确,所以小面板够用。
1:1 {{ Math.round(if2.lb.loupe.ix) }}, {{ Math.round(if2.lb.loupe.iy) }}
−
{{ Math.round((if2.lb.cz||1)*100) }}%
+
适应
1:1
工具
比例
裁切后尺寸
{{ if2CropPx() || '—' }}
原图 {{ if2.lb.natW }} × {{ if2.lb.natH }} px
重置取景框
{{ if2.lb.cropping_busy ? '裁切中…' : '✓ 裁切并保存' }}
在画布上拖框 → 生成 {图2选区N},写提示词时点芯片引用。
清除全部选区
框出要遮住的区域。逐行取选区左右的真实像素填过去——不过模型、秒出、不花钱,旁边一个像素不动。
已框 {{ if2.lb.maskRects.length }} 块
清除
{{ if2.lb.masking ? '遮盖中…' : '■ 遮住并保存' }}
点下面的「添加文字」加一行,拖着挪位置,双击字就能直接改。字在浏览器里画,不过 AI、不花钱。
字号 = 图宽的 {{ if2WbTextCur().z }}%(跟着图缩放,换尺寸也不跑偏)
还没有文字。点下面的「添加文字」加一行,然后拖到想要的位置。
+ 添加文字
共 {{ if2.lb.texts.length }} 行
全部清除
{{ if2.lb.tbusy ? '生成中…' : 'T 加字并保存' }}
只看不改:这个模式下鼠标按下去只会平移画布,不会误改任何东西。
重新生成这张
原样重新生成这张
↓ 下载这张
{{ if2SrcIm().name }}
原图 ZH
{{ if2.srcLb.marking ? '标注中,点此结束' : '⊕ 补识别选区' }}
{{ if2.srcLb.busy ? '识别中…' : '识别此区域' }}
{{ if2.srcLb.masking ? '框选中,点此结束' : '▢ 标注要遮的区域' }}
清除({{ if2.srcLb.marks.length }})
{{ if2.srcLb.busy ? '遮盖中…' : '■ 遮住选中区域' }}
‹
v{{ if2SrcShown().ver }}(历史)
›
✕
OCR 有漏时:点「⊕ 补识别选区」→ 拖框圈出漏识别的文字 → 点「识别此区域」,识别出的行会追加进文案表。
想擦掉图上不要的字:点「▢ 标注要遮的区域」→ 把框拖在背景上(压到产品或角标上会拖出条纹)→ 点「■ 遮住选中区域」。不过 AI,秒出,可切回上一版。
新建打法
{{ m.cn }}
共 {{ pbShown().length }} 条
刷新
加载失败:{{ pb.loadErr }} —— 这一页现在显示的不是打法库的真实内容,别照它判断。
重试
正在加载…
这个模块下还没有打法。清掉筛选
打法库还是空的。点左上角「新建打法」写第一条 —— 助手取不到东西时会明确说"没有",不会自己编。
打法库还是空的,而你没有编辑权限。找管理员在「部门管理」里给你的部门勾上「打法库」。
{{ r.title }}
{{ r.code }}
{{ pbMenuCn(r.menu) }}
已停用(助手取不到)
复制正文
编辑
{{ r.is_active ? '停用' : '启用' }}
{{ r.summary || '(没写一句话说明 —— 助手靠它判断该不该取这一条)' }}
{{ r.stamp }}
{{ r.body || '(还没有正文)' }}
标识 code —— 助手按它取这一条。建完就别再改:改了等于换了一把键,
助手手上那条引用会当场断掉。只能用小写字母、数字、连字符。
归属模块 —— 决定谁看得见、哪条 MCP 通道取得到。选「通用」则每个人都看得到。
{{ m.cn }}
一句话说明 —— 助手靠它判断该不该取这一条正文,写清楚"什么时候用"。
正文 —— 助手会原样读到。写成步骤或模板,越具体越不容易被它自由发挥。
{{ pv.name }}
{{ pv.use }}
{{ pv.use2 }}
{{ (ifShops||[]).length ? ((ifShops||[]).length + ' 个店已配置') : '还没配店' }}
{{ ifBridgeToks(pv).length?'插件已配':'待装插件' }}
{{ ifProviderKeys(pv.p).some(k=>k.is_enabled)?'已配置':'未配置' }}
添加店铺
签发插件令牌
添加
{{ pv.desc }}
店铺罗盘 Cookie精选联盟同步启用
{{ ifShopErr }}
ifShopRename(sh, v)">
没绑罗盘账号 · 已阻断
{{
sh.acct_ok ? ('已配' + (sh.acct_at ? ' · ' + sh.acct_at + ' 你配的' : ''))
: (sh.acct_exists ? '已停用' : '取不到') }}
{{ sh.compass_acct_id ? '换 Cookie' : '粘 Cookie' }}
测试
ifShopLink(sh, v)">
(还没关联)
{{
b.shop_name }} · {{ b.n }} 人{{ (b.linked && b.linked !== sh.id) ? '(已关联别的店)' : '' }}
未关联
已同步 {{ ifBuyinN(sh) }} 人
关联了,还没同步
还没有店 —— 点右上「添加店铺」,粘一把抖音电商罗盘的 Cookie 并起个名。
插件
{{ ifBridgeExt(pv).name }}
v{{ ifBridgeExt(pv).version }}
下载
{{ if2ExtSize(ifBridgeExt(pv).size) }} · {{ bjTimeShort(ifBridgeExt(pv).created_at) }} 上传
插件下载页里还没有「{{ pv.bridgeName }}」——
它得先由管理员传上去({{ '\u0054ikTok 运营 → 浏览器插件 → 插件下载' }} → 上传新版)。
怎么装
下载的 zip 解压到一个长期留着的目录(Chrome 记的是路径,
路径没了插件会静默失效)→ chrome://extensions →
开「开发者模式」→「加载已解压的扩展程序」→ 选那个文件夹。
装完点扩展图标右键「选项」,BL 地址保持默认,把上面签发的令牌粘进去。
换新版时:解压覆盖,再回 chrome://extensions
点那个扩展的 ↻ 重新加载。
前提
{{ pv.bridgeNeed }}
令牌
已有 {{ ifGlobalToks().length }} 枚全域令牌:{{ ifGlobalToks().map(t => t.token_head + '…' + (t.label ? '(' + t.label + ')' : '')).join('、') }}。
管理
还没有全域令牌 —— 点右上「签发插件令牌」,
记得勾上「全域」(灰豚数据是给所有部门的灵感行做增强的,部门令牌只覆盖本部门)。
已有 {{ ifBridgeToks(pv).length }} 枚令牌:{{ ifBridgeToks(pv).map(t => t.token_head + '…' + (t.label ? '(' + t.label + ')' : '')).join('、') }}。
管理 / 再签一枚
还没有令牌 —— 点右上「签发插件令牌」。
签发人必须有「商务」模块的编辑权:令牌能碰哪个模块跟着签发人当前权限走,
被降级或禁用就当场失效。
{{ ifShowLegacy ? '收起' : '旧的 Cookie 方式(不推荐,点开看为什么)' }}
不要配这个。灰豚是单设备的:服务器拿着同一份 SESSION 去取数时,
灰豚会判定会话异常并把服务器和你的浏览器一起作废(2026-08-14 线上实证:
服务器连打几次接口后,浏览器当场弹「账号已在别处登录」)。
生产上的表现是一天要重新粘 4 次 Cookie。上面那个插件方案就是为了消灭这件事。
这里保留只是因为边车还没退役,配了它 = 把问题重新装回来。
{{ pv.name }}
{{ ifKeySummary(record) }} · 密钥 •••••••• 已加密
•••••••• 已加密
{{ record.label }}
未命名 #{{ record.id }} · 点这里起个名
—
{{ record.is_enabled ? (pv.single ? '生效中' : '启用') : (pv.single ? '备用' : '停用') }}
编辑
删除
测试
{{ pv.empty || '未配置,点右上「添加」粘贴 Key。' }}
密钥经 Fernet 加密后存独立库,页面不回显明文。状态标签可点击切换启用/停用;停用全部 Key 后该接口回落到各自的免费/环境变量兜底。
数据现状
{{ jst.st.env==='prod'?'正式环境':'测试环境' }}
刷新
{{ (jst.st.counts||{}).jst_sale_day || 0 }} 行(SKU×日)
{{ (jst.st.sale_days||{}).days || 0 }} 天({{ jst.st.sale_days.from }} ~ {{ jst.st.sale_days.to }})
{{ (jst.st.counts||{}).jst_sale || 0 }} 行
{{ (jst.st.counts||{}).jst_sku || 0 }} 个 SKU
{{ (jst.st.counts||{}).jst_inv || 0 }} 行
{{ (jst.st.snapshots||{}).days || 0 }} 天({{ jst.st.snapshots.from }} ~ {{ jst.st.snapshots.to }})
{{ (jst.st.counts||{}).jst_forecast || 0 }} 行 / {{ (jst.st.forecast||{}).days || 0 }} 天
{{ (jst.st.counts||{}).jst_backtest || 0 }} 行
{{ jst.st.sync_last || '—' }}
{{ jst.st.forecast_last || '—' }}
库存接口只返回当前值、查不到历史 —— 某天是否断货只能靠当天快照判定,今天不存就永久丢。
断货日销量=0 是右删失观测(真实需求 ≥ 观测值),当成 0 会把预测系统性拉低 → 补得更少 → 更易断货。
同步(只读拉取,不回写 ERP)
回看天数
同时更新商品资料
立即同步
日常每天回看 2 天即可覆盖跨日边界(约 1 分钟);补历史区间别用这个按钮——页面是同步调用会撞网关超时,找开发走命令行
容器内存 {{ Math.round((jst.st.mem.rss||0)/1048576) }} /
{{ Math.round((jst.st.mem.limit||0)/1048576) }} MB({{ jst.st.mem.pct }}%,准入上限 {{ jst.st.mem_admit_pct }}%)
—— 此刻同步有 OOM 风险,会把全部账单业务一起打下线,请走上面的独立容器命令
预测与回测(参数系统自动,不用人调)
建议量覆盖
{{ ((jst.st.params||{}).jst_prep_days||3) + ((jst.st.params||{}).jst_ship_days||12) + ((jst.st.params||{}).jst_cover_days||20) + ((jst.st.params||{}).jst_buffer_days||5) }} 天
= 仓库备货 {{ (jst.st.params||{}).jst_prep_days||3 }} + 物流 {{ (jst.st.params||{}).jst_ship_days||12 }} +
到货后卖 {{ (jst.st.params||{}).jst_cover_days||20 }} + 运营缓冲 {{ (jst.st.params||{}).jst_buffer_days||5 }}
{{ jst.showParams ? '收起口径 ↑' : '完整口径 ↓' }}
(v3 运营口径,admin API 兜底可改)· 预测展示/回测视界固定 20 天 ·
半衰期 H={{ (jst.st.backtest||{}).h_used || (jst.st.params||{}).jst_half_life || '—' }}
(A/B/C 分层每日回测自动选,候选 7/14/28)·
统计窗口 {{ (jst.st.params||{}).jst_window_days || 90 }} 天 ·
偏差自校正:各层按自己最近回测的偏差把日均预测压回一半(λ=0.5 带夹钳,回测样本不足不校正)。
每次留档都把当次取值与模型版本一并钉死,事后分得清"这条建议当时是哪套参数给的"。
{{ jst.st.backtest.date || '—' }}
{{ jst.st.backtest.wape!=null ? Math.round(jst.st.backtest.wape*100)+'%' : '—' }}
{{ jst.st.backtest.bias!=null ? (jst.st.backtest.bias>0?'+':'')+Math.round(jst.st.backtest.bias*100)+'%' : '—' }}
{{ jst.st.backtest.windows || 0 }} 个({{ jst.st.backtest.skus || 0 }} SKU)
立即重算并留档
重跑回测(约 10 秒)
查看预测留档 →
查看回测留档 →
{{ jst.fmsg }}
数据更新日期
{{ d }}{{ d===adv.dates[0] ? '(最新)' : '' }}
A 类B 类C 类
断货风险
待补
充足
无销量
数据不足
查询
重置
{{ c.label }}
{{ advColLabel(f.key) }}
~
×
导出 xlsx
导出备货装箱单
有 {{ advSumC.thin }} 个 SKU 的数字不可直接下单:「数据不足」=该 SKU 有效观测不足 14 天,
建议量已强制置 0,等销量攒够自动恢复;「删失存疑」仅见于 2026-08-03 前的旧留档(该口径已停用——
业务确认历史无断货)。
{{ rowIndex + 1 }}
—
{{ record.risk }}
{{ record.abc }}
不备货
{{ record.sku_name }}
{{ typeof record[c[0]] === 'number' ? fmt(record[c[0]]) : record[c[0]] }}
—
{{ (record.dev7_bias>0?'+':'') + Math.round(record.dev7_bias*100) + '%' }}
今日备货单
20 天判急不急 · 40 天判发多少 · 充足的品不列出
{{ k.label }} ⓘ
{{ k.value }}
{{ k.unit }}
{{ k.sub }}
建议备货量 = 预计 {{ jdHorizon }} 天销量 − 库存数量 − 采购在途
预计 {{ jdHorizon }} 天销量 = 日均预测 d̂ × {{ jdHorizon }}
{{ sec.title }}
{{ sec.hint }}
{{ sec.stat }}
—
{{ r.name }}
{{ r.code }}{{ r.oosNote }}
{{ r.tier }}
{{ r.dhat }}
{{ r.days }}
{{ r.risk }}
{{ r.inv }}
{{ r.positionNote }}
{{ r.f20 }}
{{ r.f40 }}
{{ r.sug }}
{{ r.dev }}
{{ r.devWhy }}
筛选后没有匹配的品
{{ sec.emptyNote }}
清除筛选
{{ sec.footL }}
{{ sec.footR }}
状态与保护闸(每天自动跑,不需要设参数)
{{ r.label }}
{{ r.text }}
在途盲区
ERP 的采购单是货到后才补录,刚发出、还没入库的货系统看不见。发货后 {{ (adv.params.lead_days||15)+(adv.params.buffer_days||5) }} 天内,请人工从建议备货量里把这批扣掉,别重复发。
预测有多准
近 90 天回测 · 整体误差 {{ Math.round((adv.acc.wape||0)*100) }}% · 方向 {{ (adv.acc.bias>0?'+':'') + Math.round((adv.acc.bias||0)*100) }}%
{{ t.tier }}
{{ t.wape }}
半衰期 {{ t.half }}
{{ t.text }}
d̂ 每天回测自校正:本层最近偏高就把预测压回一半
(当前因子 {{ adv.params.bias_adj_disp }})。
口径详解 ↗
选择 SKU
{{ jsRail.length }} / {{ adv.rows.length }}
{{ s.tier }}
{{ s.name }}
{{ s.code }}
{{ s.sugTxt }}
{{ s.risk }}
其余 SKU 今日无需动作,已折叠。切「全部」可查看。
—
{{ jsC.name }}
{{ jsC.tier }} 类
{{ jsC.risk }}
{{ jsC.code }}
ⓘ
{{ n.label }}
{{ n.value }}
{{ n.unit }}
{{ n.sub }}
两个问题:急不急 · 发多少
急不急
{{ jsC.verdict }}
{{ jsC.sugAdvice }}
建议备货量(发多少)
{{ jsC.sugNote }}
{{ f.op }}
{{ f.label }} ⓘ
{{ f.label }}
{{ f.value }}
{{ jdHorizon }} 天销量拆解(一批货要盖住的时间)
{{ jsC.coverTxt }}
库存数量 {{ jsC.inv }} 件(+在途 {{ jsC.transit }})
黑线 = 预计 {{ jdHorizon }} 天销量 {{ jsC.f40 }} 件(d̂ {{ jsC.dhat }} × {{ jdHorizon }})
建议备货量 · 历史走势
{{ jsC.fhRange }} · 每天留档一次,共 {{ jsC.fhN }} 天 · 今日 {{ jsC.sug }} 件
{{ jsC.fhHint }}
{{ jsC.fhMax }}
{{ jsC.fhMid }}
0
{{ jsC.fhD0 }}
{{ jsC.fhD2 }}
每天留档的建议备货量
旧模型口径(线在此断开,与右段不可比)
今日
{{ jsC.depleteTxt }}
{{ (jsku.hover && jsku.hover.key==='fc' && jsku.hover.detail)
|| '线一路往上 = 库存在被卖掉、要补的量越来越多;到货那天会掉下来。悬停某一天看它是怎么算出来的。' }}
库存趋势 · 近 60 天(已攒 {{ jsC.invN }} 天)
每日凌晨全量快照 · 2026-07-31 起才开始攒,更早的取不到(ERP 只返回当前库存),窗口会自己长满 60 天
{{ jsC.invHint }}
{{ jsC.invMax }}
{{ jsC.invMid }}
0
{{ jsC.invD0 }}
{{ jsC.invD2 }}
{{ jsC.invNote }}
快照还不足 2 天,攒几天就能看趋势了
销量趋势 · 近 {{ jsC.salesN }} 天
日聚合出库件数 · 期间日均 {{ jsC.avgSales }} 件
{{ jsC.salesHint }}
{{ jsC.salesMax }}
{{ jsC.salesMid }}
0
{{ jsC.salesD0 }}
{{ jsC.salesD2 }}
7 日移动平均
{{ jsC.salesNote }}
预测表现
每天凌晨的预测留档,第二天就有答案——拿当天预测的日均对当天实际卖出 · 正 = 预测偏高(会略多备,安全方向)
近 {{ jsD7.days }} 天偏差
{{ jsD7.txt }}
{{ jsD7.sub }}
逐月预测表现
检验起点 {{ jsDiag.span }} · 共 {{ jsDiag.windows }} 个窗口
{{ jsDiag.mismatch }}
{{ jsDiag.headline }}
按检验起点所在月拆开(20 天视界的历史模拟;条长 = 该月对总偏差的贡献)
{{ m.label }}
{{ m.bias }}
{{ m.excess }} 件
{{ m.share }}
{{ m.windows }} 次
逐月表现是按回测留档的参数当场重算的解释性拆解,只用来看"历史上哪一段拖累最大",
不作为下单依据——下单看上面的近 {{ jsD7.days }} 天偏差。
建议量、日均预测、20/40 天销量全部读留档,一个数都不重算。
数据留档 {{ jsC.calcDate }} · 全部中间量在「预测留档」逐字可查 · 历史回看到「销量预测表」切数据更新日期
{{ jsku.busy ? '加载中…' : '左侧选择一个 SKU 查看单品报表' }}
这些数字是怎么来的
给运营看的口径说明 · 不需要懂统计
目录
全部自动计算,运营不需要调任何参数。数据每天凌晨更新一次,白天不变。
一句话:系统算出每个 SKU 的日均销量 d̂,用 20 天判急不急,用 {{ jdHorizon }} 天判发多少。
建议备货量 = 预计 {{ jdHorizon }} 天销量(d̂ × {{ jdHorizon }})− 库存数量 − 采购在途,{{ jdHorizon }} 天 = 备货 {{ jdSeg.prep }} + 物流 {{ jdSeg.ship }} + 到货后可卖 {{ jdSeg.cover }} + 运营缓冲 {{ jdSeg.buffer }}。另一边,预计可售天数(库存数量 ÷ d̂)不到 20 天就标红断货风险,告诉你先发哪些。
{{ p.label }}
{{ p.value }}
上面这一行是 兔兔坡面腮红刷T018(A 类)2026-08-03 的真实数字,全文都拿它当例子。
每天凌晨发生什么
你早上打开页面时,这五步已经跑完了。哪步没跑,「取数与预测」页会显示停更。
{{ s.time }}
{{ s.title }}
{{ s.text }}
把全部 SKU 按近 90 天销量从多到少排队,从头累加:累计占到全店 80% 销量的那几个是 A 类;继续加到 95% 的是 B 类;剩下的全是 C 类。分层每天重新算,爆款掉下去、新品冲上来都会自动换层。
{{ t.tier }}{{ t.count }} 个 SKU
{{ t.text }}
分层的作用:① 选半衰期(A/B/C 各自回测选自己的);② 分层偏差自校正(本层偏高就把本层压回一半);③ 页面筛选与可信度参考。{{ jdHorizon }} 天三层统一,不按层区分。
系统只用一个输入:这个 SKU 每天卖了几件(销量日聚合表)。算出一个"越近的日子越算数"的日均销量 d̂,后面两个判断全部基于它:20 天判急不急,{{ jdHorizon }} 天判发多少。
{{ f.n }}
{{ f.title }}
{{ f.text }}
"越近的日子越算数"是什么意思
把最近一天的销量当 1 份重,往前每过一个半衰期,重要性减半。半衰期不是拍的:每天回测在 7 / 14 / 28 里自动选,各层各选各的。
{{ h.tier }}
{{ h.days }} 天
{{ h.why }}
这些参数系统自动选,页面只做展示,运营不用改。
例子
销量王 MGA0239636(兔兔坡面腮红刷T018)校正后 d̂ ≈ 691 件/天:691 × 40 − 库存 15,129 − 在途 0 = 建议备货 12,437 件。备货+物流 15 天吃现库存,到货时手上还剩 ≈5 天缓冲,到货批正好卖 20 天——闭环。
每天凌晨从 ERP 拉一份全量库存快照(一天一份)。取的是仓库里"现在能卖出去的数量"——已下单未出库的占用不算在内。
断货低估
快照里库存为 0 的那天会被标成断货日。断货期间买不到的需求记不进销量,所以预测会偏低——断货多日的品,真实需求比页面上的预测更高,发货量可适当上浮。
在途 = 已经下单、还在路上、尚未入库的数量。它不在可用库存里,但算建议量时会一并扣除。
实话
ERP 里的采购单是货到之后才补录的,所以在途几乎永远是 0。刚下过单、货还在路上的品,页面上依旧会报"会断货"、建议量也会重复计算,需要你自己判断。
怎么办:看到预警时先对一眼最近几天已开的采购单——已经在路上且能在见底前到的,这次就不用再下。等 ERP 改成下单即录,预警才能把在路上的货算进去。
520 天判急不急,{{ jdHorizon }} 天判发多少
同一个 d̂,两个用法。20 天是级别:库存撑不到 20 天,就算今天发货,途中也可能断,所以标红优先发。{{ jdHorizon }} 天是数量:一批货从下单到卖完就是 {{ jdHorizon }} 天,把库存数量(含在途)补到预计 {{ jdHorizon }} 天销量就对了。
第一问:急不急(先发哪些)
预计可售天数 = 库存数量 ÷ d̂
小于 20 天 → 标红「断货风险」。为什么是 20?备货 + 物流要 {{ (jdSeg.prep||3)+(jdSeg.ship||12) }} 天再加 {{ jdSeg.buffer||5 }} 天缓冲,库存撑不到 20 天就意味着下一批到之前很可能先空。表里的「预计 20 天销量」(d̂ × 20)就是这根红线的件数形式。
这个判断只影响优先级,不影响数量。
第二问:发多少(数量)
建议备货量 = 预计 {{ jdHorizon }} 天销量 − 库存 − 在途
算出来是负数就归 0,即「充足」,今日备货单不列出。已录入的采购在途会一并扣除,不会重复发。
运营缓冲(d̂ × {{ jdSeg.buffer||5 }} 天)已经含在 {{ jdHorizon }} 天里,不再单独列。
{{ jdHorizon }} 天是这么来的(admin 可改,物流提速只改一个数)
下单后工厂备货 {{ jdSeg.prep }} 天、路上 {{ jdSeg.ship }} 天(这 {{ (jdSeg.prep||3)+(jdSeg.ship||12) }} 天要吃现有库存),到货后这批卖 {{ jdSeg.cover }} 天,再给运营 {{ jdSeg.buffer }} 天缓冲——卖到剩缓冲时下一批刚好到,闭环。
{{ r.op }}
{{ r.label }}
{{ r.value }}
{{ r.note }}
新口径上线首日(2026-08-03),全店建议备货合计从旧口径的 4,326 件跳到 ≈3.5 万件——那是一次性补回历史欠备的部分,之后每批会回到 ≈20 天量的节奏,可人工分批发。
从上往下判断,命中哪条就是哪个状态,不会同时是两种。
{{ r.label }}
{{ r.rule }}
{{ r.text }}
{{ r.count }}
两个常见问题:「预计可售天数」= 库存数量 ÷ d̂(只用库存,不含在途),断货品固定为 0;运营缓冲已含在预计 {{ jdHorizon }} 天销量里。原来的 z·σ 统计安全余量已停用。
每天凌晨,系统把过去每一天做出的预测和实际卖出的件数放在一起比,逐个 SKU 打分并留档。页面上两个百分比回答两个不同的问题:
问题一:猜得离得远不远?
预测误差
把每天"猜错的件数"加起来,除以实际卖出的总件数。只看差多少,不看方向,越小越准。全店整体 {{ jeWape }}。
问题二:整体是猜多了还是猜少了?
预测偏差(正 / 负)
(预测 − 实际)÷ 实际。正数 = 猜多了,照单备货会略多备,属安全方向;负数 = 猜少了,要留心。全店整体 {{ jeBias }}。系统还会按这个偏差自动把预测压回一半(分层自校正)。
分层看误差:哪层的数可以照着发
{{ t.tier }}
{{ t.wape }}
{{ t.text }}
C 类误差大不代表系统坏了:一天卖 0~2 件的品本身没法精确预测,看方向就够。
怎么读 SKU 页那张偏差图
新品或上架不足检验窗口的品显示「—」:不是漏了数据,是还没到能打分的时候。
「近 7 天偏差」怎么看、怎么参考
今日备货单、销量预测表、SKU 备货报表三处是同一个数(同一个函数、同一个窗口):
(这 7 天每天的预测日均之和 − 这 7 天实际卖出之和)÷ 实际卖出,
正号一律表示预测偏高。下单时这样用:
正数照建议量发(略多备,安全方向);负数量大的品可人工上浮几个点;标橙的品下单前复核一眼。
为什么是"每天预测对当天实际":每天凌晨 03:20 的那份预测留档,用的是截至前一天的销量、
预测的是当天起的日均——所以第二天就有答案,而且是干净的"事先说、事后对"。
滚动 7 天合起来算,是为了压掉单日波动:同一个品用 3 天算和用 7 天算,
十次里有一次会差 28 个百分点以上,那是运气不是信息。
显示「—」不是坏了:这 7 天卖不到 10 件的品,百分比会被一两件的波动放大到毫无意义
(长尾品经常整周只卖出几件),所以宁可不给结论。这些品的建议量照常算,数量按人工经验定。
{{ d.k }}
{{ d.what }}
{{ d.ask }}
两个门槛不是口径打架,是层级不同:±5% 看的是全店(模型整体有没有偏,给维护者看,
走 90 天历史模拟回测);35% 看的是单个品的近 7 天(能不能照着发,给运营看,走真留档对答案)。
两个数不会相等,也不该相等。
单品还想看更长时间的表现,点进 SKU 备货报表的逐月预测表现——那里把历史按月拆开。
⚠️ 刻意不给一个"历史合计"的单一数字:把几个月塌成一个数会把早已过去的一段
一直挂在页面上(曾把一个近期只偏 +9% 的爆款标成「明显多备」)。要看历史就看拆开的。
怀疑某个数字不对时,按这张表去对应的原始表里逐行核对。原始表只读,由每天的自动同步维护。
模型参数(覆盖 {{ jdHorizon }} 天 = 备货 {{ jdSeg.prep }} + 物流 {{ jdSeg.ship }} + 可卖 {{ jdSeg.cover }} + 缓冲 {{ jdSeg.buffer }} · 预警线 20 天 · 半衰期 {{ adv.params.h_disp || '—' }},每日回测自选)由系统自动选定并每天随预测一起留档,历史任意一天的建议表都能原样回看。
{{ g }}
{{ s.label }}
| 代码 | 中文名 | 英文名(出图 prompt 用) | 字体风格提示(英文,追加进出图 prompt;空=模仿原图字体) | 字体参考图(出图时随源图一起喂给模型当字体样板) | |
|
|
|
|
ifUploadFontRef(files, i)">
{{ l.font_ref ? '换图' : '上传' }}
清除
|
删 |
加一门语言
加语言不用改代码:代码=模型识别的语言码 · 中文名=界面显示 · 英文名=出图 prompt 里的语言名 · 字体提示=文字描述约束 · 参考图=一张该语言排版规范的成品图(比文字描述贴合得多),改动都要点「保存设置」才生效
在飞任务 · {{ ifFly.total }}
翻译任务
{{ ifStLabel(s) }} {{ n }}
共 {{ ifOps.total }} · 失败率 {{ (ifOps.failure_rate*100).toFixed(1) }}%
超过 {{ Math.round(ifFly.stuck_seconds/60) }} 分钟没动静的会被自动回捞重排;等不及可以在这里直接推它一把
强制回捞卡死
批量重排失败
刷新
{{ record.chain_cn }}
{{ record.ref_table }}#{{ record.id }}
{{ ifStLabel(record.st) }}
{{ ifDur(record.idle_sec) }}
{{ record.upstream_task_id }}
—
调用留档
强制重启
终止
现在没有在跑的图。
卡住的行会显示在这里,并标出它已经等了多久。
{{ record.job_no }}
{{ record.owner }}
{{ ifStLabel(record.status) }}
{{ record.n_done }} / {{ record.n_imgs }}
{{ record.n_rows }}
{{ record.n_failed || 0 }}
{{ record.error_message }}
{{ cfBjTime(record.created_at) }}
打开
装机与版本 · {{ xtc.installs.length }}
干活流水 · {{ xtc.tasks.length }}
推送令牌 · {{ xtc.toks.length }}
机器与窗口 · {{ xtc.mc.rows.length }}
{{ xtcSum.n_ext }} 个插件 · {{ xtcSum.n_inst }} 台安装
旧版 {{ xtcSum.n_stale }}
版本未知 {{ xtcSum.n_unknown }}
在线 {{ xtcSum.n_online }}
机器 {{ xtc.mach.slice(0, 8) }} ✕
{{ k }}
刷新
开发者模式必须一直开着:Chrome 的「扩展程序」页右上角那个开关一关,
所有未打包扩展立刻被停用(停用原因码 16777216),六个插件同时消失、不弹任何提示。
插件"突然全没了"先看这一项。
插件目录要放在桌面 / 文稿 / 下载目录的子目录里:放进资源库、Chrome 自己的配置目录
或程序目录时,一键自更新写不进去而且不报错。被 Chrome 拷进配置目录的那一份(TikTok 工具箱)
要重装一次,改成加载磁盘目录的方式。
每个浏览器各一份运行目录:Chrome 与比特浏览器指向同一个目录时,两边同时自更新会互相踩。
装机登记读不到(GET /api/img/ext/installs):{{ xtc.err }}
下面的插件版本与令牌仍然是真的,只有「装在哪台机器、今天干了什么」这一半缺着。
这一页是 admin 运维页,端点名写出来是为了让看到它的人一眼知道要去查哪条。
最近 24 小时打过 BL、但报不上版本({{ xtcUnknown.length }} 台)
这些机器上的插件还是旧的那一版——它不带 X-Ext-Ver 这个头,所以 BL 只知道"它来过"。
给它换一版带身份头的包,它就会自己出现在下面对应的插件里。
{{ i.machine || i.install_id }} · {{ i.ext_key || '未知插件' }}
{{ xtc.ext ? '这个插件底下还没有任何记录——把上面的筛选清掉看全部。' : '还没有任何插件包,也没有任何机器登记过。' }}
{{ e.ext_key }}
最新 v{{ e.latest }}
库里没有这个包
{{ e.n_stale }} 台没更新
{{ e.installs.length }} 台安装
还没有机器用这个插件连过 BL。装完并授权之后,它下一次请求就会出现在这里。
机器(名字可改)
版本
签发人
浏览器
来源 IP
最近来过
登记
今天干活
用的令牌
{{ i.machine || i.install_id || '—' }}
未知
v{{ i.ext_ver }} → v{{ xtcLatest(i, e) || '?' }}
v{{ i.ext_ver }}
{{ i.username || '—' }}
{{ xtcBrowser(i) }}
{{ i.last_ip || '—' }}
{{ i.last_seen_cn || '—' }}
{{ ifDur(i.idle_sec) }}前
{{ i.n_seen == null ? '—' : i.n_seen }}
{{ i.token_head || (i.token_id ? ('#' + i.token_id) : '—') }}
{{ (record.created_cn || '').slice(5, 16) || '—' }}
{{ record.ext_key || '—' }}
{{ record.machine || record.install_id || '—' }}
{{ record.username || '—' }}
{{ record.kind || '—' }}
{{ record.target || '—' }}
{{ record.n }}
{{ record.ok ? '成功' : '失败' }}
{{ record.err }}
装机登记读不到,这张表也就没有数据(上面那条红字写着原因)。
这个筛选下没有记录。清掉筛选
还没有任何一条干活记录。
心跳与空手而归的轮询刻意不记在这里——只有真的交了活(推了数据、发了一句、取回一批)才落一行。
{{ record.token_head }}…
{{ record.label || '未命名' }}
—
{{ xtcBind(record).ext_key }}
旧式 · 未绑插件
{{ xtc.bindOk ? (xtcBind(record).machine || '—') : '—' }}
{{ record.is_global ? '全域' : (record.group || '—') }}
{{ record.created_by || '—' }}
{{ record.n_used || 0 }}
{{ record.last_used_at ? bjTimeShort(record.last_used_at) : '从没用过' }}
{{ record.revoked ? '已吊销' : '在用' }}
还没有签发过任何推送令牌。
旧版跨平台同步脚本
v{{ xtc.helper.version }}
库里还没有包
{{ Math.round((xtc.helper.size || 0) / 1024) }} KB · 入库 {{ (xtc.helper.created_at || '').slice(0, 16) }}
下载
它随发布从 deploy/helper/ 自动入库(读那儿的
VERSION 文件比版本)。打开这一页就会对一次磁盘 ——
仍然没有的话,去看容器日志里「同步小程序包自动入库跳过」那一行。
① 先把插件解压到那台机器的 ~/bl-ext/<插件目录>/(一个插件一个子目录,
目录名照插件下载页那个 zip 里的;里面直接就是 manifest.json);
② 在下面签一枚只许下包的令牌(明文只显示一次);
③ 把这个包解压出来,跑 bash install.sh(Windows 是
install.ps1)—— 它只问三样:机器名 / 这枚令牌 /
比特浏览器 Local API Token(不管窗口的机器直接回车跳过),别的全自动。
默认只管「直播席位」这一个(纯直播机);要把淘宝/灰豚/精选联盟也管起来加
--all。
🔴 第一次必须人解压:小程序只负责更新已经装着的插件 ——
它要拿目录里那份 manifest 当基线,才判得了新版有没有偷偷多要权限。
空目录它一个字节都不会写(安装器会当场点名并给出确切路径)。
⚠️ 花名册不用在那台机器上填:装完在下面把窗口加进这台机器,它下一轮心跳自己就拿到了。
小程序拿一枚只许下包的令牌调 BL,它才登记得上这张表。
签一枚只许下包的令牌
这张表决定「开一场时,这台机器上哪几个窗口会被拉起」:小程序按它去打
比特浏览器的本地接口。名单里没有的窗口 id,BL 说了也不会被打开。
窗口 id 要填比特浏览器里那个 id(不是列表左边的 seq 序号)——
填错的表现是那个窗口一直不开,而两边都不报错。
登录是人做一次的:每个窗口的抖音登录态住在它自己的 cookie 里,
BL 一个字都拿不到。窗口起来之后插件自己会带着号上线,
在「账号席位」那一页就能看到是哪台机器、哪个号。
机器与窗口读不到(GET /api/img/ext/machines):{{ xtc.mc.err }}
这一条是本轮新加的端点;别的三个 tab 不受影响。
还没有任何机器来过。
这一页的行是
那台电脑上的小程序(
bl-ext-sync)登记出来的:
装上它、填一枚只许下包的令牌,它下一次心跳就会出现在这里。
签一枚只许下包的令牌
{{ m.machine_id }}
{{ m.os }}
小程序 v{{ m.helper_ver }}
没报版本
最近来过 {{ (m.last_seen_cn || m.last_seen || '').slice(5, 16) || '不详' }}
收一场时自动关窗口
自动托管由设备助手设置 · 按抖音实际下播关闭托管页
解绑令牌
设备助手 · 运行状态与诊断
{{ p.profile_id }} · {{ p.status }} ·
{{ p.broadcast === 'live' ? '抖音直播中' : p.broadcast === 'offline' ? '已确认下播' : '广播状态待确认' }}
· 手动接管
{{ plugin }}:实际运行 {{ version }}
{{ e.at ? new Date(e.at * 1000).toLocaleString('zh-CN', {timeZone:'Asia/Shanghai'}) : '' }}
· {{ e.message }} ({{ e.profile }})
这台机器有 {{ xtcMachDups(m).length }} 个窗口同时挂在别的机器名下 ——
开一场时它们
一台都不会被拉起(宁可不开,不可两台同开:同一份登录态被两台
同时打开,等于同一个抖音号在公屏上说两遍,而那撤不回来)。
{{ pf.profile_id }}{{ pf.label ? '(' + pf.label + ')' : '' }}
→ 还挂在 {{ (pf.dup_machines || []).join('、') }}
处置:决定让哪一台跑,把另外那些机器上的这几行「删掉」。
窗口本身与它的登录态一个字都不动(那在比特浏览器里)。
{{ (pf.dup_machines || []).length ? '⚠ ' : '' }}{{ pf.profile_id }}
{{ pf.proxy_type === 'noproxy' ? '直连' : (pf.proxy_type || 'noproxy') }}
人工授权 · #{{ pf.use.id }}
#{{ pf.seat_token_id }}
还没签
{{ ifDur(pf.seat_age_sec) }}前签的
{{ ifDur(pf.use.age_sec) }}前用过
还没被用过
在用哪一枚查不出来
这台机器来过,但花名册是空的 —— 开一场时它一个窗口都不会被拉起。
在下面填上比特浏览器里那几个窗口的 id。
{{ xtc.mc.addErr[m.machine_id] }}
签一枚只许下包的令牌
这枚令牌给那台机器上的同步小程序(bl-ext-sync)用,
写进它的 ~/.bl-helper/config.json 里的 token。
它只能做两件事:下插件包、问“这台机器现在该开着哪几个窗口”。
拿它去打别的接口一律 403——
所以它不能往公屏发言、不能推素材、动不了账单。
⚠️ 一台机器一枚:一台机器第一次心跳时会把它看到的那枚令牌认下来,
此后别的令牌再拿这个 machine_id 来报会被 403(要换得先在上面那台
机器上解绑)。几台机器共用一枚也行,代价是吊销它的时候那几台一起停。
🔴 解绑的射程比“换一枚令牌”大:解绑之后下一枚来上报的令牌就绑上这台机器,
它同时就能从心跳回执里领走这台机器花名册里全部窗口的席位令牌(那是往公屏发言的凭证),
并把原来那台挤成 403。所以别把机器晾在无主状态——解绑之后尽快让它带着新令牌上报一次。
⚠️ 令牌跟着签发人走:它不绑部门,每次用都会重新确认签发人还是不是管理员 ——
签它的人被降级或禁用那一刻它当场失效。
新令牌只显示这一次,请立刻复制保存
{{ xtc.pkt.tok }}
复制
粘进那台机器的 ~/.bl-helper/config.json 里的
"token",然后让小程序跑一轮(它会自己出现在这一页)。
{{ xtc.pkt.busy ? '签发中…' : '签发' }}
签出来的令牌也会出现在「推送令牌」tab 里(带着你填的备注),在那里吊销。