window.__ModuleLoader__.load({ id: "dsh-plugin-mcn-schedule", factory: (require) => { var module = { exports: {} }; var exports = module.exports; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); let react = require("react"); let react_dom = require("react-dom"); let react_jsx_runtime = require("react/jsx-runtime"); //#region plugin css const css = ".mcnSched_page{position:relative;background:var(--dsw-alias-bg-base);width:100%;min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex:1;grid-template-rows:minmax(0,1fr);display:grid;overflow:hidden}.mcnSched_body{display:flex;flex-direction:column;height:100%;min-height:0;padding:16px;box-sizing:border-box;gap:12px}.mcnSched_toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.mcnSched_title{font-size:16px;font-weight:600;margin:0}.mcnSched_spacer{flex:1}.mcnSched_input{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));border-radius:8px;padding:6px 10px;font:var(--dsw-font-s-14);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));color:var(--dsw-alias-label-primary);min-width:200px;box-sizing:border-box}.mcnSched_select{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));border-radius:8px;padding:6px 8px;font:var(--dsw-font-s-14);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));color:var(--dsw-alias-label-primary)}.mcnSched_textarea{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));border-radius:8px;padding:6px 10px;font:var(--dsw-font-s-14);background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));color:var(--dsw-alias-label-primary);resize:vertical;box-sizing:border-box;width:100%;min-height:64px}.mcnSched_btn{border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));border-radius:8px;padding:6px 14px;font:var(--dsw-font-s-14);cursor:pointer;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));color:var(--dsw-alias-label-primary);transition:background var(--ds-transition-duration-fast) var(--ds-ease-in-out);white-space:nowrap}.mcnSched_btn:hover{background:var(--dsw-alias-interactive-bg-hover)}.mcnSched_btnPrimary{background:var(--mcn-brand,#4176e6);color:#fff;border-color:transparent}.mcnSched_btnPrimary:hover{background:var(--mcn-brand-hover,#3359c9)}.mcnSched_btnSm{padding:2px 10px;font-size:12px;line-height:1.8}.mcnSched_tableWrap{flex:1;min-height:0;overflow:auto;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));border-radius:8px}.mcnSched_table{width:100%;border-collapse:collapse;font:var(--dsw-font-s-14);min-width:860px}.mcnSched_table th{position:sticky;top:0;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));text-align:left;padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));font-weight:600;white-space:nowrap;user-select:none}.mcnSched_table td{padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border));vertical-align:middle}.mcnSched_table tr:hover{background:var(--dsw-alias-interactive-bg-hover)}.mcnSched_cellTrack{white-space:nowrap}.mcnSched_cellContent{color:var(--dsw-alias-label-secondary);max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mcnSched_badge{display:inline-block;padding:1px 8px;border-radius:10px;font-size:12px;line-height:18px;white-space:nowrap}.mcnSched_badgeOn{background:rgba(46,190,140,.18);color:var(--dsw-alias-label-primary)}.mcnSched_badgeOff{background:rgba(236,19,19,.14);color:var(--dsw-alias-label-primary)}.mcnSched_badgeAt{background:rgba(65,118,230,.15);color:var(--dsw-alias-label-primary)}.mcnSched_badgeEvery{background:rgba(255,122,69,.15);color:var(--dsw-alias-label-primary)}.mcnSched_badgeAgent{background:rgba(139,92,246,.16);color:var(--dsw-alias-label-primary)}.mcnSched_loading{display:flex;align-items:center;justify-content:center;height:100%;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-s-14)}.mcnSched_empty{padding:40px 20px;text-align:center;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-s-14);line-height:1.8}.mcnSched_mask{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:flex-start;justify-content:center;padding-top:24vh;z-index:100}.mcnSched_modal{background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));border-radius:12px;padding:20px;min-width:380px;max-width:460px;box-shadow:0 8px 30px rgba(0,0,0,.2)}.mcnSched_modalTitle{font-size:16px;font-weight:600;margin-bottom:12px}.mcnSched_modalText{font:var(--dsw-font-s-14);color:var(--dsw-alias-label-secondary);margin-bottom:16px;line-height:1.6}.mcnSched_modalBtns{display:flex;justify-content:flex-end;gap:8px}.mcnSched_formGrid{display:grid;grid-template-columns:1fr 1fr;gap:10px 16px}.mcnSched_formItem{display:flex;flex-direction:column;gap:4px;font:var(--dsw-font-s-14);color:var(--dsw-alias-label-secondary)}.mcnSched_formItemWide{grid-column:1/-1}.mcnSched_toast{position:absolute;top:10px;left:50%;transform:translateX(-50%);z-index:120;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-elevated));border:1px solid var(--mcn-brand,#4176e6);border-radius:10px;padding:8px 16px;font:var(--dsw-font-s-14);box-shadow:0 8px 30px rgba(0,0,0,.25);max-width:70%;color:var(--dsw-alias-label-primary)}"; const key = "dsh-plugin-mcn-schedule/Schedule.module.css"; if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(key) + "]")) { const el = document.createElement("style"); el.dataset.plugin = "dsh-plugin-mcn-schedule"; el.dataset.pluginCss = key; el.textContent = css; document.head.appendChild(el); } const cssMap = { "page": "mcnSched_page", "body": "mcnSched_body", "toolbar": "mcnSched_toolbar", "title": "mcnSched_title", "spacer": "mcnSched_spacer", "input": "mcnSched_input", "select": "mcnSched_select", "textarea": "mcnSched_textarea", "btn": "mcnSched_btn", "btnPrimary": "mcnSched_btnPrimary", "btnSm": "mcnSched_btnSm", "tableWrap": "mcnSched_tableWrap", "table": "mcnSched_table", "cellTrack": "mcnSched_cellTrack", "cellContent": "mcnSched_cellContent", "badge": "mcnSched_badge", "badgeOn": "mcnSched_badgeOn", "badgeOff": "mcnSched_badgeOff", "badgeAt": "mcnSched_badgeAt", "badgeEvery": "mcnSched_badgeEvery", "badgeAgent": "mcnSched_badgeAgent", "loading": "mcnSched_loading", "empty": "mcnSched_empty", "mask": "mcnSched_mask", "modal": "mcnSched_modal", "modalTitle": "mcnSched_modalTitle", "modalText": "mcnSched_modalText", "modalBtns": "mcnSched_modalBtns", "formGrid": "mcnSched_formGrid", "formItem": "mcnSched_formItem", "formItemWide": "mcnSched_formItemWide", "toast": "mcnSched_toast" }; //#endregion //#region 图标 function IconClock({ size = 22 }) { return (0, react_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", style: { color: "var(--mcn-brand,#4176e6)" }, children: [(0, react_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "10" }), (0, react_jsx_runtime.jsx)("polyline", { points: "12 6 12 12 16 14" })] }); } //#endregion //#region 计划任务页面 function SchedulePage() { const [tasks, setTasks] = (0, react.useState)([]); const [events, setEvents] = (0, react.useState)([]); const [loading, setLoading] = (0, react.useState)(true); const [modalOpen, setModalOpen] = (0, react.useState)(false); const [editing, setEditing] = (0, react.useState)(null); const [toast, setToast] = (0, react.useState)(null); const lastSeenRef = (0, react.useRef)(Date.now()); const toastTimer = (0, react.useRef)(null); const fmtTime = (ts) => { if (ts == null) return "-"; const d = new Date(ts); const p = (n) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`; }; const showToast = (msg) => { setToast(msg); if (toastTimer.current) clearTimeout(toastTimer.current); toastTimer.current = setTimeout(() => setToast(null), 6000); }; const load = () => { fetch("/mcn/schedule/list") .then((r) => r.json()) .then((d) => { if (d.ok) { setTasks(d.tasks || []); } }) .catch(() => {}) .finally(() => setLoading(false)); }; const pollEvents = () => { const since = lastSeenRef.current; fetch("/mcn/schedule/events?since=" + since) .then((r) => r.json()) .then((d) => { if (!d.ok || !d.events || d.events.length === 0) return; const last = Math.max(...d.events.map((e) => new Date(e.at).getTime())); lastSeenRef.current = Math.max(lastSeenRef.current, last); setEvents((prev) => [...d.events, ...prev].slice(0, 20)); for (const ev of d.events) { showToast(`⏰ 计划任务「${ev.name}」${ev.prompt ? ":" + ev.prompt : "已触发"}`); } load(); }) .catch(() => {}); }; (0, react.useEffect)(() => { load(); const t1 = setInterval(pollEvents, 12000); const t2 = setInterval(load, 30000); return () => { clearInterval(t1); clearInterval(t2); if (toastTimer.current) clearTimeout(toastTimer.current); }; }, []); const openCreate = () => { setEditing(null); setModalOpen(true); }; const openEdit = (t) => { setEditing(t); setModalOpen(true); }; const toggleEnabled = (t) => { fetch("/mcn/schedule/update", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: t.id, enabled: !t.enabled }), }).then((r) => r.json()).then((d) => { if (d.ok) { load(); showToast(!t.enabled ? "已启用" : "已停用"); } }).catch(() => {}); }; const fireNow = (t) => { fetch("/mcn/schedule/fire-now", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: t.id }), }).then((r) => r.json()).then((d) => { if (d.ok) { showToast("已触发测试"); setTimeout(load, 500); } }).catch(() => {}); }; const remove = (t) => { if (!window.confirm(`确认删除任务「${t.name}」?`)) return; fetch("/mcn/schedule/delete", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: t.id }), }).then((r) => r.json()).then((d) => { if (d.ok) { load(); showToast("已删除"); } }).catch(() => {}); }; const submit = (form) => { const url = editing ? "/mcn/schedule/update" : "/mcn/schedule/create"; const body = { name: form.name, prompt: form.prompt, enabled: form.enabled, type: form.type, action: form.action || "remind" }; if (form.type === "at") body.at = form.at; else body.intervalMinutes = Number(form.intervalMinutes); if (editing) body.id = editing.id; fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }).then((r) => r.json()).then((d) => { if (d.ok) { setModalOpen(false); load(); showToast(editing ? "已保存" : "已创建"); } else { showToast(d.error || "操作失败"); } }).catch(() => showToast("请求失败")); }; return (0, react_jsx_runtime.jsxs)("div", { className: cssMap.page, children: [(0, react_jsx_runtime.jsxs)("div", { className: cssMap.body, children: [(0, react_jsx_runtime.jsxs)("div", { className: cssMap.toolbar, children: [(0, react_jsx_runtime.jsx)("span", { className: cssMap.title, children: "计划任务" }), (0, react_jsx_runtime.jsx)("span", { className: cssMap.spacer }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnPrimary, onClick: openCreate, children: "+ 新建任务" })] }), events.length > 0 && (0, react_jsx_runtime.jsx)("div", { className: cssMap.cellTrack, style: { font: "var(--dsw-font-s-13)", color: "var(--dsw-alias-label-secondary)" }, children: "最近触发:" + events.slice(0, 3).map((e) => `${e.name}(${fmtTime(new Date(e.at).getTime())})`).join("、") }), loading ? (0, react_jsx_runtime.jsx)("div", { className: cssMap.loading, children: "加载中…" }) : tasks.length === 0 ? (0, react_jsx_runtime.jsx)("div", { className: cssMap.empty, children: "暂无计划任务,点击右上角「+ 新建任务」创建" }) : (0, react_jsx_runtime.jsx)("div", { className: cssMap.tableWrap, children: (0, react_jsx_runtime.jsx)("table", { className: cssMap.table, children: [(0, react_jsx_runtime.jsx)("thead", { children: (0, react_jsx_runtime.jsx)("tr", { children: ["任务名称", "类型", "状态", "下次执行", "上次执行", "提醒内容", "操作"].map((h) => (0, react_jsx_runtime.jsx)("th", { children: h })) }) }), (0, react_jsx_runtime.jsx)("tbody", { children: tasks.map((t) => (0, react_jsx_runtime.jsx)("tr", { children: [(0, react_jsx_runtime.jsx)("td", { className: cssMap.cellTrack, children: t.name }), (0, react_jsx_runtime.jsx)("td", { children: [(0, react_jsx_runtime.jsx)("span", { className: cssMap.badge + " " + (t.type === "at" ? cssMap.badgeAt : cssMap.badgeEvery), children: t.type === "at" ? "一次性" : "循环" }), t.action === "agent" && (0, react_jsx_runtime.jsx)("span", { className: cssMap.badge + " " + cssMap.badgeAgent, children: "AI执行" })] }), (0, react_jsx_runtime.jsx)("td", { children: (0, react_jsx_runtime.jsx)("span", { className: cssMap.badge + " " + (t.enabled ? cssMap.badgeOn : cssMap.badgeOff), children: t.enabled ? "启用" : "停用" }) }), (0, react_jsx_runtime.jsx)("td", { className: cssMap.cellTrack, children: t.type === "at" ? fmtTime(t.nextRunAt) + (t.firedAt ? "(已触发)" : "") : fmtTime(t.nextRunAt) }), (0, react_jsx_runtime.jsx)("td", { className: cssMap.cellTrack, children: fmtTime(t.lastFiredAt) }), (0, react_jsx_runtime.jsx)("td", { className: cssMap.cellContent, children: t.prompt || "-" }), (0, react_jsx_runtime.jsx)("td", { children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnSm, onClick: () => openEdit(t), children: "编辑" }), " ", (0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnSm, onClick: () => toggleEnabled(t), children: t.enabled ? "停用" : "启用" }), " ", (0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnSm, onClick: () => fireNow(t), children: "触发" }), " ", (0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnSm, onClick: () => remove(t), children: "删除" })] })] }, t.id)) })] }) })] }), modalOpen && (0, react_jsx_runtime.jsxs)("div", { className: cssMap.mask, onClick: () => setModalOpen(false), children: [(0, react_jsx_runtime.jsxs)("div", { className: cssMap.modal, onClick: (e) => e.stopPropagation(), children: [(0, react_jsx_runtime.jsx)("div", { className: cssMap.modalTitle, children: editing ? `编辑任务「${editing.name}」` : "新建计划任务" }), (0, react_jsx_runtime.jsx)(ScheduleForm, { initial: editing, onSubmit: submit, onCancel: () => setModalOpen(false) })] })] }), toast && (0, react_jsx_runtime.jsx)("div", { className: cssMap.toast, children: toast })] }); } function ScheduleForm({ initial, onSubmit, onCancel }) { const [name, setName] = (0, react.useState)(initial?.name || ""); const [type, setType] = (0, react.useState)(initial?.type || "at"); const [at, setAt] = (0, react.useState)(initial?.at || ""); const [intervalMinutes, setIntervalMinutes] = (0, react.useState)(String(initial?.intervalMinutes || 10)); const [prompt, setPrompt] = (0, react.useState)(initial?.prompt || ""); const [action, setAction] = (0, react.useState)(initial?.action || "remind"); const [enabled, setEnabled] = (0, react.useState)(initial?.enabled !== false); const [err, setErr] = (0, react.useState)(""); const submit = () => { if (!name.trim()) { setErr("请填写任务名称"); return; } if (type === "at" && !at) { setErr("请选择执行时间"); return; } if (type === "every" && (!Number(intervalMinutes) || Number(intervalMinutes) < 1)) { setErr("周期间隔不能小于 1 分钟"); return; } onSubmit({ name, type, at, intervalMinutes, prompt, action, enabled }); }; return (0, react_jsx_runtime.jsxs)("div", { children: [(0, react_jsx_runtime.jsxs)("div", { className: cssMap.formGrid, children: [(0, react_jsx_runtime.jsx)("label", { className: cssMap.formItemWide, children: [(0, react_jsx_runtime.jsx)("span", { children: "任务名称" }), (0, react_jsx_runtime.jsx)("input", { className: cssMap.input, style: { width: "100%" }, value: name, onChange: (e) => setName(e.target.value), placeholder: "如:每日检查日榜" })] }), (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItem, children: [(0, react_jsx_runtime.jsx)("span", { children: "任务类型" }), (0, react_jsx_runtime.jsx)("select", { className: cssMap.select, value: type, onChange: (e) => setType(e.target.value), children: [(0, react_jsx_runtime.jsx)("option", { value: "at", children: "一次性(指定时间)" }), (0, react_jsx_runtime.jsx)("option", { value: "every", children: "循环(固定间隔)" })] })] }), (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItem, children: [(0, react_jsx_runtime.jsx)("span", { children: "动作" }), (0, react_jsx_runtime.jsx)("select", { className: cssMap.select, value: action, onChange: (e) => setAction(e.target.value), children: [(0, react_jsx_runtime.jsx)("option", { value: "remind", children: "仅页面提醒" }), (0, react_jsx_runtime.jsx)("option", { value: "agent", children: "触发 AI 执行" })] })] }), type === "every" ? (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItem, children: [(0, react_jsx_runtime.jsx)("span", { children: "间隔(分钟)" }), (0, react_jsx_runtime.jsx)("input", { className: cssMap.input, type: "number", min: 1, value: intervalMinutes, onChange: (e) => setIntervalMinutes(e.target.value) })] }) : (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItem, children: [(0, react_jsx_runtime.jsx)("span", { children: "执行时间" }), (0, react_jsx_runtime.jsx)("input", { className: cssMap.input, type: "datetime-local", value: at, onChange: (e) => setAt(e.target.value) })] }), (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItemWide, children: [(0, react_jsx_runtime.jsx)("span", { children: action === "agent" ? "任务指令(AI 将执行的内容)" : "提醒内容(可选)" }), (0, react_jsx_runtime.jsx)("textarea", { className: cssMap.textarea, value: prompt, onChange: (e) => setPrompt(e.target.value), placeholder: action === "agent" ? "如:分析今日抖音日榜数据,生成简报保存到工作区" : "到点后提醒的内容,如:检查今日日榜数据" })] }), (0, react_jsx_runtime.jsx)("label", { className: cssMap.formItemWide, children: [(0, react_jsx_runtime.jsx)("span", { children: "状态" }), (0, react_jsx_runtime.jsx)("select", { className: cssMap.select, value: enabled ? "1" : "0", onChange: (e) => setEnabled(e.target.value === "1"), children: [(0, react_jsx_runtime.jsx)("option", { value: "1", children: "启用" }), (0, react_jsx_runtime.jsx)("option", { value: "0", children: "停用" })] })] }), err && (0, react_jsx_runtime.jsx)("div", { className: cssMap.formItemWide, style: { color: "#ff6b6b" }, children: err })] }), (0, react_jsx_runtime.jsxs)("div", { className: cssMap.modalBtns, children: [(0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn, onClick: onCancel, children: "取消" }), (0, react_jsx_runtime.jsx)("button", { type: "button", className: cssMap.btn + " " + cssMap.btnPrimary, onClick: submit, children: initial ? "保存" : "创建" })] })] }); } //#endregion //#region plugin entry const inject = []; function apply() { window.__mcnEntries = window.__mcnEntries || []; // 幂等:避免插件热重载重复注册 if (!window.__mcnEntries.some((e) => e.id === "mcn-schedule")) { // unshift 置顶:入口放到 MCN 工作台上方 window.__mcnEntries.unshift({ id: "mcn-schedule", icon: IconClock, title: "计划任务", desc: "定时提醒 / 周期任务", component: SchedulePage, }); } } //#endregion exports.apply = apply; exports.inject = inject; return module.exports; } });