列表设计器
列表设计器
业务用途
列表设计器用于配置数据列表的展示方式。运营人员可视化配置哪些字段作为列显示、哪些字段支持搜索、列的排序方式、每页显示条数、操作按钮(编辑/删除/查看)等。设计产物以 list_json 存入 lc_list 表,保存时自动同步生成一条 lc_page 记录(page_code = list_{listCode}),使列表能被运行时引擎统一渲染。
列表设计器支持:
- 字段列配置:选择要展示的列、列标题、列宽、对齐方式
- 搜索条件:配置搜索字段及查询方式(EQ/LIKE/GT/LT 等)
- 排序规则:默认排序字段 + 排序方向
- 分页配置:每页条数选项
- 操作按钮:编辑、删除、查看详情、自定义按钮
- 关联表单:通过
formId绑定表单,支持新增/编辑弹窗 - 版本管理:保存版本快照、回滚、版本对比
涉及文件
| 层 | 文件路径 | 说明 |
|---|---|---|
| Controller | backend/src/main/java/com/lowcode/controller/ListController.java | /api/list 端点 |
| Controller | backend/src/main/java/com/lowcode/controller/ListVersionController.java | /api/list/version 版本端点 |
| Entity | backend/src/main/java/com/lowcode/entity/ListEntity.java | 列表实体(@TableName("lc_list")) |
| 前端 | frontend/src/views/designer/list/builder.vue | 列表设计器主界面 |
| 前端 | frontend/src/views/designer/list.vue | 列表管理列表 |
| 前端 | frontend/src/views/runtime/components/RuntimeList.vue | 运行时列表渲染 |
数据库表
lc_list(列表配置表)
CREATE TABLE `lc_list` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` BIGINT DEFAULT 1 COMMENT '租户编号',
`app_id` BIGINT COMMENT '所属应用ID',
`list_name` VARCHAR(100) NOT NULL COMMENT '列表名称',
`list_code` VARCHAR(50) NOT NULL COMMENT '列表编码',
`table_id` BIGINT COMMENT '关联表ID',
`form_id` BIGINT COMMENT '关联表单ID',
`list_json` LONGTEXT COMMENT '列表JSON配置',
`status` VARCHAR(20) DEFAULT 'draft' COMMENT '状态(draft/published)',
`create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
`create_by` VARCHAR(50) COMMENT '创建者',
`update_by` VARCHAR(50) COMMENT '更新者',
`deleted` TINYINT DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_list_code` (`list_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='列表配置表';ListEntity 实体字段
@TableName("lc_list")
public class ListEntity extends BaseEntity {
private Long appId; // 所属应用ID
private String listName; // 列表名称
private String listCode; // 列表编码(唯一)
private Long tableId; // 关联数据表ID
private Long formId; // 关联表单ID(用于新增/编辑弹窗)
private String listJson; // 列表配置JSON
private String status; // 状态: draft / published
private Long tenantId; // 租户ID
}后端实现
端点列表
列表管理(ListController -- /api/list)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/list/builder/list?appId= | 获取列表配置列表 |
| GET | /api/list/builder/{id} | 获取列表配置详情 |
| POST | /api/list/builder/save | 保存列表配置(新增或更新),自动同步页面 |
| PUT | /api/list/builder/{id} | 更新列表配置 |
| DELETE | /api/list/builder/{id} | 删除列表配置 |
| POST | /api/list/builder/build/{id} | 构建列表 |
版本管理(ListVersionController -- /api/list/version)
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/list/version/save/{listId}?versionDesc= | 保存版本快照 |
| GET | /api/list/version/history/{listId} | 获取版本历史列表 |
| GET | /api/list/version/{listId}/{version} | 获取指定版本 |
| GET | /api/list/version/current/{listId} | 获取当前版本 |
| POST | /api/list/version/rollback/{listId}/{version} | 回滚到指定版本 |
| GET | /api/list/version/compare/{listId}?version1=&version2= | 对比两个版本 |
保存列表 -- saveListBuilder() + syncListToPage()
@PostMapping("/builder/save")
public Result<Long> saveListBuilder(@RequestBody ListEntity listEntity) {
// 按 listCode 查询是否已存在
if (listEntity.getListCode() != null) {
ListEntity existing = listService.lambdaQuery()
.eq(ListEntity::getListCode, listEntity.getListCode()).one();
if (existing != null) {
listEntity.setId(existing.getId());
// 前端没传 formId 时保持原有
if (listEntity.getFormId() == null) {
listEntity.setFormId(existing.getFormId());
}
listService.updateById(listEntity);
} else if (listEntity.getId() != null) {
listService.updateById(listEntity);
} else {
listService.save(listEntity);
}
}
// 自动同步到 lc_page 表
syncListToPage(listEntity);
return Result.success(listEntity.getId());
}syncListToPage() 方法把列表配置包装成 pageJson,写入 lc_page 表:
private void syncListToPage(ListEntity listEntity) {
if (listEntity.getAppId() == null) return;
// 解析 listJson
ObjectNode listJsonNode = new ObjectMapper().readTree(listEntity.getListJson());
// 构建 pageJson:一行一列,放一个 list 组件
ObjectNode pageNode = new ObjectMapper().createObjectNode();
ArrayNode rows = pageNode.putArray("rows");
ObjectNode row = rows.addObject();
row.put("id", 1);
row.put("gutter", 16);
ArrayNode components = row.putArray("components");
ObjectNode component = components.addObject();
component.put("id", 1);
component.put("type", "list");
component.put("colSpan", 24);
ObjectNode config = component.putObject("config");
config.put("title", listEntity.getListName());
config.put("tableId", listEntity.getTableId());
config.set("listConfig", listJsonNode); // 嵌入列表配置
if (listEntity.getFormId() != null) {
config.put("formId", listEntity.getFormId());
}
// 按 pageCode = "list_" + listCode 查找或创建 lc_page 记录
LcPage existing = pageService.lambdaQuery()
.eq(LcPage::getPageCode, "list_" + listEntity.getListCode()).one();
// ... 更新或新建
}前端实现
列表设计器界面
frontend/src/views/designer/list/builder.vue 提供:
- 左侧:数据表字段列表(从
lc_column_meta加载,可勾选要展示的列) - 中间:列配置表格(列标题、宽度、对齐、是否排序)
- 右侧:搜索条件配置 + 操作按钮配置 + 分页设置

前端 API 调用
// 加载列表配置详情(builder.vue 第 505 行)
const res = await request.get(`/list/builder/${id}`)
// 保存列表配置(builder.vue 第 757 行)
const res = await request.post('/list/builder/save', payload)
// 更新列表配置(builder.vue 第 754 行)
await request.put(`/list/builder/${editingId.value}`, payload)
// 加载关联表单列表(builder.vue 第 415 行)
const res = await request.get('/form/list', { params })运行时渲染
RuntimeList.vue 在运行时根据 pageJson 中的 listConfig 渲染列表:
// RuntimeList.vue 第 649 行 -- 查询列表数据
const res = await request.post(
`/database/table/${props.config.tableId}/data/list?pageNum=${page}&pageSize=${size}`,
queryParams
)
// RuntimeList.vue 第 1027 行 -- 更新数据
const res = await request.put(
`/database/table/${props.config.tableId}/data/${row.id}`,
rowData
)
// RuntimeList.vue 第 1088 行 -- 删除数据
const res = await request.delete(
`/database/table/${props.config.tableId}/data/${row.id}`
)
// RuntimeList.vue 第 881 行 -- 获取字段下拉选项
const res = await request.get(
`/database/table/${props.config.tableId}/column/${col.columnName}/options`
)
操作步骤
- 进入「设计器 -> 列表设计」页面,点击「新建列表」
- 填写列表名称、编码,选择关联数据表(
tableId) - 选择关联表单(
formId,用于新增/编辑弹窗),如无可不选 - 从字段列表中勾选要展示的列,配置列标题、宽度、对齐方式
- 配置搜索条件:选择搜索字段、查询方式(EQ/LIKE/GT 等)
- 配置操作按钮:编辑、删除、查看详情等
- 设置分页:默认每页条数
- 点击「保存」,列表配置存入
lc_list.list_json,同时自动生成lc_page记录

常见问题
列表保存后 formId 丢失
saveListBuilder() 中有逻辑:如果前端没传 formId,会保持原有的 formId。但如果你新建列表时没选表单,保存后 formId 为 null,后续编辑时也不会自动填充。确保保存前选择了关联表单。
列表搜索不生效
运行时 RuntimeList.vue 会把搜索条件传给 POST /database/table/{id}/data/list,后端 SchemaService.queryDataList() 只对 lc_column_meta 中定义的字段做条件匹配。请确保搜索字段在元数据中存在,且 is_query = 'Y' 或前端传了 _queryTypes。
列表数据和表单数据不同步
列表和表单通过 tableId 关联同一张业务表。如果列表配置的 tableId 和表单配置的 tableId 不一致,会导致列表查询不到表单录入的数据。请确保它们关联同一张表。
