# wujies-admin **Repository Path**: wujies/wujies-admin ## Basic Information - **Project Name**: wujies-admin - **Description**: 无界平台admin端,面向B端用户 后台管理平台 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: feature/app-market - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-30 - **Last Updated**: 2026-11-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
无界平台管理端,多租户平台(PC)前端,也是远程应用的前端宿主:应用界面在运行期按清单加载,不重新构建宿主。
--- ## 这个仓库是什么 **wujies-admin** 是 Wujies 无界平台([wujies.cn](https://wujies.cn))的管理端,多租户平台的前端,同时承担一件平台特有的事: **在运行期加载「远程应用」的前端产物**。 平台本身是一套 Spring Boot 3 + Vue 3 的**多租户管理平台**,核心特点是 **业务能力以「应用」为单位在运行期安装、升级、卸载,不需要重新构建或重启**。 宿主在**构建期不可能知道**某个租户装了哪些应用(这正是"应用外置"的意义), 所以应用的前端必须在运行期按清单加载 —— 这个「按清单加载」就落在本仓库。 版本与身份(`package.json` 的事实来源):`name` = `wujies-admin` | `version` = `5.5.1-2.5.1` | license = MIT ### 技术栈 Vue 3 · TypeScript · Element Plus · Vite · Pinia · UnoCSS · Axios ## 快速开始 ### 环境要求 Node.js 18+(`engines`:node `>=18.18.0`、npm `>=8.9.0`)· pnpm/npm。 ### 开发 ```powershell npm install npm run dev ``` dev server 在 `80` 端口(`.env.development` 的 `VITE_APP_PORT`)。后端地址配在 `.env.development` 的 `VITE_APP_BASE_API`(`/dev-api`),`vite.config.ts` 把它代理到 `http://127.0.0.1:8086`(宿主端口), `/site` 也一并转发到宿主 —— CMS 主题直出的前台公开页要和后台**同源**才看得到登录态 (登录态存在 localStorage、按 origin 隔离)。 机密项一律不进本文件:`VITE_APP_CLIENT_ID`、`VITE_APP_RSA_PUBLIC_KEY` / `VITE_APP_RSA_PRIVATE_KEY` 与平台登录账号只写变量名 —— 值在 `.env.development` / `.env.production` 与后端 `application*.yml` 里,RSA 那一对必须**前后端一同更换**。 ### 生产构建 ```powershell npm run build:prod ``` ### 校验(改远程应用后建议都跑一遍) ```powershell npm run tools:check # 生成垫片 + 垫片对账 + 产物组件解析 + AutoImport 清单 ``` 菜单体检单独跑(需要先导出一份菜单清单,SQL 见脚本头部): ```powershell node scripts/audit-menus.mjs <导出文件> ``` ## 远程应用是怎么跑起来的 ``` ┌──────────────────────── 本仓库(wujies-admin) ────────────────────────┐ │ │ │ src/ 宿主管理端本体(系统管理 / 工作台 / 应用市场)│ │ src/plugins/appmarket/ 应用市场宿主侧接入 │ │ ├── 远程应用解析(清单 → 路由) │ │ ├── 可用性探测 │ │ └── 菜单刷新 │ │ │ │ (插件前端工程在**另一个仓库**:独立插件仓库 wujies--plugin/frontend,│
│ 旧布局 wujies-plugins/frontend/-app 仍兼容,那里只剩 hello-app 模板)│
│ │
│ app-market-dist/ │
│ ├── remote-apps// 远程前端**构建产物**(ESM) │
│ └── app-runtime/ 运行时垫片(vue.js / @dromara/* …) │
│ ↑ 由 index.html 的 import map 引用 │
└───────────────────────────────┬───────────────────────────────────────┘
│ GET /app/runtime/remote-apps(会话内问一次)
▼
wujies-platform(后端)
```
1. 会话内问一次后端 `GET /app/runtime/remote-apps`,拿到**已启用应用**的入口清单;
2. 菜单里的 `component: "remote:exam/exam/settings"` 只是**视图键**,不是真实路径;
3. 真正执行的是 `/remote-apps//index.js?v=<版本号>` 那份独立 ESM;
4. 裸模块说明符(`vue`、`@dromara/request`…)由 `index.html` 的 **import map**
指向 `/app-runtime/*.js` 垫片 —— 保证远程应用与宿主**共用同一份实例**
(否则会出现"双 Vue":响应式与组件注册全部失效)。
前端 `src/plugins/appmarket/bootstrap.ts` 的解析器:**静态清单优先**(`VITE_APP_MARKET_REMOTE_APPS`,
开发环境另有内置的 demo-app / gallery),其余交给后端清单,一次会话只问一次、失败不缓存(下次点击可重试)。
## 开发一个远程应用
不用打包、不用重启宿主的日常回路:起平台宿主,打开管理端「**开发工具 → 插件开发**」,
在应用那一行点「启动后端」(自动编译并加载 `target/classes`,同时开启监听)与「启动前端」
(常驻 `vite build --watch`,保存源码即重建,刷新页面生效,**没有 HMR** —— 插件前端是库模式构建,没有 dev server)。
全流程见文档站[「插件开发」](https://docs.wujies.cn/docs/wujies/5.x/platform/plugin),打包与上架见
[「打包应用」](https://docs.wujies.cn/docs/wujies/5.x/platform/package/build)。
改前端的最小路径:在插件仓库 `frontend/` 里 `npm install && npm run build`,刷新页面即可 ——
`vite/plugins/appMarketStatic.ts` 对 `/remote-apps//**` **优先**从插件仓库的构建产物读
(独立插件仓库 `wujies--plugin/frontend/dist`,旧布局 `wujies-plugins/frontend/-app/dist` 仍兼容),
命中即返回,否则回落到本仓 `app-market-dist`。
## 重要约定(改远程应用前必读)
### 1. 产物目录名必须等于 `appCode`
两种前端工程布局都要守这条:单体仓库 `wujies-plugins/frontend/-app`
与独立插件仓库 `wujies--plugin/frontend`(走 `@wujies/ui-plugin-bundler-kit` 的 `defineRemoteApp({ appCode })`)。
它们的 `outDir`、打包器 `package.ps1` 的
`-FrontendDist` 默认值、后端落位目录、后端拼出的 URL —— **四处是同一个约定**。
不一致是个**静默**故障:库里那条菜单永远解析不出来,控制台只报
`[permission] 菜单「xxx」的 component 解析不出来,已跳过该路由`。
### 2. 入口 URL 必须带 `?v=<版本号>`
不带的话升级后浏览器吃旧缓存,表现是**"升级成功了、页面还是老样子"**。
### 3. AutoImport 清单必须与宿主逐项一致
远程产物里混着**宿主源码**(单体仓库按别名 `@/store/**`、`@/utils/**` 原样打进产物;独立插件仓库是把这份源码复制进
`frontend/src/`,内容同源)。
宿主开了 `unplugin-auto-import`(`vite/plugins/auto-import.ts`),所以宿主源码里 `ref(...)`、`useStorage(...)`
这些**没有 import 语句**。插件工程少列一个 preset,它们在产物里就是**裸全局** ——
**构建期不报错**(rollup 当它是全局变量),一装载就 `ReferenceError`。
用 `npm run tools:check` 守门;其中 AutoImport 那一项也是"验尸"工具,
`node scripts/check-remote-globals.mjs --dir=<解包后的 frontend 目录>` 可直接复查已发布的历史包。
垫片侧同一类问题:**垫片少一个具名导出,浏览器在链接期整块报错、页面完全打不开**
(`import { globalHeaders } from '@dromara/request'` 就踩过)。
`@dromara/*` 是虚拟说明符、只能静态声明,所以加一个具名导出要**同时改两处**:
`scripts/gen-runtime-shims.mjs` 的 `staticNames` + `src/plugins/appmarket/runtime.ts` 里对应 slot 挂上去的名字;
改完跑 `npm run tools:check`(`scripts/check-runtime-shims.mjs` 会把已构建产物的 bare import 与垫片导出做差集)。
### 4. 垫片文件名是硬契约,**不要跟着改品牌名**
`app-market-dist/app-runtime/` 里的 `dromara-request.js` / `dromara-components.js` /
`dromara-modal.js`:名字来自**第三方 npm 作用域**(`@dromara/*`),不是本项目品牌名。
`index.html` 的 import map、`scripts/gen-runtime-shims.mjs`(生成方)、
`scripts/check-runtime-shims.mjs`(校验方)**三处必须一致**。
> 本项目改名时曾因 PowerShell `-replace` **默认大小写不敏感**,
> 把 `dromara-request.js` 误改成 `Wujies-request.js`,导致远程应用全部加载失败。
### 5. 别在远程应用里写"薄垫片"绕 SDK
已知踩过:某个应用自己复刻了一份 `src/utils/request.ts`,里面
`import { getToken } from '@/utils/auth'` —— 那正是把宿主源码拉进产物的路径。
正确做法:`@/utils/request` 直接指向 `@dromara/request`(已具名导出 `globalHeaders` / `download` / `isRelogin`),
并且用**正则精确匹配**(字符串 alias 是前缀匹配,会把 `@/utils/requestCache` 一起吃掉)。
### 另外两类「构建期全绿、浏览器里才炸」
- **模板里用到的宿主组件必须解析得到**,否则 Vue 当它是未知自定义元素:不报错、不白屏,**只剩插槽里的文字**。
三条路:该文件显式 `import`;名字在构建套件白名单里(`defineRemoteApp` 的 `sharedComponentResolver()`
读 `@wujies/components/names`);或由宿主 `src/plugins/components.ts` 全局注册。`npm run tools:check` 逐条判。
- **产物里不许残留 `process.env`**:浏览器没有 `process`,依赖在模块顶层读它 → 一装载就
`ReferenceError: process is not defined`。`define: { 'process.env.NODE_ENV': … }` 已由
`@wujies/ui-plugin-bundler-kit` 内置,`scripts/check-remote-apps.mjs` 仍会扫产物兜住回归。
⚠️ 改完构建配置/白名单**必须重新构建** —— 产物不进版本库(`app-market-dist/` 已 gitignore),
改了 config 不等于部署里那份 ESM 变了。
## 应用生态
| 仓库 | 内容 |
|---|---|
| [`wujies-platform`](https://gitee.com/wujies/wujies-platform) | 后端 + 应用市场 + 插件 SDK 契约 |
| [`wujies-plugins`](https://gitee.com/wujies/wujies-plugins) | 通用打包器 + 文档 + 本地开发沙箱 + 旧脚手架 |
| **wujies-admin**(本仓库) | 管理端前端 + **远程应用前端工程** |
| [`wujies-ui`](https://gitee.com/wujies/wujies-ui) | 前端 SDK 四件套源码仓(`@wujies/ui-shared` / `components` / `api-client` / `ui-plugin-bundler-kit`) |
| [`wujies-shop`](https://gitee.com/wujies/wujies-shop) | C 端(用户端)Web 前端 |
| [`wujies-mobile`](https://gitee.com/wujies/wujies-mobile) | 移动端(uni-app) |
| [`wujies-market`](https://gitee.com/wujies/wujies-market) | 中心上架服务(目录与上架控制台) |
## 相关文档
| 文档 | 位置 |
|---|---|
| 平台总览 | [`wujies-platform`](https://gitee.com/wujies/wujies-platform) README |
| 插件开发全流程 | [`wujies-plugins`](https://gitee.com/wujies/wujies-plugins) `docs/插件开发指南.md` |
| 插件与宿主能力边界 | [`wujies-plugins`](https://gitee.com/wujies/wujies-plugins) `docs/解耦路线图.md` |
| 平台开发须知(约束与坑) | [`wujies-platform`](https://gitee.com/wujies/wujies-platform) `AGENTS.md` |
## 许可
[MIT](LICENSE)(`The MIT License (MIT)`)。
本项目是 RuoYi-Vue-Plus 5.X 的衍生作品,遵循 MIT 协议并保留原作者版权声明。
`LICENSE` 里的版权行挂的是上游(`Copyright (c) 2019 RuoYi-Vue-Plus`),
本仓的本地改动未在其后追加独立版权行 —— 二次分发的版权声明以上游那一份为准。