refactor(editing-planner): 代码质量整理
Deploy / Deploy Staging (push) Failing after 110h30m20s
CI/CD Pipeline / Frontend Lint (push) Failing after 110h30m52s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 110h30m59s
Deploy / Production Browser E2E (push) Failing after 1769h4m56s
Deploy / Staging E2E Tests (push) Failing after 1769h4m58s
Deploy / Build Production Runtime Images (push) Failing after 1769h5m0s
Deploy / Deploy Production (push) Failing after 1769h36m24s
Deploy / Deploy Staging (push) Failing after 110h30m20s
CI/CD Pipeline / Frontend Lint (push) Failing after 110h30m52s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 110h30m59s
Deploy / Production Browser E2E (push) Failing after 1769h4m56s
Deploy / Staging E2E Tests (push) Failing after 1769h4m58s
Deploy / Build Production Runtime Images (push) Failing after 1769h5m0s
Deploy / Deploy Production (push) Failing after 1769h36m24s
- 提取 GenerationHistoryModal 组件(EditingPlanner 减少约 85 行) - 修复双重类型转换:as unknown as TaskItem → getTask(res.id) - 删除未使用的 PLAN_STATUS_LABELS import - 补充关键逻辑 JSDoc 注释(loadTemplates/handleAiRecommend/handleGenerate/轮询等)
This commit is contained in:
@@ -30,7 +30,6 @@ import {
|
||||
getMediaAssets,
|
||||
getEditPlanGenerations,
|
||||
MATERIAL_TYPE_LABELS,
|
||||
PLAN_STATUS_LABELS,
|
||||
aiRecommendClips,
|
||||
generateCover,
|
||||
} from "@/api/editPlans";
|
||||
@@ -45,6 +44,7 @@ import ClipPropertiesPanel from "./components/ClipPropertiesPanel";
|
||||
import SaveModal from "./components/SaveModal";
|
||||
import GenerationProgressModal from "./components/GenerationProgressModal";
|
||||
import type { GenPhase } from "./components/GenerationProgressModal";
|
||||
import GenerationHistoryModal from "./components/GenerationHistoryModal";
|
||||
import "./EditingPlanner.css";
|
||||
|
||||
/* ──────────── 类型 ──────────── */
|
||||
@@ -167,6 +167,10 @@ const EditingPlanner: React.FC = () => {
|
||||
|
||||
/* ──────────── 加载 ──────────── */
|
||||
|
||||
/**
|
||||
* 并行加载模板列表、分类、素材库
|
||||
* 首次挂载时调用,三个接口无依赖关系,用 Promise.all 并发
|
||||
*/
|
||||
const loadTemplates = useCallback(async () => {
|
||||
setLoadingTemplates(true);
|
||||
try {
|
||||
@@ -189,7 +193,11 @@ const EditingPlanner: React.FC = () => {
|
||||
loadTemplates();
|
||||
}, [loadTemplates]);
|
||||
|
||||
/* 加载模板详情 */
|
||||
/**
|
||||
* 加载模板详情并初始化片段列表
|
||||
* 将后端 segments 映射为前端 ClipData,取 duration_min/max 均值作为默认时长
|
||||
* 同时还原标题/字幕/BGM 配置
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!loadedTemplateId) return;
|
||||
getEditingTemplate(loadedTemplateId)
|
||||
@@ -316,7 +324,11 @@ const EditingPlanner: React.FC = () => {
|
||||
setClips((prev) => [...prev, newClip]);
|
||||
};
|
||||
|
||||
/* AI 推荐片段 */
|
||||
/**
|
||||
* AI 推荐片段方案
|
||||
* 将当前已绑定素材的片段 ID 列表 + 编辑模式 + 目标时长发送给后端
|
||||
* 返回的推荐结果直接替换当前片段列表(不保留原手动编排)
|
||||
*/
|
||||
const handleAiRecommend = async () => {
|
||||
if (!loadedTemplateId) {
|
||||
msg.warning("请先加载模板");
|
||||
@@ -448,13 +460,16 @@ const EditingPlanner: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/* 跳转到一键生成页面,携带当前编辑器配置 */
|
||||
/**
|
||||
* 跳转到一键生成页面
|
||||
* 通过 URL SearchParams 传递 edit_plan_id 和完整 planConfig(JSON 序列化)
|
||||
* 一键生成页面从 params 解析配置,无需重复请求接口
|
||||
*/
|
||||
const handleGoToGenerate = () => {
|
||||
if (!loadedTemplateId) {
|
||||
msg.warning("请先加载一个模板");
|
||||
return;
|
||||
}
|
||||
// 构建配置对象,传递给一键生成页面
|
||||
const planConfig = {
|
||||
title_config: {
|
||||
ai_auto_select: titleSettings.aiAutoSelect,
|
||||
@@ -496,6 +511,12 @@ const EditingPlanner: React.FC = () => {
|
||||
navigate(`/app/generate?${params.toString()}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建生成任务(两步)
|
||||
* 1. generateFromTemplate — 通知后端基于模板生成视频
|
||||
* 2. createGenerationTask — 创建任务记录,返回精简响应
|
||||
* 再用 getTask 查询完整 TaskItem 供轮询使用
|
||||
*/
|
||||
const handleGenerate = async () => {
|
||||
if (!loadedTemplateId) return;
|
||||
setGenSubmitting(true);
|
||||
@@ -503,7 +524,7 @@ const EditingPlanner: React.FC = () => {
|
||||
await generateFromTemplate(loadedTemplateId, {
|
||||
voiceover_duration: voiceoverDuration || totalDuration,
|
||||
});
|
||||
const task = await createGenerationTask({
|
||||
const res = await createGenerationTask({
|
||||
template_id: loadedTemplateId,
|
||||
asset_ids: clips
|
||||
.map((c) => c.media_asset_id)
|
||||
@@ -511,7 +532,9 @@ const EditingPlanner: React.FC = () => {
|
||||
title_ids: [],
|
||||
voice_ids: [],
|
||||
});
|
||||
setGenTask(task as unknown as TaskItem);
|
||||
/* 创建接口返回的是精简响应,需查询完整 TaskItem 用于轮询 */
|
||||
const task = await getTask(res.id);
|
||||
setGenTask(task);
|
||||
setGenPhase("progress");
|
||||
msg.info("生成任务已创建");
|
||||
} catch {
|
||||
@@ -521,7 +544,11 @@ const EditingPlanner: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
/* 轮询任务状态 */
|
||||
/**
|
||||
* 轮询生成任务状态(每 3 秒)
|
||||
* 仅在 genPhase === "progress" 且有任务 ID 时启动
|
||||
* 任务完成/失败时自动停止轮询
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (genPhase !== "progress" || !genTask?.id) return;
|
||||
const timer = setInterval(async () => {
|
||||
@@ -762,98 +789,13 @@ const EditingPlanner: React.FC = () => {
|
||||
onClose={handleCancelGen}
|
||||
/>
|
||||
|
||||
{/* ═══ 生成历史 Modal ═══ */}
|
||||
{genHistoryOpen && (
|
||||
<div className="ep-modal-overlay" onClick={() => setGenHistoryOpen(false)}>
|
||||
<div
|
||||
className="ep-modal"
|
||||
style={{ width: 520 }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="ep-modal-header">
|
||||
<h3>📋 生成历史</h3>
|
||||
<button
|
||||
className="ep-modal-close"
|
||||
onClick={() => setGenHistoryOpen(false)}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="ep-modal-body" style={{ maxHeight: 400, overflowY: "auto" }}>
|
||||
{genHistoryLoading ? (
|
||||
<div style={{ textAlign: "center", padding: 24, color: "#999" }}>
|
||||
加载中...
|
||||
</div>
|
||||
) : genHistory.length === 0 ? (
|
||||
<div style={{ textAlign: "center", padding: 24, color: "#999" }}>
|
||||
暂无生成记录
|
||||
</div>
|
||||
) : (
|
||||
<table style={{ width: "100%", borderCollapse: "collapse" }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: "1px solid #e8e8e8", textAlign: "left" }}>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>任务ID</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>状态</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>创建时间</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>更新时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{genHistory.map((gen) => (
|
||||
<tr key={gen.id} style={{ borderBottom: "1px solid #f0f0f0" }}>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, fontFamily: "monospace" }}>
|
||||
{gen.generation_task_id.slice(0, 8)}...
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13 }}>
|
||||
<span
|
||||
style={{
|
||||
padding: "2px 8px",
|
||||
borderRadius: 4,
|
||||
fontSize: 12,
|
||||
background:
|
||||
gen.status === "completed"
|
||||
? "#f6ffed"
|
||||
: gen.status === "failed"
|
||||
? "#fff2f0"
|
||||
: gen.status === "rendering"
|
||||
? "#e6f7ff"
|
||||
: "#fafafa",
|
||||
color:
|
||||
gen.status === "completed"
|
||||
? "#52c41a"
|
||||
: gen.status === "failed"
|
||||
? "#ff4d4f"
|
||||
: gen.status === "rendering"
|
||||
? "#1890ff"
|
||||
: "#666",
|
||||
}}
|
||||
>
|
||||
{PLAN_STATUS_LABELS[gen.status] || gen.status}
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, color: "#666" }}>
|
||||
{gen.created_at ? new Date(gen.created_at).toLocaleString("zh-CN") : "—"}
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, color: "#666" }}>
|
||||
{gen.updated_at ? new Date(gen.updated_at).toLocaleString("zh-CN") : "—"}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
<div className="ep-modal-footer">
|
||||
<button
|
||||
className="ep-btn ep-btn-secondary"
|
||||
onClick={() => setGenHistoryOpen(false)}
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* ═══ 生成历史弹窗 ═══ */}
|
||||
<GenerationHistoryModal
|
||||
open={genHistoryOpen}
|
||||
loading={genHistoryLoading}
|
||||
history={genHistory}
|
||||
onClose={() => setGenHistoryOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 生成历史弹窗 — 展示当前剪辑计划的生成任务记录
|
||||
* 从 EditingPlanner 拆分,避免主文件过大
|
||||
*/
|
||||
import React from "react";
|
||||
import type { EditPlanGeneration } from "@/api/editPlans";
|
||||
import { PLAN_STATUS_LABELS } from "@/api/editPlans";
|
||||
|
||||
interface GenerationHistoryModalProps {
|
||||
open: boolean;
|
||||
loading: boolean;
|
||||
history: EditPlanGeneration[];
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/** 状态标签颜色映射 */
|
||||
const STATUS_STYLES: Record<string, { bg: string; color: string }> = {
|
||||
completed: { bg: "#f6ffed", color: "#52c41a" },
|
||||
failed: { bg: "#fff2f0", color: "#ff4d4f" },
|
||||
rendering: { bg: "#e6f7ff", color: "#1890ff" },
|
||||
};
|
||||
const DEFAULT_STATUS_STYLE = { bg: "#fafafa", color: "#666" };
|
||||
|
||||
const GenerationHistoryModal: React.FC<GenerationHistoryModalProps> = ({
|
||||
open,
|
||||
loading,
|
||||
history,
|
||||
onClose,
|
||||
}) => {
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div className="ep-modal-overlay" onClick={onClose}>
|
||||
<div
|
||||
className="ep-modal"
|
||||
style={{ width: 520 }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="ep-modal-header">
|
||||
<h3>📋 生成历史</h3>
|
||||
<button className="ep-modal-close" onClick={onClose}>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="ep-modal-body" style={{ maxHeight: 400, overflowY: "auto" }}>
|
||||
{loading ? (
|
||||
<div style={{ textAlign: "center", padding: 24, color: "#999" }}>
|
||||
加载中...
|
||||
</div>
|
||||
) : history.length === 0 ? (
|
||||
<div style={{ textAlign: "center", padding: 24, color: "#999" }}>
|
||||
暂无生成记录
|
||||
</div>
|
||||
) : (
|
||||
<table style={{ width: "100%", borderCollapse: "collapse" }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: "1px solid #e8e8e8", textAlign: "left" }}>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>任务ID</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>状态</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>创建时间</th>
|
||||
<th style={{ padding: "8px 12px", fontSize: 12, color: "#666" }}>更新时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{history.map((gen) => {
|
||||
const style = STATUS_STYLES[gen.status] || DEFAULT_STATUS_STYLE;
|
||||
return (
|
||||
<tr key={gen.id} style={{ borderBottom: "1px solid #f0f0f0" }}>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, fontFamily: "monospace" }}>
|
||||
{gen.generation_task_id.slice(0, 8)}...
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13 }}>
|
||||
<span
|
||||
style={{
|
||||
padding: "2px 8px",
|
||||
borderRadius: 4,
|
||||
fontSize: 12,
|
||||
background: style.bg,
|
||||
color: style.color,
|
||||
}}
|
||||
>
|
||||
{PLAN_STATUS_LABELS[gen.status] || gen.status}
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, color: "#666" }}>
|
||||
{gen.created_at ? new Date(gen.created_at).toLocaleString("zh-CN") : "—"}
|
||||
</td>
|
||||
<td style={{ padding: "8px 12px", fontSize: 13, color: "#666" }}>
|
||||
{gen.updated_at ? new Date(gen.updated_at).toLocaleString("zh-CN") : "—"}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
<div className="ep-modal-footer">
|
||||
<button className="ep-btn ep-btn-secondary" onClick={onClose}>
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenerationHistoryModal;
|
||||
Reference in New Issue
Block a user