演示

如何创建公开页

用 AppRegistry.rootRoute.add() 注册无 ERP 外壳的空白布局页面——单页或带 React.lazy 的嵌套客户门户。不要使用 route.add()。

目标

交付打开时不带 ERP 侧栏 / 管理外壳的页面——厨房看板、二维码菜单、客户门户等非标准管理页。

  • 通过 AppRegistry.rootRoute.add() 使用空白布局
  • 路径始终以 /${metadata.name} 为前缀
  • 可选多页门户:一条 /* shell + 嵌套 Routes + React.lazy
  • 公开客户 ≠ Quan ERP 管理员——鉴权分离

1. 前置条件

需要已有可运行的 frontend register(),以及能打开公开 URL 的 base 栈。

  • plugins/<name>/ 下有 frontend/src/index.tsx
  • metadata.name 与目录名一致
  • 插件已构建/watch 并安装,register() 会执行
  • 明确是单页还是多页门户

2. 注册单个公开页

一次性屏幕在 register() 中调用 rootRoute.add(),传入命名空间 path 与 element。空白布局不要用 AppRegistry.route.add()。

  • path — `/${metadata.name}/…` 或 `/${metadata.name}/:id`
  • element — 页面组件(可用 @quan-erp/shared-ui)
  • 公开页不要 menu.add()——没有 ERP 菜单外壳
TSXfrontend/src/index.tsx
1import type { AppRegistryState, PluginModule } from "@quan-erp/shared-types"; 2import metadata from "../module.metadata.json" with { type: "json" }; 3import { FoodKitchenBoard } from "./page/kitchen/kitchen.page"; 4import { FoodPublicMenu } from "./page/public-menu/public-menu.page"; 5 6const Plugin: PluginModule = { 7 register(AppRegistry: AppRegistryState) { 8 AppRegistry.rootRoute.add({ 9 path: `/${metadata.name}/kitchen`, 10 element: <FoodKitchenBoard />, 11 }); 12 13 // Dynamic segment 14 AppRegistry.rootRoute.add({ 15 path: `/${metadata.name}/:id`, 16 element: <FoodPublicMenu />, 17 }); 18 }, 19}; 20 21export default Plugin;

3. 嵌套公开门户(多页)

登录 / 首页 / 注册等流程:注册一条 `/${metadata.name}/*` shell,并在内部嵌套 react-router-dom Routes。每个子页用 React.lazy 加载。

  • 整个门户只需一条 rootRoute
  • React.lazy + Suspense + LoadingState
  • 子路径相对 splat(login → /${metadata.name}/login)
TSfrontend/src/index.tsx — shell
AppRegistry.rootRoute.add({ path: `/${metadata.name}/*`, element: <PublicShellPage />, });
TSXfrontend/src/page/public/public-shell.page.tsx
1import { LoadingState } from "@quan-erp/shared-ui"; 2import { lazy, Suspense } from "react"; 3import { Navigate, Route, Routes } from "react-router-dom"; 4 5const LoginPage = lazy(() => 6 import("./login/login.page").then((m) => ({ default: m.LoginPage })), 7); 8const SignupPage = lazy(() => 9 import("./signup/signup.page").then((m) => ({ default: m.SignupPage })), 10); 11const HomePage = lazy(() => 12 import("./home/home.page").then((m) => ({ default: m.HomePage })), 13); 14 15const HOME_PATH = "home"; 16 17export function PublicShellPage() { 18 return ( 19 <Suspense fallback={<LoadingState />}> 20 <Routes> 21 <Route index element={<Navigate replace to={HOME_PATH} />} /> 22 <Route path="login" element={<LoginPage />} /> 23 <Route path="register" element={<SignupPage />} /> 24 <Route path="home" element={<HomePage />} /> 25 <Route path="*" element={<Navigate replace to={HOME_PATH} />} /> 26 </Routes> 27 </Suspense> 28 ); 29}

4. 公开客户 ≠ 管理员鉴权

外部客户不是 Quan ERP 用户。不要复用核心管理员登录、base axios 或 localStorage token。

  • 独立 public axios,withCredentials: true
  • 后端以 ${metadata.name} 前缀的 httpOnly cookie 管理会话
  • 以 cookie 认证的 API 成功与否做门禁(401 → 登录)
  • 可选内存 Zustand 资料 store——不要持久化 token

5. 验证

watch + 安装(或热更新)后,在浏览器打开公开 URL。

  • 单页 — 打开 /${metadata.name}/kitchen;无 ERP 侧栏
  • 门户 — 打开 /${metadata.name}/login 并导航到 home;嵌套路由正常
  • 刚改 register() 时强制刷新
  • Network:公开 API 使用插件 cookie,而非管理员会话

常见错误

  • 用了 AppRegistry.route.add() — 仍显示 ERP 外壳
  • 路径缺少 /${metadata.name} — 与其他插件冲突
  • 多页门户 shell 缺少 /* — 嵌套路由 404
  • 门户页全部 eager import — 登录包过大
  • 在公开客户页假设管理员已登录