update:原生确认框替换为自定义弹窗

This commit is contained in:
xiaozhu
2026-09-01 17:23:31 +08:00
parent 149e85cf10
commit 026cd085c6
15 changed files with 452 additions and 83 deletions
@@ -1,7 +1,4 @@
// Dialog slice: create-schedule dialog state + cron preview.
// Pure setters + small mutations that read/write only dialog-owned fields.
import type { CronPreview } from "../../../services/api";
import type { CronPreview, Schedule } from "../../../services/api";
import type { StateCreator } from "zustand";
import { handleError, notify, requireApi } from "./helpers";
@@ -12,6 +9,8 @@ import type { SchedulesStore } from "./useSchedulesStore";
export type DialogSliceState = {
createDialogOpen: boolean;
newScheduleName: string;
renameTarget: Schedule | null;
renameName: string;
cronResult: CronPreview | null;
};
@@ -21,10 +20,13 @@ export type DialogSliceActions = {
// pure setters
setCreateDialogOpen: (open: boolean) => void;
setNewScheduleName: (name: string) => void;
setRenameName: (name: string) => void;
setCronResult: (result: CronPreview | null) => void;
// mutations
openCreateDialog: () => void;
openRenameDialog: (target: Schedule) => void;
closeRenameDialog: () => void;
runCronPreview: () => Promise<void>;
};
@@ -35,12 +37,15 @@ export const createDialogSlice: StateCreator<SchedulesStore, [], [], DialogSlice
// ---- initial state ----
createDialogOpen: false,
newScheduleName: "",
renameTarget: null,
renameName: "",
cronResult: null,
// ---- pure setters ----
setCreateDialogOpen: (open) => set({ createDialogOpen: open }),
setNewScheduleName: (name) => set({ newScheduleName: name }),
setRenameName: (name) => set({ renameName: name }),
setCronResult: (result) => set({ cronResult: result }),
// ---- mutations ----
@@ -54,6 +59,20 @@ export const createDialogSlice: StateCreator<SchedulesStore, [], [], DialogSlice
}));
},
openRenameDialog: (target) => {
if (get().busy) return;
set({
contextMenu: null,
renameTarget: target,
renameName: target.schedule_name,
});
},
closeRenameDialog: () => {
if (get().busy === "rename-schedule") return;
set({ renameTarget: null, renameName: "" });
},
runCronPreview: async () => {
const api = requireApi();
const { scheduleForm } = get();
@@ -75,4 +94,4 @@ export const createDialogSlice: StateCreator<SchedulesStore, [], [], DialogSlice
set({ busy: null });
}
},
});
});
@@ -60,7 +60,7 @@ export type ListSliceActions = {
// mutations
addSchedule: (name: string) => Promise<void>;
removeSchedule: (target?: Schedule) => Promise<void>;
renameSchedule: (target: Schedule) => Promise<void>;
renameSchedule: (target: Schedule, scheduleName: string) => Promise<void>;
removeArtifact: (artifact: ScheduleArtifact) => Promise<void>;
saveSchedule: () => Promise<void>;
runNow: () => Promise<void>;
@@ -70,7 +70,10 @@ export type ListSliceActions = {
positionY: number,
) => Promise<void>;
saveNode: (selectedNode: ScheduleNode | null) => Promise<void>;
removeNode: (target?: ScheduleNode) => Promise<void>;
removeNode: (
target?: ScheduleNode,
options?: { deleteExecutionHistory?: boolean },
) => Promise<"ok" | "needs_history_confirm" | "noop">;
removeEdge: (target?: ScheduleEdge) => Promise<void>;
checkDag: () => Promise<void>;
connectTo: (targetNodeId: string) => Promise<void>;
@@ -234,7 +237,6 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
const target_ = target ?? get().schedule;
if (!target_ || get().busy) return;
set({ contextMenu: null });
if (!window.confirm(`确定删除调度"${target_.schedule_name}"吗?`)) return;
set({ busy: "delete-schedule" });
try {
await api.deleteSchedule(target_.schedule_id, target_.workflow_version);
@@ -263,19 +265,15 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
}
},
renameSchedule: async (target) => {
renameSchedule: async (target, scheduleName) => {
const api = requireApi();
if (get().busy) return;
set({ contextMenu: null });
const scheduleName = window
.prompt("请输入新的调度方案名称", target.schedule_name)
?.trim();
if (!scheduleName || scheduleName === target.schedule_name) return;
const trimmed = scheduleName.trim();
if (get().busy || !trimmed || trimmed === target.schedule_name) return;
set({ busy: "rename-schedule" });
try {
const updated = await api.updateSchedule(target.schedule_id, {
workflow_version: target.workflow_version,
schedule_name: scheduleName,
schedule_name: trimmed,
});
set((s: any) => ({
schedules: s.schedules.map((item: Schedule) =>
@@ -287,6 +285,8 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
s.schedule?.schedule_id === updated.schedule_id
? updated
: s.schedule,
renameTarget: null,
renameName: "",
}));
notify({ tone: "success", message: "调度方案已改名" });
} catch (error) {
@@ -301,12 +301,6 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
const api = requireApi();
if (get().busy) return;
set({ contextMenu: null });
if (
!window.confirm(
`确定将"${artifact.script_name} ${artifact.version_label}"移出调度列表吗?\n`
+ "稳定版本本身和历史运行记录不会被删除。",
)
) return;
set({ busy: "delete-artifact" });
try {
await api.hideScheduleArtifact(artifact.versions_id);
@@ -531,16 +525,15 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
}
},
removeNode: async (target) => {
removeNode: async (target, options) => {
const api = requireApi();
const state_ = get();
const { schedule, selectedNodeId } = state_;
const node = target
?? schedule?.nodes.find((item) => item.node_id === selectedNodeId)
?? null;
if (!schedule || !node || state_.busy) return;
if (!schedule || !node || state_.busy) return "noop";
set({ contextMenu: null });
if (!window.confirm(`确定删除节点"${node.node_name}"吗?`)) return;
set({ busy: "delete-node" });
try {
let updated: Schedule;
@@ -550,6 +543,9 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
schedule.schedule_id,
node.node_id,
schedule.workflow_version,
options?.deleteExecutionHistory
? { delete_execution_history: true }
: undefined,
);
} catch (error) {
const requiresHistoryConfirmation =
@@ -557,7 +553,10 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
&& error.status === 409
&& error.code === "node_execution_history_exists";
if (!requiresHistoryConfirmation) throw error;
if (!window.confirm("该节点有运行日志,是否一并删除?")) return;
// 交给 UI 弹二次确认;已确认则带 delete_execution_history 重试。
if (!options?.deleteExecutionHistory) {
return "needs_history_confirm";
}
updated = await api.deleteScheduleNode(
schedule.schedule_id,
node.node_id,
@@ -568,9 +567,11 @@ export const createListSlice: StateCreator<SchedulesStore, [], [], ListSliceStat
applyServerUpdatedSchedule(set, updated);
if (selectedNodeId === node.node_id) set({ selectedNodeId: null });
notify({ tone: "success", message: "节点及其运行日志已删除" });
return "ok";
} catch (error) {
const { handleError } = await import("./helpers");
await handleError(get, error, "删除节点失败");
return "noop";
} finally {
set({ busy: null });
}
@@ -64,6 +64,8 @@ const INITIAL: Omit<SchedulesState, "schedule" | "busy"> = {
// Dialog
createDialogOpen: false,
newScheduleName: "",
renameTarget: null,
renameName: "",
cronResult: null,
// Runs
runs: [],