一、项目定位
Elpis 是一个企业级全栈低代码后台管理系统,采用 Koa 2 + Vue 3 技术栈,核心理念是配置驱动 UI:通过编写 JSON 配置(Schema)自动生成完整的后台管理页面(菜单、表格、搜索栏、操作按钮),而不需要为每个业务模块重复编写前端页面。
二、解决的痛点
- 重复开发成本高
传统后台系统里,每个业务模块(商品管理、订单管理、客户管理...)都要写一套几乎相同的代码:表格 + 搜索栏 + CRUD 按钮 + 分页 + 接口对接。Elpis 把这些全部抽象成配置:
一个新业务模块 = 一段 JSON 配置 + 一个 RESTful API
- 多项目配置难以复用
同一套电商系统可能有"拼多多版"、"淘宝版"、"京东版",它们 80% 的菜单和字段相同,只有少量差异。Elpis 用模型继承机制解决这个问题:
基础模型 (model/buiness/model.js) ← 定义通用菜单、字段
├── 拼多多 (project/pdd.js) ← 继承 + 覆盖/新增
├── 淘宝 (project/taobao.js) ← 继承 + 覆盖/新增
└── 京东 (project/jd.js) ← 继承 + 覆盖/新增
项目配置会按 key 字段智能合并:相同 key 的菜单项递归覆盖,新增 key 的菜单项追加,未覆盖的保留继承。
- 前后端割裂
Elpis 自研了一个轻量级后端框架 elpis-core(类 Egg.js),约定了目录结构,自动加载 controller / service / middleware / router,前后端在同一个仓库里协作,降低了对接成本。
三、架构设计
整体分层
浏览器
↓
Koa Server (BFF 层)
├── /view/:page → Nunjucks 渲染 → 返回 SPA 入口 HTML
├── /api/project/* → ProjectController → ProjectService → model 配置
└── /api/product/* → BusinessController → 业务数据
↓
Vue 3 SPA (前端)
├── boot.js → 统一初始化 (Vue + ElementPlus + Pinia + Router)
├── pages/ → 按页面划分的入口 (entry.*.js)
├── widgets/ → 可复用的 Schema 驱动组件
└── store/ → Pinia 状态管理
后端:自研微框架 elpis-core
不依赖 Egg.js 等重型框架,而是在 Koa 之上用目录约定 + Glob 自动加载实现了一个轻量级框架:
| 目录 | 加载到 | 作用 |
|---|---|---|
app/controller/*.js | app.controller.* | 处理请求 |
app/service/*.js | app.service.* | 业务逻辑 |
app/middleware/*.js | app.middlewares.* | 中间件(签名校验、参数校验、项目上下文) |
app/router/*.js | Koa Router | 路由注册 |
app/router-schema/*.js | app.routerSchema | AJV 参数校验规则 |
config/*.js | app.config | 环境配置(local/beta/prod) |
前端:Schema 驱动 UI
这是 Elpis 最核心的设计。一个完整的业务模块只需要一段配置:
{
key: 'product',
moduleType: 'schema',
schemaConfig: {
api: '/api/product', // 数据源
schema: { // 字段定义
properties: {
product_name: {
label: '商品名称',
tableOption: { width: 200 }, // → 自动生成表格列
searchOption: { comType: 'input' } // → 自动生成搜索输入框
}
}
},
tableConfig: { // 按钮定义
headerButtons: [...], // → 自动生成头部操作按钮
rowButtons: [...] // → 自动生成行内操作按钮
}
}
}
数据流:
JSON 配置 → useSchema() Hook 解析
├── buildDtoSchema(schema, 'table') → schema-table 组件 → 动态列渲染
├── buildDtoSchema(schema, 'search') → schema-search-bar → 动态表单渲染
└── tableConfig → table-panel → 动态按钮渲染
模型继承系统
model/index.js 实现了一套配置继承与合并机制:
model/buiness/model.js ← 基础模型(通用菜单、通用字段)
model/buiness/project/pdd.js ← 项目配置(覆盖/新增)
↓
lodash.mergeWith
↓
最终配置(按 key 字段智能合并数组)
这意味着:
- 基础模型定义了"商品管理"有 6 个字段
- pdd 项目可以只写"我要覆盖商品名称的列宽",其他字段自动继承
- pdd 项目也可以新增基础模型没有的菜单项
构建系统
Webpack 5 多入口构建,每个 entry.*.js 生成一个独立的 SPA 页面:
| 入口 | 页面 |
|---|---|
entry.project-list.js | 项目列表页(无路由) |
entry.dashboard.js | 业务仪表盘(带子路由:schema / sider / iframe / todo) |
开发时双服务器:Koa(9090)负责 API + 模板渲染,Express(9003)负责 Webpack HMR 热更新。
四、关键设计理念
-
约定优于配置 目录结构即规范,放对位置就自动加载,不需要手动注册。
-
配置驱动视图 一份 Schema 同时描述表格列、搜索表单、操作按钮,
buildDtoSchema按xxxOption后缀分别提取,同一份数据源、不同维度的消费。 -
组件即插件 搜索栏的输入类型通过
schema-item-config.js注册,新增一种输入组件只需要:写组件 → 注册 key → 配置里用对应comType,搜索栏框架代码零改动。 -
继承优于复制 项目配置继承基础模型,只写差异部分,避免多项目间大量重复配置。
-
前后端同构约定 前端
$curl自动带签名和项目标识,后端中间件自动校验,controller 统一响应格式(success/fail),减少前后端对接的心智负担。