简介:这是一套基于SpringBoot与Vue3构建的企业级低代码开发平台,面向需要快速搭建信息化系统的开发者与企业团队,可支撑OA协同办公、人事、CRM、办公用品、项目及合同管理等多类业务场景。资源包共2000个文件,约188.34MB,以965个Java后端源码、705个JavaScript脚本、91个HTML页面及85个CSS样式为主,另含properties配置、xml映射、md说明文档等,前后端结构完整,便于二次开发与部署。目前已有124人学习下载。其核心价值在于内置低代码能力,通过图形化配置与少量编码即可快速生成应用,显著降低开发门槛;同时提供成熟OA功能模块与清晰目录结构,读者可据此快速上手、实施与交付,适合作为企业信息化项目的参考模板或学习SpringBoot+Vue3全栈开发的实战素材。
1. 为什么我建议你先跑通这套 OA 低代码底座再谈二开
很多团队接 OA 项目时,第一反应是拿现成 SaaS 凑合用,结果流程一改就卡死,表单加个字段要等排期。这套基于 SpringBoot + Vue3 的 OA 办公系统源码,核心价值不在“功能多”,而在它自带低代码开发平台——表单、流程、权限、报表都能在界面上拖出来,改完即时生效。它适合两类人:一是想快速交付人事、合同、办公用品等内部系统的外包团队;二是想拿一套成熟底座做二次开发、省掉权限和流程引擎从零造轮子的自研同学。下面我按“能跑起来 → 能改得动 → 不踩坑”的顺序拆一遍。
2. 环境准备与前后端分离架构:从 JDK 到 Vue3 的版本对齐
这套系统是典型的前后端分离结构,后端 SpringBoot 提供 REST 接口,前端 Vue3 + Vite 负责渲染,低代码引擎的元数据存在数据库里,运行时动态解析。很多人第一次跑不起来,八成是版本没对齐——SpringBoot 2.x 和 3.x 对 JDK 要求不同,Vue3 的构建工具也分 Vite 和 Webpack 两代。先把版本锁死,后面能省掉大量玄学报错。
2.1 后端依赖清单与 JDK 选择
我一般会先看pom.xml里的spring-boot-starter-parent版本,再决定 JDK。常见做法是:SpringBoot 2.7.x 配 JDK 8 或 11,SpringBoot 3.x 必须 JDK 17 起步。数据库方面,MySQL 5.7 和 8.0 在驱动类名上有区别,8.0 要用com.mysql.cj.jdbc.Driver,连接串还得带时区参数,否则启动就报时区错误。
<!-- pom.xml 关键依赖,版本按实际源码调整 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 若源码是3.x,这里换3.x并升JDK --> </parent> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> </dependencies>这段依赖里,spring-boot-starter-parent决定整个项目的依赖管理基线,改它等于改全局。MyBatis-Plus 是低代码平台动态表单常用的 ORM,因为它的条件构造器能拼出运行时才确定的查询条件。MySQL 驱动 8.x 必须配serverTimezone=Asia/Shanghai,否则连接直接失败。
2.2 前端 Vue3 工程结构与低代码渲染入口
前端用 Vue3 的setup语法加 Vite 构建,低代码平台的核心在src/views/lowcode这类目录下,里面通常有一个动态渲染组件,根据后端返回的 JSON schema 决定渲染什么表单控件。跑前端之前先确认 Node 版本,Vite 4 要求 Node 14.18+ 或 16+,Node 18 最稳。
# 前端启动标准流程 npm install # 或 pnpm install,看源码锁文件 npm run dev # Vite 默认 5173 端口 # 若接口跨域,检查 vite.config.js 里的 proxy 配置npm install如果卡在某个包,先看.npmrc有没有配镜像源。npm run dev起来后页面空白,优先看浏览器控制台是不是接口 404——那说明后端没起或者代理没配对。低代码渲染入口一般会监听路由变化,从后端拉当前页面的 schema,所以后端不通,前端就是个空壳。
2.3 数据库初始化与低代码元数据表
低代码平台的“低代码”能力,本质是把表单字段、流程节点、权限规则都存成元数据。初始化 SQL 里通常有几张关键表:lc_form_schema(表单结构)、lc_flow_define(流程定义)、sys_permission(权限)。导入顺序不能乱,先建库再导结构,最后导初始数据。
-- 建库时字符集必须用 utf8mb4,否则中文和 emoji 会翻车 CREATE DATABASE oa_lowcode DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 导入源码自带的 sql 文件后,检查元数据表是否有初始记录 SELECT COUNT(*) FROM lc_form_schema; -- 正常应大于 0,否则低代码页面空白字符集选utf8mb4是血泪经验,utf8在 MySQL 里是残废的,存不了四字节字符。导完 SQL 后如果低代码设计器打不开,先查lc_form_schema有没有数据,没有就说明初始化脚本没跑全。权限表初始数据一般包含超级管理员角色,登录账号密码在源码文档里找,别自己瞎猜。
3. 低代码平台核心机制:表单 Schema 与流程引擎怎么落地
低代码不是魔法,它把“写代码”换成了“配 JSON”。表单设计器拖出来的每个控件,最终都变成一段 schema;流程设计器连的每条线,最终变成流程定义。理解这两层,你才能改得动、扩得开。下面拆表单渲染和流程流转两个核心链路。
3.1 表单 Schema 的生成与动态渲染
表单设计器保存时,前端把控件树序列化成 JSON 发给后端,后端存进lc_form_schema的schema_json字段。运行时页面加载,前端拿这个 JSON 递归渲染组件。常见做法是用component :is动态挂载,控件类型映射到具体 Vue 组件。
// 动态渲染核心逻辑(简化示意) const schema = await api.getFormSchema(formId); // 从后端拉 schema const renderWidget = (node) => { // node.type 如 'input'、'select'、'date' // node.props 是控件属性,如 placeholder、options return h(resolveComponent(`widget-${node.type}`), node.props); }; // 递归渲染 children,形成完整表单这段逻辑里,formId是表单唯一标识,node.type决定渲染哪个控件组件。参数node.props直接透传给组件,所以设计器里配的“必填”“默认值”都在这里生效。如果某个控件渲染不出来,先看resolveComponent的组件名有没有注册,再看 schema 里type拼写对不对——大小写敏感是常见翻车点。
3.2 流程引擎的节点定义与流转条件
流程引擎一般基于 Activiti 或 Flowable 这类 BPMN 引擎封装,低代码层把 BPMN 的 XML 藏起来,让用户在界面上连线。每条连线可以配条件表达式,比如“金额大于 5000 走总监审批”。条件表达式在运行时被解析成布尔值,决定走哪条分支。
// 流程流转条件判断(简化示意) public String evaluateCondition(String expression, Map<String, Object> variables) { // expression 如 "${amount > 5000}" ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(expression); Boolean result = exp.getValue(variables, Boolean.class); return Boolean.TRUE.equals(result) ? "approve" : "reject"; }expression是设计器里配的条件串,variables是当前流程实例的变量池,包含表单字段值。SpEL 解析时如果变量名拼错,会抛SpelEvaluationException,流程就卡住。我一般会在流程设计器里加一个“条件测试”按钮,保存前先跑一遍,避免上线后才发现表达式写错。
3.3 权限模型:菜单、按钮、数据三级控制
OA 系统的权限通常分三层:菜单权限决定你能看到哪些页面,按钮权限决定你能点哪些操作,数据权限决定你能看哪些数据。低代码平台一般把前两层做成角色绑定,第三层用部门或自定义规则过滤。
| 权限层级 | 存储位置 | 生效时机 | 常见坑 |
|---|---|---|---|
| 菜单权限 | sys_menu+sys_role_menu | 登录后加载路由 | 路由缓存导致改权限不生效 |
| 按钮权限 | sys_permission标识 | 页面渲染时指令判断 | 标识拼写不一致 |
| 数据权限 | 查询拦截器 + 规则表 | SQL 执行前拼接条件 | 规则冲突导致查不到数据 |
数据权限最容易出问题,因为它是运行时拼 SQL。常见做法是用 MyBatis-Plus 的DataPermissionInterceptor,根据当前用户角色往 where 条件里塞dept_id in (...)。如果发现用户看不到本该看到的数据,先看拦截器有没有生效,再看规则表里该角色的数据范围配的是“全部”还是“本部门”。
4. 从零跑通一个合同管理模块:配置步骤与参数说明
光看架构不够,得动手配一个真实模块。合同管理是 OA 里典型场景:有表单、有流程、有权限、有列表。下面按“建表单 → 配流程 → 挂菜单 → 测权限”四步走,每步给可抄的配置和参数。
4.1 用表单设计器建合同主表
进入低代码设计器,新建表单,拖入以下控件:合同编号(单行文本)、合同名称(单行文本)、签约方(单行文本)、金额(数字)、签约日期(日期)、合同附件(文件上传)。每个控件的“字段名”要按数据库命名规范来,比如contract_no、contract_name,别用中文拼音混搭。
// 表单 schema 片段示例 { "formId": "contract_main", "fields": [ {"type": "input", "name": "contract_no", "label": "合同编号", "required": true}, {"type": "number", "name": "amount", "label": "金额", "precision": 2}, {"type": "date", "name": "sign_date", "label": "签约日期", "format": "yyyy-MM-dd"} ] }formId是表单唯一标识,后续流程和菜单都引用它。required为 true 的字段在前端会加校验,后端也要同步加非空校验,别只靠前端。precision控制小数位,金额字段必须设,否则会出现 0.1+0.2 的浮点玄学。
4.2 流程设计器配审批链
新建流程,绑定contract_main表单。流程节点一般有:开始 → 部门经理审批 → 法务审批 → 总经理审批(金额大于 10 万时)→ 结束。条件分支配在“部门经理审批”之后,用金额判断。
流程节点配置: - 节点1:开始 - 节点2:部门经理审批(审批人:发起人所在部门经理) - 节点3:条件网关(条件:amount > 100000) - 是 → 节点4:总经理审批 - 否 → 节点5:法务审批 - 节点4:总经理审批 → 节点5 - 节点5:法务审批 → 结束审批人用“发起人所在部门经理”这种动态表达式,别写死具体人,否则人员变动就断流程。条件网关的表达式要引用表单字段名amount,和 schema 里保持一致。配完先点“测试”跑一遍,看分支走对没有。
4.3 菜单挂载与按钮权限绑定
表单和流程配好后,要在菜单管理里新增一个菜单,路由指向低代码渲染页,参数带上formId=contract_main。按钮权限方面,给“新增合同”“导出合同”分别配权限标识,比如contract:add、contract:export。
-- 菜单插入示例(表名按实际源码调整) INSERT INTO sys_menu (menu_name, path, component, perms) VALUES ('合同管理', '/lowcode/render', 'lowcode/render', 'contract:list'); -- 按钮权限 INSERT INTO sys_permission (perm_code, perm_name) VALUES ('contract:add', '新增合同');path是前端路由路径,component指向低代码渲染组件。perms是权限标识,前端用v-permission指令判断。插完数据要清缓存或重启,否则菜单不刷新——这是最常见的“改了没生效”原因。
4.4 数据权限规则配置与验证
最后给合同模块配数据权限:普通员工只能看自己创建的合同,部门经理能看本部门,总经理能看全部。在数据权限规则表里按角色配范围。
| 角色 | 数据范围 | 规则表达式 |
|---|---|---|
| 普通员工 | 仅本人 | create_by = ${userId} |
| 部门经理 | 本部门 | dept_id = ${deptId} |
| 总经理 | 全部 | 无附加条件 |
配完用不同账号登录验证:普通员工登录后列表应该只有自己那条,部门经理能看到同部门别人的。如果普通员工看到了全部,先查拦截器有没有对该模块生效,再查规则表达式里的变量有没有被正确替换。
5. 避坑与排查:低代码 OA 落地时最容易翻车的五件事
这套系统功能全,但坑也集中。下面五条是我和同行踩过的真实翻车记录,按“现象 → 原因 → 解决”写,你遇到时可以直接对号入座。
5.1 低代码设计器保存后页面不更新
现象:设计器里改了表单,保存成功,但运行时页面还是旧样式。原因:schema 有缓存,前端或后端缓存了旧 JSON。解决:先看后端有没有加 Redis 缓存,有就清对应 key;再看前端有没有用keep-alive缓存组件,有就强制刷新或加版本号参数。我一般会在 schema 保存接口里主动清缓存,别等它自己过期。
5.2 流程提交后卡在第一个节点
现象:表单填完提交,流程实例创建了,但审批人待办里没有。原因:审批人表达式解析失败,或者该节点没配审批人。解决:查流程实例的ACT_RU_TASK表,看有没有任务记录;没有就说明节点没生成任务,回去检查审批人表达式里的变量名是否和表单字段一致。常见错误是写了${deptManager}但变量池里叫dept_manager。
5.3 数据权限导致列表查不到数据
现象:管理员能看到数据,普通用户登录后列表空白。原因:数据权限拦截器拼的 SQL 条件过严,或者变量没取到。解决:打开 MyBatis 的 SQL 日志,看实际执行的 where 条件。如果条件里dept_id = null,说明当前用户没绑部门,去用户表补上部门字段。别急着关拦截器,先确认规则配对了没。
5.4 前端打包后接口 404
现象:开发环境正常,npm run build后部署,接口全 404。原因:Vite 的base路径没配,或者 Nginx 没做反向代理。解决:检查vite.config.js里base是不是/,部署到子目录要改成子目录名。Nginx 里配location /api/ { proxy_pass http://后端地址:端口/; },注意结尾斜杠,少一个就 404。
5.5 数据库迁移后中文乱码
现象:本地正常,迁到服务器后中文变问号。原因:服务器 MySQL 字符集不是utf8mb4,或者连接串没指定字符集。解决:改数据库和表的字符集为utf8mb4,连接串加characterEncoding=utf8mb4。已经乱码的数据没法自动恢复,只能重新导——所以初始化时就把字符集锁死,别等出事再补。
6. 二次开发进阶:扩展低代码控件与接口鉴权加固
跑通之后,你大概率要加自己的控件或加固接口。低代码平台的扩展点一般在控件注册表和拦截器链上。下面说两个我常用的进阶操作,附验证方法。
6.1 注册自定义控件
内置控件不够用时,可以注册自定义控件。前端建一个 Vue 组件,然后在控件注册表里加一条映射,设计器里就能拖出来。
// 注册自定义控件(简化示意) import MyDeptPicker from './widgets/MyDeptPicker.vue'; const widgetRegistry = { 'dept-picker': MyDeptPicker, // 设计器里 type 选 dept-picker }; // 设计器拖拽面板配置里加一项,label 写“部门选择器”dept-picker是控件类型标识,要和 schema 里存的type一致。组件 props 接收value和onChange,遵循 Vue3 的 v-model 约定。注册完刷新设计器,拖出来配一下默认值,保存后运行时能正常渲染就算通过。验证方法:在设计器里拖一个,保存,看 schema JSON 里有没有type: "dept-picker"。
6.2 接口鉴权加固:从 Token 到签名
默认的 Token 鉴权够用,但对外接口建议加签名。常见做法是请求头带timestamp、nonce、sign,服务端用相同算法验签,并校验时间戳偏差不超过 5 分钟。
// 签名校验拦截器(简化示意) String sign = request.getHeader("sign"); String timestamp = request.getHeader("timestamp"); // 1. 校验时间戳偏差 // 2. 用 appSecret + timestamp + nonce + body 算签名 // 3. 比对 sign,不一致返回 401appSecret存在服务端,不下发。时间戳校验防重放,nonce防重复请求。验证方法:用 Postman 手动算一次签名,能通过就说明算法对;再把时间戳改成 10 分钟前,应该被拒。这套加固不影响低代码内部调用,只加在对外网关层。
6.3 验证清单与我的习惯
每次改完低代码配置或加完控件,我会强制走一遍这个清单:表单保存后清缓存刷新、流程提交后查待办、不同角色登录看数据范围、打包部署后点一遍核心接口。这套习惯帮我挡掉过至少三次“上线才发现”的事故。从那以后我每次动低代码元数据表之前,都先备份lc_form_schema和lc_flow_define,后悔药不好买。希望帮到你。
本文还有配套的精品资源,点击获取