docs: 为全部代码补充中文注释并加入 AGENTS 注释规范

This commit is contained in:
cat-shark
2026-08-13 22:09:55 +08:00
parent 0a028bedcc
commit 94c7867c5a
7 changed files with 92 additions and 0 deletions
+6
View File
@@ -13,3 +13,9 @@
- 页面不直接暴露 API 调用细节给普通用户。 - 页面不直接暴露 API 调用细节给普通用户。
- 前端只通过 `/api/...` 调用后端,不内联业务逻辑。 - 前端只通过 `/api/...` 调用后端,不内联业务逻辑。
- 后续迁移到 React 时,页面行为应与当前静态页面保持一致。 - 后续迁移到 React 时,页面行为应与当前静态页面保持一致。
## 代码注释规范
- 本仓库所有源码(HTML、JavaScript、CSS 等支持注释的文件)必须配有详细中文注释,说明页面职责、函数作用与关键交互逻辑,确保后续维护人员可以快速理解代码工作原理。
- 新增或修改代码时,必须同步补充或更新对应注释;不得删除已有注释。
- 每个 HTML 页面需要在文件头说明页面用途;公共脚本需要说明各函数的职责。
+5
View File
@@ -1,4 +1,5 @@
<!doctype html> <!doctype html>
<!-- WOV 管理后台:节点注册、节点列表、手动调用与实例管理。 -->
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -20,6 +21,7 @@
<main class="container"> <main class="container">
<h1>节点管理</h1> <h1>节点管理</h1>
<!-- 注册节点:粘贴 node.manifest.json,由后端校验后写入注册表。 -->
<section class="panel"> <section class="panel">
<h2>注册节点</h2> <h2>注册节点</h2>
<label for="manifestJson">节点 Manifest JSON</label> <label for="manifestJson">节点 Manifest JSON</label>
@@ -30,6 +32,7 @@
</div> </div>
</section> </section>
<!-- 已注册节点:展示节点能力与版本,可删除节点。 -->
<section class="panel"> <section class="panel">
<h2>已注册节点</h2> <h2>已注册节点</h2>
<div class="table-wrap"> <div class="table-wrap">
@@ -48,6 +51,7 @@
</div> </div>
</section> </section>
<!-- 调用节点:管理后台手动触发一次节点调用,用于联调。 -->
<section class="panel"> <section class="panel">
<h2>调用节点</h2> <h2>调用节点</h2>
<div class="form-grid"> <div class="form-grid">
@@ -68,6 +72,7 @@
<pre id="invokeResult" class="result">等待调用...</pre> <pre id="invokeResult" class="result">等待调用...</pre>
</section> </section>
<!-- 节点实例:查看 NodeManager 启动的进程状态,可手动停止。 -->
<section class="panel"> <section class="panel">
<h2>节点实例</h2> <h2>节点实例</h2>
<div class="table-wrap"> <div class="table-wrap">
+33
View File
@@ -1,3 +1,6 @@
// WOV 静态前端公共脚本:所有页面共用的 API 封装、渲染函数与事件绑定。
// Echo 演示节点的预置 manifest,便于管理后台一键填入。
const ECHO_MANIFEST = { const ECHO_MANIFEST = {
id: "echo", id: "echo",
name: "Echo Node", name: "Echo Node",
@@ -14,6 +17,7 @@ const ECHO_MANIFEST = {
keep_warm: false, keep_warm: false,
}; };
// 演示“视频字幕生成”工作流的 DAG 定义,预填在工作流编排页。
const DEMO_WORKFLOW = { const DEMO_WORKFLOW = {
name: "视频字幕生成", name: "视频字幕生成",
version: 1, version: 1,
@@ -55,6 +59,7 @@ const DEMO_WORKFLOW = {
}, },
}; };
// 统一封装 fetch:自动携带 JSON 头、解析响应并在失败时抛出可读错误。
async function api(path, options = {}) { async function api(path, options = {}) {
const response = await fetch(path, { const response = await fetch(path, {
headers: { "Content-Type": "application/json", ...(options.headers || {}) }, headers: { "Content-Type": "application/json", ...(options.headers || {}) },
@@ -62,12 +67,14 @@ async function api(path, options = {}) {
}); });
const data = await response.json().catch(() => null); const data = await response.json().catch(() => null);
if (!response.ok) { if (!response.ok) {
// FastAPI 的校验错误 detail 可能是数组,统一序列化为字符串展示。
const detail = data && data.detail ? JSON.stringify(data.detail) : response.statusText; const detail = data && data.detail ? JSON.stringify(data.detail) : response.statusText;
throw new Error(`${response.status} ${detail}`); throw new Error(`${response.status} ${detail}`);
} }
return data; return data;
} }
// 转义用户可控文本,防止 XSS 注入到表格或状态 HTML 中。
function escapeHtml(value) { function escapeHtml(value) {
return String(value) return String(value)
.replaceAll("&", "&amp;") .replaceAll("&", "&amp;")
@@ -77,7 +84,9 @@ function escapeHtml(value) {
.replaceAll("'", "&#039;"); .replaceAll("'", "&#039;");
} }
// 根据状态生成带语义颜色的徽章 HTML。
function badge(status) { function badge(status) {
// 统一转小写比较,兼容后端返回的不同大小写。
const value = String(status).toLowerCase(); const value = String(status).toLowerCase();
const className = const className =
value === "ready" || value === "completed" || value === "ok" value === "ready" || value === "completed" || value === "ok"
@@ -88,12 +97,14 @@ function badge(status) {
return `<span class="badge ${className}">${escapeHtml(status)}</span>`; return `<span class="badge ${className}">${escapeHtml(status)}</span>`;
} }
// 把 ISO 时间格式化为本地时间;非法值原样返回。
function formatTime(value) { function formatTime(value) {
if (!value) return ""; if (!value) return "";
const date = new Date(value); const date = new Date(value);
return Number.isNaN(date.getTime()) ? value : date.toLocaleString(); return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
} }
// 计算距给定时间的流逝时长,用于任务页展示已运行时间。
function formatElapsed(value) { function formatElapsed(value) {
if (!value) return ""; if (!value) return "";
const seconds = Math.floor((Date.now() - new Date(value).getTime()) / 1000); const seconds = Math.floor((Date.now() - new Date(value).getTime()) / 1000);
@@ -102,6 +113,7 @@ function formatElapsed(value) {
return `${Math.floor(seconds / 60)} 分钟`; return `${Math.floor(seconds / 60)} 分钟`;
} }
// 健康检查失败时在页面顶部展示后端不可用提示。
async function loadHealth() { async function loadHealth() {
try { try {
await api("/health"); await api("/health");
@@ -113,6 +125,7 @@ async function loadHealth() {
} }
} }
// 加载已注册节点并渲染节点表格与调用下拉框。
async function loadNodes() { async function loadNodes() {
const nodes = await api("/api/admin/nodes"); const nodes = await api("/api/admin/nodes");
const tbody = document.getElementById("nodeList"); const tbody = document.getElementById("nodeList");
@@ -136,6 +149,7 @@ async function loadNodes() {
.join(""); .join("");
} }
// 加载节点实例并渲染实例表格。
async function loadInstances() { async function loadInstances() {
const instances = await api("/api/admin/node-instances"); const instances = await api("/api/admin/node-instances");
const tbody = document.getElementById("instanceList"); const tbody = document.getElementById("instanceList");
@@ -156,10 +170,12 @@ async function loadInstances() {
: '<tr><td colspan="6">暂无实例</td></tr>'; : '<tr><td colspan="6">暂无实例</td></tr>';
} }
// 管理后台页面同时刷新节点与实例两个列表。
async function refresh() { async function refresh() {
await Promise.all([loadNodes(), loadInstances()]); await Promise.all([loadNodes(), loadInstances()]);
} }
// 注册节点:解析文本域中的 manifest JSON 并提交到后端。
async function registerNode() { async function registerNode() {
const raw = document.getElementById("manifestJson").value.trim(); const raw = document.getElementById("manifestJson").value.trim();
try { try {
@@ -172,6 +188,7 @@ async function registerNode() {
} }
} }
// 手动调用节点:读取表单参数并展示返回结果。
async function invokeNode() { async function invokeNode() {
const nodeId = document.getElementById("invokeNodeId").value; const nodeId = document.getElementById("invokeNodeId").value;
const runId = document.getElementById("invokeRunId").value.trim() || "run_web"; const runId = document.getElementById("invokeRunId").value.trim() || "run_web";
@@ -190,6 +207,7 @@ async function invokeNode() {
} }
} }
// 删除节点前不做二次确认,由点击按钮触发。
async function deleteNode(nodeId) { async function deleteNode(nodeId) {
try { try {
await api(`/api/admin/nodes/${nodeId}`, { method: "DELETE" }); await api(`/api/admin/nodes/${nodeId}`, { method: "DELETE" });
@@ -199,6 +217,7 @@ async function deleteNode(nodeId) {
} }
} }
// 请求后端停止指定节点实例。
async function stopInstance(instanceId) { async function stopInstance(instanceId) {
try { try {
await api(`/api/admin/node-instances/${instanceId}/stop`, { method: "POST" }); await api(`/api/admin/node-instances/${instanceId}/stop`, { method: "POST" });
@@ -208,6 +227,7 @@ async function stopInstance(instanceId) {
} }
} }
// 应用中心的 Echo 演示:直接调用 echo 节点并展示结果。
async function runEcho() { async function runEcho() {
const text = document.getElementById("echoText").value; const text = document.getElementById("echoText").value;
const resultBox = document.getElementById("echoResult"); const resultBox = document.getElementById("echoResult");
@@ -227,6 +247,7 @@ async function runEcho() {
} }
} }
// 加载工作流列表并渲染发布状态与操作按钮。
async function loadWorkflows() { async function loadWorkflows() {
const workflows = await api("/api/admin/workflows"); const workflows = await api("/api/admin/workflows");
const tbody = document.getElementById("workflowList"); const tbody = document.getElementById("workflowList");
@@ -250,6 +271,7 @@ async function loadWorkflows() {
: '<tr><td colspan="5">暂无工作流</td></tr>'; : '<tr><td colspan="5">暂无工作流</td></tr>';
} }
// 加载最近任务并渲染任务表格,失败任务提供重试按钮。
async function loadRuns() { async function loadRuns() {
const tbody = document.getElementById("runList"); const tbody = document.getElementById("runList");
if (!tbody) return; if (!tbody) return;
@@ -259,6 +281,7 @@ async function loadRuns() {
.map((run) => { .map((run) => {
const percent = Math.round((run.progress || 0) * 100); const percent = Math.round((run.progress || 0) * 100);
const error = run.error || ""; const error = run.error || "";
// RUNNING/QUEUED 附加耗时或排队提示,其余状态只显示徽章。
const statusHtml = const statusHtml =
run.status === "RUNNING" run.status === "RUNNING"
? `${badge(run.status)} <span class="muted">已运行 ${formatElapsed(run.updated_at)}</span>` ? `${badge(run.status)} <span class="muted">已运行 ${formatElapsed(run.updated_at)}</span>`
@@ -285,6 +308,7 @@ async function loadRuns() {
: '<tr><td colspan="8">暂无任务</td></tr>'; : '<tr><td colspan="8">暂无任务</td></tr>';
} }
// 请求后端重试失败任务,成功后刷新列表。
async function retryRun(runId) { async function retryRun(runId) {
try { try {
const result = await api(`/api/runs/${runId}/retry`, { method: "POST" }); const result = await api(`/api/runs/${runId}/retry`, { method: "POST" });
@@ -295,6 +319,7 @@ async function retryRun(runId) {
} }
} }
// 创建或更新工作流:解析 DAG JSON 后提交,随后刷新列表。
async function createWorkflow() { async function createWorkflow() {
const workflowId = document.getElementById("workflowId").value.trim(); const workflowId = document.getElementById("workflowId").value.trim();
const name = document.getElementById("workflowName").value.trim(); const name = document.getElementById("workflowName").value.trim();
@@ -323,6 +348,7 @@ async function createWorkflow() {
} }
} }
// 发布指定工作流,使其出现在用户应用中心。
async function publishWorkflow(workflowId) { async function publishWorkflow(workflowId) {
try { try {
await api(`/api/admin/workflows/${workflowId}/publish`, { method: "POST" }); await api(`/api/admin/workflows/${workflowId}/publish`, { method: "POST" });
@@ -332,6 +358,7 @@ async function publishWorkflow(workflowId) {
} }
} }
// 删除指定工作流。
async function deleteWorkflow(workflowId) { async function deleteWorkflow(workflowId) {
try { try {
await api(`/api/admin/workflows/${workflowId}`, { method: "DELETE" }); await api(`/api/admin/workflows/${workflowId}`, { method: "DELETE" });
@@ -341,6 +368,7 @@ async function deleteWorkflow(workflowId) {
} }
} }
// 上传视频并轮询任务进度,完成后渲染下载链接。
async function uploadVideo() { async function uploadVideo() {
const fileInput = document.getElementById("videoFile"); const fileInput = document.getElementById("videoFile");
const progress = document.getElementById("runProgress"); const progress = document.getElementById("runProgress");
@@ -365,6 +393,7 @@ async function uploadVideo() {
} }
} }
// 每秒轮询一次任务状态,最多 3600 次(约 1 小时),超时后提示查看接口。
async function pollRun(runId, progress, downloads) { async function pollRun(runId, progress, downloads) {
for (let i = 0; i < 3600; i += 1) { for (let i = 0; i < 3600; i += 1) {
const run = await api(`/api/runs/${runId}`); const run = await api(`/api/runs/${runId}`);
@@ -383,6 +412,7 @@ async function pollRun(runId, progress, downloads) {
progress.textContent = "轮询超时,请到 /api/runs 查看任务状态"; progress.textContent = "轮询超时,请到 /api/runs 查看任务状态";
} }
// 只展示用户关心的最终产物(中文 SRT 与 ASS)下载入口。
function renderArtifacts(runId, artifacts, container) { function renderArtifacts(runId, artifacts, container) {
container.innerHTML = artifacts container.innerHTML = artifacts
.filter((item) => ["cn_srt", "ass"].includes(item.name)) .filter((item) => ["cn_srt", "ass"].includes(item.name))
@@ -395,6 +425,7 @@ function renderArtifacts(runId, artifacts, container) {
.join(""); .join("");
} }
// 全局点击委托:按按钮上的 data-* 属性分发到对应操作。
document.addEventListener("click", (event) => { document.addEventListener("click", (event) => {
const deleteNodeButton = event.target.closest("[data-delete-node]"); const deleteNodeButton = event.target.closest("[data-delete-node]");
if (deleteNodeButton) { if (deleteNodeButton) {
@@ -422,6 +453,7 @@ document.addEventListener("click", (event) => {
} }
}); });
// 页面初始化:预填 manifest/DAG、绑定按钮事件并加载对应页面数据。
document.addEventListener("DOMContentLoaded", async () => { document.addEventListener("DOMContentLoaded", async () => {
const manifestBox = document.getElementById("manifestJson"); const manifestBox = document.getElementById("manifestJson");
if (manifestBox) { if (manifestBox) {
@@ -471,6 +503,7 @@ document.addEventListener("DOMContentLoaded", async () => {
if (document.getElementById("workflowList")) { if (document.getElementById("workflowList")) {
await loadWorkflows(); await loadWorkflows();
} }
// 任务管理页每 5 秒自动刷新一次状态。
if (document.getElementById("runList")) { if (document.getElementById("runList")) {
await loadRuns(); await loadRuns();
setInterval(loadRuns, 5000); setInterval(loadRuns, 5000);
+39
View File
@@ -1,3 +1,6 @@
/* WOV 静态前端全局样式:定义色彩变量、布局与通用组件样式。 */
/* 设计令牌:集中管理配色,后续换肤只需修改变量。 */
:root { :root {
--bg: #f5f7fa; --bg: #f5f7fa;
--surface: #ffffff; --surface: #ffffff;
@@ -10,10 +13,12 @@
--radius: 8px; --radius: 8px;
} }
/* 全局盒模型与基础排版重置。 */
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
/* 页面主体:浅灰背景与默认文字颜色。 */
body { body {
margin: 0; margin: 0;
font-family: "Segoe UI", "Microsoft YaHei", sans-serif; font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
@@ -21,6 +26,7 @@ body {
background: var(--bg); background: var(--bg);
} }
/* 顶部导航栏:品牌标题与页面导航。 */
.topbar { .topbar {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -30,6 +36,7 @@ body {
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
/* 品牌标题样式。 */
.brand { .brand {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
@@ -37,11 +44,13 @@ body {
text-decoration: none; text-decoration: none;
} }
/* 导航链接横向排列。 */
nav { nav {
display: flex; display: flex;
gap: 18px; gap: 18px;
} }
/* 导航链接默认使用弱化色,悬停时高亮。 */
nav a { nav a {
color: var(--muted); color: var(--muted);
text-decoration: none; text-decoration: none;
@@ -52,12 +61,14 @@ nav a:hover {
color: var(--primary); color: var(--primary);
} }
/* 内容容器:限制最大宽度并居中。 */
.container { .container {
max-width: 1080px; max-width: 1080px;
margin: 0 auto; margin: 0 auto;
padding: 28px 20px 60px; padding: 28px 20px 60px;
} }
/* 一级标题与二级标题的字号控制。 */
h1 { h1 {
margin: 0 0 8px; margin: 0 0 8px;
font-size: 28px; font-size: 28px;
@@ -68,10 +79,12 @@ h2 {
font-size: 18px; font-size: 18px;
} }
/* 弱化文字:用于说明、时间等次要信息。 */
.muted { .muted {
color: var(--muted); color: var(--muted);
} }
/* 应用中心卡片网格:自适应列数的最小宽度布局。 */
.app-grid { .app-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
@@ -79,6 +92,7 @@ h2 {
margin-top: 24px; margin-top: 24px;
} }
/* 卡片与面板共用白底、边框和圆角外观。 */
.card, .card,
.panel { .panel {
background: var(--surface); background: var(--surface);
@@ -87,14 +101,17 @@ h2 {
padding: 20px; padding: 20px;
} }
/* 面板之间保持纵向间距。 */
.panel { .panel {
margin-top: 22px; margin-top: 22px;
} }
/* 禁用态样式:降低透明度表达不可用。 */
.disabled { .disabled {
opacity: 0.6; opacity: 0.6;
} }
/* 表单标签:块级显示并加粗。 */
label { label {
display: block; display: block;
margin: 10px 0 6px; margin: 10px 0 6px;
@@ -103,6 +120,7 @@ label {
color: var(--muted); color: var(--muted);
} }
/* 输入控件统一样式,宽度撑满容器。 */
input, input,
select, select,
textarea { textarea {
@@ -114,11 +132,13 @@ textarea {
background: #fff; background: #fff;
} }
/* 文本域使用等宽字体并允许纵向拉伸。 */
textarea { textarea {
font-family: Consolas, monospace; font-family: Consolas, monospace;
resize: vertical; resize: vertical;
} }
/* 按钮基础样式。 */
button { button {
margin-top: 12px; margin-top: 12px;
padding: 9px 16px; padding: 9px 16px;
@@ -131,21 +151,25 @@ button {
cursor: pointer; cursor: pointer;
} }
/* 按钮悬停强调边框与文字颜色。 */
button:hover { button:hover {
border-color: var(--primary); border-color: var(--primary);
color: var(--primary); color: var(--primary);
} }
/* 主按钮:填充品牌色。 */
button.primary { button.primary {
background: var(--primary); background: var(--primary);
border-color: var(--primary); border-color: var(--primary);
color: #fff; color: #fff;
} }
/* 幽灵按钮:透明背景,用于次要操作。 */
button.ghost { button.ghost {
background: transparent; background: transparent;
} }
/* 操作区与表单网格:弹性换行排列。 */
.actions, .actions,
.form-grid { .form-grid {
display: flex; display: flex;
@@ -153,25 +177,30 @@ button.ghost {
flex-wrap: wrap; flex-wrap: wrap;
} }
/* 表单网格:默认两列,宽字段占整行。 */
.form-grid { .form-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
/* 宽字段跨满整行。 */
.form-grid .wide { .form-grid .wide {
grid-column: 1 / -1; grid-column: 1 / -1;
} }
/* 表格容器:窄屏时允许横向滚动。 */
.table-wrap { .table-wrap {
overflow-x: auto; overflow-x: auto;
} }
/* 表格基础样式。 */
table { table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
font-size: 14px; font-size: 14px;
} }
/* 表头与单元格的间距、对齐与分隔线。 */
th, th,
td { td {
padding: 10px; padding: 10px;
@@ -180,12 +209,14 @@ td {
white-space: nowrap; white-space: nowrap;
} }
/* 表头使用弱化色与小字号。 */
th { th {
color: var(--muted); color: var(--muted);
font-size: 12px; font-size: 12px;
text-transform: uppercase; text-transform: uppercase;
} }
/* 结果块:深色等宽字体,适合展示 JSON 或日志。 */
.result { .result {
margin: 16px 0 0; margin: 16px 0 0;
padding: 14px; padding: 14px;
@@ -199,6 +230,7 @@ th {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
/* 下载区:横向排列的下载链接。 */
.downloads { .downloads {
display: flex; display: flex;
gap: 12px; gap: 12px;
@@ -206,6 +238,7 @@ th {
margin-top: 14px; margin-top: 14px;
} }
/* 下载链接:描边按钮式链接。 */
.download-link { .download-link {
display: inline-block; display: inline-block;
padding: 8px 14px; padding: 8px 14px;
@@ -216,10 +249,12 @@ th {
text-decoration: none; text-decoration: none;
} }
/* 下载链接悬停时轻微填充背景。 */
.download-link:hover { .download-link:hover {
background: #e8f1f9; background: #e8f1f9;
} }
/* 状态徽章基础样式。 */
.badge { .badge {
display: inline-block; display: inline-block;
padding: 3px 8px; padding: 3px 8px;
@@ -228,21 +263,25 @@ th {
font-weight: 700; font-weight: 700;
} }
/* 成功徽章:绿色背景。 */
.badge.ok { .badge.ok {
background: #e2f4ea; background: #e2f4ea;
color: var(--ok); color: var(--ok);
} }
/* 警告徽章:黄色背景。 */
.badge.warn { .badge.warn {
background: #fff2d9; background: #fff2d9;
color: #8a5a00; color: #8a5a00;
} }
/* 错误徽章:红色背景。 */
.badge.error { .badge.error {
background: #fde8e6; background: #fde8e6;
color: var(--danger); color: var(--danger);
} }
/* 危险操作按钮文字颜色。 */
.danger { .danger {
color: var(--danger); color: var(--danger);
} }
+4
View File
@@ -1,4 +1,5 @@
<!doctype html> <!doctype html>
<!-- WOV 应用中心:用户端首页,提供 Echo 演示与视频字幕生成两个入口。 -->
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -18,10 +19,12 @@
</header> </header>
<main class="container"> <main class="container">
<!-- 用户端只展示应用卡片,不暴露底层工作流与节点细节。 -->
<h1>WOV 应用中心</h1> <h1>WOV 应用中心</h1>
<p class="muted">选择一个已发布的应用,上传或输入内容后由后台自动执行。</p> <p class="muted">选择一个已发布的应用,上传或输入内容后由后台自动执行。</p>
<div class="app-grid"> <div class="app-grid">
<!-- Echo 演示:验证前端到后端再到节点进程的完整调用链路。 -->
<article class="card"> <article class="card">
<h2>Echo 演示</h2> <h2>Echo 演示</h2>
<p>调用已注册的 echo 节点,验证前端到后端再到节点进程的完整链路。</p> <p>调用已注册的 echo 节点,验证前端到后端再到节点进程的完整链路。</p>
@@ -31,6 +34,7 @@
<pre id="echoResult" class="result">等待运行...</pre> <pre id="echoResult" class="result">等待运行...</pre>
</article> </article>
<!-- 视频字幕生成:上传视频后自动执行提音、转写、翻译和 ASS 生成。 -->
<article class="card"> <article class="card">
<h2>视频字幕生成</h2> <h2>视频字幕生成</h2>
<p>上传视频后自动执行提音、转写、翻译和 VR 双眼 ASS 生成。</p> <p>上传视频后自动执行提音、转写、翻译和 VR 双眼 ASS 生成。</p>
+2
View File
@@ -1,4 +1,5 @@
<!doctype html> <!doctype html>
<!-- WOV 任务管理页:查看所有任务状态,失败任务可重新排队。 -->
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -21,6 +22,7 @@
<h1>任务管理</h1> <h1>任务管理</h1>
<p class="muted">查看所有任务的执行状态;失败的任务可以重新排队执行。</p> <p class="muted">查看所有任务的执行状态;失败的任务可以重新排队执行。</p>
<!-- 任务列表由 assets/app.js 的 loadRuns 定期刷新填充。 -->
<section class="panel"> <section class="panel">
<div class="table-wrap"> <div class="table-wrap">
<table> <table>
+3
View File
@@ -1,4 +1,5 @@
<!doctype html> <!doctype html>
<!-- WOV 工作流编排页:以 DAG JSON 创建/更新工作流并发布。 -->
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@@ -20,6 +21,7 @@
<main class="container"> <main class="container">
<h1>工作流编排</h1> <h1>工作流编排</h1>
<!-- 工作流定义:输入概要信息与 DAG JSON,创建或追加新版本。 -->
<section class="panel"> <section class="panel">
<h2>工作流定义</h2> <h2>工作流定义</h2>
<label for="workflowId">工作流 ID</label> <label for="workflowId">工作流 ID</label>
@@ -36,6 +38,7 @@
</div> </div>
</section> </section>
<!-- 已发布工作流:展示发布状态,支持发布与删除操作。 -->
<section class="panel"> <section class="panel">
<h2>已发布工作流</h2> <h2>已发布工作流</h2>
<div class="table-wrap"> <div class="table-wrap">