需求:最近项目需求中产品提到了列表需要支持拖动排序的问题,而当前系统中的列表是支持分页+条件搜索的,也就是说我们列表返回的orderNum肯定不是连续的。
动态效果如图:
20220427_112316
方案一
实现方案一: 不管数据向上还是向下移动,只会改变目标数据当前的位置和目标数据最后存放位置的之前的数据的orderNum值,其他数据的orderNum保持不变
- 向上移动
- 向下移动
代码实现
/**
* 前端传递的body
* @Author xiaosuhe
* @Date 2022/4/11 11:05
* @Version 1.0
*/
@Data
public class MoveSortRequest {
/**
* 页码
*/
@ApiModelProperty("页码")
private Integer pageNum;
/**
* 条数
*/
@ApiModelProperty("条数")
private Integer pageSize;
/**
* 对象所属当前的 下标 索引位置
*/
@ApiModelProperty("对象所属当前的 下标 索引位置")
private Integer startIndex;
/**
* 对象所属 拖拽后所属的 下标 索引的 位置
*/
@ApiModelProperty("对象所属 拖拽后所属的 下标 索引的 位置")
private Integer endIndex;
/**
* targetId(拖拽的对象)
*/
@ApiModelProperty("targetId(拖拽的对象)")
private Long targetId;
/**
* 名称搜索
*/
private String name;
}
因为系统的列表是数据size是支持自己选择的,所以前端移动的数据要跟后端查询的数据对应上 ,所以需要前端传递pageSize和pageNum
排序代码如下:
/**
* @param allList 排序序 的集合(跟列表顺序保持一致,数据保存一直,也就是我们需要前端传递pageSize和pageNum的原因)
* @param targetId 目标对象的Id(需要移动数据的ID)
* @param startIndex 对象在列表的开始索引值[索引值从0 开始
* @param endIndex 移动后的目标索引值[索引值从0 开始
* @param isAsc 集合列表 是否正序 TRUE 正序
* @Description: 构建 需要 排序的 list集合
* @Return: java.util.List<T> 返回sort值变动的数据集合
*/
public static <T> List<T> getNeedUpdateSortList(List<T> allList, Long targetId, Integer startIndex, Integer endIndex, boolean isAsc) throws NoSuchFieldException, IllegalAccessException {
List<T> buildList = new ArrayList<>();
if (targetId == null || startIndex == null || endIndex == null) {
throw new ServiceException(ErrorCodeEnum.PARAM_MISSING);
}
if (CollectionUtils.isEmpty(allList) || startIndex.equals(endIndex)) {
return buildList;
}
//需要 修改排序值得 数据集合 不包含 目标对象 本身
List<T> excludeList = new ArrayList();
//目标 对象
T targetBean = null;
//截取的 开始 索引 和 结束 索引 之间的 list 包含 targetBean(拖拽排序目标自身) 的一个 集合
List<T> subList = null;
//需改排序规则 (只修改 索引区间的值)
//== ================获取 索引区间的 数据集合begin ==================
boolean flag = endIndex > startIndex;
//不管上移还是下移,值改变目标当前位置和移动之后位置的orderNum
if (flag) {
//下移
subList = allList.subList(Integer.parseInt(startIndex.toString()), Integer.parseInt(endIndex.toString()) + 1);
} else {
// 上移 subList左闭右开
subList = allList.subList(Integer.parseInt(endIndex.toString()), Integer.parseInt(startIndex.toString()) + 1);
}
//================获取 索引区间的 数据集合 end ==================
if (CollectionUtils.isEmpty(subList)) {
return buildList;
}
//=================== 获取 排序区间 里面的 目标对象 + 排出目标对象之后的集合 begin =============
for (T bean : subList) {
Field idField = bean.getClass().getDeclaredField("id");
idField.setAccessible(true);
Object currentId = idField.get(bean);
//目标对象
if (targetId.equals((Long) (currentId))) {
targetBean = bean;
} else {
//区间数据(不包含目标对象)
excludeList.add(bean);
}
}
if (targetBean == null) {
throw new ServiceException(ErrorCodeEnum.CHECKTABLE_IS_NULL);
}
//=================== 获取 排序区间 里面的 目标对象 + 排出目标对象之后的集合 end
//============获取 拖拽对象 上移/下移 之后的 sort 值
//需要修改自己的排序值
//需要 对索引区间的排序值(不包含排序对象自身) 进行 加1 或者 减 1
T tempBean = null;
if (flag) {
//下移
tempBean = excludeList.get(excludeList.size() - 1);
} else {
//上移动
tempBean = excludeList.get(0);
}
Field sortField = tempBean.getClass().getDeclaredField("orderNum");
sortField.setAccessible(true);
Integer sort = (Integer) sortField.get(tempBean);
//============获取 拖拽对象 上移/下移 之后的 sort 值 end========================
//===================修改 目标对象 排序值 begin ==============
Field targetBeanSortField = targetBean.getClass().getDeclaredField("orderNum");
targetBeanSortField.setAccessible(true);
//保存tagBean的orderNum值
targetBeanSortField.set(targetBean, sort);
//===================修改 目标对象 排序值 begin ==============
//================修改 需要排序 区间(排序目标对象自身) 排序值 begin ========
//处理
for (T bean : excludeList) {
sortField = bean.getClass().getDeclaredField("orderNum");
sortField.setAccessible(true);
sort = (Integer) sortField.get(bean);
//flag true : 下移 非拖动对象 自身 排序索引值 需要 -1; false 反之
if (isAsc) {
//正序
sortField.set(bean, flag ? (sort - 1) : (sort + 1));
} else {
//倒叙
sortField.set(bean, flag ? (sort + 1) : (sort - 1));
}
}
//================修改 需要排序 区间(排序目标对象自身) 排序值 end ========
buildList.addAll(excludeList);
buildList.add(targetBean);
return buildList;
}
细心的可以发现,方案一功能是能实现的,但是存在2个问题
- 当在移动列表的时候,有人新增数据,那么展示的列表和调用移动排序回数据库查的数据就不会一一对应,导致潜在的移动排序失败。
- 拖动排序的时候是支持条件搜索的,那说明我们列表展示的数据orderNum不是连续的数值,只是相对有序,我们对区间数据orderNum进行+1或者-1的时候可能导致数据表中的orderNum重复。
如图:经过筛选的数据移动出现orderNum重复的情况
一系列操作之后,清除筛选条件之后,发现orderNum经过上面的操作之后出现了多个orderNum重复的情况,所以对应orderNum不连续的,使用这种方式并不友好。
方案二
实现方案二、前端将移动前后列表的数据都给到后端,其实是一种数据平移的思想。
这种做法对排序的列表数据完全可靠,移动排序不会受到新增数据的影响,也解决了orderNum出现重复的问题
下面是代码实现,相对简单
请求body
@Data
public class MoveSortRequest {
@ApiModelProperty("移动之前集合,需要传递orderNum")
List<TableDTO> preMoveSortList;
@ApiModelProperty("移动之后集合,不需要传递orderNum")
List<TableDTO> afterMoveSortList;
}
@Data
public class TableDTO {
private Long Id;
private Integer orderNum;
}
逻辑处理之后批量更新
List<TableDO> list = new ArrayList<>();
List<TableDTO> afterMoveSortList = request.getAfterMoveSortList();
List<TableDTO> preMoveSortList = request.getPreMoveSortList();
if (CollectionUtils.isEmpty(afterMoveSortList )||CollectionUtils.isEmpty(preMoveSortList )){
throw new ServiceException(ErrorCodeEnum.PARAM_MISSING);
}
for (int i = Constants.INDEX_ONE; i < afterMoveSortList .size(); i++) {
TableDO tableDO = new TableDO();
tableDO .setId(afterMoveSortList .get(i).getId());
tableDO .setOrderNum(preMoveSortList .get(i).getOrderNum());
list.add(tableDO);
}
return list;
总结
- 方案一对于数据量比较大、排序准确度一般、容错较高的列表,
- 方案一适合增删操作较少的列表
- 方案二适合数据量一般,数据排序准确度较高,容错较低的列表
- 方案二对操作列表不受限制