项目:xiaoye-admin-react | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
文档集(00-20 共 21 篇)各篇分工明确、互为引用,但两件事始终缺一个"总入口":新接手者遇到一个词不知道去哪篇查;执行一个任务不知道按什么顺序读。本篇补上这两个入口——第 2 节是术语表,每个术语给出定义、源码锚点与权威展开篇目(首次定义处);第 3 节是源码文件与篇目的双向索引,改某个文件前先查它被哪些篇目覆盖;第 4 节是四条按任务的阅读路径,每条标注篇目顺序与关键检查点。术语表按"概念词 → 机制词 → 数据词"分三组,均以源码与既有篇目为准,本文不新增定义只做收拢。
菜单即路由:本框架权限体系的第一原则——菜单数据是路由注册的唯一事实源,不在菜单 = 不注册路由 = 组件代码不加载。与"全量注册路由 + 守卫校验"方案相对,是结构性隔离而非视觉遮罩。源码锚点 src/routers/helper/ConvertRouter.tsx 与 src/stores/modules/auth.ts;权威展开《08-安全与权限设计》第 3 节、《16-决策记录ADR》ADR-01。
契约三对齐:手写 API(src/api/modules/ 42 个函数)、MSW handler(src/mocks/modules/ 四域)、OpenAPI 契约(openapi/oneapi.json)三处同步维护的对齐纪律,类型单源于 src/api/interface/index.ts。改接口必须三处同步,漏改 handler 的第一信号是控制台 onUnhandledRequest: "warn" 告警。权威展开《03-接口设计》第 4-5 节、《16-决策记录ADR》ADR-04。
主题双轨制:同一份 global store 驱动两条主题通道——antd 轨(ThemeProvider 的 algorithm + 种子 token)与 CSS 变量轨(useTheme 注入 --xiaoye-* 与 html.dark)。两轨不通信但永远同步。权威展开《07-模块详解》第 5 节、《16-决策记录ADR》ADR-03。
无 Less 三件套:Tailwind v4(原子样式)+ antd 6 CSS-in-JS(组件样式)+ 原生 CSS 自定义属性(设计变量)的组合,通过 @layer theme, base, antd, components, utilities; 层序声明与 StyleProvider layer 实现无 !important 共存。不安装任何预处理器是机制性约束。权威展开《01-系统架构》4.3、《16-决策记录ADR》ADR-02。
演示模式(演示构建):pnpm build:mock 产出的 PROD 形态,靠 .env.mock 的 VITE_ENABLE_MOCK=true 在生产产物中保留 MSW,无需真实后端即可完整演示;与生产构建的唯一差异即此开关(.env.mock 是 production 的最小差分)。权威展开《00-项目总览》第 6 节、《16-决策记录ADR》ADR-06。
开发态捷径:非 PROD 构建的三项便利——预置 dev-token 跳过登录(VITE_DEV_AUTO_LOGIN 可关)、菜单短路取静态 JSON 零等待、MSW 恒开。判定口径统一为 !import.meta.env.PROD(P1-3 修复后全仓不用 DEV)。权威展开《00-项目总览》第 6 节、《16-决策记录ADR》ADR-12。
单源原则:三个"唯一事实源"——菜单数据(路由与权限)、global store(外观偏好)、{code,msg,data} 信封(接口语义),分别由 useAuthStore、useGlobalStore 与响应拦截器守护,其余模块全是派生或消费方。权威展开《01-系统架构》第 5-6 节。
ProTable(范式):框架没有独立的 <ProTable> 组件,"ProTable"指三层共同承担的列表页范式——全局分页配置(src/config/proTable.ts)、范本页面(src/views/list/useProTable/index.tsx:params + searchVersion + latestRequestId 防竞态 + 弹窗 CRUD)、同构变体(useEditTable/useDragTable/useProList)。新列表页复制该范式而非自造状态结构。权威展开《07-模块详解》第 8 节。
标签栏(Tabs):src/layouts/components/Tabs/index.tsx 实现的多标签导航,dnd-kit 拖拽排序、affix 固定标签、路由联动 addTab;标签集合同时是 KeepAlive 缓存的存活名单(关标签即释放缓存)。tabsList 不持久化——刷新后仅 affix 项回归,这是"会话内导航足迹"的产品语义。权威展开《02-页面设计》6.2。
KeepAlive:src/components/KeepAlive/index.tsx 的页面缓存方案——缓存节点常驻 DOM、以 display:none 隐藏(非卸载-恢复),缓存 key 为 pathname + search,组件状态与 effects 完整保留。明确不用 React 19 <Activity>(实测与懒加载 Suspense、rc-tabs 动画叠加有回归)。meta.isKeepAlive 控制是否缓存。权威展开《07-模块详解》第 7 节、《16-决策记录ADR》ADR-08。
刷新当前页(RefreshContext):src/context/Refresh.tsx 把"刷新当前页"建模为两个状态——outletShow 短暂置 false 制造卸载窗口、refreshNonce 自增作为缓存失效信号;MoreButton 的刷新动作最终落在 refresh() 一个函数上。权威展开《01-系统架构》2.7。
ChunkLoadError 自愈:src/components/ErrorBoundary/index.tsx 对代码分片加载失败的三段式处理——识别(错误名 + 两类消息正则)、重试(自增 retryCount 改 key 强制重挂载 lazy 组件,因 lazy 缓存已 reject 的 promise,单纯重置 hasError 无效)、恢复联网自动整页刷新。发版后旧 chunk 404 的兜底。权威展开《10-性能设计与优化》第 5 节、《18-代码走读实录》3.2。
MSW 接管自愈:src/main.tsx 的 enableMocking() 三重防护——5 秒竞速超时(渲染前 await)、onUnhandledRequest: "warn"、SW 首装 clients.claim() 时序缺口的自动整页刷新一次(sessionStorage 键 xiaoye-msw-claim-reload 防死循环)。权威展开《10-性能设计与优化》第 4 节、《18-代码走读实录》3.1。
路由守卫(RouterGuard):src/routers/helper/RouterGuard.tsx,每个路由元素外层的守卫组件:挂载 window.$navigate、按 meta.key 写 document.title、白名单放行(/500)、已登录禁回登录页、无 token 跳登录带 ?redirect=。权威展开《05-核心流程》流程一。
safeRedirect(防 Open Redirect):src/utils/safeRedirect.ts 的 getSafeRedirectUrl 三条拒绝规则——拒绝非 / 开头(外链与伪协议)、拒绝 // 开头(协议相对地址)、拒绝跨 origin 解析结果;不安全返回 null 回退 HOME_URL。配套 buildLoginUrlWithRedirect 防跳转死循环。权威展开《08-安全与权限设计》5.1。
resetAuth:src/stores/modules/auth.ts 的统一清理出口——401(双路径)与登出都执行"清 token + resetAuth",把菜单、按钮权限一并清空,防止账号切换权限串号(bug-audit P1-4 修复)。新增任何退出/切换账号入口都不得只清 token。权威展开《08-安全与权限设计》5.2。
useMessage 单例:src/hooks/useMessage.tsx 的模块级单例——App.tsx 在 antd <App> 上下文内把 message/notification/modal 实例注入模块变量,axios 拦截器等非 React 场景取用,未注入时回退静态方法兜底;全项目提示因此继承主题与语言。权威展开《16-决策记录ADR》ADR-10。
失败从紧:权限体系的总原则——任何权限获取失败都收窄而非放宽权限:菜单失败清登录态(绝不回退全量菜单,P0-2)、按钮失败按空权限渲染、空菜单是合法结果。任何"接口失败就给全量菜单"的"修复"都是权限漏洞。权威展开《08-安全与权限设计》5.2。
信封(Result):{ code, msg, data, showType? } 的统一响应结构(src/api/interface/index.ts),成功 code=200;页面取数统一 const { data } = await api(),catch 拿到的 reject 值是信封对象(取 err.msg)。showType 控制错误呈现(0 静默/1 警告/2 错误/3 通知/9 跳登录)。权威展开《03-接口设计》1.3-1.4、《19-数据契约与示例集》第 4 节。
flatMenuList / showMenuList / authButtonList:useAuthStore 的三个派生视图——flatMenuList(剥 children 的扁平表,路由注册与标签/缓存的依据)、showMenuList(过滤 meta.isHide 后的菜单树,仅驱动侧边菜单渲染)、authButtonList({ [menuKey]: string[] } 按页分组的按钮权限码表)。一次 setAuthMenuList 调用派生前两者。权威展开《04-数据模型》2.2。
meta(MetaProps):路由元信息(src/routers/interface/index.ts):key(i18n 词条与按钮权限命名空间)、icon、title、activeMenu(动态详情归属父级)、isLink(外链新窗口,不注册本地路由)、isHide(可访问但菜单不显示,不是权限控制)、isFull(脱离 Layout 全屏)、isAffix(固定标签不可关)、isKeepAlive(页面缓存开关)。经路由 loader 下发、useLoaderData 消费,或经 RouteMetaContext 读取。权威展开《02-页面设计》第 1 节。
x-access-token:自定义鉴权请求头(非标准 Authorization),值实时读 useUserStore.getState().token;配套 withCredentials: false 构成无 Cookie 会话。接真实后端必须按此头名核对。权威展开《08-安全与权限设计》第 2 节。
xiaoye-user / xiaoye-global:两个 zustand persist 的 localStorage 键——前者存 token/userInfo/tokenUpdatedAt(version 2 + 容错 migrate),后者存 23 项外观偏好。全项目共 5 个存储键,不使用 Cookie。权威展开《04-数据模型》第 4 节。
wrapperEnv:build/getEnv.ts 的环境变量类型转换器——只转布尔字面量、VITE_PORT(数字)、VITE_PROXY(JSON),其余保持字符串;因此 VITE_ENABLE_MOCK 以字符串 "true" 比较,写无引号 true 不匹配(最易踩的暗坑)。权威展开《14-配置与环境详解》2.2。
manualChunks 三分 vendor:vite.config.ts 把 react 系 / antd 系 / echarts 系拆成三个 vendor chunk(vendor-react/vendor-antd/vendor-echarts),动机是长期缓存命中率——业务迭代不失效 vendor 缓存。权威展开《10-性能设计与优化》2.2。
dev-token:开发态预置的演示凭证(src/main.tsx),写入顺序必须"先 localStorage 后 setToken"(persist 的 rehydrate 是异步的,顺序颠倒会被旧值覆盖导致权限初始化被跳过)。生产构建产物不含此逻辑。权威展开《04-数据模型》2.3、5.3。
原子切换:nas-deploy/sync.sh 的发布方式——tar 管道直传 dist-new 后以 mv 同文件系统改名完成瞬时切换,dist.old 留一代回滚;换 dist 后必须重启容器(bind-mount 指向 inode)。权威展开《06-部署运行》5.2、《14-配置与环境详解》7。
确定性 Mock 数据:src/mocks/modules/demo.ts 的数据建模原则——固定 87 条用户由索引按固定算法派生,分页与筛选行为稳定可复现;配套 nextId() 自增游标防同毫秒重复 id、auth 域内存数据源"改密即生效、刷新即重置"。权威展开《04-数据模型》第 7 节、《19-数据契约与示例集》第 2 节。
改下列文件前,先查表中列出的篇目(含该文件的设计动机、改动规约与易错点)。
| 源码路径 | 覆盖篇目(篇目内位置) |
|---|---|
src/main.tsx | 01(2.1)、04(2.3/5.3)、05(流程零)、06(2)、16(ADR-06/12)、18(三) |
src/App.tsx | 01(2.1)、16(ADR-10) |
src/api/index.ts | 01(2.4)、03(1.1-1.3)、07(3)、08(2.1)、13(D5)、18(二) |
src/api/helper/errorHandler.ts / checkStatus.ts / errorShowType.ts | 03(1.4)、09(3.2)、19(4) |
src/api/interface/index.ts | 03(2.3)、04(3.3)、15(3)、19(2/5) |
src/api/modules/*.ts | 03(2.1-2.2)、15(3) |
src/api/openapiRequest.ts | 03(3.3)、15(5) |
src/routers/index.tsx | 01(2.2/4.1)、05(流程一/二)、07(1)、18(1.4) |
src/routers/helper/ConvertRouter.tsx | 01(4.1)、07(1)、09(5 缺口三)、10(2.1)、16(ADR-01/07)、18(一) |
src/routers/helper/RouterGuard.tsx | 01(2.2)、05(流程一)、08(3)、13(D6) |
src/routers/modules/staticRouter.tsx | 02(2)、07(1) |
src/routers/interface/index.ts | 02(1)、04(6)、20(meta 词条) |
src/stores/modules/user.ts | 04(2.1/2.3)、08(2.1)、13(D5/D10)、16(ADR-05/11) |
src/stores/modules/auth.ts | 04(2.2)、05(流程二)、08(3/5.2)、16(ADR-01) |
src/stores/modules/global.ts | 04(3.1)、05(流程四)、11(场景六) |
src/stores/modules/tabs.ts | 02(5)、04(3.2)、07(7) |
src/hooks/usePermissions.ts | 01(4.2)、05(流程二)、07(2)、08(5.2)、16(ADR-09) |
src/hooks/useTheme.ts / src/utils/themeTokens.ts | 05(流程四)、07(5)、11(场景四)、16(ADR-03) |
src/hooks/useMessage.tsx | 01(2.7)、16(ADR-10)、13(D2) |
src/utils/menu.ts | 01(2.6)、04(2.2)、09(5 缺口二)、11(场景六) |
src/utils/safeRedirect.ts / navigate.ts | 01(4.5)、03(1.5)、08(5.1)、09(3.2) |
src/components/KeepAlive/index.tsx | 01(4.4)、07(7)、10(3.1)、12(3.5)、16(ADR-08) |
src/components/ErrorBoundary/index.tsx | 01(4.5)、10(5)、12(3.1/4)、18(3.2) |
src/components/Echarts/index.tsx | 07(8)、10(2.2/3.2) |
src/components/AuthButton/index.tsx / src/hooks/useAuthButton.ts | 05(流程二)、08(4) |
src/components/OfflineBanner/index.tsx | 01(4.5)、09(3.2) |
src/layouts/index.tsx | 01(2.5)、12(3.5 Watermark) |
src/layouts/components/Tabs/** | 02(6.2)、07(7) |
src/styles/global.css | 00(3.3)、01(4.3)、14()、16(ADR-02)、17(4.3) |
src/mocks/**(browser/handlers/modules) | 03(4)、04(7)、07(4)、15(4)、19(全篇) |
src/languages/** | 05(流程三)、07(6)、11(场景三) |
src/views/list/useProTable/index.tsx | 02(3.3)、05(流程五)、07(8) |
src/views/system/** | 02(3.4)、05(流程五)、13(D2)、15(3.3) |
src/views/login/** | 02(3.1)、05(流程一)、08(2.3) |
vite.config.ts / build/*.ts | 06(3-4)、10(2)、14(3-4)、17(4.1) |
.env.* 五件套 | 06(3)、13(D3)、14(2) |
eslint.config.js / tsconfig.* / .prettierrc.json | 00(3.4)、09(4.2)、14(5-6) |
package.json / pnpm-lock.yaml | 00(3)、06(1)、17(全篇) |
openapi/oneapi.json / scripts/openapi.mjs / src/services/** | 03(3)、15(5)、19(5) |
nas-deploy/** | 06(5)、14(7) |
docs/bug-audit.md | 08(5.2)、09(4.1)、13(全篇) |
顺序:《20》本节 → 《02-页面设计》第 1 节(三条组织原则)→ 《11-扩展开发指南》场景一(SOP 全步骤)→ 需要列表页时加读《02》3.3 与《07》第 8 节。关键检查点五条:视图必须落在 src/views/**/index.tsx 且默认导出;meta.key 全局唯一并补 menu.* 双语词条;接口走三处对齐(《11》场景二);验证四态(菜单出现/手输未登记路径落 404/语言切换/缓存联动);过《11》末尾的扩展总检查单。
顺序:《11-扩展开发指南》场景五(五步动作与混合 mock 策略)→ 《06-部署运行》第 8 节(六步迁移清单)→ 《08-安全与权限设计》第 2 节(token 生命周期与无刷新的扩展点)→ 《03-接口设计》1.5 与《19-数据契约与示例集》第 2/4/5 节(鉴权头、信封、错误契约口诀)。关键检查点:与后端核对三件事(x-access-token 头名、信封语义含 401 双路径、菜单/按钮权限数据结构);菜单失败即登出是从紧设计勿"修复";接生产前过一遍《13-技术债务与演进建议》第一阶段清单(token 刷新 D5 是最高优先级)。
顺序:《06-部署运行》5.4-5.5(公网接入四件套与排错清单)→ 《06》4 与《10-性能设计与优化》第 5 节(缓存链与发版自愈的联动)→ 《14-配置与环境详解》7(nas-deploy 逐文件)。关键检查点:演示站必须 --mode mock 构建而生产站必须普通 pnpm build;服务器 try_files history 回退是唯一硬性要求;index.html no-cache 与 /assets/ immutable 分级缓存逐项核对;换 dist 必须重启容器(inode);验收 curl 三连(301/200/深链 200)加五项人工用例(错误密码、退出登录、深链刷新、断网恢复、PWA 安装)。
顺序:《12-FAQ与故障排查》第 2 节决策树(先定域)→ 3.2(登录与权限类 FAQ)→ 《08-安全与权限设计》第 3/5 节(结构性隔离与失败语义的机制根源)→ 《05-核心流程》流程二(分步时序)。关键检查点:先分清开发态与生产态的权限来源差异(《00-项目总览》第 6 节,"dev 看得到生产看不到"几乎都是后端菜单未下发);登录后弹回登录页是菜单接口失败的设计行为;整页按钮消失查 /api/hooks/auth/buttons 可达性(5 秒重试窗口后按无权限处理);改权限相关代码前读 P0-2 注释——生产失败分支只能从紧不能从宽。
四条路径之外的两个提示。其一,任何任务都值得先扫一遍《01-系统架构》第 6 节的六条规约——它们是"改动即需遵守"的红线,违反任何一条的症状都出现在远离病因的位置(《12》第 1 节排查总原则)。其二,路径可以拼接:真实任务常是多路径组合(例如"加页面并接后端"= 4.1 + 4.2),此时以改动对象决定主线路径、以涉及机制补充支线路径,第 3 节的反向索引就是拼接的查表工具。文档集的分工边界重申:机制细节在功能篇(01/03/04/05/07)、操作步骤在指南篇(11/14)、决策与取舍在 16/13、样张在 19、本篇只做导航——若在本篇之外发现术语或路径缺位,按第 2/3 节的既有格式续编,索引的生命力同样来自续写。