尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

如何在 Spree React Dashboard 中注册自定义导航项、路由与页面?

发布时间:2026/9/15 20:42:16

资讯中心
01
ARTICLE

如何在 Spree React Dashboard 中注册自定义导航项、路由与页面?

如何在 Spree React Dashboard 中注册自定义导航项、路由与页面?
如何在 Spree React Dashboard 中注册自定义导航项、路由与页面【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree本文解决一个具体任务在 Spree 的 React Dashboard管理后台中增加一个自己的侧边栏导航项、它对应的路由和页面组件并让点击导航项后页面正常渲染。所有改动都发生在你自己的 dashboard 应用代码里通常是src/plugins.ts和src/pages/下的新文件不需要构建插件、也不需要发布任何 npm 包。前提条件按 quickstart 的说明需要有两样东西在运行一个 Spree API server你的 Spree 商店后端你的 dashboard 应用——渲染管理后台的小型 Vite 项目。在create-spree-app项目中它位于apps/dashboard/也可以向已有项目添加npx spree add dashboard或用npx spree add dashboard --template指向自己的模板生成。启动 dashboard 用pnpm dev。之后所有注册代码都由 dev server 热更新生效保存文件即可看到侧边栏变化。定制入口文件是src/plugins.tsstarter 已自带这个文件并已接好线starter 的 main.tsx 在渲染前import ./plugins所以注册代码会在首次渲染前执行。如果你要自建页面组件用到框架和设计系统先按 starter 注释里的提示执行pnpm add spree/dashboard-core spree/dashboard-ui。主路径注册导航项 → 建页面 → 注册路由以下示例完整走完一遍往侧边栏加一个 Analytics 项并让/analytics路径渲染出对应页面。第 1 步在src/plugins.ts注册导航项import { defineDashboardPlugin } from spree/dashboard-core import { BarChartIcon } from lucide-react defineDashboardPlugin({ nav: [{ key: analytics, label: Analytics, path: /analytics, icon: BarChartIcon, // Built-in entries use positions 100–600 (Home … Reports). // 650 places Analytics after Reports, at the end. position: 650, }], })字段说明均来自 navigation 文档path在渲染时会被加上/$storeId前缀所以这里写相对路径/analyticsposition控制排序内置项占用 100–600Home 100、Orders 200、Products 300、Customers 400、Promotions 500、Reports 600条件显示的 Getting Started 在 50中间留了空位供插入不指定时默认 100此时点击新导航项会落在 Page not found 屏幕——因为只注册了导航项还没有页面。这是正常现象继续第 2 步。第 2 步创建页面组件在src/pages/analytics.tsx新建页面组件用spree/dashboard-ui的ResourceLayout获得与内置页面一致的 header/main/sidebar 栅格import { PageHeader } from spree/dashboard-core import { ResourceLayout } from spree/dashboard-ui export function AnalyticsPage() { return ( ResourceLayout header{PageHeader titleAnalytics subtitleStore performance over time /} main{pComing soon./p} / ) }第 3 步注册路由回到src/plugins.ts把页面挂到路由注册表上import { defineDashboardPlugin } from spree/dashboard-core import { BarChartIcon } from lucide-react import { AnalyticsPage } from ./pages/analytics defineDashboardPlugin({ nav: [{ key: analytics, label: Analytics, path: /analytics, icon: BarChartIcon, position: 650, }], routes: [{ key: analytics, path: /analytics, component: AnalyticsPage, }], })自定义路由挂载在/$storeId/...之下注册的路由path是相对于/$storeId的前缀由分发器在匹配时补上且必须以/开头。所以path: /analytics匹配的是 URL/store_xyz/analytics。结果验证保存后 dev server 热更新侧边栏出现 Analytics点击它页面在/store id/analytics处渲染在 dashboard 的框架侧边栏、顶栏内。导航项和页面同时可见即完成。必须遵守的命名与排序约束所有key必须唯一包括与内置项相比。内置 key 有getting-started、home、orders、products、customers、promotions、reports、settings。注册重复 key 会在启动时抛错错误信息会点名冲突的 key。路由注册比导航更宽容分发器在每次导航时读取注册表所以即使路由在首屏渲染后才注册用户导航过去时也能生效。唯一需要注意的顺序问题是 i18n——如果你的label用了i18n.t(admin.foo.label)翻译 bundle 必须在使用它的注册调用之前注册把i18n.addResourceBundle(...)放在plugins.ts顶部。更多导航注册方式defineDashboardPlugin只是门面它最终写入spree/dashboard-core导出的nav单例完整类型见 nav-registry.ts。需要更精细的布局时可以直接用nav的方法在自有的父项下挂子项——子项各自独立声明subject父项不会自动为它们做权限控制nav.add({ key: analytics, label: Analytics, icon: BarChartIcon, position: 650, children: [ { key: analytics.sales, label: Sales, path: /analytics/sales }, { key: analytics.traffic, label: Traffic, path: /analytics/traffic }, ], })嵌套进内置菜单——用addChild把项加进你不拥有的菜单例如在内置 Products 菜单下加 Brands它保留父项已有子项而nav.update(products, { children: [...] })会替换掉内置子项nav.addChild(products, { key: products.brands, label: Brands, path: /products/brands, subject: Spree::Brand, })也可以用声明式形式defineDashboardPlugin({ nav: { addChildren: { products: [{ key: products.brands, label: Brands, path: /products/brands }] } } })。父项不存在或子 key 重复都会抛错内置父项products、orders、customers、promotions在应用引导阶段、任何插件运行之前就已注册随时可以嵌套。相对某个已有项插入nav.insertBefore(customers, {...})、nav.insertAfter(orders, {...})新项默认继承目标项的position± 1也可以自己指定。修改或删除nav.update(orders, { label: All orders })、nav.remove(legacy-thing)。声明式对象形式一次完成增删改defineDashboardPlugin({ nav: { add: [{ key: reviews, label: Reviews, path: /reviews, position: 650 }], remove: [promotions], update: { products: { label: Catalog, position: 150 } }, }, })注意remove一个不存在的 key 是 no-opupdate一个不存在的 key 会抛错。数组形式nav: [...]等价于{ add: [...] }的简写。底部固定项section: bottom把项钉在侧边栏底部Settings 默认所在区域nav.add({ key: help, label: Help, path: /help, icon: HelpCircleIcon, section: bottom, })条件显示与权限门控顶层项支持if收到当前 store/user/permissions返回false隐藏和badgelabel 后渲染一个组件nav.add({ key: onboarding, label: Onboarding, path: /onboarding, // Receives the current store/user/permissions; return false to hide. if: ({ store }) !storeFullyConfigured(store), // A component, not an element — it can call hooks and return null. badge: PendingTasksBadge, })subject字段检查permissions.can(read, subject)失败时侧边栏项被隐藏。文档明确强调这是 UX 而不是授权边界——后端仍然通过 CanCanCan 的authorize!强制校验每一次 API 调用隐藏链接只是提示不是安全边界。if与subject可以叠加两者都通过才会显示。设置页有独立的子导航settingsNav分组条目如 Store、Localization、Team Access先settingsNav.addGroup({ key, label, position })定义分组再settingsNav.add({ key, label, path, group, position, subject })加条目条目支持comingSoon: true显示 Soon 徽标。路由细节路径参数与权限回退路由注册表支持 TanStack Router 风格的$name路径参数每个$name匹配一个非空段routes: [ { key: report-detail, path: /reports/$reportId, component: ReportDetailPage }, { key: report-export, path: /reports/$reportId/export, component: ReportExportPage }, ],页面组件从分发器收到三个 propsinterface PluginRouteProps { /** Path params extracted from the URL. Keys match the $name tokens in your path. */ params: Recordstring, string /** Always set — every route is scoped to a store. */ storeId: string /** URL search-state. Pass to ResourceTable searchParams{searchParams} / so filters * round-trip through the URL. */ searchParams: Recordstring, unknown }searchParams被类型化为Recordstring, unknown因为分发器无法静态知道你的页面形态如果要把它传给ResourceTable需要转成ResourceSearchimport type { ResourceSearch } from spree/dashboard-core。给路由条目加subject可以渲染 403 回退页用户没有read权限时显示回退页而不是页面本身routes: [{ key: reports, path: /reports, component: ReportsPage, subject: Spree::Order, }],文档建议把路由subject与导航项的subject配对导航项的subject让无权限用户看不到链接路由的subject是拦截直接输入 URL 的防御层。注册表路由不在生成的路由树里所以静态Link无法类型检查需要按文档给出的方式绕过类型检查import { Link } from tanstack/react-router Link to{/$storeId/reports/$reportId as string} params{{ storeId, reportId } as never} View report /Link可选分支分发式插件用 file routes上面的注册表形式适用于应用内定制plugins.ts和运行时动态注册。如果你的目标是分发可复用插件而不是定制自己的应用routes 文档 推荐的机制是 file routes在插件package.json中声明spree: { dashboard: { plugin: true, routes: ./src/routes } }然后放入 TanStack 路由文件如brands.index.tsx→/$storeId/brands。宿主应用在每次 dev 启动和构建时按已安装的插件包重新生成routeTree.gen.tsfile route 的链接完全类型检查。更新插件后需要重启 dev server与安装插件相同。两条机制同时存在时注意优先级file routes 永远压过注册表路由——catch-all 是最低优先级匹配。如果一个你在运行时注册的页面始终不渲染检查是否有已安装插件声明了相同路径这种情况不会产生构建错误file route 会静默胜出。另外两个插件包声明同一个 file route 路径会在生成路由树之前失败构建报错会点名两个包、路径和各自文件重命名其中一条路由或移除不该拥有它的插件即可。排错速查点击导航项显示 Page not found只注册了nav没有注册routes回到第 3 步补上路由注册。启动即抛错且信息点名某个 keykey 与内置项或其他注册重复改掉重复的 key。页面始终不渲染确认路径没有与已安装插件的 file route 冲突冲突时 file route 静默胜出无构建报错确认运行时路由path以/开头且是相对于/$storeId的。链接被隐藏条目声明了subject而当前用户对该对象没有read权限——这是设计行为不是 bug。完成导航与页面注册后后续扩展可以按同一注册表机制继续在内置页面中注入插槽组件slots、扩展或自定义表格列tables、给内置表单加扩展字段formFields对应文档分别是 slots 和 tables。【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。