一、快速开始
Maven 依赖
根据您的 Java 版本选择对应的依赖:
<!-- 最新版本:3.1.2,适用于 Java 17 / Spring Boot 3 -->
<dependency>
<groupId>io.github.ezadmin126</groupId>
<artifactId>ezadmin-java17-spring-starter</artifactId>
<version>3.1.2</version>
</dependency>
<!-- Java 8 -->
<dependency>
<groupId>io.github.ezadmin126</groupId>
<artifactId>ezadmin-java8-spring-starter</artifactId>
<version>3.1.2</version>
</dependency>
启用配置
在 Spring Boot 主类上添加 @EnableEzadmin 注解:
@SpringBootApplication
@EnableEzadmin
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
当前文档按 3.1.2 版本整理,重点覆盖 src/main/resources/topezadmin/config/layui/dsl 下的 DSL 资源组织和配置写法。
启动应用后,JSON / QL / HTML 配置文件会自动生效。访问 http://localhost:{port}/topezadmin/edit/create- 可直接用 AI 生成列表和表单。
组件速查、AI 提示词和常用配置片段可访问 /doc/reference.html。
application.yml 配置
在 application.yml 中配置 EzAdmin 相关参数:
topezadmin:
# 缓存开关,开发时建议设为 false
cacheFlag: false
# 数据源配置(多个用逗号分隔)
datasourceBeanNames: 'dataSource'
# 文件上传配置
uploadPath: /data/upload
downloadUrl: /core/downloadDesc.html?fileId=
uploadUrl: /system/upload.html
# 日志配置
logType: 1000-10000
# 界面配置
adminStyle: layui
layout: fluid
# 系统路由配置
clearUrl: /topezadmin/clearCache.html
signoutUrl: /login/signout.html
indexUrl: /doc/index.html
navUrl: /laynavs.html
# AI 功能配置(可选)
apiUrl: https://api.deepseek.com/v1/chat/completions
apiKey: sk-xxxxxxxxxxxxxxxx
model: deepseek-chat
temperature: 0.8
| 配置项 | 说明 | 默认值 |
|---|---|---|
cacheFlag |
是否开启配置文件缓存 | true(生产环境)/ false(开发环境) |
datasourceBeanNames |
数据源 Bean 名称,多个用逗号分隔 | dataSource |
uploadPath |
文件上传存储路径 | - |
logType |
日志级别配置 | 1000-10000 |
adminStyle |
管理界面风格 | layui |
layout |
页面布局模式 | fluid |
apiUrl |
AI 服务接口地址 | - |
apiKey |
AI 服务 API 密钥 | - |
model |
AI 模型名称 | deepseek-chat |
temperature |
AI 生成随机性参数(0-1) | 0.8 |
二、DSL 总览:可以从哪几个方面理解
对于 src/main/resources/topezadmin/config/layui/dsl 这套配置,建议不要按“字段一个个背”的方式学习,而是按页面搭建的完整链路来理解。用户真正关心的是:页面放哪里、长什么样、查什么数据、怎么交互、怎么扩展。
| 维度 | 要解决的问题 | 核心配置 |
|---|---|---|
| 1. 目录与资源组织 | 配置文件放哪,页面类型怎么区分 | list/、form/、data/、同名 *.ql / *.html |
| 2. 页面骨架 | 一个列表或表单最少要写哪些字段 | id、name、dataSource、body、cardList |
| 3. 字段与组件 | 搜索项、列、输入框、上传、选择器如何渲染 | search、column、fieldList、component、props |
| 4. 数据来源 | 下拉、字典、级联、远程选项从哪里来 | initData.dataJson、dataSql、dataUrl、@import(...json) |
| 5. 查询与提交逻辑 | 列表怎么查,表单怎么初始化、保存、删除 | express.main、initExpress、submitExpress、deleteExpress |
| 6. 页面交互 | 工具栏、行按钮、弹窗、刷新、删除如何串起来 | tableButton、rowButton、buttonList、opentype |
| 7. 布局与扩展 | 树形、iframe、顶部注入、自定义脚本怎么加 | tree、iframe、appendHead、appendFoot、tabList |
| 8. 路由与命名约定 | 页面 URL 如何和配置文件名对应 | /topezadmin/list/page-模块/页面、/topezadmin/form/page-模块/页面 |
先看 list/*.json 或 form/*.json 了解页面结构,再看同名 *_express_main.ql 或 *_submitExpress.ql 理解数据逻辑,最后看 appendFoot.html、appendHead.html 处理增强脚本。
三、配置格式
JSON 配置文件存放路径:
- 列表配置:
topezadmin/config/layui/dsl/list/业务模块/*.json - 表单配置:
topezadmin/config/layui/dsl/form/业务模块/*.json - 静态字典:
topezadmin/config/layui/dsl/data/*.json - 列表查询表达式:通常与列表同目录,例如
warehouse_express_main.ql - 表单生命周期表达式:例如
*_initExpress.ql、*_submitExpress.ql、*_deleteExpress.ql - 页面扩展片段:例如
*_appendHead.html、*_appendFoot.html
1. 目录职责
| 目录 | 职责 | 典型文件 |
|---|---|---|
list |
定义查询页、表格列、搜索栏、列表按钮、树结构 | warehouse/warehouse.json |
form |
定义新增/编辑/详情页、卡片分区、表单提交行为 | purchase/purchase-form.json |
data |
沉淀公共静态选项,避免重复写枚举 | checkstatus.json |
2. 列表页面要点
列表页通常由五部分组成:页面元信息、搜索区、表格列、按钮区、查询表达式。你可以把它理解成“前端布局 + 后端查询”的一个组合配置。
列表配置结构
{
"id": "列表ID",
"name": "列表名称",
"dataSource": "数据源Bean名称",
"initApi": "自定义接口(可选)",
"hideSearch": false,
"body": {
"emptyShow": "-",
"showIndex": true,
"selectable": true,
"rowActionWidth": 175
},
"tabList": [],
"search": [{"row": []},{"row": []}],
"column": [],
"tableButton": [],
"rowButton": [],
"tree": {},
"express": {
"main": "查询SQL表达式",
"orderBy": "order by xxx",
"groupBy": "group by xxx",
"count": "自定义count语句"
}
}
| 字段 | 说明 | 常见写法 |
|---|---|---|
id |
当前页面唯一标识,便于缓存、事件、脚本定位 | warehouse |
name |
页面显示名称 | 仓库管理 |
dataSource |
使用哪个 Spring 数据源 Bean | dataSource |
body |
控制表格显示细节 | showIndex、selectable、rowActionWidth |
search |
搜索栏配置,按行组织 | [{ "row": [...] }] |
column |
表格列定义 | tdText、tdSelect、tdCascader |
tableButton |
表头工具栏按钮 | 新增、刷新、导出 |
rowButton |
每一行的操作按钮 | 编辑、删除、详情、子列表 |
express.main |
核心查询表达式,一般返回 search(sql) |
@import(xxx_express_main.ql) |
appendHead / appendFoot |
页面头尾扩展 HTML / JS | @import(review-list_appendFoot.html) |
3. 表单页面要点
表单页的重点是“分区 + 字段 + 生命周期”。从现有配置看,EzAdmin 的表单不只用于录入,也常用于详情页、流程页、带 iframe 的复合业务页。
表单配置结构
{
"id": "表单ID",
"name": "表单名称",
"dataSource": "数据源Bean名称",
"successUrl": "保存成功后跳转URL",
"cardList": [{
"type": "card",
"label": "卡片标题",
"fieldList": [{"row": []},{"row": []}],
"buttonList": []
}],
"buttonList": [],
"initExpress": [],
"submitExpress": [],
"deleteExpress": [],
"statusExpress": []
}
| 字段 | 说明 | 常见写法 |
|---|---|---|
cardList |
表单主体,每个 card 是一个分区 | 基本信息、供应商信息、审核记录 |
type=card |
普通字段卡片 | 配合 fieldList 使用 |
type=iframe |
嵌入子列表或第三方页面 | /topezadmin/list/page-purchase/... |
fieldList |
卡片中的字段布局,按行组织 | [{ "row": [...] }] |
buttonList |
页面级按钮,通常放在表单底部或卡片中 | 关闭采购单、自定义脚本按钮 |
initExpress |
打开表单时执行,负责回填数据 | @import(xxx_initExpress.ql) |
submitExpress |
点击保存时执行,负责新增或更新 | @import(xxx_submitExpress.ql) |
deleteExpress |
删除入口对应逻辑 | @import(xxx_deleteExpress.ql) |
statusExpress |
提交后的前端行为 | ["reload"]、["reloadlocal"] |
successUrl |
保存成功后的跳转或刷新动作 | reloadlocal |
4. 数据字典与公共枚举
如果多个页面都会用到同一组选项,不要反复内联写 dataJson。更推荐放到 dsl/data 目录,然后通过 @import 引入。
{
"item_name": "CHECK_STATUS",
"label": "审核状态",
"component": "select",
"initData": {
"dataJson": "@import(checkstatus.json)"
}
}
这样做有三个直接好处:
- 一处维护,多处复用,避免多个页面选项不一致。
- 列表展示组件
tdSelect和表单输入组件select可以共用同一份字典。 - 更适合 AI 生成后再人工整理,把通用枚举沉淀成公共资源。
5. 一个典型页面通常由哪些文件组成
| 页面类型 | 必备文件 | 可选文件 |
|---|---|---|
| 列表页 | xxx.json |
xxx_express_main.ql、xxx_appendHead.html、xxx_appendFoot.html |
| 表单页 | xxx.json |
xxx_initExpress.ql、xxx_submitExpress.ql、xxx_deleteExpress.ql、xxx_appendFoot.html |
| 公共字典 | xxx.json |
无 |
四、组件配置
基本结构
无论是搜索项、表格列还是表单字段,本质上都是“字段元信息 + 组件类型 + 数据初始化 + 展示属性”四部分的组合。先理解这个最小单元,再看具体组件会容易很多。
{
"item_name": "字段名(大写)",
"label": "显示名称",
"component": "组件类型",
"alias": "SQL别名(可选)",
"jdbcType": "数据类型(可选)",
"operator": "操作符(可选)",
"initData": {
"dataJson": [{"label":"选项1","value":"1"}],
"dataSql": "SELECT value,label FROM table",
"dataSource": "数据源名称"
},
"props": {
// Layui 原生属性或自定义属性
}
}
| 配置块 | 用途 | 典型场景 |
|---|---|---|
item_name |
字段名,也是请求参数名和 SQL 占位符名 | STATUS、WAREHOUSE_ID |
label |
页面展示名称 | 状态、仓库、创建时间 |
component |
决定渲染成什么控件 | input、select、tdText |
operator |
搜索条件如何拼接到 SQL | like、EQ、between、in |
jdbcType |
声明字段类型,便于时间、数字等处理 | DATETIME、NUMBER |
initData |
初始化选项或远程数据 | 下拉、级联、远程搜索 |
props |
组件的 UI / 交互属性 | 宽度、占位文案、校验、格式化 |
classAppend |
补充布局 class,控制栅格宽度 | layui-col-md6、layui-col-md12 |
props 通用属性
所有组件的 props 都支持以下通用属性:
| 属性 | 类型 | 说明 | 示例 |
|---|---|---|---|
placeholder |
String | 输入提示文本 | "请输入..." |
lay-verify |
String | Layui 验证规则 | "required", "phone", "email" |
required |
Boolean | 是否必填(显示红色*) | true / false |
disabled |
Boolean | 是否禁用 | true / false |
readonly |
Boolean | 是否只读 | true / false |
description |
String | 字段说明文本(显示在输入框下方) | "请输入真实姓名" |
validate |
Object | jQuery Validate 验证规则(见下方详细说明) | 见下方示例 |
jQuery Validate 验证配置
通过 props.validate 可以配置更复杂的表单验证规则:
{
"props": {
"validate": {
"rule": {
"required": true,
"minlength": 2,
"maxlength": 20,
"range": [0, 100],
"email": true,
"url": true,
"number": true,
"digits": true
},
"message": {
"required": "此字段不能为空",
"minlength": "至少输入2个字符",
"maxlength": "最多输入20个字符",
"range": "请输入0-100之间的数字",
"email": "请输入有效的邮箱地址",
"url": "请输入有效的URL",
"number": "请输入数字",
"digits": "请输入整数"
}
}
}
}
常用验证规则:
required: 必填验证email: 邮箱格式url: URL格式number: 数字digits: 整数minlength/maxlength: 长度限制min/max: 数值范围range: 数值范围(数组形式)uploadMin/uploadMax: 上传文件数量限制(仅 upload 组件)
1. lay-verify 用于 Layui 原生验证(提交时验证)
2. validate 用于 jQuery Validate 验证(实时验证,功能更强大)
3. 两者可以同时使用,建议复杂验证使用 validate
组件类型
点击组件名称可查看详细文档 (展开/收起)
📝 输入组件
input
单行文本输入框
使用场景:姓名、账号、标题等单行文本输入
基本配置示例:
{
"item_name": "NAME",
"label": "姓名",
"component": "input",
"props": {
"placeholder": "请输入姓名",
"lay-verify": "required",
"maxlength": 20
}
}
带 validate 验证的示例:
{
"item_name": "AGE",
"label": "年龄",
"component": "input",
"props": {
"placeholder": "请输入年龄",
"type": "number",
"validate": {
"rule": {
"required": true,
"range": [0, 150],
"digits": true
},
"message": {
"required": "年龄不能为空",
"range": "请输入0-150之间的数字",
"digits": "请输入整数"
}
}
}
}
支持的 HTML5 type 属性:
text(默认):普通文本number:数字输入email:邮箱输入tel:电话号码url:URL地址password:密码输入
常用 props 属性:
placeholder:提示文本maxlength:最大长度lay-verify:Layui验证规则(required/phone/email等)validate:jQuery Validate验证(更强大)disabled:禁用状态readonly:只读状态
textarea
多行文本域
使用场景:备注、描述、内容等多行文本输入
配置示例:
{
"item_name": "REMARK",
"label": "备注",
"component": "textarea",
"props": {
"placeholder": "请输入备注",
"rows": 5
}
}
支持属性:placeholder, rows, maxlength, disabled, readonly
hidden
隐藏域
使用场景:ID、状态标识等隐藏字段
配置示例:
{
"item_name": "ID",
"label": "ID",
"component": "hidden",
}
注意:classAppend 设为 layui-col-md0,不占用页面空间
tinymce
富文本编辑器
使用场景:文章内容、产品详情等富文本编辑
配置示例:
{
"item_name": "CONTENT",
"label": "内容",
"component": "tinymce",
"props": {
"height": 400
}
}
功能:支持图片上传、表格、链接等富文本功能
🎯 选择组件
select
下拉选择框
使用场景:状态选择、分类选择等单选场景
配置示例:
{
"item_name": "STATUS",
"label": "状态",
"component": "select",
"initData": {
"dataJson": [
{"label": "启用", "value": "1"},
{"label": "禁用", "value": "0"}
]
},
"props": {
"lay-verify": "required",
"placeholder": "请选择状态"
}
}
数据源:支持 dataJson(静态)和 dataSql(动态查询)
注意:SQL 查询必须使用 value 和 label 别名
select-multiple
多选下拉框
使用场景:多标签、多权限、多分类选择
配置示例:
{
"item_name": "TAGS",
"label": "标签",
"component": "select-multiple",
"operator": "in",
"initData": {
"dataSource": "dataSource",
"dataSql": "SELECT id value, name label FROM tags"
}
}
第三方插件:基于 xm-select 实现
存储格式:逗号分隔的值,如 "1,2,3"
cascader
级联选择器
使用场景:省市区、部门层级、类目层级
配置示例:
{
"item_name": "REGION_ID",
"label": "地区",
"component": "cascader",
"initData": {
"dataJson": [
{
"label": "北京市",
"value": "110000",
"children": [
{"label": "东城区", "value": "110101"}
]
}
]
}
}
第三方插件:基于 lay-cascader 实现
radio
单选框
使用场景:性别、是否、状态等简单选项
配置示例:
{
"item_name": "GENDER",
"label": "性别",
"component": "radio",
"initData": {
"dataJson": [
{"label": "男", "value": "1"},
{"label": "女", "value": "2"}
]
}
}
checkbox
复选框
使用场景:多项选择、权限勾选
配置示例:
{
"item_name": "HOBBIES",
"label": "爱好",
"component": "checkbox",
"initData": {
"dataJson": [
{"label": "阅读", "value": "1"},
{"label": "运动", "value": "2"},
{"label": "旅游", "value": "3"}
]
}
}
存储格式:逗号分隔的值
🔧 其他组件
date
日期时间选择器
使用场景:日期、时间、日期范围选择
配置示例:
{
"item_name": "CREATE_TIME",
"label": "创建时间",
"component": "date",
"props": {
"type": "datetime",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
type 选项:year, month, date, time, datetime
upload
文件上传
使用场景:图片、文件、附件上传
基本配置示例:
{
"item_name": "AVATAR",
"label": "头像",
"component": "upload",
"props": {
"accept": "images",
"multiple": false,
"size": 2048,
"description": "支持jpg、png格式,大小不超过2MB"
}
}
props 属性:
| 属性 | 说明 | 示例 |
|---|---|---|
accept |
文件类型:images/file/video/audio | "images" |
acceptMime |
自定义MIME类型 | "image/jpg,image/png" |
multiple |
是否允许多文件上传 | true / false |
number |
最大上传数量(界面限制) | 5 |
size |
单个文件大小限制(KB) | 10240 (10MB) |
上传数量验证(使用 validate):
{
"item_name": "PRODUCT_IMAGES",
"label": "产品图片",
"component": "upload",
"props": {
"accept": "images",
"multiple": true,
"validate": {
"rule": {
"uploadMin": 1,
"uploadMax": 10
},
"message": {
"uploadMin": "至少上传1张图片",
"uploadMax": "最多上传10张图片"
}
}
}
}
数据格式:
- 单文件:
"/upload/2024/01/01/abc.jpg" - 多文件:
"/upload/abc.jpg,/upload/def.jpg"(逗号分隔)
注意事项:
number属性:界面层面的数量限制uploadMin/uploadMax:表单提交时的数量验证size单位为 KB,1MB = 1024KB- 数据库字段建议:单文件 VARCHAR(255),多文件 TEXT
🔘 按钮组件
button-normal
行操作按钮
位置:列表行操作列(rowButton)
配置示例:
{
"item_name": "EDIT",
"label": "编辑",
"component": "button-normal",
"props": {
"opentype": "MODAL",
"windowname": "编辑用户",
"url": "/topezadmin/form/page-user-form?id={{=d.ID}}",
"area": ["60%", "80%"]
}
}
opentype:MODAL, AJAX, CONFIRM_AJAX, _BLANK 等
button-toolbar
工具栏按钮
位置:列表表头工具栏(tableButton)
配置示例:
{
"item_name": "ADD",
"label": "新增",
"component": "button-toolbar",
"props": {
"opentype": "MODAL",
"windowname": "新增用户",
"url": "/topezadmin/form/page-user-form",
"area": ["60%", "80%"]
}
}
button-dropdown / button-bread / button-span
其他按钮类型
button-dropdown:下拉菜单项
button-bread:面包屑链接
button-span:文本按钮
📊 表格展示组件
tdText / tdLink
文本和链接展示
tdText:普通文本展示
tdLink:带链接的文本展示
tdPic
图片展示
使用场景:列表中展示缩略图
功能:自动生成缩略图,点击可预览大图
tdSelect / tdSelectMultiple / tdCascader
选择值展示
tdSelect:将 value 转换为 label 展示(单选)
tdSelectMultiple:将多个 value 转换为 label 展示(多选)
tdCascader:级联选择的值展示
配置示例:
{
"item_name": "STATUS",
"label": "状态",
"component": "tdSelect",
"initData": {
"dataJson": [
{"label": "启用", "value": "1"},
{"label": "禁用", "value": "0"}
]
}
}
按钮 opentype 属性
| 值 | 说明 | 适用场景 |
|---|---|---|
MODAL |
模态框 | 表单编辑、详情查看 |
FORM |
全屏表单 | 复杂表单编辑 |
FULL |
全屏窗口 | 需要更大空间的页面 |
_BLANK |
新窗口 | 打开外部链接 |
LOCATION |
当前页跳转 | 页面跳转 |
PARENT |
父窗口 | 关闭当前窗口并刷新父窗口 |
AJAX |
Ajax 请求 | 后端操作(不打开页面) |
CONFIRM_AJAX |
确认后 Ajax | 需要确认的删除等操作 |
CONFIRM_MODEL |
确认后打开模态框 | 需要确认的编辑操作 |
从页面搭建角度,组件可以分成 5 类
| 分类 | 用途 | 代表组件 |
|---|---|---|
| 搜索组件 | 收集查询条件,影响 search(sql) 自动拼接 |
input、select、date、hidden |
| 表单输入组件 | 收集提交参数,进入 submitExpress |
input、textarea、upload、radio |
| 详情展示组件 | 表单中只展示不编辑 | span、select-span |
| 表格展示组件 | 把结果集字段渲染为列 | tdText、tdLink、tdSelect |
| 动作组件 | 发起跳转、弹窗、删除、脚本执行 | button-table、button-single、button-normal |
五、表达式系统
DSL 里的 JSON 负责“页面长什么样”,QLExpress 负责“页面的数据和行为怎么跑起来”。实际项目中,这两部分通常是一一配套的。
内置函数
| 函数 | 说明 |
|---|---|
select(sql) |
查询多条数据 |
selectOne(sql) |
查询单条数据 |
search(sql) |
带搜索条件的查询(自动拼接条件) |
count(sql) |
查询总数 |
insert(sql) |
插入数据(SQL 方式) |
insertSimple(param) |
插入数据(参数对象方式) |
update(sql) |
更新数据(SQL 方式) |
updateSimple(param) |
更新数据(参数对象方式) |
$("param") |
获取请求参数 |
$$("session") |
获取 session 参数 |
isNotBlank(param) |
判断参数是否非空 |
isBlank(param) |
判断参数是否为空 |
表达式在页面生命周期中的职责
| 表达式 | 触发时机 | 主要职责 |
|---|---|---|
express.main |
列表加载、查询、分页时 | 拼主 SQL,通常最终 return search(sql); |
initExpress |
表单打开时 | 根据 ID 查询详情并回填 |
submitExpress |
点击保存/提交时 | 新增、更新、联动写入、业务校验 |
deleteExpress |
删除动作触发时 | 逻辑删除或物理删除 |
statusExpress |
表达式执行成功后 | 控制前端刷新、关闭、跳转 |
搜索条件是如何自动拼接的
列表页中真正节省工作量的点,是你不需要在 QL 里手动拼每一个查询条件。只要在 search 配置中声明了 item_name 和 operator,再在主查询里调用 search(sql),系统就会按字段和值自动补条件。
{
"item_name": "ADD_TIME",
"label": "变动时间",
"component": "date",
"operator": "between",
"props": {
"range": true
}
}
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM T_WMS_LOG A WHERE A.DELETE_FLAG = 0");
return search(sql);
这时用户在页面输入的时间范围、下拉值、文本模糊查询,都会围绕这个基础 SQL 自动拼接。
表达式示例
列表查询:
StringBuilder sql = new StringBuilder();
sql.append("SELECT ID, NAME, STATUS FROM users WHERE delete_flag=0");
return search(sql); // 系统会自动拼接搜索条件
表单保存:
if(!isNotBlank("ID")) {
// 新增
param = new InsertParam();
param.table("users");
param.add("#{NAME}");
param.add("#{STATUS}");
param.add("#{CREATE_TIME,value=NOW()}");
return insertSimple(param);
} else {
// 更新
param = new UpdateParam();
param.table("users");
param.add("#{NAME}");
param.add("#{STATUS}");
param.where("id=#{ID}");
return updateSimple(param);
}
推荐的表达式拆分方式
- 列表页优先把 SQL 写到独立
*_express_main.ql文件中,JSON 里通过@import引用,避免 JSON 过长。 - 表单页把初始化、保存、删除分成独立文件,便于后续扩展业务逻辑。
- 复杂页面优先使用
InsertParam/UpdateParam,比直接拼接 SQL 更易维护。 - 需要取登录人、公司、租户信息时,优先通过
$$("...")从 session 取值。
六、完整示例
最小列表配置
{
"id": "user-list",
"name": "用户列表",
"dataSource": "dataSource",
"body": {
"showIndex": true,
"selectable": true
},
"search": [
{"row": [{
"item_name": "NAME",
"label": "姓名",
"component": "input",
"operator": "like"
}, {
"item_name": "STATUS",
"label": "状态",
"component": "select",
"operator": "eq",
"initData": {
"dataJson": [
{"label": "启用", "value": "1"},
{"label": "禁用", "value": "0"}
]
}
}]}
],
"column": [{
"item_name": "ID",
"label": "ID",
"props": {"width": 80}
}, {
"item_name": "NAME",
"label": "姓名"
}, {
"item_name": "STATUS",
"label": "状态",
"component": "tdSelect",
"initData": {
"dataJson": [
{"label": "启用", "value": "1"},
{"label": "禁用", "value": "0"}
]
}
}],
"tableButton": [{
"item_name": "add",
"label": "新增",
"component": "button-toolbar",
"props": {
"opentype": "MODAL",
"windowname": "新增用户",
"url": "/topezadmin/form/page-user-form",
"area": ["60%", "80%"]
}
}],
"rowButton": [{
"item_name": "edit",
"label": "编辑",
"component": "button-normal",
"props": {
"opentype": "MODAL",
"windowname": "编辑用户",
"url": "/topezadmin/form/page-user-form?id={{=d.ID}}",
"area": ["60%", "80%"]
}
}, {
"item_name": "delete",
"label": "删除",
"component": "button-normal",
"props": {
"opentype": "CONFIRM_AJAX",
"url": "/topezadmin/form/delete-user-form?id={{=d.ID}}"
}
}],
"express": {
"main": "return search('SELECT ID, NAME, STATUS FROM users WHERE 1=1')",
"orderBy": "ORDER BY ID DESC"
}
}
真实项目中的列表页拆分示意
以仓库管理为例,一个可维护的列表页通常会拆成下面几层:
warehouse.json:定义搜索、列、按钮、跳转关系。warehouse_express_main.ql:只关心查询 SQL 和业务过滤条件。- 公共字典文件:例如状态、审核状态等用
data/*.json复用。 - 可选 HTML 片段:当页面需要特殊脚本或补充 DOM 时,再加
appendHead/appendFoot。
真实项目中的表单页拆分示意
以采购单详情为例,一个表单不只是一组输入框,还可能包含多张卡片和多个 iframe 子区域:
purchase-form.json:定义“基本信息、供应商信息、产品管理、单据记录”等区域。purchase-form_initExpress.ql:查询主单数据并回填。purchase-form_submitExpress.ql:负责新增或更新。purchase-form_appendFoot.html:承载自定义按钮脚本和页面联动。
最小表单配置
{
"id": "user-form",
"name": "用户表单",
"dataSource": "dataSource",
"cardList": [{
"type": "card",
"label": "基本信息",
"fieldList": [
{"row": [{
"item_name": "ID",
"label": "ID",
"component": "hidden",
}, {
"item_name": "NAME",
"label": "姓名",
"component": "input",
"props": {
"placeholder": "请输入姓名",
"lay-verify": "required",
"validate": {
"rule": {"required": true, "minlength": 2},
"message": {
"required": "姓名不能为空",
"minlength": "姓名至少2个字符"
}
}
}
}, {
"item_name": "STATUS",
"label": "状态",
"component": "select",
"initData": {
"dataJson": [
{"label": "启用", "value": "1"},
{"label": "禁用", "value": "0"}
]
},
"props": {
"lay-verify": "required"
}
}]
}]}
],
"initExpress": [
"return selectOne('SELECT ID, NAME, STATUS FROM users WHERE id=${ID}')"
],
"submitExpress": [
"if(!isNotBlank('ID')) {",
" param = new InsertParam();",
" param.table('users');",
" param.add('#{NAME}');",
" param.add('#{STATUS}');",
" param.add('#{CREATE_TIME,value=NOW()}');",
" return insertSimple(param);",
"} else {",
" param = new UpdateParam();",
" param.table('users');",
" param.add('#{NAME}');",
" param.add('#{STATUS}');",
" param.add('#{UPDATE_TIME,value=NOW()}');",
" param.where('ID=#{ID}');",
" return updateSimple(param);",
"}"
],
"deleteExpress": [
"return update('UPDATE users SET delete_flag=1 WHERE ID=#{ID}')"
]
}
七、路由规则
路由的核心规则可以概括为一句话:访问路径决定页面类型,{id} 决定要加载哪份 DSL 配置。实际项目里,通常会结合模块目录做命名,例如 page-warehouse/warehouse、page-purchase/purchase-form。
| URL | 说明 |
|---|---|
/topezadmin/list/page-{id} |
列表页面 |
/topezadmin/list/data-{id} |
列表数据接口 |
/topezadmin/form/page-{id} |
表单页面 |
/topezadmin/form/data-{id} |
表单初始化数据接口 |
/topezadmin/form/submit|delete|status-{id} |
表单提交删除状态更新接口 |
/topezadmin/edit/create- |
AI新增列表表单+拖拽 |
/topezadmin/edit/list-{id} | /topezadmin/edit/form-{id} |
AI修改列表表单+拖拽 |
/topezadmin/dsl/list-{id} | /topezadmin/dsl/form-{id} |
源码编辑 |
推荐命名方式
- 列表页:模块目录和页面 ID 尽量同名,例如
list/warehouse/warehouse.json对应仓库主列表。 - 表单页:编辑页、详情页、步骤页建议通过名称区分,例如
purchase-form、saleorder-step1。 - 表达式文件:直接跟随页面主文件命名,例如
warehouse_express_main.ql、purchase-form_submitExpress.ql。 - 扩展 HTML:优先使用
appendHead/appendFoot后缀,便于快速识别加载时机。
八、注意事项
- 字段命名规范:
item_name必须大写,且与数据库字段名保持一致 - 数据格式:
initData支持dataJson(静态)和dataSql(动态)两种方式 - 模板语法:支持 laytpl 模板语法,如
{{=d.field}} - 属性兼容:
props支持所有 Layui 原生属性及第三方插件属性 - 表达式引擎:基于 QLExpress,支持 Java 语法和 SQL 拼接
- 多数据源:默认使用配置的
dataSource,可在initData中指定其他数据源 - 验证规则:支持 jQuery Validate 的所有验证规则
1. 使用大写字段名保持统一性
2. 合理使用表达式避免复杂 SQL
3. 善用组件的 props 属性进行定制
4. 利用 AI 提示词快速生成配置文件
九、AI开发流程说明
1. 解析用户请求,是否包含SQL
2. 如果包含SQL,则解析SQL,生成DSL
3. 如果不包含SQL,则将所有库表名称发送给大模型,让其分析哪些是相关库表
4. 获取相关库表的库表字段信息,生成 DSL
1. 将当前所有DSL信息、发送给大模型,分析哪些需要修改
2. 如果涉及到express,则将库表全部发给大模型
3. 如果不涉及express,则让大模型告知哪些节点需要怎样修改
4. 做dsl patch操作,保存修改后的DSL