Explain guided server installation flow

This commit is contained in:
npc0-hue
2026-07-25 10:35:45 +08:00
parent 15789e15b4
commit 4e2bff4639
4 changed files with 32 additions and 2 deletions
@@ -1,4 +1,4 @@
import { CheckCircle2, CircleDashed, Compass, FolderCog, Rocket, ServerCog } from "lucide-react";
import { CheckCircle2, CircleDashed, Compass, Download, FolderCog, HeartPulse, Rocket, ScanSearch, ServerCog, SlidersHorizontal } from "lucide-react";
import { type ChangeEvent, type FormEvent, useEffect, useMemo, useState } from "react";
import type { GamePluginResponse, RunEndpointResponse, ServerDeploymentResponse } from "../api/types";
@@ -108,6 +108,7 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi
)}</label>
))}
</div>
{form.deploymentMode === "guided-install" && <GuidedInstallPlan pluginName={pluginLabel(selectedPlugin, form.pluginId)} isScum={isScum} />}
{form.deploymentMode === "existing-server" && <div className="form-guidance"><strong></strong><span></span></div>}
{form.deploymentMode === "custom-command" && <details className="provider-advanced-settings" open><summary></summary><p className="field-help"></p><div className="form-grid"><label><input name="startCommand" value={form.startCommand} onChange={updateForm} placeholder={deployment?.startCommandConfigured ? "留空保持已配置启动命令" : "必填,例如 ./start-server"} autoComplete="off" required={!deployment?.startCommandConfigured} /></label><label><select name="shell" value={form.shell} onChange={updateForm}><option value=""> argv</option><option value="posix-sh">POSIX sh</option><option value="powershell">PowerShell</option><option value="cmd">Windows cmd</option></select></label><label><input name="workingDirectory" value={form.workingDirectory} onChange={updateForm} placeholder={deployment?.workingDirectoryConfigured ? "留空保持已配置执行目录" : "默认使用服务器目录"} autoComplete="off" /></label><label><input name="installCommand" value={form.installCommand} onChange={updateForm} autoComplete="off" placeholder="留空保持原值或不使用" /></label><label><input name="stopCommand" value={form.stopCommand} onChange={updateForm} autoComplete="off" /></label><label><input name="statusCommand" value={form.statusCommand} onChange={updateForm} autoComplete="off" /></label></div></details>}
{kind === "create" && bindingFields.length > 0 && <details className="provider-advanced-settings"><summary></summary><p className="field-help"></p><div className="form-grid">{bindingFields.map((field) => <label key={field.key}>{field.key}{field.required ? "(必填)" : ""}<input type={field.sensitive ? "password" : "text"} autoComplete="off" value={form.bindings[field.key] ?? ""} onChange={(event) => updateBinding(field.key, event.target.value)} placeholder={field.sensitive ? "托管凭据引用" : "安全逻辑值"} required={field.required} /></label>)}</div></details>}
@@ -119,3 +120,19 @@ export function ServerDeploymentWorkflow({ open, kind, plugins, endpoints, initi
}
function ModeOption({ active, title, copy, onClick }: { active: boolean; title: string; copy: string; onClick: () => void }) { return <button type="button" className={cx("deployment-mode-option", active && "deployment-mode-option-active")} onClick={onClick}><strong>{title}</strong><span>{copy}</span></button>; }
function GuidedInstallPlan({ pluginName, isScum }: { pluginName: string; isScum: boolean }) {
const steps = isScum ? [
{ icon: ScanSearch, title: "预检目录与端口", copy: "确认安装目录可用、节点兼容且端口可绑定。" },
{ icon: Download, title: "下载 SCUM Server", copy: "通过 SteamCMD 安装 App 3792580 到该目录。" },
{ icon: SlidersHorizontal, title: "写入游戏配置", copy: "把本页的名称、端口与人数写入 ServerSettings.ini。" },
{ icon: HeartPulse, title: "启动并健康验证", copy: "检查可执行文件、版本、配置、端口和服务进程。" }
] : [
{ icon: ScanSearch, title: "预检目录与节点", copy: "确认安装目录、权限、端口与运行节点可用。" },
{ icon: Download, title: "安装游戏服务端", copy: "按插件声明的推荐方案安装到该目录。" },
{ icon: SlidersHorizontal, title: "写入游戏配置", copy: "将本页填写的游戏参数交给受控部署流程。" },
{ icon: HeartPulse, title: "启动并健康验证", copy: "只有启动与插件要求的验证通过才会显示成功。" }
];
return <section className="guided-install-plan" aria-label="新建并安装执行流程"><div className="guided-install-plan-heading"><div><strong>{pluginName} </strong><span></span></div><small>{isScum ? "全部 4 步通过才算安装成功" : "节点按插件契约执行"}</small></div><ol>{steps.map(({ icon: Icon, title, copy }, index) => <li key={title}><span><Icon size={16} /></span><div><strong>{index + 1}. {title}</strong><small>{copy}</small></div></li>)}</ol><p><strong></strong>{isScum ? "不会跳过验证就标记成功;失败时不会暴露你的目录、命令或凭据。" : "不会把受保护的路径、命令或凭据回显给浏览器。"}</p></section>;
}
+4
View File
@@ -180,6 +180,10 @@ describe("first-party console pages", () => {
expect(serverDeploymentWorkflowSource).toContain("执行目录(可选)");
expect(serverDeploymentWorkflowSource).toContain("默认使用服务器目录");
expect(serverDeploymentWorkflowSource).toContain("安装目录{isScum ? \"(必填)\" : \"(可选)\"}");
expect(serverDeploymentWorkflowSource).toContain("新建并安装执行流程");
expect(serverDeploymentWorkflowSource).toContain("安装目录”就是游戏服务端、数据和配置将落地的位置");
expect(serverDeploymentWorkflowSource).toContain("通过 SteamCMD 安装 App 3792580 到该目录");
expect(serverDeploymentWorkflowSource).toContain("全部 4 步通过才算安装成功");
expect(serverDeploymentWorkflowSource).toContain("已有服务器目录");
expect(serverDeploymentWorkflowSource).toContain("不会重装或覆盖现有游戏配置");
expect(serverDeploymentWorkflowSource).toContain("运行连接设置");
+8
View File
@@ -172,6 +172,14 @@ describe("platform web shared theme CSS", () => {
expect(css).toContain(".deployment-workflow-body.form-grid{align-content:start;align-items:start;grid-auto-rows:min-content}");
});
it("keeps the guided-install plan compact and responsive inside the deployment workflow", () => {
const css = compact(readThemeCss());
expect(css).toContain(".guided-install-plan{display:grid;gap:10px;padding:12px;border:1pxsolidcolor-mix(insrgb,var(--accent)42%,var(--line));border-radius:8px");
expect(css).toContain(".guided-install-planol{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px");
expect(css).toContain(".guided-install-planol,.maintenance-node-item");
});
it("keeps magical card decoration from becoming square background patches", () => {
const css = compact(readThemeCss());
+2 -1
View File
@@ -496,6 +496,7 @@ to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))}
.management-dialog-panel .management-form{grid-template-columns:repeat(2,minmax(0,1fr))}
.management-dialog-panel .dialog-form .confirm-actions,.management-dialog-panel .dialog-form .role-selector{grid-column:1/-1}
.server-deployment-workflow{display:grid;gap:14px}.deployment-workflow-steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:0;padding:0;list-style:none}.deployment-workflow-steps li{display:flex;align-items:center;gap:6px;min-height:38px;padding:8px;border:1px solid var(--line);border-radius:8px;background:var(--control-surface);color:var(--ink-faint);font-size:12px}.deployment-workflow-steps li>span{display:inline-flex;align-items:center;justify-content:center;color:var(--ink-soft)}.deployment-workflow-steps strong{font-size:12px}.deployment-workflow-step-active{border-color:var(--accent)!important;background:var(--primary-command-surface)!important;color:#fff!important;box-shadow:0 0 16px var(--accent-soft)}.deployment-workflow-step-complete{border-color:color-mix(in srgb,var(--teal) 68%,var(--line));color:var(--ink)!important}.deployment-workflow-body{display:grid;align-content:start;gap:12px;min-height:0}.deployment-workflow-body .form-grid{align-content:start;align-items:start;grid-auto-rows:min-content}.deployment-workflow-body .form-grid>label{align-content:start}.deployment-draft-choice{grid-template-columns:auto minmax(0,1fr);align-items:start;padding:10px 12px;border:1px dashed var(--line-strong);border-radius:8px;background:color-mix(in srgb,var(--surface-solid) 62%,transparent)}.deployment-draft-choice input{width:auto!important;min-height:auto!important;margin-top:3px}.deployment-draft-choice span{display:grid;gap:3px}.deployment-draft-choice small{color:var(--ink-faint);font-weight:600;line-height:1.4}.deployment-mode-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.deployment-mode-option{display:grid;align-content:start;gap:5px;min-height:130px;padding:14px;border:1px solid var(--line);border-radius:8px;background:var(--control-surface);color:var(--ink-soft);cursor:pointer;text-align:left}.deployment-mode-option strong{color:var(--ink);font-size:14px}.deployment-mode-option span{font-size:12px;line-height:1.45}.deployment-mode-option:hover,.deployment-mode-option:focus-visible,.deployment-mode-option-active{border-color:var(--accent);outline:0;box-shadow:inset 0 1px 0 var(--crystal-rim),0 0 0 2px var(--accent-soft);background:var(--menu-item-active-bg)}.deployment-review{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.deployment-review>div{display:grid;gap:3px;padding:10px;border:1px solid var(--line);border-radius:8px;background:color-mix(in srgb,var(--surface-solid) 64%,transparent)}.deployment-review span{color:var(--ink-faint);font-size:11.5px;font-weight:700}.deployment-review strong{color:var(--ink);font-size:13px;overflow-wrap:anywhere}
.guided-install-plan{display:grid;gap:10px;padding:12px;border:1px solid color-mix(in srgb,var(--accent) 42%,var(--line));border-radius:8px;background:linear-gradient(135deg,color-mix(in srgb,var(--primary-command-surface) 46%,transparent),color-mix(in srgb,var(--surface-solid) 72%,transparent));box-shadow:inset 0 1px 0 var(--crystal-rim)}.guided-install-plan-heading{display:flex;align-items:start;justify-content:space-between;gap:12px}.guided-install-plan-heading>div{display:grid;gap:3px}.guided-install-plan-heading strong,.guided-install-plan li strong,.guided-install-plan p strong{color:var(--ink);font-size:13px}.guided-install-plan-heading span,.guided-install-plan small,.guided-install-plan p{color:var(--ink-soft);font-size:12px;line-height:1.45}.guided-install-plan-heading>small{flex:0 0 auto;padding:3px 6px;border:1px solid var(--line);border-radius:999px;color:var(--accent);font-weight:750}.guided-install-plan ol{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin:0;padding:0;list-style:none}.guided-install-plan li{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start;gap:7px;padding:8px;border:1px solid var(--line);border-radius:6px;background:color-mix(in srgb,var(--surface-solid) 62%,transparent)}.guided-install-plan li>span{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:1px solid color-mix(in srgb,var(--accent) 44%,var(--line));border-radius:50%;color:var(--accent);background:var(--control-surface)}.guided-install-plan li>div{display:grid;gap:2px}.guided-install-plan li small{font-size:11.5px}.guided-install-plan p{margin:0;padding-top:2px}
.management-dialog-panel .plugin-detail-panel,.management-dialog-panel .plugin-group{border:0;padding:0;background:0 0;box-shadow:none;clip-path:none}
.management-dialog-panel .plugin-group::after,.management-dialog-panel .plugin-group::before{content:none}
.plugin-detail-actions{align-items:stretch}
@@ -624,7 +625,7 @@ to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))}
}
@media (max-width:760px){.auth-shell{padding:14px;place-items:stretch}
.auth-card{align-self:center}
.access-list-row,.deployment-mode-grid,.deployment-review,.deployment-workflow-steps,.maintenance-node-item,.maintenance-triage-grid,.management-form,.profile-background-grid,.profile-palette-grid,.profile-settings-form-row,.profile-settings-grid,.profile-settings-hero,.provider-preset-grid,.runtime-task-stages,.user-management-item,.workflow-hint-grid{grid-template-columns:1fr}
.access-list-row,.deployment-mode-grid,.deployment-review,.deployment-workflow-steps,.guided-install-plan ol,.maintenance-node-item,.maintenance-triage-grid,.management-form,.profile-background-grid,.profile-palette-grid,.profile-settings-form-row,.profile-settings-grid,.profile-settings-hero,.provider-preset-grid,.runtime-task-stages,.user-management-item,.workflow-hint-grid{grid-template-columns:1fr}.guided-install-plan-heading{display:grid}
.console-row-actions,.maintenance-actions,.profile-settings-actions,.user-actions{justify-content:flex-start}
.app-shell{display:block;grid-template-columns:1fr}
.app-sidebar{position:fixed;inset:0 auto 0 0;z-index:60;width:min(82vw,300px);min-width:0;height:100dvh;display:flex;flex-direction:column;gap:12px;align-items:stretch;padding:max(14px,env(safe-area-inset-top)) 12px max(14px,env(safe-area-inset-bottom));border-right:1px solid color-mix(in srgb,var(--line-strong) 74%,transparent);border-bottom:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;opacity:0;visibility:hidden;pointer-events:none;transform:translateX(calc(-100% - 18px));transition:transform .2s ease,opacity 160ms ease,visibility .2s step-end}