当前位置: 首页 > 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响应同步,确保用户看到的是最新的排序结果,同时提升页面交互体验。

http://www.lqws.cn/news/509959.html

相关文章:

  • 针对基于深度学习的侧信道分析(DLSCA)进行超参数的贝叶斯优化
  • VIVADO导出仿真数据到MATLAB中进行分析
  • python基础(字符串-复习)
  • 关于素数、唯一分解等内容的详细讲解(从属于GESP五级)
  • vscode + Jlink 一键调试stm32 单片机程序(windows系统版)
  • HarmonyOS Next的HiLog日志系统完全指南:从入门到精通
  • I2C设备树参数详解
  • 猿人学js逆向比赛第一届第十三题
  • 多线程的同步
  • docker部署oracle数据库
  • Leetcode+JAVA+回溯1
  • i.MX平台下 Linux + FreeRTOS 协同启动与通讯全解(含Yocto实战与核心机制分析)
  • ​CentOS 7 单用户模式重置 root 密码完整指南
  • 无人机神经网络模块运行与技术难点
  • Dify与代理商奇墨科技为企业定制AI应用开发专属方案,适配多样化业务需求
  • vue-25( Composition API 与现有的 Options API 组件集成)
  • 采用ArcGIS10.8.2 进行插值图绘制
  • DEYOLO 全面复现,将双增强跨模态目标检测网络 DEYOLO 融合到 YOLOFuse 框架
  • C++字符大小
  • P0/P1级重大故障根因分析:技术挑战与无指责复盘文化
  • Leaking GAN
  • 医学数据分析实战:冠心病发病因素可视化
  • git学习资源
  • 轨迹降噪API及算法
  • 应用层协议 HTTP
  • 洛谷P1092 [NOIP 2004 提高组] 虫食算
  • openai-agents实现out_guardrails
  • DataSophon 1.2.1集成Flink 1.20并增加JMX 监控
  • [ruby on rails] ActiveJob中 discard_on,retry_on和 rescue_from的应用
  • 用福昕阅读器打开pdf文件,整个程序窗口自动缩小的问题