当前位置: 首页 > news >正文

【技术难题】el-table的全局数据排序实现示例,不受分页影响,以及异步请求带来的页面渲染问题

参考链接:https://blog.csdn.net/qq_35770559/article/details/131183121

问题代码

编辑页面detail.vue

<el-form title="列表信息" name="detail"><el-form><el-form-item><el-buttontype="cyan"icon="el-icon-plus"size="mini"@click="openProduct">选择商品</el-button></el-form-item></el-form><el-tableref="inBillDetailTabless":data="addup == '2' ? dataList : addList"max-height="450"stripev-loading="loading"@sort-change="sortChange"><el-table-column align="center" label="商品sku" width="180"prop="skuCode"><!-- <template slot-scope="scope"><el-form :model="scope.row"><el-form-item prop="skuCode"><el-inputv-model="scope.row.skuCode"clearable:disabled="disabled"></el-input></el-form-item></el-form></template> --></el-table-column><el-table-column align="center" label="商品名称" width="180"prop="skuName"><el-table-columnlabel="操作"fixed="right"align="center"class-name="small-padding fixed-width"><template slot-scope="scope"><el-buttonsize="mini"type="text"@click="delDetail(scope.$index, scope.row)">删除</el-button></template></el-table-column></el-table>调用事件:
// 排序改变sortChange(row) {// 显示加载状态,提升用户体验this.loading = true;const { column, prop, order } = row//column表示选中排序列的详细信息,prop表示选中的拍序列,order表示选中拍序列的排序规则// 其他代码逻辑if (!order) return; // 无排序时直接返回if(order){console.log("===allMySort", row)allMySort({items:this.form.items,orderBy:order}).then((res) => {console.log(" res.data",JSON.stringify( res))// this.$nextTick()this.form.items = res.data;//根据addup类型更新列表// 列表查询方法if (this.addup == "1") {this.total=this.form.items.lengththis.addList = this.form.items.slice((this.queryParams.pageNum - 1) * this.queryParams.pageSize,this.queryParams.pageNum * this.queryParams.pageSize);}//if (this.addup == "1") this.form.items=array;if (this.addup == "2"){this.dataList= this.form.items;}// 隐藏加载状态this.loading = false;}) .catch((error) => {console.error("排序请求失败", error);this.loading = false;});}this.$nextTick()// this.$forceUpdate();}},

js请求:

  //全局排序export function allSort(params) {return request({url: '/ipwms/gyp/check/allMySort',method: 'post',data: params})
}

后台全局排序方法:controller

/*** 全局排序方法*/@Log(title = "全局排序方法 ", businessType = BusinessType.OTHER)@PostMapping(value = "/allMySort")public AjaxResult allSort(@RequestBody TW06CheckDGypSortEntity tw06CheckDGypList) throws IOException {return AjaxResult.success(tW06CheckGypService.allSort(tw06CheckDGypList));}

后端service:

 /*** 全局排序方法*/@Overridepublic List<TW06CheckDGyp> allMySort(TW06CheckDGypSortEntity tw06CheckDGypEntityList) {log.info("============allMySort-start============>{}",JSONUtil.toJsonStr(tw06CheckDGypEntityList));if(ObjectUtil.isNotEmpty(tw06CheckDGypEntityList.getOrderBy())){if("ascending".equals(tw06CheckDGypEntityList.getOrderBy())){log.info("============allMySort-ascending============");Collections.sort(tw06CheckDGypEntityList.getItems(),new CustomComparator());}else if("descending".equals(tw06CheckDGypEntityList.getOrderBy())) {log.info("============allMySort-ascending============");Collections.sort(tw06CheckDGypEntityList.getItems(),new CustomComparator().reversed());}}log.info("============allMySort-end============>{}",JSONUtil.toJsonStr(tw06CheckDGypEntityList));return tw06CheckDGypEntityList.getItems();}/*public static void main(String[] args) {List<String> strings = new ArrayList<>();strings.add("03");strings.add("CQ01-01-01-1-101");strings.add("CQ01-01-01-1-90");strings.add("CQ01-01-01-1-100");strings.add("CQ01-01-01-100-100");strings.add("CQ01-01-01-2-100");strings.add("CQ01-01-01-2-100");strings.add("CQ01-01-01-2-001");strings.add("CQ01-01-01-2-002");strings.add("CQ01-01-01-2-020");strings.add("CQ01-01-01-1-2");strings.add("CQ01-02-01-1-2");strings.add("AQ01-01-01-1-3");strings.add("DQ01-01-01-1-4");strings.add("DZ01-01-01-1-1");strings.add("BC01-01-01-1-1");strings.add("BC08-01-01-1-1");strings.add("BZ01-01-01-1-1");strings.add("CZ01-01-01-1-1");strings.add("YK-01-01-1-1");strings.add("CS1-01-03-01");strings.add("测试_2_13");strings.add("退货库位");strings.add("测试库位10-3");strings.add("测试库位2-1");strings.add("测试库位2-2");Collections.sort(strings);System.out.println(strings);System.out.println("================");Collections.sort(strings,new CustomComparator());System.out.println(strings);}
*//*** 自定义比较器*/static class CustomComparator implements Comparator<TW06CheckDGyp> {private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");@Overridepublic int compare(TW06CheckDGyp gyp1, TW06CheckDGyp gyp2) {String s1 = gyp1.getStockLoc();String s2 = gyp2.getStockLoc();int len1 = s1.length();int len2 = s2.length();int i = 0, j = 0;while (i < len1 && j < len2) {char c1 = s1.charAt(i);char c2 = s2.charAt(j);if (Character.isDigit(c1) && Character.isDigit(c2)) {// 提取连续的数字部分并转为整数比较String numStr1 = extractNumber(s1, i);String numStr2 = extractNumber(s2, j);int num1 = Integer.parseInt(numStr1);int num2 = Integer.parseInt(numStr2);if (num1 != num2) {return num1 - num2;}// 跳过已比较的数字部分i += numStr1.length();j += numStr2.length();} else {// 非数字部分按字符比较if (c1 != c2) {return c1 - c2;}i++;j++;}}// 处理剩余字符return len1 - len2;}private String extractNumber(String s, int start) {Matcher matcher = NUMBER_PATTERN.matcher(s);if (matcher.find(start) && matcher.start() == start) {return matcher.group();}return "";}}

后台实现全局排序的主要逻辑是:

/*public static void main(String[] args) {List<String> strings = new ArrayList<>();strings.add("03");strings.add("CQ01-01-01-1-101");strings.add("CQ01-01-01-1-90");strings.add("CQ01-01-01-1-100");strings.add("CQ01-01-01-100-100");strings.add("CQ01-01-01-2-100");strings.add("CQ01-01-01-2-100");strings.add("CQ01-01-01-2-001");strings.add("CQ01-01-01-2-002");strings.add("CQ01-01-01-2-020");strings.add("CQ01-01-01-1-2");strings.add("CQ01-02-01-1-2");strings.add("AQ01-01-01-1-3");strings.add("DQ01-01-01-1-4");strings.add("DZ01-01-01-1-1");strings.add("BC01-01-01-1-1");strings.add("BC08-01-01-1-1");strings.add("BZ01-01-01-1-1");strings.add("CZ01-01-01-1-1");strings.add("YK-01-01-1-1");strings.add("CS1-01-03-01");strings.add("测试_2_13");strings.add("退货库位");strings.add("测试库位10-3");strings.add("测试库位2-1");strings.add("测试库位2-2");Collections.sort(strings);System.out.println(strings);System.out.println("================");Collections.sort(strings,new CustomComparator());System.out.println(strings);}
*//*** 自定义比较器*/static class CustomComparator implements Comparator<TW06CheckDGyp> {private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");@Overridepublic int compare(TW06CheckDGyp gyp1, TW06CheckDGyp gyp2) {String s1 = gyp1.getStockLoc();String s2 = gyp2.getStockLoc();int len1 = s1.length();int len2 = s2.length();int i = 0, j = 0;while (i < len1 && j < len2) {char c1 = s1.charAt(i);char c2 = s2.charAt(j);if (Character.isDigit(c1) && Character.isDigit(c2)) {// 提取连续的数字部分并转为整数比较String numStr1 = extractNumber(s1, i);String numStr2 = extractNumber(s2, j);int num1 = Integer.parseInt(numStr1);int num2 = Integer.parseInt(numStr2);if (num1 != num2) {return num1 - num2;}// 跳过已比较的数字部分i += numStr1.length();j += numStr2.length();} else {// 非数字部分按字符比较if (c1 != c2) {return c1 - c2;}i++;j++;}}// 处理剩余字符return len1 - len2;}private String extractNumber(String s, int start) {Matcher matcher = NUMBER_PATTERN.matcher(s);if (matcher.find(start) && matcher.start() == start) {return matcher.group();}return "";}}

通过以上代码就能在点击某列排序时,实现整个数据的排序,之后再在前端收到排序后的响应结果,然后再进行手动分割分页。这样就实现了分页情况下对全局数据的排序。

需要特别注意的一个点:我最开始前端请求后端的代码是这样写的:也就是我手动更新响应结果是在请求的then方法之外的,因为是异步请求,所以此时就出现了,响应结果还没有返回,页面就已经渲染完了,因为在前端开发中,当我们发起一个后端请求时,请求是异步进行的,不会阻塞 JavaScript 的执行。因此,代码会继续执行,而不会等待后端响应。所以导致了每次点击后出现的是上一次排序的结果,但是其实请求每次返回的排序数据是没问题的

// 排序改变sortChange(row) {// 显示加载状态,提升用户体验this.loading = true;const { column, prop, order } = row//column表示选中排序列的详细信息,prop表示选中的拍序列,order表示选中拍序列的排序规则// 其他代码逻辑if (!order) return; // 无排序时直接返回if(order){console.log("===allSort", row)allSort({items:this.form.items,orderBy:order}).then((res) => {console.log(" res.data",JSON.stringify( res))// this.$nextTick()this.form.items = res.data;}) .catch((error) => {console.error("排序请求失败", error);this.loading = false;});}//根据addup类型更新列表// 列表查询方法if (this.addup == "1") {this.total=this.form.items.lengththis.addList = this.form.items.slice((this.queryParams.pageNum - 1) * this.queryParams.pageSize,this.queryParams.pageNum * this.queryParams.pageSize);}//if (this.addup == "1") this.form.items=array;if (this.addup == "2"){this.dataList= this.form.items;}// 隐藏加载状态this.loading = false;this.$nextTick()// this.$forceUpdate();}}

问题分析

分析一下问题:
用户在使用 Element UI 的 el-table 进行自定义排序时,遇到了一个时序问题。具体来说:
用户点击排序三角时,页面没有立即更新
当取消排序时,页面显示了上一次排序的结果
在 sortChange 方法中,调用后端接口后,页面先显示了上一次的排序结果,然后才输出 console.log 信息
用户猜测这是因为在数据请求返回之前,前端已经进行了页面渲染。
这是一个典型的异步操作时序问题。在前端开发中,当我们发起一个后端请求时,请求是异步进行的,不会阻塞 JavaScript 的执行。因此,代码会继续执行,而不会等待后端响应。
问题在于:
allSort 是一个异步 API 调用,使用了 .then() 处理响应
在 API 调用之后,不管响应是否返回,代码继续执行,立即更新了 addList 和 dataList
由于此时 form.items 还没有被 API 响应更新,所以使用的是旧数据
当 API 响应返回后,form.items 被更新,但此时 addList 和 dataList 并没有重新计算
这就是为什么用户看到的是上一次的排序结果,因为在后端响应返回之前,前端已经使用旧数据进行了渲染。
我需要为用户提供一个解决方案,解决这个异步时序问题,确保在 API 响应返回后再更新视图

你遇到的排序更新延迟问题,确实是由于异步请求与DOM更新的时序不一致导致的。在你的sortChange方法中:

  1. allSort是一个异步API请求(返回Promise)
  2. 无论API是否返回,代码会立即执行后续的addListdataList更新
  3. 此时form.items尚未被API响应更新,导致使用旧数据渲染页面
  4. 当API响应返回后,form.items被更新,但addListdataList未重新计算

解决方案

需要将视图更新逻辑放在API响应的回调函数中,确保使用最新数据渲染页面:

sortChange(row) {const { column, prop, order } = row;if (!order) return; // 无排序时直接返回console.log("===allSort", row);// 显示加载状态,提升用户体验this.loading = true;allSort({ items: this.form.items, orderBy: order }).then((res) => {console.log("res.data", JSON.stringify(res));// 更新数据this.form.items = res.data;// 根据addup类型更新列表if (this.addup === "1") {this.total = this.form.items.length;this.addList = this.form.items.slice((this.queryParams.pageNum - 1) * this.queryParams.pageSize,this.queryParams.pageNum * this.queryParams.pageSize);} else if (this.addup === "2") {this.dataList = this.form.items;}// 隐藏加载状态this.loading = false;}).catch((error) => {console.error("排序请求失败", error);this.loading = false;});
}

关键改进说明

  1. 异步操作统一处理

    • 将所有视图更新逻辑放在then回调中,确保使用API返回的最新数据
    • 添加loading状态,避免用户在等待时误操作
  2. 移除无效的$nextTick

    • 原代码中的$nextTick()没有实际作用,因为它在API请求之外执行
    • 现在更新逻辑都在API响应回调中,自然触发Vue的响应式更新
  3. 错误处理

    • 添加catch块处理请求失败的情况
    • 无论请求成功与否,都要重置loading状态

优化建议

  1. 添加加载状态
    在表格上方添加加载提示,提升用户体验:

    <el-table :data="addup == '2' ? dataList : addList"max-height="450"stripev-loading="loading" // 添加加载状态@sort-change="sortChange"
    >
    
  2. 防抖处理
    避免用户快速点击排序时发起多次请求:

    import { debounce } from 'lodash'; // 需要安装lodashexport default {data() {return {// 其他数据...debouncedSort: null}},created() {// 初始化防抖函数,300ms内多次点击只执行一次this.debouncedSort = debounce(this.sortChange, 300);},methods: {sortChange(row) {// 原有逻辑...}}
    }
    
  3. 优化表格更新策略
    当数据量较大时,使用key属性优化渲染:

    <el-table-column v-for="(column, index) in tableColumns" :key="column.prop"// 其他属性...
    >
    

通过以上修改,排序更新将与API响应同步,确保用户看到的是最新的排序结果,同时提升页面交互体验。

相关文章:

  • 如何进行网站的seo什么平台推广效果最好
  • 手机网站 o2o百度搜索入口
  • 德钦网站建设网站优化公司怎么选
  • 房地产网站建设联系方式外贸网站搭建
  • 找人做个网站多少钱鄂州网站seo
  • 深圳专业网站建设产品运营之中的广度讲解网店运营在哪里学比较好些
  • 数据结构与算法:图论——深度优先搜索dfs
  • 数据结构知识点总结--绪论
  • vue2中前端实现图片裁剪上传到服务器
  • go语言 *和
  • IoT创新应用场景,赋能海外市场拓展
  • ARM架构鲲鹏主机BClinux离线安装docker步骤
  • “免费音乐下载工具推荐:小梦音乐绿色版体验,下载速度实测“
  • ​CentOS 7 单用户模式重置 root 密码完整指南
  • CUDA12.1+高版本pytorch复现Mtrans环境
  • MySQL 8.x配置MGR高可用+ProxySQL读写分离(三):配置ProxySQL主从分组信息
  • 索引优化SEO帮助你的网站内容更快被搜索引擎发现
  • PLA/PHA生物降解化妆品包装材料的稳定性与货架期契合性研究
  • 从UI前端到数字孪生:构建全方位的用户体验体系
  • 记录一下小程序城市索引栏开发经历
  • 战地2042(战地风云)因安全启动(Secure Boot)无法启动的解决方案以及其他常见的启动或闪退问题
  • centos 7 mysql 8 离线部署
  • HTTP响应头Content-Disposition解析
  • 前端react框架实现打包时间动态加入配置展示在指定页面
  • 【Bluedroid】蓝牙启动之 bta_dm_enable 流程梳理 源码解析
  • 开源分享:我开发了一个智能文本提取浏览器插件,彻底解决复制粘贴的烦恼