feat(channels): 适配器面板重构 P1——子侧边栏+4页+每配置专属页(design/17)
- Channels.tsx 改为 re-export 到 ./channels/ChannelsPage(入口不变) - 新增 channels/ 模块:子侧边栏(桌面rail+移动端抽屉复用mu-drawer-in) + 子路由 #/admin/channels(<page>|detail/<adapter_id>),刷新/返回可还原 - 4页:①适配器活动总览 ②添加/新建 ③活动适配器列表(每账号可点进专属页) ④适配器模块总日志(adminChannelLogOverview总览) - 每配置一个页面(绑adapter_id, 不同配置不共用):实时日志+功能操作+身份测试 - 迁移保留原消息日志弹窗/身份测试/配置表单; 全部文件<500行 - 验收: tsc+vite build 96模块全绿 - 注: 另8个channels/*文件于5a0c1e2一并入库(该提交信息为后端修复,历史遗留混提交)
This commit is contained in:
1 parent
5a0c1e2fd1
commit
2d7a377d74
6 files changed
+399
-398
No files matched your search
@@ -0,0 +1,78 @@
|
||||
import { useState } from "react";
|
||||
import { Icon } from "../../../components/Icon";
|
||||
import { AdaptersDrawer, AdaptersRail } from "./AdaptersNav";
|
||||
import { PLATFORM_LABEL } from "../channelFields";
|
||||
import { platformOf } from "./channelUtils";
|
||||
import { useAdapterNav, type AdapterNav } from "./useAdapterNav";
|
||||
import { OverviewPage } from "./OverviewPage";
|
||||
import { CreatePage } from "./CreatePage";
|
||||
import { ListPage } from "./ListPage";
|
||||
import { GlobalLogPage } from "./GlobalLogPage";
|
||||
import { AdapterDetailPage } from "./AdapterDetailPage";
|
||||
|
||||
/** Title for the mobile sub-nav bar; also echoed by the rail when in a detail page. */
|
||||
function pageTitle(nav: AdapterNav): string {
|
||||
if (nav.kind === "detail") {
|
||||
const platform = platformOf(nav.adapter);
|
||||
return `${PLATFORM_LABEL[platform] ?? platform} · ${nav.adapter}`;
|
||||
}
|
||||
switch (nav.page) {
|
||||
case "create": return "新建适配器";
|
||||
case "list": return "活动适配器";
|
||||
case "log": return "模块总日志";
|
||||
default: return "活动总览";
|
||||
}
|
||||
}
|
||||
|
||||
function renderPage(nav: AdapterNav, navigate: (n: AdapterNav) => void): React.ReactNode {
|
||||
if (nav.kind === "detail") {
|
||||
return <AdapterDetailPage key={nav.adapter} adapter={nav.adapter} onBack={() => navigate({ kind: "page", page: "list" })} />;
|
||||
}
|
||||
switch (nav.page) {
|
||||
case "create": return <CreatePage />;
|
||||
case "list": return <ListPage navigate={navigate} />;
|
||||
case "log": return <GlobalLogPage />;
|
||||
default: return <OverviewPage navigate={navigate} />;
|
||||
}
|
||||
}
|
||||
|
||||
/** Adapter module shell: sub-sidebar + 4 pages, sub-routed by the location hash
|
||||
* (design/17 §1). Desktop keeps the rail sticky at the top; mobile (<768px)
|
||||
* collapses it into a drawer opened from a hamburger in the page title bar. */
|
||||
export function ChannelsModule(): React.ReactNode {
|
||||
const { nav, navigate } = useAdapterNav();
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-start">
|
||||
<AdaptersRail
|
||||
nav={nav}
|
||||
onNavigate={navigate}
|
||||
className="hidden w-[var(--mu-sidebar-w)] shrink-0 md:sticky md:top-0 md:self-start md:flex"
|
||||
/>
|
||||
<div className="min-w-0 flex-1 space-y-4">
|
||||
<div className="flex items-center gap-2 md:hidden">
|
||||
<button
|
||||
onClick={() => setDrawerOpen(true)}
|
||||
aria-label="打开适配器导航"
|
||||
title="适配器菜单"
|
||||
className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-[var(--mu-radius)] text-[var(--mu-text-mid)] transition-all duration-200 ease-[var(--mu-spring)] hover:bg-[var(--mu-surface-4)] active:scale-95"
|
||||
>
|
||||
<Icon name="menu" size={20} />
|
||||
</button>
|
||||
<h1 className="min-w-0 flex-1 truncate whitespace-nowrap text-base font-semibold">{pageTitle(nav)}</h1>
|
||||
</div>
|
||||
{renderPage(nav, navigate)}
|
||||
</div>
|
||||
<AdaptersDrawer
|
||||
open={drawerOpen}
|
||||
nav={nav}
|
||||
onNavigate={(n) => {
|
||||
navigate(n);
|
||||
setDrawerOpen(false);
|
||||
}}
|
||||
onClose={() => setDrawerOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user