Merge branch 'develop' of http://8.153.151.51:8888/team_group/model-develop into develop
This commit is contained in:
@@ -22,10 +22,12 @@ import {
|
||||
} from "../../services/api";
|
||||
|
||||
import { useApi } from "../../context/AuthContext";
|
||||
import Icon from "../../components/Icon";
|
||||
import Icon from "../../components/common/Icon";
|
||||
import { ScheduleList } from "./ScheduleList";
|
||||
import { ArtifactList } from "./ArtifactList";
|
||||
import { ScheduleCanvasHeader } from "./ScheduleCanvasHeader";
|
||||
import "../../styles/schedule.css";
|
||||
|
||||
|
||||
type Notice = {
|
||||
tone: "success" | "error" | "info";
|
||||
message: string;
|
||||
@@ -222,8 +224,6 @@ function edgePath(
|
||||
const curve = Math.max(70, Math.abs(x2 - x1) * 0.45);
|
||||
return `M ${x1} ${y1} C ${x1 + curve} ${y1}, ${x2 - curve} ${y2}, ${x2} ${y2}`;
|
||||
}
|
||||
|
||||
|
||||
export default function SchedulePage({
|
||||
onNotify,
|
||||
onConnectionChange,
|
||||
@@ -244,10 +244,8 @@ export default function SchedulePage({
|
||||
const [busy, setBusy] = useState<string | null>(null);
|
||||
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
||||
const [newScheduleName, setNewScheduleName] = useState("");
|
||||
const [contextMenu, setContextMenu] =
|
||||
useState<ScheduleContextMenu | null>(null);
|
||||
const [scheduleForm, setScheduleForm] =
|
||||
useState<ScheduleForm>(EMPTY_SCHEDULE_FORM);
|
||||
const [contextMenu, setContextMenu] = useState<ScheduleContextMenu | null>(null);
|
||||
const [scheduleForm, setScheduleForm] = useState<ScheduleForm>(EMPTY_SCHEDULE_FORM);
|
||||
const [nodeForm, setNodeForm] = useState<NodeForm>(EMPTY_NODE_FORM);
|
||||
const [cronResult, setCronResult] = useState<CronPreview | null>(null);
|
||||
const [runs, setRuns] = useState<ScheduleRunSummary[]>([]);
|
||||
@@ -431,18 +429,14 @@ export default function SchedulePage({
|
||||
};
|
||||
}, [schedule?.schedule_id]);
|
||||
|
||||
const hasActiveRuns = runs.some(
|
||||
(item) => item.run_status === "queued" || item.run_status === "running",
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const scheduleId = schedule?.schedule_id;
|
||||
if (!scheduleId || !hasActiveRuns) return;
|
||||
if (!scheduleId || !runs.some((item) => item.run_status === "queued" || item.run_status === "running")) return;
|
||||
const timer = window.setInterval(() => {
|
||||
void refreshRuns(scheduleId);
|
||||
}, 1500);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [schedule?.schedule_id, hasActiveRuns]);
|
||||
}, [schedule?.schedule_id, runs]);
|
||||
|
||||
const handleError = async (
|
||||
error: unknown,
|
||||
@@ -561,7 +555,7 @@ export default function SchedulePage({
|
||||
const selectedSchedule = target ?? schedule;
|
||||
if (!selectedSchedule || busy) return;
|
||||
setContextMenu(null);
|
||||
if (!window.confirm(`确定删除调度“${selectedSchedule.schedule_name}”吗?`)) return;
|
||||
if (!window.confirm(`确定删除调度"${selectedSchedule.schedule_name}"吗?`)) return;
|
||||
setBusy("delete-schedule");
|
||||
try {
|
||||
await api.deleteSchedule(
|
||||
@@ -621,7 +615,7 @@ export default function SchedulePage({
|
||||
setContextMenu(null);
|
||||
if (
|
||||
!window.confirm(
|
||||
`确定将“${artifact.script_name} ${artifact.version_label}”移出调度列表吗?\n`
|
||||
`确定将"${artifact.script_name} ${artifact.version_label}"移出调度列表吗?\n`
|
||||
+ "稳定版本本身和历史运行记录不会被删除。",
|
||||
)
|
||||
) return;
|
||||
@@ -721,7 +715,7 @@ export default function SchedulePage({
|
||||
if (positionDraftCount > 0) {
|
||||
onNotify({
|
||||
tone: "info",
|
||||
message: "还有节点位置未保存,请先点击“保存配置”再运行",
|
||||
message: "还有节点位置未保存,请先点击'保存配置'再运行",
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -791,15 +785,6 @@ export default function SchedulePage({
|
||||
}
|
||||
};
|
||||
|
||||
const onArtifactDragStart = (
|
||||
event: DragEvent<HTMLDivElement>,
|
||||
artifact: ScheduleArtifact,
|
||||
): void => {
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.setData(ARTIFACT_MIME, artifact.versions_id);
|
||||
event.dataTransfer.setData("text/plain", artifact.versions_id);
|
||||
};
|
||||
|
||||
const onCanvasDrop = (event: DragEvent<HTMLDivElement>): void => {
|
||||
event.preventDefault();
|
||||
const versionsId = event.dataTransfer.getData(ARTIFACT_MIME)
|
||||
@@ -962,7 +947,7 @@ export default function SchedulePage({
|
||||
const node = target ?? selectedNode;
|
||||
if (!schedule || !node || busy) return;
|
||||
setContextMenu(null);
|
||||
if (!window.confirm(`确定删除节点“${node.node_name}”吗?`)) return;
|
||||
if (!window.confirm(`确定删除节点"${node.node_name}"吗?`)) return;
|
||||
const updated = await withMutation(
|
||||
"delete-node",
|
||||
() => api.deleteScheduleNode(
|
||||
@@ -1094,124 +1079,30 @@ export default function SchedulePage({
|
||||
|
||||
<div className="schedule-workbench">
|
||||
<aside className="schedule-left">
|
||||
<section className="schedule-panel schedule-list-panel">
|
||||
<div
|
||||
className="schedule-panel__title"
|
||||
onContextMenu={(event) => openContextMenu(event, { kind: "schedule-list" })}
|
||||
>
|
||||
<div><strong>调度方案</strong><span>{schedules.length}</span></div>
|
||||
</div>
|
||||
<label className="schedule-search">
|
||||
<Icon name="search" size={15} />
|
||||
<input
|
||||
value={scheduleKeyword}
|
||||
placeholder="搜索调度名称"
|
||||
onChange={(event) => setScheduleKeyword(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<div
|
||||
className="schedule-list"
|
||||
onContextMenu={(event) => openContextMenu(event, { kind: "schedule-list" })}
|
||||
>
|
||||
{loading ? (
|
||||
<p className="schedule-empty">正在加载调度方案…</p>
|
||||
) : filteredSchedules.length ? (
|
||||
filteredSchedules.map((item) => (
|
||||
<button
|
||||
className={`schedule-card${
|
||||
schedule?.schedule_id === item.schedule_id
|
||||
? " schedule-card--active"
|
||||
: ""
|
||||
}`}
|
||||
key={item.schedule_id}
|
||||
type="button"
|
||||
onClick={() => void chooseSchedule(item.schedule_id)}
|
||||
onContextMenu={(event) => openContextMenu(event, {
|
||||
kind: "schedule",
|
||||
schedule: item,
|
||||
})}
|
||||
>
|
||||
<span className={`schedule-status${item.enabled ? " is-enabled" : ""}`} />
|
||||
<span>
|
||||
<strong>{item.schedule_name}</strong>
|
||||
<small>
|
||||
{item.node_count} 个节点 · {item.enabled ? "已启用" : "未启用"}
|
||||
</small>
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<p className="schedule-empty">暂无调度方案</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="schedule-panel schedule-artifacts-panel">
|
||||
<div className="schedule-panel__title">
|
||||
<div><strong>稳定版本脚本</strong><span>{artifacts.length}</span></div>
|
||||
</div>
|
||||
<label className="schedule-search">
|
||||
<Icon name="search" size={15} />
|
||||
<input
|
||||
value={artifactKeyword}
|
||||
placeholder="搜索稳定版本"
|
||||
onChange={(event) => setArtifactKeyword(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<p className="artifact-tip">拖动卡片到画布,节点将保存 versions_id</p>
|
||||
<div className="artifact-list">
|
||||
{filteredArtifacts.length ? filteredArtifacts.map((artifact) => (
|
||||
<div
|
||||
className="artifact-card"
|
||||
draggable
|
||||
key={artifact.versions_id}
|
||||
onDragStart={(event) => onArtifactDragStart(event, artifact)}
|
||||
onContextMenu={(event) => openContextMenu(event, {
|
||||
kind: "artifact",
|
||||
artifact,
|
||||
})}
|
||||
onDoubleClick={() => void addArtifactAt(
|
||||
artifact,
|
||||
90 + (schedule?.nodes.length ?? 0) * 245,
|
||||
130,
|
||||
)}
|
||||
>
|
||||
<span className={`artifact-card__icon artifact-card__icon--${artifact.script_type}`}>
|
||||
<Icon
|
||||
name={artifact.script_type === "notebook" ? "notebook" : "python"}
|
||||
size={17}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<strong>{artifact.script_name}</strong>
|
||||
<small>
|
||||
{artifact.version_label} · {shortHash(artifact.content_hash)}
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
)) : (
|
||||
<p className="schedule-empty">
|
||||
暂无稳定版本,请先在“构建脚本”中发布
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
<ScheduleList
|
||||
schedules={filteredSchedules}
|
||||
selectedScheduleId={schedule?.schedule_id ?? null}
|
||||
keyword={scheduleKeyword}
|
||||
loading={loading}
|
||||
onSelect={chooseSchedule}
|
||||
onContextMenu={(event, item) => openContextMenu(event as ReactMouseEvent<HTMLElement | SVGElement>, item ? { kind: "schedule", schedule: item } : { kind: "schedule-list" })}
|
||||
onKeywordChange={setScheduleKeyword}
|
||||
/>
|
||||
<ArtifactList
|
||||
artifacts={filteredArtifacts}
|
||||
keyword={artifactKeyword}
|
||||
onDrop={(artifact, x, y) => void addArtifactAt(artifact, x, y)}
|
||||
onDoubleClick={(artifact) => void addArtifactAt(artifact, 90 + (schedule?.nodes.length ?? 0) * 245, 130)}
|
||||
onContextMenu={(event, artifact) => openContextMenu(event as ReactMouseEvent<HTMLElement | SVGElement>, { kind: "artifact", artifact })}
|
||||
onKeywordChange={setArtifactKeyword}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<main className="schedule-center">
|
||||
<div className="schedule-canvas-title">
|
||||
<div>
|
||||
<strong>流程画布</strong>
|
||||
<span>
|
||||
拖入稳定版本;点击节点右侧圆点,再点击目标左侧圆点完成连线
|
||||
</span>
|
||||
</div>
|
||||
{linkSourceId && (
|
||||
<button type="button" onClick={() => setLinkSourceId(null)}>
|
||||
取消连线
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<ScheduleCanvasHeader
|
||||
linkSourceId={linkSourceId}
|
||||
onCancelLink={() => setLinkSourceId(null)}
|
||||
/>
|
||||
<div
|
||||
className={`schedule-canvas${linkSourceId ? " is-linking" : ""}`}
|
||||
ref={canvasRef}
|
||||
@@ -1366,7 +1257,7 @@ export default function SchedulePage({
|
||||
<div className="schedule-canvas-empty">
|
||||
<Icon name="schedule" size={34} />
|
||||
<strong>还没有选中调度方案</strong>
|
||||
<p>点击“新建”创建一个调度,然后拖入稳定版本脚本。</p>
|
||||
<p>点击"新建"创建一个调度,然后拖入稳定版本脚本。</p>
|
||||
<button type="button" onClick={openCreateScheduleDialog}>
|
||||
<Icon name="plus" size={15} />新建调度
|
||||
</button>
|
||||
@@ -1579,8 +1470,6 @@ export default function SchedulePage({
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function RunHistory({
|
||||
runs,
|
||||
nodes,
|
||||
|
||||
Reference in New Issue
Block a user