el-table 多表格弹窗嵌套数据显示异常错乱问题

  • el-table 多表格弹窗嵌套数据显示异常错乱问题已关闭评论
  • 94 次浏览
  • A+
所属分类:Web前端
摘要

使用vue+element开发报表功能时,需要列表上某列的超链接按钮弹窗展示,在弹窗的el-table列表某列中再次使用超链接按钮点开弹窗,以此类推多表格弹窗嵌套,本文以弹窗两次为例
最终效果如下示例页面


1、业务背景

使用vue+element开发报表功能时,需要列表上某列的超链接按钮弹窗展示,在弹窗的el-table列表某列中再次使用超链接按钮点开弹窗,以此类推多表格弹窗嵌套,本文以弹窗两次为例
最终效果如下示例页面
el-table 多表格弹窗嵌套数据显示异常错乱问题

2、具体实现和问题抛出

<template>     <div class="el_main">       <el-table         stripe         style="width: 100%"         v-loading="loading"         row-key="Id"         :data="list"       >         <el-table-column label="ID" prop="Id" min-width="3"> </el-table-column>         <el-table-column label="类型" prop="Type" min-width="5">           <template slot-scope="scope">             {{ formatTaskType(scope.row.Type) }}           </template>         </el-table-column>         <el-table-column label="详情" prop="TaskTitle" min-width="10" show-overflow-tooltip="true"></el-table-column>         <el-table-column           label="详情弹窗"            min-width="3">           <template slot-scope="scope">             <el-button @click="handleClick(scope.row)" type="text">查看</el-button>           </template>         </el-table-column>         <el-table-column label="创建时间" prop="AddTime" min-width="5">           <template slot-scope="scope" v-if="scope.row.AddTime">             {{ (scope.row.AddTime * 1000) | formatDate(2) }}           </template>         </el-table-column>       </el-table>     </div>     <!-- 详情弹窗 -->     <el-dialog       title="详情弹窗"       :visible.sync="detailInfoDialogVisible"       append-to-body       width="50%">         <el-table             stripe             style="width: 100%"             v-loading="loading"             row-key="Id"             height="300" max-height="650"             :data="detailInfo">             <el-table-column label="ID" prop="TaskId" min-width="80"></el-table-column>             <el-table-column label="名称" prop="TaskName" min-width="65"></el-table-column>             <el-table-column label="成功数量" prop="SuccessNum" min-width="22"></el-table-column>             <el-table-column label="失败数量" prop="ErrorNum" min-width="22"></el-table-column>             <el-table-column label="状态列表" min-width="22">               <template slot-scope="scope">                 <el-button @click="handleStatusListClick(scope.row)" type="text">查看</el-button>               </template>             </el-table-column>             <el-table-column label="队列列表" min-width="30">               <template slot-scope="scope">                 <el-button @click="handleQueueDataClick(scope.row)" type="text">查看</el-button>               </template>             </el-table-column>           </el-table>     </el-dialog>     <!-- 状态列表弹窗 -->     <el-dialog       title="状态弹窗"       :visible.sync="statusListDialogVisible"       append-to-body       width="30%">         <el-table             stripe             style="width: 100%"             v-loading="loading"             row-key="Id"             height="300" max-height="300"             :data="statusListInfo">             <el-table-column label="ID" prop="Id" min-width="80" show-overflow-tooltip="true"> </el-table-column>             <el-table-column label="标题" prop="Title" min-width="80" show-overflow-tooltip="true"></el-table-column>             <el-table-column label="返回信息" prop="Msg" min-width="80" show-overflow-tooltip="true"></el-table-column>           </el-table>     </el-dialog>     <!-- 队列列表弹窗 -->     <el-dialog       title="队列弹窗"       :visible.sync="queueDataDialogVisible"       append-to-body       width="30%">         <el-table             stripe             style="width: 100%"             v-loading="loading"             row-key="Id"             height="300" max-height="300"             :data="queueDataInfo">             <el-table-column label="ID" prop="Id" min-width="80" show-overflow-tooltip="true"> </el-table-column>             <el-table-column label="名称" prop="Name" min-width="80" show-overflow-tooltip="true"></el-table-column>           </el-table>     </el-dialog> </template>  <script type="text/ecmascript-6"> import { GetXXXReportList, ExportXXXReportList } from '@/api/reportManage'  const urlQuery = [   'id|number',   'type|number',   'currPage|number',   'pageSize|number', ]  export default {   components: {   },    data () {     return {       id: '',       type: '',       collectTime: '',       loading: false,       list: [],       currPage: 1,       pageSize: 10,       counts: 0,       detailInfo: [], // 详情弹窗       detailInfoDialogVisible: false,       statusListInfo: [], // 状态列表弹窗       statusListDialogVisible: false,       queueDataInfo: [], // 队列列表弹窗       queueDataDialogVisible: false,       typeArray: [         {           value: 1,           label: '类型一',         },         {           value: 2,           label: '分类二',         },         {           value: 3,           label: '分类三',         },         {           value: 4,           label: '分类四',         },         {           value: 5,           label: '分类五',         },         {           value: 6,           label: '分类六',         },       ],       exportLoading: false,     }   },   created () {     this._getList(true)   },   methods: {     async _getList (init = false) {       this.loading = true       if (init) {         this.currPage = 1       }       let startTime, endTime       if (this.collectTime) {         startTime = this.collectTime[0] / 1000         endTime = this.collectTime[1] / 1000 + 86399       }       this._setQuery(urlQuery)       try {         const data = await GetXXXReportList({           Id: this.id || 0,           StartTime: startTime || 0,           EndTime: endTime || 0,           Type: this.type || 0,           CurrPage: this.currPage,           PageSize: this.pageSize,         })         this.list = data.List         this.counts = data.Counts        } catch (error) {         this.counts = 0         this.list = []       }       this.loading = false     },     search () {       this._getList(true)     },     reset () {       this.id = ''       this.type = ''       this.collectTime = ''       this.list = []       this.counts = 0       this._getList(true)     },     pageChange () {       this._getList()     },     pageSizeChange (val) {       this.pageSize = val       this._getList(true)     },     handleClick (row) {       if (row.Type === 1) {         this.detailInfoDialogVisible = true         this.detailInfo = row.detailInfo        } else if (row.Type === 2) {         this.xxxDialogVisible = true         this.xxxInfo = row.xxx       } else if (row.Type === 3) {         this.xxxDialogVisible = true         this.xxxInfo = row.xxx       }     },     handleStatusListClick (row) {       this.statusListDialogVisible = true       this.statusListInfo = row.StatusList     },     handleQueueDataClick (row) {       this.queueDataDialogVisible = true       this.queueDataInfo = row.queueData     },     // 导出     async exportData () {       this.exportLoading = true       let startTime, endTime       if (this.collectTime) {         startTime = this.collectTime[0] / 1000         endTime = this.collectTime[1] / 1000 + 86399       }       try {         const data = await ExportXXXReportList({           Id: this.id || 0,           StartTime: startTime || 0,           EndTime: endTime || 0,           Type: this.type || 0,         })         var raw = window.atob(data)         var uInt8Array = new Uint8Array(data.length)         for (var i = 0; i < raw.length; i++) {           uInt8Array[i] = raw.charCodeAt(i)         }         const url = window.URL.createObjectURL(new Blob([ uInt8Array ], { type: 'application/vnd.ms-excel' }))         const link = document.createElement('a')         link.style.display = 'none'         link.href = url         link.setAttribute('download', 'xxxx报表.xlsx')         document.body.appendChild(link)         link.click()         document.body.removeChild(link)       } catch (error) {         this.exportLoading = false       }       this.exportLoading = false     },   }, } </script>  <style lang="scss"> </style>  

3、分析问题

这里有几个可能的原因和建议来解决这个问题:
①数据问题:首先确保你的数据源是正确的。检查你的表格数据是否有任何错误或遗漏。
②嵌套表格的渲染时机:如果你的嵌套表格(子表格)是在父表格的某一行展开时才渲染的,那么你需要确保子表格的数据在正确的时机进行加载。如果数据加载过早,可能会导致异常。
③弹窗的v-if与v-show:如果你使用了v-if来控制弹窗的显示与隐藏,那么每次弹窗打开都会重新渲染弹窗内的内容。这可能会导致表格的重新初始化,使用v-show可能会避免这个问题。但需要注意的是,v-show只是在视觉上隐藏元素,元素仍然会被渲染。
④表格的key:如前面所说,Vue使用key来追踪节点的身份。如果在嵌套表格的场景中,你使用了相同的key,可能会导致身份识别混乱。确保每个表格都有一个独特的key。
⑤样式冲突:确保没有其他样式影响到表格或弹窗的正常显示。特别是当你使用了自定义样式或与Element UI样式冲突的其他UI库时。
⑥组件版本:确保你使用的Element UI是最新的版本。旧版本可能存在已知的错误,而在新版本中可能已经被修复。

4、解决问题

下面我从表格的key角度解决下问题
1)尝试给每个弹窗的el-table加个key -- 未解决数据错乱的问题
示例代码如下:

<el-table 	:key="Id" 	stripe 	style="width: 100%" 	v-loading="loading" 	row-key="Id" 	height="300" max-height="300"> </el-table> 

2)尝试给每个弹窗的el-table加个唯一的key -- 解决数据错乱的问题
示例代码如下:

<el-table 	:key="Id" 	stripe 	style="width: 100%" 	v-loading="loading" 	row-key="Id" 	height="300" max-height="300"> </el-table> 

虽然此种方法解决了我们的问题,但是考虑到每次打开弹窗都会生成随机数存在一定风险性,具体分析如下:

    随机数改变了每次渲染时的key值,打破了Vue的节点身份追踪机制。
    在这种情况下,由于每次渲染都有一个新的随机数作为key,Vue会将该组件视为全新的节点,从而重新渲染。这样可以避免由于身份追踪导致的问题,例如在嵌套表格场景中可能出现的报错。
    然而,需要注意的是,使用随机数作为key并不是一个推荐的做法。因为key的主要作用是帮助Vue高效地识别和追踪节点的身份,以便进行差异化更新。随机数作为key会破坏这一机制,可能导致性能下降和潜在的问题。
    因此,尽管使用随机数作为key可以解决某些情况下的报错,但并不是一个优雅的解决方案。更好的方式是仔细排查问题,找到导致报错的根本原因,并采取相应的措施进行修复。如果实在无法找到其他解决方案,再考虑使用随机数作为临时方案。但在长期开发中,仍然建议寻求更合适、更稳定的解决方案。

3)尝试给每个弹窗的el-table加个唯一的key(固定不是随机数) -- 解决数据错乱的问题(推荐)
示例代码如下:

<el-table 	:key="generateKey(scheduledDataDownloadInfo)" 	stripe 	header-row-class-name="bos_table_header" 	style="width: 100%" 	v-loading="loading" 	row-key="Id" 	height="300" max-height="650" 	:data="scheduledDataDownloadInfo"> </el-table> 

在methods中添加方法

// 生成唯一的key,可以根据具体情况定义 generateKey (data) {   const uniqueIdentifier = data.map(item => item.Id).join('_')   return `table_${uniqueIdentifier}` }, 

至此,更合适、更稳定的解决方案完成,我们开头提到的问题得以解决。有更好办法或者见解的同学欢迎评论区留言,互相学习。

若本文有帮助到阅读本文的同学,欢迎点赞、关注、收藏,互相学习交流