feat(table): 添加动态表格功能实现

- 新增 KitDynamicTableColumn 注解用于定义表格列属性
- 创建 KitDynamicTableResponse 类处理动态表格响应数据
- 实现 KitDynamicTableUtil 工具类提供排序和查询构建功能
- 添加 SystemMenuDynamicTableModel 示例渲染对象
- 集成动态表格到 CuteDynamicTableDemoApi 提供三种查询方式
- 支持表格列的标题、宽度、排序等动态配置
- 实现分页查询和条件筛选功能
This commit is contained in:
2026-02-07 15:02:32 +08:00
parent cb5e516e6f
commit 7b8e7190fd
5 changed files with 436 additions and 0 deletions
@@ -0,0 +1,43 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.feature;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface KitDynamicTableColumn {
/**
* 标题
*/
String value() default "";
/**
* 宽度
*/
int width() default 0;
/**
* 是否排序字段
*/
boolean sortable() default false;
}
@@ -0,0 +1,172 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.feature;
import cn.hutool.core.util.ReflectUtil;
import cn.odboy.base.KitObject;
import cn.odboy.base.KitPageArgs;
import cn.odboy.util.KitBeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* 动态表格
*
* @param <E> 数据表对象
* @param <T> 渲染对象
* @author odboy
* @date 2026-02-05
*/
@Getter
public class KitDynamicTableResponse<E, T> extends KitObject {
/**
* 主键
*/
@Setter
private String primaryKey = "id";
/**
* 表格列定义
*/
private List<Column> columns = new ArrayList<>();
/**
* 数据总数
*/
private Long totalElements;
/**
* 数据内容
*/
private List<T> content;
/**
* @param clazz 渲染类型
* @param pageResult 渲染类型分页参数
*/
public KitDynamicTableResponse(Class<T> clazz, IPage<E> pageResult) {
this.renderColumns(clazz);
this.setContent(clazz, pageResult);
}
/**
* @param clazz 渲染类型
* @param pageResult 渲染类型分页参数
* @param primaryKey 主键
*/
public KitDynamicTableResponse(Class<T> clazz, IPage<E> pageResult, String primaryKey) {
this.primaryKey = primaryKey;
this.renderColumns(clazz);
this.setContent(clazz, pageResult);
}
/**
* @param clazz 渲染类型
* @param pageArgs 分页参数
* @param baseMapper 数据表Mapper
* @param wrapper 查询条件
*/
public KitDynamicTableResponse(Class<T> clazz, KitPageArgs<?> pageArgs, BaseMapper<E> baseMapper, LambdaQueryWrapper<E> wrapper) {
this.renderColumns(clazz);
Page<?> pageResult = baseMapper.selectPage(new Page<>(pageArgs.getPage(), pageArgs.getSize()), wrapper);
this.setContent(clazz, pageResult);
}
/**
* @param clazz 渲染类型
* @param pageArgs 分页参数
* @param primaryKey 主键
* @param baseMapper 数据表Mapper
* @param wrapper 查询条件
*/
public KitDynamicTableResponse(Class<T> clazz, KitPageArgs<?> pageArgs, String primaryKey, BaseMapper<E> baseMapper, LambdaQueryWrapper<E> wrapper) {
this.primaryKey = primaryKey;
this.renderColumns(clazz);
Page<?> pageResult = baseMapper.selectPage(new Page<>(pageArgs.getPage(), pageArgs.getSize()), wrapper);
this.setContent(clazz, pageResult);
}
/**
* 渲染表格列
*
* @param clazz 渲染类型
*/
private void renderColumns(Class<T> clazz) {
List<Column> columns = new ArrayList<>();
if (clazz == null) {
this.columns = columns;
return;
}
Field[] fields = ReflectUtil.getFields(clazz);
for (Field field : fields) {
if ("serialVersionUID".equals(field.getName())) {
continue;
}
Column column = new Column();
column.setName(field.getName());
KitDynamicTableColumn annotation = field.getAnnotation(KitDynamicTableColumn.class);
if (annotation == null) {
column.setTitle(field.getName());
column.setWidth(null);
} else {
column.setTitle(annotation.value());
column.setWidth(annotation.width() == 0 ? null : annotation.width());
column.setSortable(annotation.sortable());
}
columns.add(column);
}
this.columns = columns;
}
/**
* @param clazz 渲染类型
* @param pageResult 渲染结果
*/
public void setContent(Class<T> clazz, IPage<?> pageResult) {
if (pageResult == null) {
return;
}
this.totalElements = pageResult.getTotal();
this.content = KitBeanUtil.copyToList(pageResult.getRecords(), clazz);
}
@Getter
@Setter
public static class Column {
/**
* 索引名称
*/
private String name;
/**
* 索引标题
*/
private String title;
/**
* 表格宽度
*/
private Integer width;
/**
* 是否排序列
*/
private Boolean sortable = false;
}
}
@@ -0,0 +1,65 @@
/*
* Copyright 2021-2026 Odboy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.odboy.feature;
import cn.odboy.base.KitPageArgs;
import cn.odboy.framework.mybatisplus.core.KitMpQUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.experimental.UtilityClass;
/**
* 动态表格辅助工具
*
* @author odboy
*/
@UtilityClass
public class KitDynamicTableUtil {
/**
* 渲染排序规则
*
* @param pageArgs 分页参数
* @param <M> 数据表对象
* @param <T> 渲染对象
* @return /
*/
public static <M, T> QueryWrapper<M> renderOrder(KitPageArgs<T> pageArgs) {
QueryWrapper<M> wrapper = new QueryWrapper<>();
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
if (orderBy != null) {
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
}
return wrapper;
}
/**
* 渲染所有规则(需要配合 @KitMpQuery 注解使用)
*
* @param pageArgs 分页参数
* @param <M> 数据表对象
* @param <T> 渲染对象
* @return /
*/
public static <M, T> LambdaQueryWrapper<M> renderAll(KitPageArgs<T> pageArgs) {
QueryWrapper<M> wrapper = KitMpQUtil.build(pageArgs.getArgs());
KitPageArgs.OrderBy orderBy = pageArgs.getOrderBy();
if (orderBy != null) {
wrapper.orderBy(true, orderBy.isAsc(), orderBy.getColumn());
}
return wrapper.lambda();
}
}