94 lines
4.6 KiB
TypeScript
94 lines
4.6 KiB
TypeScript
import { type FormEvent, useState } from "react";
|
|
|
|
import { platformApiClient } from "../api/client";
|
|
import { scumAnnouncementCommand, scumManagementRCONCommandRequest } from "../schemas/scumManagementRcon";
|
|
import { ResultBadge } from "./StateViews";
|
|
|
|
interface SourceRCONCommandPanelProps {
|
|
serverId: string;
|
|
pluginId: string;
|
|
}
|
|
|
|
type DispatchState = { status: "pending" | "succeeded" | "failed"; label: string } | null;
|
|
|
|
export function SourceRCONCommandPanel({ serverId, pluginId }: SourceRCONCommandPanelProps) {
|
|
const [announcement, setAnnouncement] = useState("");
|
|
const [rawCommand, setRawCommand] = useState("");
|
|
const [pending, setPending] = useState<"announcement" | "command" | null>(null);
|
|
const [dispatch, setDispatch] = useState<DispatchState>(null);
|
|
|
|
if (pluginId !== "game.scum") {
|
|
return null;
|
|
}
|
|
|
|
async function sendAnnouncement(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setPending("announcement");
|
|
setDispatch({ status: "pending", label: "正在提交服务器公告" });
|
|
try {
|
|
const submitted = await platformApiClient.queueGameClientBridgeCommand(serverId, scumManagementRCONCommandRequest(serverId, scumAnnouncementCommand(announcement)));
|
|
setAnnouncement("");
|
|
setDispatch({ status: "succeeded", label: protectedRCONDispatchLabel(submitted.id, submitted.state) });
|
|
} catch (error) {
|
|
setDispatch({ status: "failed", label: error instanceof Error ? error.message : "服务器公告提交失败" });
|
|
} finally {
|
|
setPending(null);
|
|
}
|
|
}
|
|
|
|
async function sendRawCommand(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
setPending("command");
|
|
setDispatch({ status: "pending", label: "正在提交原始管理员指令" });
|
|
try {
|
|
const submitted = await platformApiClient.queueGameClientBridgeCommand(serverId, scumManagementRCONCommandRequest(serverId, rawCommand));
|
|
setRawCommand("");
|
|
setDispatch({ status: "succeeded", label: protectedRCONDispatchLabel(submitted.id, submitted.state) });
|
|
} catch (error) {
|
|
setDispatch({ status: "failed", label: error instanceof Error ? error.message : "原始管理员指令提交失败" });
|
|
} finally {
|
|
setPending(null);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<article className="console-panel" aria-label="SCUM protected RCON controls">
|
|
<div className="panel-header">
|
|
<div>
|
|
<h2>SCUM 公告与管理员指令</h2>
|
|
<p className="page-status">通过插件声明的 protected RCON 通道派发;只显示安全状态,不保留指令原文。</p>
|
|
</div>
|
|
{dispatch && <ResultBadge status={dispatch.status} label={dispatch.label} />}
|
|
</div>
|
|
<div className="operations-command-grid">
|
|
<section className="console-module" aria-label="SCUM announcement command">
|
|
<div className="panel-header"><h2>发送公告</h2></div>
|
|
<form className="provider-form" onSubmit={(event) => void sendAnnouncement(event)}>
|
|
<label>
|
|
公告内容
|
|
<textarea value={announcement} maxLength={1024} rows={3} onChange={(event) => setAnnouncement(event.target.value)} disabled={pending !== null} placeholder="输入单行公告内容" />
|
|
</label>
|
|
<p className="page-status">会生成 SCUM 管理命令:#Announce 公告内容。</p>
|
|
<div className="action-strip"><button type="submit" className="primary-command" disabled={pending !== null || !announcement.trim()}>{pending === "announcement" ? "提交中…" : "发送公告"}</button></div>
|
|
</form>
|
|
</section>
|
|
<section className="console-module" aria-label="SCUM raw administrator command">
|
|
<div className="panel-header"><h2>原始管理员指令</h2></div>
|
|
<form className="provider-form" onSubmit={(event) => void sendRawCommand(event)}>
|
|
<label>
|
|
指令
|
|
<textarea value={rawCommand} maxLength={8192} rows={5} onChange={(event) => setRawCommand(event.target.value)} disabled={pending !== null} placeholder="例如 #ListPlayers 或 #SetTime 12" />
|
|
</label>
|
|
<p className="page-status">裸命令会交给当前运行中的 SCUM;执行结果以 Run 日志为准。</p>
|
|
<div className="action-strip"><button type="submit" className="icon-command" disabled={pending !== null || !rawCommand.trim()}>{pending === "command" ? "提交中…" : "发送指令"}</button></div>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
function protectedRCONDispatchLabel(commandId: string, state: string): string {
|
|
return `已${state === "pending" ? "排队" : "提交"} · 桥接命令 ${commandId}`;
|
|
}
|