功能修改
This commit is contained in:
@@ -3,10 +3,12 @@ import { type ChangeEvent, useCallback, useEffect, useMemo, useState } from "rea
|
||||
|
||||
import { platformApiClient } from "../api/client";
|
||||
import type { GamePluginStatus, MarketplacePluginFilterRequest, MarketplacePluginResponse, MarketplacePluginStateAction } from "../api/types";
|
||||
import { ManagementDialog } from "../components/OperationControls";
|
||||
import { ConfirmDialog, ManagementDialog } from "../components/OperationControls";
|
||||
import { PluginLifecycleWorkbench } from "../components/PluginLifecycleWorkbench";
|
||||
import { EmptyState, ErrorState, LoadingState, ResultBadge } from "../components/StateViews";
|
||||
import { PageFrame } from "../components/PageFrame";
|
||||
import type { PageComponentProps } from "../contracts/page";
|
||||
import { isPlatformAdmin } from "../contracts/workspace";
|
||||
import { cx } from "../utils/classes";
|
||||
|
||||
type ListState = "loading" | "ready" | "error";
|
||||
@@ -34,7 +36,7 @@ const statusFilters: Array<{ id: StatusFilter; label: string }> = [
|
||||
{ id: "updating", label: "更新中" }
|
||||
];
|
||||
|
||||
export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
export function PluginsPage({ initialState, session, operations }: PluginsPageProps = {}) {
|
||||
const [listState, setListState] = useState<ListState>(initialState?.listState ?? "loading");
|
||||
const [listError, setListError] = useState(initialState?.listError ?? "");
|
||||
const [plugins, setPlugins] = useState<MarketplacePluginResponse[]>(initialState?.plugins ?? []);
|
||||
@@ -47,6 +49,7 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
const [serverType, setServerType] = useState("");
|
||||
const [capability, setCapability] = useState("");
|
||||
const [actionPending, setActionPending] = useState<MarketplacePluginStateAction | null>(null);
|
||||
const [confirmAction, setConfirmAction] = useState<MarketplacePluginStateAction | null>(null);
|
||||
const [actionResult, setActionResult] = useState<{ status: "succeeded" | "failed"; label: string } | null>(initialState?.actionResult ?? null);
|
||||
const [usingFallback, setUsingFallback] = useState(initialState?.usingFallback ?? false);
|
||||
|
||||
@@ -110,6 +113,22 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
.finally(() => setDetailPending(false));
|
||||
}, [plugins, selectedId, usingFallback]);
|
||||
|
||||
const retryDetail = useCallback(async () => {
|
||||
if (!selectedId || usingFallback) {
|
||||
return;
|
||||
}
|
||||
setDetailPending(true);
|
||||
setDetailError("");
|
||||
try {
|
||||
const plugin = await platformApiClient.getMarketplacePlugin(selectedId);
|
||||
setDetail(plugin);
|
||||
} catch (error) {
|
||||
setDetailError(error instanceof Error ? error.message : "插件详情加载失败");
|
||||
} finally {
|
||||
setDetailPending(false);
|
||||
}
|
||||
}, [selectedId, usingFallback]);
|
||||
|
||||
const serverTypes = useMemo(() => unique(plugins.map((plugin) => plugin.serverType)), [plugins]);
|
||||
const capabilities = useMemo(() => unique(plugins.flatMap((plugin) => [...plugin.capabilities, ...plugin.bridgeActions])), [plugins]);
|
||||
const installedCount = plugins.filter((plugin) => plugin.status === "installed").length;
|
||||
@@ -129,6 +148,8 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
if (!detail || usingFallback) {
|
||||
return;
|
||||
}
|
||||
const intent = `${stateActionLabel(action)}插件`;
|
||||
const operationId = operations?.begin({ intent, targetKind: "plugin", targetId: detail.id, requester: session?.displayName });
|
||||
setActionPending(action);
|
||||
setActionResult(null);
|
||||
try {
|
||||
@@ -136,23 +157,37 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
setDetail(updated);
|
||||
setPlugins((current) => current.map((plugin) => (plugin.id === updated.id ? updated : plugin)));
|
||||
setActionResult({ status: "succeeded", label: `${updated.name} 已${stateActionLabel(action)}` });
|
||||
if (operationId) operations?.succeed(operationId, `${updated.name} 已${stateActionLabel(action)}`);
|
||||
setConfirmAction(null);
|
||||
} catch (error) {
|
||||
setActionResult({ status: "failed", label: error instanceof Error ? error.message : "状态更新失败" });
|
||||
const reason = error instanceof Error ? error.message : "状态更新失败";
|
||||
setActionResult({ status: "failed", label: reason });
|
||||
if (operationId) operations?.fail(operationId, reason, operationId);
|
||||
} finally {
|
||||
setActionPending(null);
|
||||
}
|
||||
}
|
||||
|
||||
function requestStateChange(action: MarketplacePluginStateAction) {
|
||||
if (!canManage || usingFallback || actionPending !== null) {
|
||||
return;
|
||||
}
|
||||
setActionResult(null);
|
||||
setConfirmAction(action);
|
||||
}
|
||||
|
||||
const canManage = Boolean(session && isPlatformAdmin(session));
|
||||
|
||||
return (
|
||||
<div className="console-page">
|
||||
<PageFrame
|
||||
kicker="扩展"
|
||||
title="插件市场"
|
||||
status={usingFallback ? "本地演示数据" : "平台 API"}
|
||||
status={usingFallback ? "本地演示数据" : listState === "loading" ? "正在连接平台 API" : listState === "error" ? "平台 API 不可用" : "平台 API"}
|
||||
metrics={[
|
||||
{ label: "已安装", value: `${installedCount}`, tone: "success" },
|
||||
{ label: "桥接动作", value: `${bridgeActionCount}`, tone: "success" },
|
||||
{ label: "校验失败", value: `${invalidCount}`, tone: invalidCount > 0 ? "warning" : "success" }
|
||||
{ label: "已安装", value: listState === "ready" ? `${installedCount}` : "--", tone: "success" },
|
||||
{ label: "桥接动作", value: listState === "ready" ? `${bridgeActionCount}` : "--", tone: "success" },
|
||||
{ label: "校验失败", value: listState === "ready" ? `${invalidCount}` : "--", tone: invalidCount > 0 ? "warning" : "success" }
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -196,6 +231,7 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
</div>
|
||||
|
||||
{usingFallback && <ResultBadge status="pending" label="本地演示数据仅用于前端开发,连接平台 API 后会自动替换" />}
|
||||
{!canManage && !usingFallback && <ResultBadge status="pending" label="当前账号为只读模式,插件状态动作需要平台管理员权限" />}
|
||||
{actionResult && <ResultBadge status={actionResult.status} label={actionResult.label} />}
|
||||
|
||||
{listState === "loading" && <LoadingState label="正在加载插件市场…" />}
|
||||
@@ -248,13 +284,23 @@ export function PluginsPage({ initialState }: PluginsPageProps = {}) {
|
||||
</section>
|
||||
)}
|
||||
|
||||
<ManagementDialog open={selectedId !== ""} title={detail?.name ?? "插件详情"} wide onClose={() => { setSelectedId(""); setDetail(null); setDetailError(""); }}>
|
||||
<ManagementDialog open={selectedId !== ""} title={detail?.name ?? "插件详情"} wide onClose={() => { if (actionPending === null) { setSelectedId(""); setDetail(null); setDetailError(""); setConfirmAction(null); } }}>
|
||||
<div className="plugin-detail-panel" aria-label="plugin marketplace detail">
|
||||
{detailPending && <LoadingState label="正在加载插件详情…" compact />}
|
||||
{detailError && <ErrorState title="插件详情加载失败" reason={detailError} diagnosticId={`plugin-detail:${selectedId}`} compact />}
|
||||
{detail && <PluginDetail plugin={detail} actionPending={actionPending} actionsDisabled={usingFallback} onAction={(action) => void changeState(action)} />}
|
||||
{detailError && <ErrorState title="插件详情加载失败" reason={detailError} diagnosticId={`plugin-detail:${selectedId}`} onRetry={() => void retryDetail()} compact />}
|
||||
{detail && <PluginDetail plugin={detail} actionPending={actionPending} actionsDisabled={usingFallback || !canManage} onAction={requestStateChange} />}
|
||||
</div>
|
||||
</ManagementDialog>
|
||||
<ConfirmDialog
|
||||
open={confirmAction !== null}
|
||||
title="确认插件状态变更"
|
||||
description={detail ? `将对 ${detail.name} 执行“${stateActionLabel(confirmAction ?? "disable")}”。平台会返回持久状态,失败时保留当前状态并允许重试。` : "请确认插件状态变更。"}
|
||||
confirmLabel={stateActionLabel(confirmAction ?? "disable")}
|
||||
danger={confirmAction === "disable"}
|
||||
busy={actionPending !== null}
|
||||
onCancel={() => { if (actionPending === null) setConfirmAction(null); }}
|
||||
onConfirm={() => { if (confirmAction) void changeState(confirmAction); }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -282,6 +328,7 @@ function PluginDetail({ plugin, actionPending, actionsDisabled, onAction }: Plug
|
||||
<DetailStat label="权限" value={`${plugin.declaredPermissions.length}`} />
|
||||
<DetailStat label="AI 用途" value={plugin.aiPurposes.length ? plugin.aiPurposes.join(", ") : "--"} />
|
||||
</div>
|
||||
<PluginLifecycleWorkbench pluginId={plugin.id} pluginName={plugin.name} operations={plugin.productionLifecycle?.operations} disabled={actionsDisabled} />
|
||||
<dl className="detail-list">
|
||||
<div>
|
||||
<dt>能力</dt>
|
||||
@@ -345,5 +392,8 @@ function statusLabel(status: string): string {
|
||||
}
|
||||
|
||||
function stateActionLabel(action: MarketplacePluginStateAction): string {
|
||||
if (action === "install") {
|
||||
return "安装";
|
||||
}
|
||||
return action === "disable" ? "停用" : "启用";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user