feat(定时任务): 内置「模板数据转换器」按 HTML 模板填充 JSON 生成大屏
- 模板填充引擎 template_fill.py:模板注册表 + schema 骨架 + 结构校验 + 行级安全填充; 模板自带 DATA 即 schema 来源。no.1 可转换填充(含 MANIFEST 更新时间刷新), no.2 暂为「只展示」(convertible=False) 原样输出已有数据 - 内置 agent template-json-builder(seed + SOUL),随 lifespan 注入,自动出现在 定时任务「执行智能体」下拉 - 调度器新增模板填充执行分支 _run_template_fill_attempt:源 JSON→大模型转换→ 结构校验→失败回喂重试;空源/只展示模板直接用模板默认数据,绝不失败 - API:GET /api/scheduled-tasks/html-templates - 前端:选中该 agent 后条件渲染「HTML模板」下拉 + .json 上传,只展示型模板隐藏 JSON 输入 - 文档同步(前后端 CLAUDE.md) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1a2ef0a537
commit
cd33aaeded
17 files changed
+1511
-57
No files matched your search
@@ -10,6 +10,7 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ExternalLink,
|
||||
FileSearch,
|
||||
@@ -95,16 +96,27 @@ export function TaskJumpBar({
|
||||
});
|
||||
};
|
||||
|
||||
const nextButtons = jumpButtons.filter(isNextTaskButton);
|
||||
const menuButtons = jumpButtons.filter((btn) => !isNextTaskButton(btn));
|
||||
// 底部栏只放「上一步 / 下一步」:prev 组渲染在 next 组之前 → 视觉上「上一步」在「下一步」左边
|
||||
// (容器 justify-end 右对齐,数组先后即左右先后)。其余按钮进右侧悬浮菜单。
|
||||
const prevButtons = jumpButtons.filter(isPrevTaskButton);
|
||||
const nextButtons = jumpButtons.filter((btn) => isNextTaskButton(btn) && !isPrevTaskButton(btn));
|
||||
const barButtons = [...prevButtons, ...nextButtons];
|
||||
const menuButtons = jumpButtons.filter(
|
||||
(btn) => !isNextTaskButton(btn) && !isPrevTaskButton(btn),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{menuButtons.length > 0 && <TaskSideJumpMenu buttons={menuButtons} onRun={handleRun} />}
|
||||
{nextButtons.length > 0 && (
|
||||
{barButtons.length > 0 && (
|
||||
<div className="border-border/70 bg-background/90 flex shrink-0 flex-wrap items-center justify-end gap-3 border-t px-5 py-3 backdrop-blur-xl dark:border-[#1b477e]/70 dark:bg-[#061b35]/85">
|
||||
{nextButtons.map((btn) => (
|
||||
<TaskJumpButton key={btn.id} button={btn} onRun={handleRun} />
|
||||
{barButtons.map((btn) => (
|
||||
<TaskJumpButton
|
||||
key={btn.id}
|
||||
button={btn}
|
||||
onRun={handleRun}
|
||||
variant={isPrevTaskButton(btn) ? "secondary" : "primary"}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
@@ -122,21 +134,35 @@ function isNextTaskButton(btn: TaskButton) {
|
||||
return btn.id.toLowerCase().includes("next") || btn.label.trim() === "下一步";
|
||||
}
|
||||
|
||||
/** 「上一步」按钮:id 含 prev 或文案为「上一步」——底部栏里渲染到「下一步」左边。 */
|
||||
function isPrevTaskButton(btn: TaskButton) {
|
||||
return btn.id.toLowerCase().includes("prev") || btn.label.trim() === "上一步";
|
||||
}
|
||||
|
||||
function TaskJumpButton({
|
||||
button,
|
||||
onRun,
|
||||
variant = "primary",
|
||||
}: {
|
||||
button: TaskButton;
|
||||
onRun: (button: TaskButton) => void;
|
||||
/** primary=下一步(实心主色)/ secondary=上一步(描边次要,置于下一步左侧)。 */
|
||||
variant?: "primary" | "secondary";
|
||||
}) {
|
||||
const styles =
|
||||
variant === "secondary"
|
||||
? "border-[var(--color-main)]/40 bg-transparent text-[var(--color-main)] hover:bg-[var(--color-main)]/10 dark:text-[#7eb6ff] dark:border-[#7eb6ff]/40 dark:hover:bg-[#7eb6ff]/10"
|
||||
: "border-transparent bg-[var(--color-main)] text-white shadow-sm hover:bg-[var(--brand-950)]";
|
||||
return (
|
||||
<Button
|
||||
className="h-11 gap-2 border-transparent bg-[var(--color-main)] px-6 text-base text-white shadow-sm hover:bg-[var(--brand-950)]"
|
||||
variant={variant === "secondary" ? "outline" : "default"}
|
||||
className={cn("h-11 gap-2 px-6 text-base", styles)}
|
||||
onClick={() => onRun(button)}
|
||||
title={desensitize(button.label)}
|
||||
>
|
||||
{variant === "secondary" && <ArrowLeft className="h-5 w-5 shrink-0" />}
|
||||
<span className="truncate">{desensitize(button.label)}</span>
|
||||
{renderTaskJumpIcon(button, "h-5 w-5 shrink-0")}
|
||||
{variant === "primary" && renderTaskJumpIcon(button, "h-5 w-5 shrink-0")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user