R29: 账号详情视频列表「时长」前新增标签列(40px固定+悬停完整标签)
- data-pages.js: renderAccountDetail 表头 收藏后加 th-tag;数据行加 td-tag(title=完整标签, CSS省略); colspan 8→9/batch 9→10 - style.css: th-tag/td-tag(40px min/max + ellipsis) - 冒烟 cdp-verify-r29.mjs 全 PASS; 版本 ?v=20260903l - MEMORY.md 二次压缩 11148→7213B(注入截断修复)
This commit is contained in:
1 parent
8fe246fb51
commit
7c98af6ecb
6 files changed
+95
-36
No files matched your search
@@ -228,3 +228,11 @@
|
||||
- style.css:.vd-tools(flex 右对齐,margin 2px 0 8px 20px 对齐 page-detail md-body 缩进)
|
||||
- 冒烟 cdp-verify-r28.mjs(9225):未解析58=2 tabs(无分析 tab)+双 tab 空态解析按钮+解析弹窗;已解析117=vd-tools 刷新按钮(icon);deconstruct 无 analysis→空态解析(符合分支)全 PASS
|
||||
- 版本 ?v=20260903k;待实测:解析完成后 account_video_source 更新链路(F5 产物在 视频对标/ 目录,import/account 只扫 视频分析/——需看技能侧 backfill 或 import 扩展)
|
||||
|
||||
## R29 账号详情视频列表加「标签」列(09-03 收尾)
|
||||
- 需求:视频列表中「时长」前增加标签列,宽度固定 40px,鼠标停留显示完整标签
|
||||
- data-pages.js renderAccountDetail(#/account/{id}/persona 视频列表 tab):①L869 数据行在 collect_count 后、duration 前插 `<td class="td-tag"${v.tags?title:''}>`(title=完整标签悬停);②L921 表头 收藏 后插 `<th class="th-tag">标签</th>`;③colspan 8→9/batch 9→10。数据源 account_videos.tags(listAccountVideos SELECT v.* 已带出;格式逗号分隔 # 话题)
|
||||
- style.css:.table th.th-tag(40px/min-width 40/居中/收 padding)+ td.td-tag(max-width 40 + ellipsis 省略 + nowrap)
|
||||
- 冒烟 cdp-verify-r29.mjs(9225 新 profile r29):表头 9 列序=…收藏|标签|时长…、th 实测宽 40px、首行 td-tag 视觉截断(scrollWidth>clientWidth)+title 完整含 #、无 num 类 全 PASS
|
||||
- 版本 ?v=20260903k→l
|
||||
- MEMORY.md 二次压缩 11148→7213 字节(注入截断修复)
|
||||
+24
-32
@@ -1,41 +1,33 @@
|
||||
# MCNVideo AI 项目长期记忆(09-03 压缩整理)
|
||||
# MCNVideo AI 项目长期记忆(09-03 二次压缩;详细规则权威源=SKILL.md+references,此处仅索引与红线)
|
||||
|
||||
## 环境判定两步模型 + 产出落盘(08-26 定稿)
|
||||
- 两步判定:①本机无 `D:\AgentSkill`=③用户环境;有→②技能路径在 `D:\AgentSkill\` 下=①开发机(源技能)、含 `.dsh`=②dsh 环境。单一 `.dsh` 锚点会把用户环境误判为开发机(P0 已修)
|
||||
- 产出落盘:①③=桌面 `MCNSkill项目/{账号名}/`;②dsh=`D:\dshworkspace\` 优先。三技能共用账号文件夹。权威=`references-add/路径配置.md`(运行时优先于 SKILL.md)
|
||||
- 两步:①本机无 `D:\AgentSkill`=③用户环境;有→②技能路径在 `D:\AgentSkill\` 下=①开发机、含 `.dsh`=②dsh。单一 `.dsh` 锚点会把用户环境误判为开发机(P0 已修)
|
||||
- 产出:①③=桌面 `MCNSkill项目/{账号名}/`;②dsh=`D:\dshworkspace\`。权威=`references-add/路径配置.md`(运行时优先于 SKILL.md)
|
||||
|
||||
## 核心技能
|
||||
- **短视频脚本创作** `project/短视频脚本创作/{V1.0,Lite1.0}`:11 步流程+知识库;V1.0 含接口、Lite1.0 去接口模型自带 WebSearch。**★软链事实(09-03 实测)**:`C:\Users\maidou\.workbuddy\skills\短视频脚本创作` → 软链 `/d/AgentSkill/mcn-video-script/project/短视频脚本创作/V1.0`(同一文件)——改源即改 WorkBuddy 技能**无需单独同步**;dsh 副本 `C:\Users\maidou\.dsh\skills\short-video-script\` 是唯一需手动同步的独立副本。同步策略(08-26 用户定):只改 V1.0 源+dsh 副本;Lite1.0 默认不动(用户刻意 divergence)。**🧹后台任务清理**:触发「后台执行什么/清空没用的任务」→ 按 `references/接口调用/自动化任务清理规范.md` 执行;红线=只清过期/错误失败 once(ACCEPTED 或 PENDING_REVIEW/result_success=0/last_error/从未运行),禁清 recurring/未来/running/QUEUED;删除走宿主 automation_update 软删置 deleted_at,**禁 SQL 直改库**
|
||||
- **MCN工作台**(仅 V1.0)`V1.0/mcn-work-shop/`(server.js+public,零依赖 Node,**端口 8900**,argv 可指定;8899 为错误旧值勿引用)。触发「打开工作台」→启动服务+内置浏览器。**AI 任务触发**:`POST /api/run` 写 `~/.workbuddy/workbuddy.db` automations(once+now,cwds=`D:\AgentSkill\mcn-workshop`)→客户端调度器建会话;轮询 `GET /api/run/status?id=`。**★调度机制实测定稿**(详见工作台 `自动化任务调度机制.md`):显式写 next_run_at(+5s)、scheduled_at 须留 +60s 余量(+17s 失败)、并发上限≈3 超出 QUEUED、runs 状态机 QUEUED→IN_PROGRESS→ACCEPTED、改 server.js 必须重启进程才生效。**★创作任务 prompt 必须带 SKILL_HINT_CREATE 前缀**(data-pages.js L386 弹窗/L711/L920 详情页改写;根因=漏前缀致模型跳步一步直出成品)——**新增创作/改写入口必须检查**。AI脚本选题 `/api/ai/topics`(Dify recall ~50s;key 内置 `scripts/MCN_CYLG_API.py` DEFAULT_CYLG_KEY,env `DIFY_MCN_CYLG_KEY` 优先;curl 中文被 CDN 拦用 Python;search 易超时用 recall)。AI脚本诊断=#/reviews 复盘页(prompt 明确「按 mcn-script-review 执行」)
|
||||
- **mcn-dou-analysis** 源=`V1.0/subskills/mcn-dou-analysis/`,dsh=`C:\Users\maidou\.dsh\skills\mcn-dou-analysis/`。F1账号信息/F2视频列表/F3账号设定(人设卡)/F4分析数据/F5解析单视频/F6账号设定卡片(SVG 长图)。红线:达人表15列、TOP6筛选(点赞+分享降序)、≥60条停止、29赛道枚举、浏览器 9223 调试实例+标签页复用、**账号表禁从 DB 回填**(工作台 `hot_accounts`=MCP 内部库副本;身份标识留空待浏览器回填)、源技能只读单向同步、脚本必须切脚本创作技能。F6 配色两系(深色系深沉调性/浅色系明快甜美,如玫瑰金 #D6A3A9→#9A5563),**调性优先于赛道标签**,「治愈」双义须回读情绪锚点(俊希=浅色系暖橙非深焦糖);模块名统一「叙事结构」「内容标签」「内容基因·TOPn(n=实际条数)」;F3→F6 自动联动(删 06 文件禁用;md↔SVG 双向维护,交付前 grep 校验)。browser-harness:功能一/二执行前必检,未装→`uv tool install --python 3.12 --upgrade --force browser-harness`+doctor 三要素。**★产物入库(09-01)**:技能产出只落盘不入库(dsh 插件导入未接通=俊希全0根因)→工作台 `POST /api/import/account` 直写 mcn-plugin.db(analysis.json→account_video_analysis、content.json→account_video_source、设定md→account_persona、分析md→account_analysis、xlsx→account_videos;映射=`scripts/export_video_map.py`);**aweme_id 匹配必删全角标点+下划线转空格**,幂等删旧保新+清 null。选题 `/api/ai/topics` 读最新 3 条视频解析 JOIN account_videos 拼 prompt
|
||||
- **mcn-data-insight(抖音数据获取技能集,09-03 定稿)**:源=`V1.0/subskills/mcn-data-insight/`+dsh=`C:\Users\maidou\.dsh\skills\mcn-data-insight/`(09-03 部署 diff=0)。**纯 11 个红狐系数据获取子技能**(account-diagnosis/daily-hot/hot-trend/works-crawler/ai-feed/prohibited-word/content-surge/weekly-surge/search/top-account/playlet-douyin-feed),**非数据获取技能不再放此包**(nuwa-skill 已删、multi-copywrite-alchemy 移至桌面);无 `.workbuddy`。注意 `mcn-dou-analysis/subskills/nuwa-skill-main/` 是**另一份**账号分析方法论组件(保留勿混)。原 `D:\AgentSkill\mcn-data-insight` 源已剩 SKILL.md+references 壳。**★榜单查询「即查即缓存」落盘(09-03 定稿,commit 4c18e7e)**:红狐 API 按调用计费+榜单=同日期固定快照→强制落盘免重复花钱;桌面 `MCNSkill项目\` 顶层分**业务账号/三方数据**,三方数据统一 `三方数据\{数据源}\`(当前=红狐数据 → `三方数据\红狐数据\抖音榜单\`,后续每数据源一文件夹),文件名**不加数据源前缀**(保 `抖音周榜_` 开头兼容 dsh RANKING_DIR 正则);②dsh=`D:\dshworkspace\抖音榜单\`;查询前先 ls 命中免 API(同榜名+赛道+数据日期全机器仅 1 次 API 调用);固化于主 SKILL.md「榜单数据落盘规范」+抖音数据规则 §2.1/2.2/2.4+三榜单子技能
|
||||
- **短视频分镜脚本** `project/短视频提示词生成/`(去 V1.0 层):F1反推图/F2反推视频/F3生图/F4生成分镜;三层引用 references→references-add(运行时优先);F4 输出=主题+导演阐述+代码块(>5s 时间戳分段/≤5s 单段);模板分档 A剧情/B Vlog/C旁白/D产品/E氛围 +4层×5类型;dsh 副本无独有内容需回流
|
||||
- **短视频脚本创作** `project/短视频脚本创作/{V1.0,Lite1.0}`:11 步 S1-S11;V1.0 含 MCP/Dify、Lite1.0 去接口模型自带 WebSearch。**★软链事实(09-03)**:`C:\Users\maidou\.workbuddy\skills\短视频脚本创作` 软链到源技能 V1.0(同一文件)→改源即自动同步 WorkBuddy,无需单独同步;dsh 副本 `C:\Users\maidou\.dsh\skills\short-video-script\` 是唯一需手动同步的副本。同步策略(08-26 用户定):只改 V1.0 源+dsh 副本;Lite1.0 默认不动(刻意 divergence)。🧹后台任务清理按 `references/接口调用/自动化任务清理规范.md`:只清过期/失败 once(禁 recurring/未来/running/QUEUED),走宿主 automation_update 软删,禁 SQL 直改库
|
||||
- **MCN工作台**(仅 V1.0)`V1.0/mcn-work-shop/`(零依赖 Node,**端口 8900**,argv 可指定;8899 是错误旧值)。**AI 任务触发**:`POST /api/run` 写 automations(once,显式 next_run_at+5s)→轮询 `GET /api/run/status`;调度机制实测定稿见工作台 `自动化任务调度机制.md`(scheduled_at 须 +60s 余量、并发≈3 QUEUED、改 server.js 须重启进程)。**★创作类 prompt 必须带 SKILL_HINT_CREATE 前缀**(漏前缀=模型跳步一步直出成品;新增创作/改写入口必查)。AI选题 `/api/ai/topics`(Dify recall ~50s,key 内置 `scripts/MCN_CYLG_API.py`,curl 中文被 CDN 拦用 Python);复盘页 prompt 按 mcn-script-review。UI 改完**必重开页面带 `?v=`**(内置浏览器同 URL 复用旧标签,重开是唯一刷新方式)
|
||||
- **mcn-dou-analysis** 源=`V1.0/subskills/mcn-dou-analysis/`、dsh=`C:\Users\maidou\.dsh\skills\mcn-dou-analysis/`。F1信息/F2视频表(13列xlsx)/F3账号设定/F4数据分析/F5解析单视频/F6设定卡片SVG。红线:达人表15列、TOP6(点赞+分享降序)、≥60条停止、29赛道枚举、浏览器 9223 调试实例+标签页复用、**账号身份标识禁从 DB 回填**(工作台 hot_accounts=MCP 内部库副本;留空待浏览器回填)、源技能只读单向同步。F6 配色按调性分深/浅两系(深=深沉催泪系;浅=明快甜美系如玫瑰金 #D6A3A9→#9A5563;「治愈」须回读情绪锚点)。browser-harness 执行功能一/二前必检。**★产物入库(09-01)**:产出落盘后须 `POST /api/import/account` 直写 mcn-plugin.db(analysis.json→account_video_analysis、content.json→account_video_source、设定/分析 md→account_persona/account_analysis、xlsx→account_videos);aweme_id 匹配必删全角标点+下划线转空格、幂等删旧保新。选题读最新 3 条视频解析拼 prompt
|
||||
- **mcn-data-insight(09-03 定稿)**:源=`V1.0/subskills/mcn-data-insight/`+dsh 副本(deployed diff=0)。**纯 11 个红狐系数据获取子技能**,非数据获取技能不放此包;`mcn-dou-analysis/subskills/nuwa-skill-main/` 是另一份账号方法论组件勿混。**★榜单即查即缓存落盘(09-03)**:红狐 API 计费+同日期固定快照→先 ls 命中免 API;桌面 `MCNSkill项目\三方数据\{数据源}\`(当前 红狐数据\抖音榜单\),文件名不加数据源前缀(保 `抖音周榜_` 开头兼容 dsh RANKING_DIR);dsh=`D:\dshworkspace\抖音榜单\`。固化于主 SKILL.md「榜单数据落盘规范」
|
||||
- **短视频分镜脚本** `project/短视频提示词生成/`:F1反推图/F2反推视频/F3生图/F4生成分镜;F4 输出=主题+导演阐述+代码块(>5s 时间戳分段);模板分档 A剧情/B Vlog/C旁白/D产品/E氛围
|
||||
|
||||
## AI创作弹窗 v2-v4(09-02 定稿;实现=data-pages.js + server.js + tmp/cdp-req-dialog-smoke.mjs)
|
||||
- 单弹窗 confirmAiScript:REQ_FIELDS 10 项(idea/form/dur/mood/aud/plot/end/sym/ad/note)需求框常显+对话流+胶囊输入行;后端 `/api/ai/clarify`(同步 Dify chat-messages 100s;{accountName,turn,reqMd,history}→{answer};强制「对话+==REQ==+10行需求」;query=人设摘要+reqMd+最近2轮+本轮);AI 禁生成脚本正文
|
||||
- v4 附件:`/api/req/parse-attachment` + `scripts/attachment-parser.js`(零依赖 node:zlib 解 .docx/.pdf,.doc 400);提交拼【附件素材】段
|
||||
- CDP 冒烟坑(9224):同 URL navigate 不 reload→`?v=Date.now()` 强制;DOM 句柄跨连接失效→Runtime.evaluate+requestNode;setFileInputFiles 抛错→Uint8Array+DataTransfer 注入;**单条持久 WebSocket 必需**(新连接=新 session 句柄全失效)
|
||||
## 创作技能关键红线(权威=SKILL.md+references/创作流程规范.md)
|
||||
- 三态创作:⚡自动(默认静默只落最终交付物)/🤝共创(+确认门)/🛠开发(+全落盘);产物链 05选题→06框架→07大纲 顺序禁跳步、「产出与传递」永不省略;硬编码禁止(P0);references-add 增量优先
|
||||
- **技能边界**:凡生成/改写脚本必须切脚本创作技能走 S1-S11(含 S11 数字账取证/开场钩子=绝对时长 3-7s 硬上限 10s/编导终审),禁会话内直写脚本;交付物验收口径=执行约束非输出内容,铁律禁在 S9/S10 正文自证
|
||||
- MCP:myai-mcp-production(正式)/test;首次提问检测提醒;飞书登录每天检查
|
||||
|
||||
## 创作技能关键规则(权威源=SKILL.md+references/创作流程规范.md)
|
||||
- 三态创作:⚡自动(默认静默只落最终交付物)/🤝共创(+确认门)/🛠开发(+全落盘);产物链铁律 05选题→06框架→07大纲 顺序禁跳步、「产出与传递」永不省略;硬编码禁止(P0);references-add 增量优先;技能边界=凡生成/改写脚本必须切脚本创作技能走 S1-S11(含 S11 数字账取证/开场钩子铁律=绝对时长3-7s硬上限10s不看占比/编导终审),禁会话内直写脚本
|
||||
- 交付物输出边界:验收口径=执行约束非输出内容,铁律/时长占比禁在 S9/S10 正文自证
|
||||
- MCP:myai-mcp-production(正式)/myai-mcp-test;首次提问检测提醒安装;飞书登录每天检查
|
||||
|
||||
## 工作方式约定(红线)
|
||||
- **治本原则**(09-01 用户明确):解决问题从根因入手→修机制→重新执行验证;禁补数据/临时绕过
|
||||
- **改完必重开页面**:网页改动完成最后一步必须 present_files 重开,**URL 必带 `?v=` 版本参数**(内置浏览器同 URL 复用旧标签不真加载;重开是唯一刷新方式)。排查顺序=先重开(?v=)排除旧缓存再改代码
|
||||
- 逐条修复:读一条→改一条→验证一条,禁批量读全部文件(上下文压缩丢质量);复查式修复:批量后多次「再检查之前要求」
|
||||
- git:**2 小时 push 一次**,本地随时 commit;push 前 fetch 查分叉;代理 socks5://127.0.0.1:10800 破坏连接需清空(local http/https proxy "");杀 node 用 PowerShell `Stop-Process -Id X -Force`(Git Bash taskkill //PID 无效);push 后 origin 跟踪引用可能被沙箱吞需重建 refs/remotes
|
||||
- 同步时清 tmp(mcn-work-shop/tmp/ + 仓库根 tmp/,已 gitignore;**禁清 .workbuddy/tmp/**);先杀自己启动的测试 Chrome(netstat+taskkill /T /F)再删
|
||||
- 沙箱代理:HTTP(S)_PROXY=127.0.0.1:7505(沙箱注入非用户配置)——curl localhost 必须 `--noproxy '*'`;服务用 run_in_background 起,禁 (cmd &) 子 shell
|
||||
- **Chrome 不主动关闭**(9223 调试实例/用户 Chrome/daemon 连接一律保留,除非用户明确要求;只回收自己启动的临时实例)——固化于 mcn-dou-analysis `references/浏览器搜索抖音账号操作规范.md` 5.7
|
||||
- .dsh 单向只读:副本只能读取→回流源技能,禁反向写回(除非用户明确授权)
|
||||
## 工作方式红线(09-03 全量保留)
|
||||
- **治本原则**:解决问题从根因入手→修机制→重新执行验证;禁补数据/临时绕过
|
||||
- **改完必重开**:网页改动完成最后一步 present_files 重开,URL 必带 `?v=YYYYMMDDx`;排查顺序=先重开排除旧缓存再改代码
|
||||
- 逐条修复:读一条→改一条→验证一条,禁批量读全部文件;复查式修复:批量后多次复查
|
||||
- git:**2 小时 push 一次**,本地随时 commit;push 前 fetch 查分叉;代理 socks5://127.0.0.1:10800 破坏连接需清 local proxy;杀 node 用 PowerShell `Stop-Process`(Git Bash taskkill 无效);push 后 origin 跟踪引用可能被沙箱吞需重建 refs/remotes
|
||||
- 同步时清 tmp(mcn-work-shop/tmp/+仓库根 tmp/,gitignore;**禁清 .workbuddy/tmp/**);先杀自启测试 Chrome 再删
|
||||
- 沙箱代理 HTTP(S)_PROXY=127.0.0.1:7505——curl localhost 必须 `--noproxy '*'`;服务 run_in_background 起
|
||||
- **Chrome 不主动关闭**(9223 实例/用户 Chrome/daemon 连接保留;只回收自启临时实例)
|
||||
- .dsh 单向只读:副本只读→回流源技能,禁反向写回(除非用户明确授权)
|
||||
- **★git rm 沙箱坑(09-03,2 次踩)**:`git rm -r` 删大范围被 SIGTERM 中断=只删磁盘不写索引+留 index.lock+半删除。修法:删 index.lock → `git checkout HEAD -- <路径>` 恢复 → **纯 `rm -rf` + `git add -A -- <精确路径>`**(不经 git rm)
|
||||
- 三层化沉淀:治本(改技能文件)→失误(append 日志)→记忆(MEMORY.md);日志 append-only 不改写历史;技能版本标签 frontmatter updated_at/last_change
|
||||
- 生成网页必先读 taste-skill 与 impeccable(08-27 强约束)
|
||||
- 技能版本标签:frontmatter updated_at/last_change;dsh 副本 deployed_at<源 updated_at=滞后
|
||||
- **★git rm 沙箱坑(09-03,2 次踩)**:`git rm -r` 删大范围文件被 SIGTERM 中断=只删磁盘不写索引(全变 ` D` 未暂存)+留 index.lock+**范围失控半删除**。修法:`rm -f .git/index.lock` → `git checkout HEAD -- <路径>` 恢复 → **纯 `rm -rf` 删磁盘 + `git add -A -- <精确路径>` 暂存**(不经 git rm)
|
||||
- 三层化沉淀:治本(改技能文件固化)→失误(append 日志)→记忆(MEMORY.md);偏好 append-only 日志不改写历史
|
||||
|
||||
## 历史事件(摘要)
|
||||
- 07-17 大炮创作方法;07-20 知识库重组;07-29 短剧知识库 V5.0;07-30 短视频知识库 V2.0
|
||||
- 08-11/12 素材库扁平化(8类型)+去硬编码+产物链铁律;08-16 赛道 8→27;08-17 三态创作定稿;08-20 分镜落库+seedance25;08-21 Lite1.0;08-22 .dsh 只读确立;08-25 更名 mcn-video-script
|
||||
- 08-26 路径重构+环境判定两步模型;08-27 dou-analysis 主版+分镜技能解耦+工作台;08-31 数据预览条/AI脚本诊断/调度器根因(scheduledAt 须未来);09-01 产物入库机制+自动化清理规范;09-02 工作台 AI创作弹窗 v2-v4+Chrome 不关红线;09-03 全树相对路径审计+`路径引用规范.md` 固化、mcn-data-insight 瘦身定稿(纯 11 数据技能)+dsh 部署、符号链接事实实测
|
||||
07-17 大炮方法;07-20 知识库重组;07-29 短剧库 V5.0;07-30 短视频库 V2.0;08-11/12 素材库扁平化+产物链铁律;08-16 赛道 8→27;08-17 三态创作;08-20 分镜落库;08-21 Lite1.0;08-22 .dsh 只读;08-25 更名 mcn-video-script;08-26 路径重构+两步模型;08-27 dou-analysis+分镜解耦+工作台;08-31 数据预览条/AI脚本诊断/调度器 scheduledAt 须未来;09-01 产物入库+清理规范;09-02 AI创作弹窗 v2-v4+Chrome 不关红线;09-03 路径引用规范固化、data-insight 瘦身+部署、软链事实、git rm 坑
|
||||
@@ -0,0 +1,55 @@
|
||||
// R29 冒烟:账号详情「视频列表」tab——「时长」前新增「标签」列(固定 40px,超长省略,悬停 title 显示完整)
|
||||
const list = await fetch('http://localhost:9225/json').then((r) => r.json());
|
||||
const page = list.find((t) => t.type === 'page');
|
||||
if (!page) { console.log('ERR: no page'); process.exit(1); }
|
||||
const ws = new WebSocket(page.webSocketDebuggerUrl);
|
||||
let id = 0; const pending = new Map();
|
||||
const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); });
|
||||
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending.has(m.id)) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } };
|
||||
await new Promise((r) => (ws.onopen = r));
|
||||
const ev = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 200) }; return r.result.value; };
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
await send('Page.enable'); await send('Runtime.enable');
|
||||
const waitFor = async (expr, ms = 15000) => { const t0 = Date.now(); while (Date.now() - t0 < ms) { if (await ev(expr)) return true; await sleep(300); } return false; };
|
||||
const fail = (m) => { console.log('FAIL@' + m); process.exit(1); };
|
||||
const goto = async (hash) => { await ev(`location.hash = ${JSON.stringify(hash)}`); await sleep(800); };
|
||||
|
||||
await send('Page.navigate', { url: 'http://localhost:8900/#/' });
|
||||
if (!(await waitFor(`!!document.querySelector('#view')`))) fail('app boot');
|
||||
await ev(`document.querySelectorAll('.modal-mask').forEach((m) => m.remove())`); // 清 root 设置弹窗
|
||||
|
||||
/* ============ 账号 1629 视频列表 tab(persona) ============ */
|
||||
await goto('#/account/1629/persona');
|
||||
if (!(await waitFor(`document.querySelector('#view .table tbody tr')`))) fail('video table rows');
|
||||
const out = await ev(`(() => {
|
||||
const ths = [...document.querySelectorAll('#view .table thead th')].map((t) => t.textContent.trim());
|
||||
const thTag = document.querySelector('#view .table thead th.th-tag');
|
||||
const firstTr = document.querySelector('#view .table tbody tr');
|
||||
const cells = [...firstTr.children];
|
||||
const tagTd = firstTr.querySelector('td.td-tag');
|
||||
return {
|
||||
ths, // 完整表头文本序列
|
||||
thTagIdx: ths.indexOf('标签'),
|
||||
durIdx: ths.indexOf('时长'),
|
||||
thTagW: thTag ? Math.round(thTag.getBoundingClientRect().width) : null,
|
||||
tagBeforeDur: ths.indexOf('标签') === ths.indexOf('时长') - 1,
|
||||
// 数据行
|
||||
rowCells: cells.map((c) => ({ cls: c.className, txt: (c.textContent || '').trim().slice(0, 24) })),
|
||||
tagTdText: tagTd ? (tagTd.textContent || '').trim() : null,
|
||||
tagTdTitle: tagTd ? (tagTd.getAttribute('title') || '') : null,
|
||||
tagTdClips: tagTd ? tagTd.scrollWidth > tagTd.clientWidth : null, // 是否发生省略截断
|
||||
tagTdW: tagTd ? Math.round(tagTd.getBoundingClientRect().width) : null,
|
||||
hasNumClass: tagTd ? tagTd.classList.contains('num') : null,
|
||||
};
|
||||
})()`);
|
||||
console.log(JSON.stringify(out, null, 1));
|
||||
const ok = out && !out.__err
|
||||
&& out.ths.length === 9 && out.thTagIdx === 6 && out.durIdx === 7 && out.tagBeforeDur === true
|
||||
&& out.thTagW === 40
|
||||
&& out.rowCells.length === 9 && out.rowCells[6].cls.includes('td-tag')
|
||||
&& !!out.tagTdText && out.tagTdText.length > 0
|
||||
&& !!out.tagTdTitle && out.tagTdTitle.includes('#') && out.tagTdTitle.length >= out.tagTdText.length
|
||||
&& out.tagTdClips === true && out.tagTdW === 40 && out.hasNumClass === false;
|
||||
console.log(ok ? '\n=== PASS ===' : '\n=== CHECK ===');
|
||||
ws.close();
|
||||
process.exit(ok ? 0 : 1);
|
||||
@@ -866,6 +866,7 @@ async function renderAccountDetail(view, id) {
|
||||
<td class="num">${fmtWan(v.comment_count)}</td>
|
||||
<td class="num">${fmtWan(v.share_count)}</td>
|
||||
<td class="num">${fmtWan(v.collect_count)}</td>
|
||||
<td class="td-tag"${v.tags ? ` title="${esc(v.tags)}"` : ''}>${v.tags ? esc(v.tags) : ''}</td>
|
||||
<td class="num">${fmtDuration(v.duration)}</td>
|
||||
<td class="cell-act"><button class="btn btn-sm ${(v.has_source || v.has_analysis) ? 'btn-view' : ''}" data-rewrite='${esc(JSON.stringify({ id: v.id, name: acc.account_name, title: cleanTitle(v.video_title), aweme: v.aweme_id, url: v.video_url || '', parsed: !!(v.has_source || v.has_analysis) }))}'>AI创作</button></td>
|
||||
</tr>`).join('');
|
||||
@@ -917,7 +918,7 @@ async function renderAccountDetail(view, id) {
|
||||
${personaPanel()}`;
|
||||
} else if (tab === 'persona') {
|
||||
panel = `<div class="trend-chart"><div id="trendChart"></div></div>
|
||||
<div class="table-wrap"><table class="table"><thead><tr>${bstate.batch ? '<th class="cb-cell"><input type="checkbox" class="batch-cb-all"></th>' : ''}<th class="th-pub">发布时间</th><th class="th-title">视频标题</th><th class="num">点赞</th><th class="num">评论</th><th class="num">分享</th><th class="num">收藏</th><th class="num">时长</th><th class="th-act">操作</th></tr></thead><tbody>${rows || `<tr><td colspan="${bstate.batch ? 9 : 8}" class="empty">暂无视频数据</td></tr>`}</tbody></table></div>
|
||||
<div class="table-wrap"><table class="table"><thead><tr>${bstate.batch ? '<th class="cb-cell"><input type="checkbox" class="batch-cb-all"></th>' : ''}<th class="th-pub">发布时间</th><th class="th-title">视频标题</th><th class="num">点赞</th><th class="num">评论</th><th class="num">分享</th><th class="num">收藏</th><th class="th-tag">标签</th><th class="num">时长</th><th class="th-act">操作</th></tr></thead><tbody>${rows || `<tr><td colspan="${bstate.batch ? 10 : 9}" class="empty">暂无视频数据</td></tr>`}</tbody></table></div>
|
||||
<div class="list-footer">
|
||||
${batchBarHtml(bstate, 0)}
|
||||
${pagerHtml(dv.total, dv.page, 20, (p) => { page = p; load(); })}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MCN工作台</title>
|
||||
<link rel="stylesheet" href="style.css?v=20260903k">
|
||||
<link rel="stylesheet" href="style.css?v=20260903l">
|
||||
<script src="vendor/echarts.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="app.js?v=20260903k"></script>
|
||||
<script src="data-pages.js?v=20260903k"></script>
|
||||
<script src="app.js?v=20260903l"></script>
|
||||
<script src="data-pages.js?v=20260903l"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -488,6 +488,9 @@ main#view { height: calc(100vh - 55px); overflow: auto; } /* 55px=topbar 实际
|
||||
th.th-pub { width: 135px; }
|
||||
td.td-pub { font-variant-numeric: tabular-nums; }
|
||||
td.td-title { max-width: 240px; }
|
||||
/* 账号详情视频列表标签列:固定 40px,超长省略,悬停 title 显示完整标签 */
|
||||
.table th.th-tag { width: 40px; min-width: 40px; padding: 8px 2px; text-align: center; }
|
||||
.table td.td-tag { max-width: 40px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 7px 2px; }
|
||||
|
||||
/* ================= 账号设定 卡片版/文字版 切换(右上角图标按钮组) ================= */
|
||||
.persona-wrap { position: relative; } /* 账号设定内容左右无间隔(09-02 用户要求去掉 20px) */
|
||||
|
||||
Reference in new issue
Block a user