vue+elementui el-table,解决无论如何翻页,之前页码勾选过的数据依然是选中状态(表格翻页数据持久化)

 

<template>
<div class="persistent-table-container">
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
<!-- 接口返回的数据 -->
<div>
<el-table
ref="tableRef"
:data="topList"
border
stripe
highlight-current-row
:row-key="getRowKey"
class="w-full">
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
<el-table-column prop="age" label="年龄" width="80"></el-table-column>
<el-table-column prop="gender" label="性别" width="80">
<template slot-scope="scope">
<span v-if="scope.row.gender === 1"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
</el-table>
</div>
<div style="width:100%;height:20px;background:#ccc"></div>
<!-- 下边进行操作改变的数据 -->
<div class="overflow-x-auto">
<el-table
ref="sourceTableRef"
:data="currentTableData"
border
stripe
highlight-current-row
@select="onTableSelect"
@selection-change="handleSelectionChange"
:row-key="getRowKey"
 
class="w-full">
 
<el-table-column type="selection" width="55" :reserve-selection="true"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
<el-table-column prop="age" label="年龄" width="80"></el-table-column>
<el-table-column prop="gender" label="性别" width="80">
<template slot-scope="scope">
<span v-if="scope.row.gender === 1"></span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
</el-table>
</div>
 
<div class="flex justify-between items-center mt-4">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>

<div class="flex items-center gap-2">
<span class="text-gray-600">当前选择项数:</span>
<el-badge :value="alreadyData.length" class="ml-2">
<el-button size="mini" type="info">查看</el-button>
</el-badge>
</div>
 
<div class="flex flex-col md:flex-row md:items-center justify-between mb-4 gap-4">
<div class="flex items-center gap-4">
<el-button type="success" icon="el-icon-upload" @click="submitSelection">提交选择</el-button>
</div>
</div>
</div>

</div>
</template>

<script>
export default {
name: 'PersistentTable',
data() {
return {
// 表格数据
tableData: [],
// 当前页数据
currentTableData: [],
// 每页条数
pageSize: 10,
// 当前页码
currentPage: 1,
// 总记录数
total: 0,
// 已选择的完整数据对象
selectedItems: [],
// 模拟API请求延迟
apiDelay: 500,
topList:[
{
"id": 3,
"name": "李四",
"age": 56,
"gender": 0,
"address": "南京市鼓楼区新街口"
},
{
"id": 4,
"name": "吴十",
"age": 30,
"gender": 1,
"address": "成都市武侯区天府广场"
},
{
"id": 15,
"name": "赵六",
"age": 46,
"gender": 0,
"address": "武汉市武昌区黄鹤楼"
},
{
"id": 27,
"name": "王五",
"age": 51,
"gender": 1,
"address": "武汉市武昌区黄鹤楼"
}
],
alreadyData:[]
}
},
 
mounted() {
this.fetchData();
this.getAlreadyList()
},
 
methods: {
getAlreadyList(){
this.alreadyData = JSON.parse(JSON.stringify(this.topList))
},
// 获取表格数据
fetchData() {
console.log(111,'-=-=');
 
// 显示加载状态
if (this.$refs.tableRef) {
this.$refs.tableRef.loading = true;
}
 
// 模拟API请求延迟
setTimeout(() => {
// 如果是第一页,初始化表格数据
if (this.currentPage === 1) {
this.tableData = this.generateMockData(100);
this.total = this.tableData.length;
}
 
// 计算当前页数据
const start = (this.currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
this.currentTableData = this.tableData.slice(start, end);
this.currentTableData.forEach(item=>{
this.topList.forEach(type=>{
if(item.id==type.id){
this.$refs.sourceTableRef.toggleRowSelection(item, true);
}
})
})

 
// 隐藏加载状态
if (this.$refs.tableRef) {
this.$refs.tableRef.loading = false;
}
}, this.apiDelay);
},
 
// 生成模拟数据
generateMockData(count) {
const names = ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'];
const genders = [1, 0];
const addresses = [
'北京市朝阳区建国路88号',
'上海市浦东新区张江高科技园区',
'广州市天河区珠江新城',
'深圳市南山区科技园',
'杭州市西湖区西溪湿地',
'南京市鼓楼区新街口',
'成都市武侯区天府广场',
'武汉市武昌区黄鹤楼'
];
 
return Array.from({ length: count }, (_, i) => ({
id: i + 1,
name: names[Math.floor(Math.random() * names.length)],
age: Math.floor(Math.random() * 40) + 20,
gender: genders[Math.floor(Math.random() * genders.length)],
address: addresses[Math.floor(Math.random() * addresses.length)]
}));
},
 
// 设置行唯一标识
getRowKey(row) {
return row.id;
},
//复选框改变会走这个方法,
//两个参数,rows:改变后剩余的已勾选的元素
//row:当前操作的行数据
onTableSelect(rows, row) {
//根据剩余的已勾选元素中是否存在当前操作行的数据判断,如果包含则是选中操作,否则就是取消选中。就是判断是勾选还是取消勾选
let selected = rows.length && rows.indexOf(row) !== -1;
//如果是选中操作,把选中行数据加入上方table
//如果是取消操作,判断上方table中存在当前id,删除该元素
if(selected){//勾选
this.alreadyData.push(row);
}else{//取消
let index = this.alreadyData.findIndex(item => item.id === row.id);//获取当前操作行,在alreadyData数据中的索引值
if(index!=-1){//如果值不是-1,说明他存在alreadyData数据中,并给删除掉
this.alreadyData.splice(index, 1);
}
}
},
// 处理选择变化
handleSelectionChange(selection) {
// console.log(selection,'=====');
 
// // 更新已选择的完整数据对象
// this.selectedItems = [...selection];

// let qzArr = Array.from(new Set(this.selectedItems.map(item => item.id))).map(id => {
// return this.selectedItems.find(item => item.id === id);
// })

 
 
 
 
// // 打印当前选择项
// console.log('去重前选择项:', this.selectedItems);
// console.log('去重前选择项ID:', this.selectedItems.map(item => item.id));
// console.log('=======================================================');
// console.log('去重后的项',qzArr);
// console.log('去重后的项ID',qzArr.map(item => item.id));
// console.log('=======================================================');
// console.log('最初上边数据',this.topList);

 
 
},
 
// 处理每页条数变化
handleSizeChange(newSize) {
this.pageSize = newSize;
this.currentPage = 1;
this.fetchData();
},
 
// 处理页码变化
handleCurrentChange(newPage) {
this.currentPage = newPage;
this.fetchData();
},
 
 
// 提交选择
submitSelection() {
console.log('要提交的数据',this.alreadyData);
 
},
 

}
}
</script>

<style scoped>
.persistent-table-container {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}

pre {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
white-space: pre-wrap;
word-break: break-all;
}
</style>
 

 

 

附加一个:根据上方表格数据,下方表格进行实时动态勾选或取消勾选

<template>
  <div class="container">
    <h3>上方表格 (ID数据源)</h3>
    <el-table
      ref="upperTableRef"
      :data="upperTableData"
      stripe
      border
      :row-key="row => row.id">
      
      <el-table-column prop="id" label="ID" width="100" align="center"></el-table-column>
      <el-table-column prop="name" label="姓名" width="120"></el-table-column>
      <el-table-column prop="age" label="年龄" width="80" align="center"></el-table-column>
      <el-table-column label="操作" width="120">
        <template #default="scope">
          <el-button 
            size="mini" 
            type="danger" 
            @click="removeRow(scope.row.id)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    
    <div class="divider"></div>
    
    <h3>下方表格 (自动勾选匹配ID)</h3>
    <el-table
      ref="lowerTableRef"
      :data="lowerTableData"
      stripe
      border
      :row-key="row => row.id">
      
      <el-table-column type="selection" width="55" align="center"></el-table-column>
      <el-table-column prop="id" label="ID" width="100" align="center"></el-table-column>
      <el-table-column prop="name" label="姓名" width="120"></el-table-column>
      <el-table-column prop="age" label="年龄" width="80" align="center"></el-table-column>
      <el-table-column prop="status" label="状态" width="100" align="center"></el-table-column>
    </el-table>
    
    <div class="stats" v-if="selectedCount > 0">
      <el-tag type="success" effect="plain">
        下方表格已自动勾选 {{ selectedCount }} 条匹配数据
      </el-tag>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 上方表格数据
      upperTableData: [
        { id: 2, name: '李四', age: 30 },
        { id: 3, name: '王五', age: 22 },
        { id: 4, name: '赵六', age: 35 },
        { id: 5, name: '孙七', age: 28 }
      ],
      
      // 下方表格数据
      lowerTableData: [
        { id: 1, name: '张三', age: 25, status: '未匹配' },
        { id: 2, name: '李四', age: 30, status: '匹配中' },
        { id: 3, name: '王五', age: 22, status: '匹配中' },
        { id: 4, name: '赵六', age: 35, status: '匹配中' },
        { id: 5, name: '孙七', age: 28, status: '匹配中' },
        { id: 6, name: '周八', age: 40, status: '未匹配' }
      ]
    };
  },
  computed: {
    // 计算当前匹配并被勾选的行数
    selectedCount() {
      return this.lowerTableData.filter(row => 
        this.upperTableData.some(upperRow => upperRow.id === row.id)
      ).length;
    }
  },
  created() {
    // 初始化时同步一次勾选状态
    this.$nextTick(() => {
      this.syncLowerTableSelection();
    });
  },
  methods: {
    // 删除上方表格行
    removeRow(id) {
      // 从上方表格数据中移除
      const index = this.upperTableData.findIndex(row => row.id === id);
      if (index !== -1) {
        this.upperTableData.splice(index, 1);
        
        // 同步下方表格的勾选状态
        this.syncLowerTableSelection();
        
        this.$message({
          type: 'success',
          message: `已删除ID为 ${id} 的数据`
        });
      }
    },
    
    // 同步下方表格的勾选状态
    syncLowerTableSelection() {
      // 确保表格DOM已更新
      this.$nextTick(() => {
        // 清空下方表格的所有选中状态
        if (this.$refs.lowerTableRef) {
          this.$refs.lowerTableRef.clearSelection();
          
          // 遍历下方表格数据,勾选与上方表格ID匹配的行
          this.lowerTableData.forEach(row => {
            if (this.upperTableData.some(upperRow => upperRow.id === row.id)) {
              this.$refs.lowerTableRef.toggleRowSelection(row, true);
            }
          });
          
          // 更新状态文本
          this.updateStatusTexts();
        }
      });
    },
    
    // 更新状态文本
    updateStatusTexts() {
      this.lowerTableData.forEach(row => {
        row.status = this.upperTableData.some(upperRow => upperRow.id === row.id)
          ? '匹配中'
          : '未匹配';
      });
    }
  }
};
</script>

<style scoped>
.container {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}

.divider {
  height: 20px;
  border-bottom: 1px solid #ebeef5;
  margin: 30px 0;
}

.stats {
  margin-top: 15px;
  text-align: center;
}
</style>

 

posted @ 2025-05-22 09:55  夏小夏吖  阅读(214)  评论(0)    收藏  举报