修改部份ui
This commit is contained in:
@@ -205,6 +205,35 @@ const NAV: NavDef[] = [
|
||||
{ id: "trash", page: "trash", label: "垃圾桶", icon: "trash" },
|
||||
{ id: "settings", page: "settings", label: "设置", icon: "settings" }
|
||||
];
|
||||
const RESOURCE_NAV: Array<{ id: string; page: Page; label: string }> = [
|
||||
{ id: "products", page: "products", label: "商品库" },
|
||||
{ id: "models", page: "models", label: "模特库" },
|
||||
{ id: "library", page: "library", label: "资产库" },
|
||||
// 成品库先隐藏,后续再加回资源中心
|
||||
{ id: "account", page: "account", label: "消费" },
|
||||
{ id: "trash", page: "trash", label: "垃圾桶" },
|
||||
];
|
||||
|
||||
export type TopModule = "workbench" | "image" | "video";
|
||||
const OPEN_PALETTE_EVENT = "airshelf:open-palette";
|
||||
|
||||
export function openCommandPalette() {
|
||||
window.dispatchEvent(new Event(OPEN_PALETTE_EVENT));
|
||||
}
|
||||
|
||||
export function topModuleForPage(page: Page): TopModule | null {
|
||||
if (page === "dashboard") return "workbench";
|
||||
if (
|
||||
page === "assetFactory"
|
||||
|| page === "imageOptimize"
|
||||
|| page === "modelPhoto"
|
||||
|| page === "modelPhotoDemoA"
|
||||
|| page === "modelPhotoDemoB"
|
||||
|| page === "platformCover"
|
||||
) return "image";
|
||||
if (page === "projects" || page === "projectWizard" || page === "pipeline" || page === "freeCreate") return "video";
|
||||
return null;
|
||||
}
|
||||
|
||||
const PAGE_TO_NAV: Partial<Record<Page, Page>> = {
|
||||
dashboard: "dashboard",
|
||||
@@ -251,6 +280,14 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
const activeNav = PAGE_TO_NAV[page];
|
||||
// 子账号:导航里去掉「团队」「消费」两项(命令面板、账户菜单同步过滤)。
|
||||
const navItems = NAV.filter((item) => canManageBilling || (item.page !== "team" && item.page !== "account"));
|
||||
const resourceNavItems = RESOURCE_NAV.filter((item) => navItems.some((nav) => nav.page === item.page));
|
||||
const topModule = topModuleForPage(page);
|
||||
const activeMajor: "resource" | "team" | "settings" | null =
|
||||
activeNav === "team" ? "team"
|
||||
: activeNav === "settings" ? "settings"
|
||||
: topModule ? null
|
||||
: resourceNavItems.some((item) => item.page === activeNav) ? "resource"
|
||||
: null;
|
||||
// 徽标用后端真实总数(分页 count),不用已加载的页内条数
|
||||
const badges: Partial<Record<string, number>> = { products: productTotal ?? products.length, projects: projectTotal ?? projects.length };
|
||||
// YYX#row22:有未读生成任务才显示胶囊(0 不显示)
|
||||
@@ -275,6 +312,12 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
document.addEventListener("keydown", onKey);
|
||||
return () => document.removeEventListener("keydown", onKey);
|
||||
}, [mobileNavOpen]);
|
||||
// 二级类目展开/收起:进资源中心时展开;切到顶栏模块(工作台/图片/视频)时收起,避免左边再出现选中。
|
||||
const [resourceOpen, setResourceOpen] = useState(activeMajor === "resource");
|
||||
useEffect(() => {
|
||||
if (activeMajor === "resource") setResourceOpen(true);
|
||||
else if (topModule) setResourceOpen(false);
|
||||
}, [activeMajor, topModule]);
|
||||
|
||||
// 账户下拉菜单:点侧栏 .user 用其 DOMRect 当锚点展开,再次点击收起(toggle)。
|
||||
const [accountAnchor, setAccountAnchor] = useState<DOMRect | null>(null);
|
||||
@@ -290,9 +333,8 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
window.location.href = "/login";
|
||||
};
|
||||
|
||||
// 命令面板:Ctrl/Cmd K 开关,点搜索框打开
|
||||
// 命令面板:Ctrl/Cmd K,以及顶栏搜索按钮
|
||||
const [paletteOpen, setPaletteOpen] = useState(false);
|
||||
const openCommandPalette = () => setPaletteOpen(true);
|
||||
useEffect(() => {
|
||||
const onKey = (event: KeyboardEvent) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
||||
@@ -300,8 +342,13 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
setPaletteOpen((value) => !value);
|
||||
}
|
||||
};
|
||||
const onOpen = () => setPaletteOpen(true);
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
window.addEventListener(OPEN_PALETTE_EVENT, onOpen);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKey);
|
||||
window.removeEventListener(OPEN_PALETTE_EVENT, onOpen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -328,28 +375,61 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
<span className="sidebar-toggle-icon sidebar-toggle-icon--collapse"><IconKitSvg name="chevronLeft" size={18} strokeWidth={1.8} /></span>
|
||||
<span className="sidebar-toggle-icon sidebar-toggle-icon--expand"><IconKitSvg name="chevronRight" size={18} strokeWidth={1.8} /></span>
|
||||
</button>
|
||||
<div className="search-box" title="搜索 (Ctrl K)" role="button" tabIndex={0} onClick={openCommandPalette} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); openCommandPalette(); } }}>
|
||||
<IconKitSvg name="search" />
|
||||
<input id="global-search" placeholder="搜索" readOnly aria-label="打开全局搜索" />
|
||||
<span className="kbd">Ctrl K</span>
|
||||
<div className="yz-side-main">
|
||||
<button
|
||||
className={`yz-resource-head${activeMajor === "resource" ? " active" : ""}`}
|
||||
type="button"
|
||||
aria-expanded={resourceOpen}
|
||||
onClick={() => {
|
||||
// 在资源中心内点击=折叠/展开;在其它大类点击=切回资源中心并展开
|
||||
if (activeMajor !== "resource") {
|
||||
setResourceOpen(true);
|
||||
navigate("products");
|
||||
return;
|
||||
}
|
||||
setResourceOpen((value) => !value);
|
||||
}}
|
||||
>
|
||||
<span className="major-dot" aria-hidden="true" />
|
||||
<IconKitSvg name="library" />
|
||||
<span>资源中心</span>
|
||||
<span className="chev"><IconKitSvg name="chevronRight" /></span>
|
||||
</button>
|
||||
<div className={`yz-resource-sub${resourceOpen ? " open" : " collapsed"}`}>
|
||||
{resourceNavItems.map((item) => (
|
||||
<a
|
||||
key={item.id}
|
||||
href={`/${item.id}`}
|
||||
className={activeNav === item.page ? "active" : ""}
|
||||
title={item.label}
|
||||
aria-label={item.label}
|
||||
onClick={(event) => { event.preventDefault(); setMobileNavOpen(false); navigate(item.page); }}
|
||||
>
|
||||
<span className="dot" />
|
||||
<span>{item.label}</span>
|
||||
{badges[item.id] !== undefined && <span className="pill-mini">{badges[item.id]}</span>}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<a
|
||||
className={`yz-side-link${activeMajor === "team" ? " active" : ""}`}
|
||||
href="/team"
|
||||
onClick={(event) => { event.preventDefault(); setMobileNavOpen(false); navigate("team"); }}
|
||||
>
|
||||
<span className="major-dot" aria-hidden="true" />
|
||||
<IconKitSvg name="users" />
|
||||
<span>团队中心</span>
|
||||
</a>
|
||||
<a
|
||||
className={`yz-side-link${activeMajor === "settings" ? " active" : ""}`}
|
||||
href="/settings"
|
||||
onClick={(event) => { event.preventDefault(); setMobileNavOpen(false); navigate("settings"); }}
|
||||
>
|
||||
<span className="major-dot" aria-hidden="true" />
|
||||
<IconKitSvg name="settings" />
|
||||
<span>设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="nav-section">主要</div>
|
||||
<nav>
|
||||
{navItems.map((item) => (
|
||||
<a
|
||||
key={item.id}
|
||||
href={`/${item.id}`}
|
||||
className={activeNav === item.page ? "active" : ""}
|
||||
title={item.label}
|
||||
aria-label={item.label}
|
||||
onClick={(event) => { event.preventDefault(); setMobileNavOpen(false); navigate(item.page); }}
|
||||
>
|
||||
<IconKitSvg name={item.icon} />
|
||||
<span>{item.label}</span>
|
||||
{badges[item.id] !== undefined && <span className="pill-mini">{badges[item.id]}</span>}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
{user.is_platform_admin && onOpenAdmin && (
|
||||
<>
|
||||
<div className="nav-section">平台</div>
|
||||
@@ -399,16 +479,7 @@ export function Sidebar({ page, navigate, user, team, canManageBilling = true, p
|
||||
}
|
||||
|
||||
export function Decorations() {
|
||||
// 与设计稿 shell.js 一致:grid-bg 底纹 + 4 个 sq-mark(写死坐标,不压内容)
|
||||
return (
|
||||
<>
|
||||
<div className="grid-bg" />
|
||||
<span className="sq-mark" style={{ top: 238, left: 478 }} />
|
||||
<span className="sq-mark" style={{ top: 478, left: 1198 }} />
|
||||
<span className="sq-mark" style={{ bottom: 300, left: 238 }} />
|
||||
<span className="sq-mark" style={{ top: 718, right: 240 }} />
|
||||
</>
|
||||
);
|
||||
return <div className="grid-bg" />;
|
||||
}
|
||||
|
||||
export function CornerMarks() {
|
||||
|
||||
Reference in New Issue
Block a user