227 lines
8.4 KiB
TypeScript
227 lines
8.4 KiB
TypeScript
import {
|
|
Film,
|
|
FolderKanban,
|
|
Home,
|
|
ImageIcon,
|
|
Library,
|
|
MessageSquare,
|
|
Package,
|
|
Settings,
|
|
UserRound,
|
|
Users,
|
|
Wallet,
|
|
WandSparkles
|
|
} from "lucide-react";
|
|
|
|
export type Page =
|
|
| "dashboard"
|
|
| "products"
|
|
| "productDetail"
|
|
| "productCreateUpload"
|
|
| "models"
|
|
| "projects"
|
|
| "projectWizard"
|
|
| "pipeline"
|
|
| "library"
|
|
| "account"
|
|
| "team"
|
|
| "messages"
|
|
| "assetFactory"
|
|
| "freeCreate"
|
|
| "imageOptimize"
|
|
| "modelPhoto"
|
|
| "modelPhotoDemoA"
|
|
| "modelPhotoDemoB"
|
|
| "platformCover"
|
|
| "settings"
|
|
| "settingsNotify"
|
|
| "trash";
|
|
|
|
export type AuthMode = "login" | "register";
|
|
export type ResolvedRoute = {
|
|
page: Page;
|
|
authMode: AuthMode;
|
|
productId?: string;
|
|
projectId?: string;
|
|
hash?: string;
|
|
// 视频项目页初始 tab(all/wip/done/fail):由 navigate 透传,刷新/前进后退不持久(回默认 all)。
|
|
tab?: string;
|
|
// 平台超管后台:/admin → ""(概览),/admin/<section> → section slug。undefined = 非 admin 路由。
|
|
admin?: string;
|
|
};
|
|
export type NavigateOptions = {
|
|
productId?: string;
|
|
projectId?: string;
|
|
replace?: boolean;
|
|
hash?: string;
|
|
// 视频项目页初始 tab(all/wip/done/fail):仅经 route state 透传给 ProjectsPage,不进 URL path。
|
|
tab?: string;
|
|
};
|
|
export type NavigateFn = (page: Page, options?: NavigateOptions) => void;
|
|
export type Notice = { type: "success" | "error" | "info"; text: string } | null;
|
|
export type NavItem = { page: Page; label: string; icon: typeof Home; badge?: string };
|
|
|
|
// 仅团队主账号可访问的团队级页面。入口与路由守卫共用此规则,避免权限逻辑分散。
|
|
const OWNER_ONLY_PAGES = new Set<Page>(["team", "account"]);
|
|
|
|
export function isOwnerOnlyPage(page: Page) {
|
|
return OWNER_ONLY_PAGES.has(page);
|
|
}
|
|
|
|
export const mainNav: NavItem[] = [
|
|
{ page: "dashboard", label: "工作台", icon: Home },
|
|
{ page: "products", label: "商品库", icon: Package },
|
|
{ page: "models", label: "模特库", icon: UserRound },
|
|
{ page: "projects", label: "视频创作", icon: FolderKanban },
|
|
{ page: "pipeline", label: "生产管线", icon: WandSparkles },
|
|
{ page: "library", label: "成品库", icon: Library },
|
|
{ page: "account", label: "账户", icon: Wallet }
|
|
];
|
|
|
|
export const supplementNav: NavItem[] = [
|
|
{ page: "team", label: "团队", icon: Users },
|
|
{ page: "messages", label: "消息", icon: MessageSquare, badge: "3" },
|
|
{ page: "assetFactory", label: "图片工具", icon: ImageIcon, badge: "AI" },
|
|
{ page: "settings", label: "设置", icon: Settings }
|
|
];
|
|
|
|
export const allNav = [...mainNav, ...supplementNav];
|
|
export const routeLabels: Record<Page, string> = {
|
|
dashboard: "工作台",
|
|
products: "商品库",
|
|
productDetail: "商品详情",
|
|
productCreateUpload: "上传创建商品",
|
|
models: "模特库",
|
|
projects: "视频创作",
|
|
projectWizard: "新建视频项目",
|
|
pipeline: "生产管线",
|
|
library: "成品库",
|
|
account: "账户",
|
|
team: "团队",
|
|
messages: "消息",
|
|
assetFactory: "图片工具",
|
|
freeCreate: "自由创作",
|
|
imageOptimize: "图片创作",
|
|
modelPhoto: "模特上身图",
|
|
modelPhotoDemoA: "模特图方案 A",
|
|
modelPhotoDemoB: "模特图方案 B",
|
|
platformCover: "平台套图",
|
|
settings: "设置",
|
|
settingsNotify: "通知设置",
|
|
trash: "垃圾桶"
|
|
};
|
|
export const routePages = Object.keys(routeLabels) as Page[];
|
|
|
|
export function isPage(value: string): value is Page {
|
|
return routePages.includes(value as Page);
|
|
}
|
|
|
|
export function parentPage(page: Page): Page {
|
|
if (["productDetail", "productCreateUpload"].includes(page)) return "products";
|
|
if (page === "projectWizard") return "projects";
|
|
if (["imageOptimize", "modelPhoto", "modelPhotoDemoA", "modelPhotoDemoB", "platformCover"].includes(page)) {
|
|
return "assetFactory";
|
|
}
|
|
if (page === "settingsNotify") return "settings";
|
|
return page;
|
|
}
|
|
|
|
export function resolveRoute(): ResolvedRoute {
|
|
const rawPath = window.location.pathname.replace(/\/+$/, "") || "/";
|
|
const path = rawPath.toLowerCase();
|
|
const search = new URLSearchParams(window.location.search);
|
|
const hash = window.location.hash.replace("#", "");
|
|
|
|
if (path === "/register" || hash === "register") return { page: "dashboard", authMode: "register", hash };
|
|
if (path === "/login") return { page: "dashboard", authMode: "login", hash };
|
|
// 平台超管后台:独立外壳,page 仅占位(渲染由 route.admin 接管)
|
|
if (path === "/admin") return { page: "dashboard", authMode: "login", admin: "", hash };
|
|
if (path.startsWith("/admin/")) {
|
|
return { page: "dashboard", authMode: "login", admin: path.slice("/admin/".length), hash };
|
|
}
|
|
if (path === "/" && isPage(hash)) return { page: hash, authMode: "login", hash };
|
|
if (path === "/" || path === "/dashboard") return { page: "dashboard", authMode: "login", hash };
|
|
if (path === "/products") return { page: "products", authMode: "login", hash };
|
|
if (path === "/products/new") return { page: "productCreateUpload", authMode: "login", hash };
|
|
if (path.startsWith("/products/")) {
|
|
return { page: "productDetail", authMode: "login", productId: decodeURIComponent(path.slice("/products/".length)), hash };
|
|
}
|
|
if (path === "/models") return { page: "models", authMode: "login", hash };
|
|
if (path === "/projects") return { page: "projects", authMode: "login", hash };
|
|
if (path === "/projects/new") return { page: "projectWizard", authMode: "login", hash };
|
|
if (path === "/pipeline") {
|
|
return { page: "pipeline", authMode: "login", projectId: search.get("project_id") || undefined, hash };
|
|
}
|
|
if (path.startsWith("/pipeline/")) {
|
|
return { page: "pipeline", authMode: "login", projectId: decodeURIComponent(path.slice("/pipeline/".length)), hash };
|
|
}
|
|
if (path === "/library") return { page: "library", authMode: "login", hash };
|
|
if (path === "/account") return { page: "account", authMode: "login", hash };
|
|
if (path === "/team") return { page: "team", authMode: "login", hash };
|
|
if (path === "/messages") return { page: "messages", authMode: "login", hash };
|
|
if (path === "/asset-factory") return { page: "assetFactory", authMode: "login", hash };
|
|
if (path === "/free-create") return { page: "freeCreate", authMode: "login", hash };
|
|
if (path === "/image-optimize") {
|
|
return { page: "imageOptimize", authMode: "login", productId: search.get("product_id") || undefined, hash };
|
|
}
|
|
if (path === "/model-photo") return { page: "modelPhoto", authMode: "login", hash };
|
|
if (path === "/model-photo/demo-a") return { page: "modelPhotoDemoA", authMode: "login", hash };
|
|
if (path === "/model-photo/demo-b") return { page: "modelPhotoDemoB", authMode: "login", hash };
|
|
if (path === "/platform-cover") return { page: "platformCover", authMode: "login", hash };
|
|
if (path === "/settings/notify") return { page: "settingsNotify", authMode: "login", hash };
|
|
if (path === "/settings") return { page: "settings", authMode: "login", hash };
|
|
if (path === "/trash") return { page: "trash", authMode: "login", hash };
|
|
return { page: "dashboard", authMode: "login", hash };
|
|
}
|
|
|
|
export function pathForPage(page: Page, options: NavigateOptions = {}) {
|
|
switch (page) {
|
|
case "dashboard":
|
|
return "/dashboard";
|
|
case "products":
|
|
return "/products";
|
|
case "productCreateUpload":
|
|
return "/products/new";
|
|
case "productDetail":
|
|
return options.productId ? `/products/${encodeURIComponent(options.productId)}` : "/products";
|
|
case "models":
|
|
return "/models";
|
|
case "projects":
|
|
return "/projects";
|
|
case "projectWizard":
|
|
return "/projects/new";
|
|
case "pipeline":
|
|
return options.projectId ? `/pipeline/${encodeURIComponent(options.projectId)}` : "/pipeline";
|
|
case "library":
|
|
return "/library";
|
|
case "account":
|
|
return "/account";
|
|
case "team":
|
|
return "/team";
|
|
case "messages":
|
|
return "/messages";
|
|
case "assetFactory":
|
|
return "/asset-factory";
|
|
case "freeCreate":
|
|
return "/free-create";
|
|
case "imageOptimize":
|
|
return options.productId ? `/image-optimize?product_id=${encodeURIComponent(options.productId)}` : "/image-optimize";
|
|
case "modelPhoto":
|
|
return "/model-photo";
|
|
case "modelPhotoDemoA":
|
|
return "/model-photo/demo-a";
|
|
case "modelPhotoDemoB":
|
|
return "/model-photo/demo-b";
|
|
case "platformCover":
|
|
return "/platform-cover";
|
|
case "settingsNotify":
|
|
return "/settings/notify";
|
|
case "settings":
|
|
return "/settings";
|
|
case "trash":
|
|
return "/trash";
|
|
default:
|
|
return "/dashboard";
|
|
}
|
|
}
|