diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
index 2d4d557..1149305 100644
--- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
+++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js
@@ -922,55 +922,121 @@ async function renderReviews(view) {
bindPager(view, (p) => { page = p; render(); });
view.querySelectorAll('[data-open]').forEach((a) => a.addEventListener('click', () => openScriptDetail(a.dataset.open)));
view.querySelectorAll('[data-review]').forEach((a) => a.addEventListener('click', () => openReviewDetail(a.dataset.review)));
- view.querySelectorAll('[data-review-row]').forEach((b) => b.addEventListener('click', async () => {
- if (b.dataset.lock) return; b.dataset.lock = '1'; // 防 await 窗口期重复点击
- try {
- // 按该脚本是否有原视频源脚本决定复盘范围:有 → 三模式;无(输入选题/视频未解析)→ 仅 AI 脚本评分
- const row = items.find((x) => String(x.id) === String(b.dataset.reviewRow));
- let hasSource = false;
- if (row && Number(row.video_id) > 0) {
- const vs = await api('/api/dsh/video-script?videoId=' + row.video_id).catch(() => ({ ok: false, error: 1 }));
- hasSource = !!(!vs.error && vs.ok && vs.source && String(vs.source).trim()); // 查询失败视为有源(沿用原文案,不误砍对比模式)
- }
- const [desc, prompt] = hasSource
- ? ['将对该 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式),按 mcn-script-review(脚本复盘)技能执行。',
- '请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式),按 mcn-script-review(脚本复盘)技能执行。']
- : ['将对该 AI 脚本做质量复盘评分(该脚本无原视频脚本,仅评 AI 脚本:独立分析模式·10 维评分制),按 mcn-script-review(脚本复盘)技能执行。',
- '请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析,按 mcn-script-review(脚本复盘)技能执行。'];
- confirmRun({
- title: hasSource ? '复盘评分' : 'AI脚本复盘',
- desc,
- prompt,
- label: '复盘指令',
- skills: ['脚本复盘'],
- btn: b,
- });
- } finally {
- delete b.dataset.lock;
- }
+ view.querySelectorAll('[data-review-row]').forEach((b) => b.addEventListener('click', () => {
+ const row = items.find((x) => String(x.id) === String(b.dataset.reviewRow));
+ if (row) startReview(row, b);
}));
};
render();
+ // 发起复盘任务:按该脚本是否有原视频源脚本决定复盘范围(有 → 三模式;无 → 仅 AI 脚本评分);行按钮与详情弹窗共用
+ async function startReview(row, btn) {
+ if (btn.dataset.lock) return; btn.dataset.lock = '1'; // 防 await 窗口期重复点击
+ try {
+ let hasSource = false;
+ if (row && Number(row.video_id) > 0) {
+ const vs = await api('/api/dsh/video-script?videoId=' + row.video_id).catch(() => ({ ok: false, error: 1 }));
+ hasSource = !!(!vs.error && vs.ok && vs.source && String(vs.source).trim()); // 查询失败视为有源(沿用原文案,不误砍对比模式)
+ }
+ const [desc, prompt] = hasSource
+ ? ['将对该 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式),按 mcn-script-review(脚本复盘)技能执行。',
+ '请对这条 AI 脚本做复盘评分(AI脚本 / 原视频脚本 / 对比分析三种模式),按 mcn-script-review(脚本复盘)技能执行。']
+ : ['将对该 AI 脚本做质量复盘评分(该脚本无原视频脚本,仅评 AI 脚本:独立分析模式·10 维评分制),按 mcn-script-review(脚本复盘)技能执行。',
+ '请对这条 AI 脚本做质量复盘评分(独立分析模式·10 维评分制)。该脚本无原视频脚本,只评 AI 脚本本身,不做源脚本复盘、不做对比分析,按 mcn-script-review(脚本复盘)技能执行。'];
+ confirmRun({
+ title: hasSource ? '复盘评分' : 'AI脚本复盘',
+ desc,
+ prompt,
+ label: '复盘指令',
+ skills: ['脚本复盘'],
+ btn,
+ });
+ } finally {
+ delete btn.dataset.lock;
+ }
+ }
+
async function openScriptDetail(rewriteId) {
- // 打开源 vs AI 对比(复用视频脚本弹窗逻辑的精简版)
+ // 脚本详情弹窗:样式对齐「AI写脚本」查看弹窗(rw-stack 三列)——源脚本(可折叠) | AI脚本(版本切换) | AI诊断(复盘结果)
const row = items.find((x) => String(x.id) === String(rewriteId));
if (!row) return;
const d2 = await api('/api/dsh/video-script?videoId=' + row.video_id).catch(() => ({ ok: false }));
const rewrites = (d2.rewrites || []);
- const idx = rewrites.findIndex((x) => x.id === row.id);
- const cur = idx >= 0 ? rewrites[idx] : null;
- const mask = document.createElement('div'); mask.className = 'modal-mask';
- mask.innerHTML = `
-
${esc(fmtTitleQ(row.title || ''))}
-
-
源脚本
${renderMarkdown(d2.source || '(暂无源脚本数据)')}
-
AI脚本
${cur ? renderMarkdown(cur.script_text || '(暂无内容)') : '(暂无 AI 脚本)'}
-
-
`;
- document.body.appendChild(mask);
- mask.addEventListener('click', (e) => { if (e.target === mask) mask.remove(); });
- mask.querySelector('[data-x]').addEventListener('click', () => mask.remove());
+ const hasSource = !!(d2.source && String(d2.source).trim());
+ let verIdx = Math.max(0, rewrites.findIndex((x) => x.id === row.id));
+ let showSrc = false; // 源脚本列默认收起(与查看弹窗一致)
+ const DIMS_ALL = ['设定承接', '框架节奏', '钩子设置', '叙事逻辑', '极致事件', '共鸣和共情', '格式规范性', '台词口语化', '内容合规', '教程完整链', '其他'];
+ const scoreColor = (v) => (v >= 75 ? '#3a9e6d' : v >= 60 ? '#d99a2b' : '#d9534f');
+ // 诊断内容 HTML(取 AI 视角评分:compare 类型取 ai 侧 dims/total 与源对比结论,列表总分口径一致)
+ const diagBodyHtml = (dRow) => {
+ const rv = reviewOf(dRow);
+ if (!rv) return '';
+ let dims = {}, total = NaN;
+ try { const j = JSON.parse(rv.dims_json || '{}'); dims = rv.type === 'compare' ? (j.ai || {}) : j; } catch (e) {}
+ try { const t = JSON.parse(rv.total || '{}'); total = rv.type === 'compare' ? Number(t.ai) : Number(rv.total); } catch (e) { total = Number(rv.total); }
+ const srcTotal = (() => { try { const t = JSON.parse(rv.total || '{}'); return rv.type === 'compare' ? Number(t.source) : NaN; } catch (e) { return NaN; } })();
+ const scoreBar = DIMS_ALL.map((k) => { const v = dims[k]; const has = typeof v === 'number' && Number.isFinite(v); return `${esc(k)}${has ? v : '—'} `; }).join('');
+ let html = `AI 总分 ${fmtScore(Number.isFinite(total) ? total : rv.total)}${rv.type === 'compare' && Number.isFinite(srcTotal) ? `(对比源脚本 ${fmtScore(srcTotal)})` : ''}${rv.verdict ? ` · ${esc(rv.verdict)}` : ''}
${scoreBar}
`;
+ let issues = []; try { issues = JSON.parse(rv.issues_json || '[]'); } catch (e) {}
+ if (issues.length) html += `问题清单
${issues.map((it) => `
${esc(it.severity || '低')}${esc(it.issue || '')}${it.suggestion ? `→ ${esc(it.suggestion)}` : ''}
`).join('')}
`;
+ if (rv.summary) html += `综合评语:${esc(rv.summary)}
`;
+ html += ``;
+ return html;
+ };
+ const diagEmptyHtml = (dRow) => (dRow && dRow.review_running)
+ ? `复盘运行中…(完成后重新打开本弹窗即可查看评分)
`
+ : `该版本 AI 脚本尚未诊断
+ `;
+
+ function renderModal() {
+ const curRow = rewrites.length ? rewrites[Math.min(verIdx, rewrites.length - 1)] : null;
+ const dRow = curRow ? (items.find((x) => String(x.id) === String(curRow.id)) || curRow) : null; // items 行带 review_ai/compare;兜底 raw 行(无诊断)
+ const diag = dRow ? reviewOf(dRow) : null;
+ const srcSec = hasSource
+ ? `
+
源脚本
+
${renderMarkdown(d2.source)}
+
+
` : '';
+ const srcToggle = hasSource ? `` : '';
+ const verCtrl = (() => {
+ const len = rewrites.length;
+ if (len <= 1) return `${len === 1 ? '版本 1/1' : ''}`;
+ return `版本 ${verIdx + 1}/${len}`;
+ })();
+ const running = !!(dRow && dRow.review_running);
+ const diagFoot = diag
+ ? ``
+ : '';
+ const mask = document.createElement('div'); mask.className = 'modal-mask';
+ mask.innerHTML = `
+
${esc(fmtTitleQ(row.title || ''))}
+
+ ${srcSec}
+
+
AI脚本${srcToggle}
+
${curRow ? renderMarkdown(curRow.script_text || '(暂无内容)') : '(暂无 AI 脚本)'}
+ ${verCtrl ? `` : ''}
+
+
+
AI诊断
+
${diag ? diagBodyHtml(dRow) : diagEmptyHtml(dRow)}
+ ${diagFoot}
+
+
+
`;
+ document.body.appendChild(mask);
+ mask.addEventListener('click', (e) => { if (e.target === mask) mask.remove(); });
+ mask.querySelector('[data-x]').addEventListener('click', () => mask.remove());
+ const q = (s) => mask.querySelector(s);
+ const srcT = q('[data-src-toggle]'); if (srcT) srcT.addEventListener('click', () => { showSrc = !showSrc; mask.remove(); renderModal(); });
+ const vp = q('[data-vprev]'); if (vp) vp.addEventListener('click', () => { if (verIdx > 0) { verIdx--; mask.remove(); renderModal(); } });
+ const vn = q('[data-vnext]'); if (vn) vn.addEventListener('click', () => { if (verIdx < rewrites.length - 1) { verIdx++; mask.remove(); renderModal(); } });
+ const diagBtn = q('[data-diag-review]');
+ if (diagBtn) diagBtn.addEventListener('click', () => { const t = dRow; if (t) startReview(t, diagBtn); });
+ const full = q('[data-review]'); if (full) full.addEventListener('click', () => openReviewDetail(full.dataset.review));
+ }
+ renderModal();
}
async function openReviewDetail(reviewId) {
diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css
index 4db5a58..237c417 100644
--- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css
+++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css
@@ -438,6 +438,7 @@ td.td-title { max-width: 240px; }
.review-issues { margin-bottom: 10px; }
.issue-row { display: flex; gap: 6px; font-size: 14px; margin-bottom: 4px; }
.review-summary { margin-bottom: 10px; font-size: 15px; }
+.diag-empty { color: var(--dim); font-size: 14px; padding: 14px 2px 10px; line-height: 1.6; } /* AI脚本诊断弹窗:该版本未诊断时的占位提示 */
/* ================= 首页 AI写剧本 快捷卡片 ================= */
.recent-scripts { margin: 4px 0 24px; }