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 = ``; - 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 + ? `
${running ? '复盘运行中…' : ''}
` + : '
'; + const mask = document.createElement('div'); mask.className = 'modal-mask'; + mask.innerHTML = ``; + 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; }