移动端总览
适用端:移动端 | 类型:机制
移动端(ainb-ui-admin-uniapp)不是「另一个后台」,而是同一套后端、同一套权限体系与租户的第二个运行时宿主:工作台 / 审批 / 消息 / 我的 + 在线表单的移动端运行时。它的工作台菜单由服务端 system_nav_node 下发,不在前端写死。
本页讲清楚三件事:这个仓库是什么混合体(unibest 骨架 + 芋道业务页)、哪些分包真的交付了哪些是编译进包但点不到的尸体、以及它与 Web 管理端能力差在哪。后面 8 页是逐层展开。
前置阅读:后端架构与模块依赖(三仓共用同一套 /admin-api)、仓库结构与改名约定(yudao → enlev 的改名规律在三仓一致)。
它解决什么问题
三端共用同一套接口、同一套权限判定、同一套租户。移动端与 Web 端唯一的结构性差别是菜单来源不同:Web 端的路由由 system_menu 动态生成(见路由与权限机制),移动端的工作台由 system_nav_node 下发(见服务端驱动导航)。
移动端不走 Vite proxy
env/.env:33 的 VITE_APP_PROXY_ENABLE = false 让 vite.config.ts:231-240 产出 proxy: undefined,请求拦截器于是直接拼绝对地址(src/http/interceptor.ts:33-46)。Web 端同样直连(它的 vite.config.ts 里 server.proxy 整段是注释掉的)。所以「改后端接口,前端刷新一下就好」在这里成立;反过来,跨域问题也不能指望 dev proxy 兜。
代码在哪
仓库根就是工程根,没有 monorepo。src/ 下共 30 个目录,其中 17 个是页面目录,按能不能被用户点到分成三类(目录清单与逐项职责见工程结构与约定式路由):
| 类别 | 目录 | .vue 文件数 | 说明 |
|---|---|---|---|
| 主包 | src/pages/ | 28 | 4 个 tabbar 页:工作台 index、审批 bpm、消息 message、我的 user,外加 contact |
| 已交付分包 | src/pages-core/ | 24 | 登录 / 注册 / 验证码登录 / 404 / 个人中心 / 设置 / 联系客服 / FAQ |
src/pages-system/ | 120 | 通知、操作日志、登录日志等;只有 3 个进了导航树 | |
src/pages-infra/ | 62 | API 访问/错误日志 + 在线表单运行时 form-design/run/ | |
src/pages-bpm/ | 69 | 审批中心 + 自研 form-create 渲染器 components/form-create/ | |
src/pages-ai/ | 86 | AI 对话 / 智能体 | |
| 🔴 编译进包但不可达 | src/pages-{mes,mall,im,erp,crm,iot,wms,member,pay,mp,statistics}/ | 1,082 | 后端没有对应模块,导航树不建节点 |
支撑目录:src/http/(请求层)、src/store/(6 个 Pinia store)、src/router/(登录拦截)、src/components/(只有 dict-tag/、system-select/、yudao-ui/ 三个)、src/hooks/(5 个)、src/utils/、src/style/、src/tabbar/(自定义 tabbar)、src/api/(按后端模块分目录)、src/layouts/(只有一个空壳 default.vue)。
根目录的工程文件:vite.config.ts、uno.config.ts、pages.config.ts、manifest.config.ts、env/.env*、scripts/、vite-plugins/。
那 1,082 个文件不是「以后会用」,是正在拖累产物
后端 reactor 里只有 5 个业务模块(ainb/pom.xml:16-20:system / infra / bpm / ai / agent)。上游的支付 / 商城 / CRM / ERP / 会员 / IoT / 公众号 / IM / MES / WMS 模块已被整体删除,所以这 11 个分包的页面发不出任何有效请求。
但它们照样被编译进产物——vite.config.ts:104-121 的 subPackages 把 16 个目录全列上了。实测 H5 生产包 dist/build/h5 共 13.1 MB,其中 569 个 chunk / 6.3 MB 属于这 11 个不可达分包,接近一半。最大的单个是 pages-im-home-conversation-rtc-call-index.js 512 KB(IM 实时音视频页,连带把 livekit-client 打进产物——这也是它出现在 THIRD-PARTY-NOTICES.md 里的原因)。
复现:
cd ainb-ui-admin-uniapp && pnpm build:h5
cd dist/build/h5/assets && du -sk . | awk '{printf "%.1f MB total\n",$1/1024}'
find . -name '*.js' -exec du -k {} + \
| grep -E 'pages-(crm|erp|im|iot|mall|member|mes|mp|pay|wms|statistics)-' \
| awk '{s+=$1;n++} END {printf "%d chunks / %.1f MB unreachable\n", n, s/1024}'想瘦身就从 subPackages 里摘掉这 11 个目录(连带 src/api/ 下同名目录),但这是不可逆的删除,且会让将来真要接某个模块时得从上游重新捞——所以本项目选择留着,代价记在这里。
核心机制
1. 它是一个混合体,两套上游的缝在工程文件里
| 来自谁 | 具体是什么 | 怎么认出来 |
|---|---|---|
| unibest 4.1.0(工程骨架) | vite.config.ts 的 18 个插件链、pages.config.ts / manifest.config.ts 约定式生成、src/layouts/、src/tabbar/ 自定义 tabbar、src/router/interceptor.ts(文件头自己写着「unibest 官方维护的代码」)、docs/ 整站 | package.json:5-7 的 unibest-version / unibest-update-time;docs/ 里还有上游的打赏与赞助页 |
| yudao-ui-admin-uniapp(业务页底座) | src/pages-*/ 全部业务页、src/api/、src/components/yudao-ui/、src/store/{token,user,dict}.ts、src/http/ 的双 token | package.json:6 的 yudao-version;src/components/yudao-ui/ 目录名刻意未改(见下) |
| 本项目自研 ★ | 服务端驱动导航 system_nav_node、src/pages-bpm/components/form-create/(form-create 规则模型的独立 uni-app 实现)、src/style/tokens.scss 品牌令牌 + pnpm check:color 守卫、src/pages-infra/form-design/run/ 的 rule-adapter.ts | 见各专页 |
src/components/yudao-ui/ 为什么没改名
它被两处机制引用:easycom 规则 '^yd-(.*)': '@/components/yudao-ui/yd-$1/yd-$1.vue'(pages.config.ts:16)与 YudaoUiResolver 自动导入(vite.config.ts:33-47)。改名要同时动这两处 + 56 个文件的 import + 重新生成类型声明,收益为零。目录名不代表归属变更。
2. 工作台菜单是服务端下发的
登录成功后 src/store/nav.ts:31-48 拉一次 GET /system/nav-node/tree?terminal=app,持久化到本地,src/pages/index/index.ts:128-134 把它映射成工作台的三层结构(分组 → 子分组 → 导航项)。前端没有任何菜单白名单——服务端已经按当前用户的权限过滤过了(src/api/system/navNode/index.ts:6-8 的注释明说这一点)。
这条链路是本仓最重要的自研能力,单独一页讲:服务端驱动导航。
3. 与 Web 端的能力差
| 能力 | Web 管理端 | 移动端 | 差在哪 |
|---|---|---|---|
| 菜单来源 | system_menu → 动态路由 | system_nav_node → 工作台卡片 | 两套表、两个维护界面 |
| 低代码表单设计 | ✅ 设计器(vendored src/lib/form-designer) | ❌ 没有设计器 | 移动端只跑运行时 |
| 低代码表单运行时 | ✅ src/lib/form-create(Element Plus) | ✅ src/pages-bpm/components/form-create/(wot-design-uni) | 两套独立实现,控件覆盖有缺口,见在线表单在移动端 |
| 权限判定 | 后端 GrantDecisionEngine | 同一套 | 无差异 |
| 行级数据过滤 | 后端 FormRowFilter | 同一套 | 无差异(服务端做的) |
| 字段级权限脱敏 | ✅ | ⚠️ 渲染器有 API、在线表单页没调用 | 见在线表单在移动端 |
| 公式 / 脚本 / 联动规则 | ✅ | ❌ 声明了但零消费者 | 同上 |
门户 infra_portal | ✅ | ❌ | 移动端首页是工作台,不是门户 |
| Agent 对话 SSE | ✅ fetchEventSource | ✅ src/http/sse.ts | 两套实现 |
| 单元测试 | 59 个 vitest spec | 0 个 | 见「测试」节 |
数据模型
移动端自己不拥有任何表,它读的是后端已有的表。与它强相关的两张:
| 表 | 关系 | 详见 |
|---|---|---|
system_nav_node | 移动端专属:terminal 列现阶段只有 app 一个取值,Web 端不写这张表 | 服务端驱动导航 |
infra_form_design.app_nav_node_id | 「这张表单发布到 App 端了吗」的唯一判据,NULL = 未发布 | 在线表单在移动端 |
本机实测(ainb-professional-init,2026-10-25):system_nav_node 共 97 行,其中 20 行存活(deleted=0)、77 行是软删墓碑;infra_form_design 41 行中 3 行绑定了 app_nav_node_id。
接口清单
移动端调用的后端接口与 Web 端完全同源,没有一套「移动端专用 API」。差异只在鉴权头的拼装(见请求封装与登录态)。少数几个对移动端语义特别重的:
| 端点 | 用途 |
|---|---|
GET /system/nav-node/tree?terminal=app | 工作台导航树(服务端已按权限过滤) |
POST /system/auth/login · POST /system/auth/refresh-token | 双 token 登录与刷新 |
GET /system/auth/get-permission-info | 权限码集合,喂给 useAccess |
GET /system/tenant/simple-list · /get-by-website | 登录页租户选择器 |
GET /infra/form-design/runtime/{code}/config | 在线表单运行时配置 |
POST /infra/file/upload | 上传(uni.uploadFile,走同一个拦截器) |
怎么扩展
加一个移动端页面的完整步骤在移动端页面开发范式——注意其中没有「注册路由」这一步:UniPages 插件自动扫描,definePage() 宏声明页面配置,而「让用户点得到」是去后台 system_nav_node 加一条记录,不是改前端文件。
其余入口:
测试
🔴 本仓没有任何单元测试——全仓 .spec.ts / .test.ts 命中数为 0,package.json 里也没有 test 脚本。可用的三道检查是:
| 命令 | 查什么 |
|---|---|
pnpm type-check | vue-tsc --noEmit(新克隆必须先跑一次 pnpm dev,见坑位) |
pnpm lint | ESLint |
pnpm check:color | 品牌色硬编码守卫,#3370ff 出现即 exit 1 |
不要假称这里有测试体系。作为对照,Web 仓有 59 个 vitest spec、后端 agent 模块有 2,225 个 @Test。移动端唯一的回归网是手工点 + 巡检脚本(C:\code\xpy\_evidence\,不入库)。
控件契约没有任何东西守着
src/pages-infra/form-design/run/components/rule-adapter.ts:63-72 的 UNSUPPORTED_TYPES 是手工维护的清单,没有任何测试对账。它可以静默漂移——详见在线表单在移动端。
坑与注意
- 只能 pnpm:
preinstall挂了npx only-allow pnpm(package.json:25),用 npm / yarn 直接报错。Node ≥ 20。 - 不能跳过 install 直接
pnpm dev:src/pages.json与src/manifest.json都被 gitignore,靠scripts/create-base-files.js生成。 src/types/uni-pages.d.ts也不入库,而src/utils/index.ts:3直接import { pages, subPackages } from '@/pages.json'—— 新克隆跑type-check必然报找不到模块。- 那 11 个分包不要「顺手修好」:它们不可达是因为后端模块被删了,不是前端写错了。
src/uni_modules/是空的,只有一个.gitkeep;别以为漏拉了子模块。
→ 本节是贴着上下文的提醒;按症状查的移动端全域清单见移动端坑位,跨端清单见坑位索引。
未证实
- 小程序 / App 端从未在本机实测过。本章所有结论基于 H5(
:9000)+ 后端(:48080)实跑与源码静态阅读。build:mp-weixin与build:app的产物结构、miniprogram-ci上传、原生插件资源复制都只有源码依据。 dist/build/h5的 13.1 MB 是某一次构建的快照,不是稳定值;它会随依赖版本变。要引用请按上面的命令重测。- 那 11 个不可达分包里,是否有页面能被直接输 URL 打开(H5 下路由是注册的,只是导航树不给入口)没有逐页验证过。已知的巡检假阳性说明「能打开」和「能用」是两回事,见移动端坑位。
AI 大模型分组(system_nav_node.id=291)在库里存活但零个存活子节点,于是被/tree的空组剪枝规则整组剪掉。它是「本来就该没有」还是「子节点被误删」,源码里没有答案。
相关页面
- 移动端环境准备与启动:装什么、怎么起、怎么验证起来了;
- 工程结构与约定式路由:
src/逐目录职责、pages.json是产物不是源码、easycom 规则; - 请求封装与登录态:拦截器七步、双 token、401 静默刷新与重放队列;
- 移动端页面开发范式:z-paging 列表页骨架、
yd-*组件、UnoCSS 与品牌令牌; - 服务端驱动导航:★
system_nav_node三仓全链路; - 在线表单在移动端:★ 自研渲染器与控件覆盖契约;
- 多端构建与发布:H5 / 小程序 / App 三端产物与发布;
- 移动端坑位:按症状查的移动端专属坑;
- 仓库结构与改名约定:三仓共用的
yudao→enlev判断法。
