Appearance
表格组件实际渲染数据与原数据不一致
影响范围:主要是对于合并行的影响
影响因素:表格数据排序、vxe-table触发虚拟滚动之后
影响原因:主要原因为表格实际渲染的数据变了,表格排序之后,实际渲染的数据为排序后的数据,触发虚拟滚动后(原理为仅渲染滚动范围的数据),而行合并时如果使用原数据,则会因为数据不一致,计算出需要合并的行数量与原数据不一样,而出现错乱
一、表格数据排序
1、错误示例:
当行合并使用的是索引的方式时,只有索引方法处理行合并时会受到排序的影响,此时表格数据排序之后,数据的实际索引就发生了变化,如果仍使用原表格数据去处理索引,则会发生错乱的情况(如下所示)。
vue
<!-- 采购计划汇总表 -->
<template>
<div class="bgwh p16 bs-border-radius mt5 font-container">
<el-table ref="table" border :data="tableData" size="small" :height="tableHeight" :header-cell-style="TABLE_HEADER_CELL_STYLE" :span-method="spanMethod">
<el-table-column type="index" width="65" align="center" label="序号"></el-table-column>
<el-table-column v-for="(item, i) in activeTableTitle" :key="item.columnCode + i" :prop="item.columnCode" :label="item.columnName" header-align="center" :align="item.dataType === 'string' ? 'left' : 'right'" :min-width="item.columnWidth + 'px'" :fixed="item.isLocking === 1 ? 'left' : false" :show-overflow-tooltip="item.dataType === 'string'" :sortable="['asc', 'desc'].includes(item.sort)" :sort-orders="sortOrders(item.sort)">
<template slot-scope="scope">
<template v-if="item.dataType !== 'decimal'">
<template v-if="item.columnCode === 'billNo'">
<span class="bs-color-primary poi" @click="handlePreview(scope.row.billId)">{{scope.row.billNo}}</span>
</template>
<template v-else>
{{scope.row[item.columnCode]}}
</template>
</template>
<template v-else>
{{scope.row[item.columnCode] | formatCurrency}}
</template>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="bs-table-foot fix">
<div class="l">
<span class="b"> 合计:</span>
<span class="pl5 pr5 bs-color-orange">{{moneyTotal | formatCurrency}} 元</span>
</div>
<el-pagination class="r" @size-change="handleSizeChange" @current-change="handleCurrentPageChange" :current-page="page.currentPage" :page-sizes="page.sizes" :page-size="page.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="page.total"></el-pagination>
</div>
<!-- 侧边栏设置 -->
<v-pur-table-col-setting :columns="setItemEles" :table-field-data="allTableTitle" :table-height="tableHeight" @update="handleUpdateSetting" />
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { GET_LOGIN_INFO, GET_CONTEXT_AGY_ACB, GET_AGY_TREE, IS_FONT_BIG } from '@/store/login';
import {
getEnumerate,
innerHeight,
TABLE_HEADER_CELL_STYLE,
pagination,
} from '@/mixin/page';
import { getSettingColField, getBillType } from '@/modules/pur/common/service';
import form from '@/mixin/form';
import vPurTableColSetting from '@/modules/pur/components/pur-table-col-setting';
import fetch from '@/config/fetch';
import util from '@/assets/js/util';
export default {
name: 'PUR_REPT_PURPLAN_SUMMARY',
mixins: [innerHeight, pagination],
components: {
vPurTableColSetting,
},
data() {
return {
allTableTitle: [], //侧边栏字段配置
setItemEles: [
{
//侧边栏设置项要素
prop: 'columnName',
label: '列名',
editor: 'span',
},
{
prop: 'isShow',
label: '采购表显示列',
editor: 'checkbox',
},
{
prop: 'columnWidth',
label: '列宽像素',
editor: 'input',
},
{
prop: 'isLocking',
label: '锁定列',
editor: 'checkbox',
},
{
prop: 'extField06',
label: '合并行',
editor: 'checkbox',
},
{
prop: 'sort',
label: '排序',
editor: 'select',
},
],
TABLE_HEADER_CELL_STYLE,
};
},
methods: {
/**
* 初始化页面
* 1、查询条件里的单位赋默认值
* 2、获取表格列要素
* 3、获取资金来源选项数据
* 4、获取采购品目选项数据
* 5、获取采购计划选项数据
*/
async initPage() {
try {
this.allTableTitle = await getSettingColField('pur/pur-rept-purplan-summary');
this.getPageInfo();
} catch (err) {
console.error(err);
}
},
/**
* 获取数据
*/
getPageInfo() {
this.page.currentPage = 1;
this.pageData = [];
this.getTableData();
},
/**
* 获取页面数据
*/
getTableData() {
this.$loading();
fetch
.post('/pur/bill/getPurplanSummary', {
agyCodes: [this.GET_CONTEXT_AGY_ACB.agyCode],
mofDivCode: this.GET_LOGIN_INFO.mofDivCode,
pageIndex: this.page.currentPage,
pageSize: this.page.pageSize,
})
.then(({ data }) => {
this.$loadingClose();
this.pageData = data.result;
this.page.total = data.total;
this.moneyTotal = data.moneyTotal;
this.$nextTick(() => {
this.$refs.table.doLayout();
});
})
.catch((err) => {
this.$loadingClose();
console.error(err);
this.$message({
type: 'error',
message: err.msg,
});
});
},
/**
* 更新表头列设置数据
* 设置值
* 刷新表格
*/
handleUpdateSetting(val) {
this.allTableTitle = val;
this.$nextTick(() => {
this.$refs.table.doLayout();
});
},
/**
* 合并列操作
* 1、获取不参与合并操作的列索引值(extField06为‘0’标识不合并,第一列序号为写死列)
* 2、参与合并操作的列
* 获取行主键id
* 查找对应主键id第一条数据索引
* 查找对应主键id数据长度
* 若当前行数据为对应主键id第一条数据,合并对应长度行
*/
spanMethod({ row, column, rowIndex, columnIndex }) {
let notMatchIndex = [];
this.activeTableTitle.forEach((item, i) => {
if (item.extField06 === '0') {
notMatchIndex.push(i + 1);
}
});
if (!notMatchIndex.includes(columnIndex)) {
// 合并行使用的索引方案,使用原数据处理行合并逻辑
// let { tableData } = this.$refs.table;
let { billId } = row;
let firstIndex = this.tableData.findIndex((e) => e.billId === billId);
let length = this.tableData.filter((e) => e.billId === billId).length;
if (firstIndex === rowIndex) {
return {
rowspan: length,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 1,
};
}
}
},
/**
* 排序方式
*/
sortOrders(code) {
switch (code) {
case 'asc':
return ['ascending'];
case 'desc':
return ['descending'];
default:
return [null];
}
},
},
computed: {
...mapGetters([GET_LOGIN_INFO, GET_CONTEXT_AGY_ACB, GET_AGY_TREE, IS_FONT_BIG]),
/**
* 表格高度
*/
tableHeight() {
return this.IS_FONT_BIG ? this.innerHeight - 210 : this.innerHeight - 195;
},
/**
* 激活列
*/
activeTableTitle() {
return this.allTableTitle.filter((e) => e.isShow === 1);
},
},
created() {
this.initPage();
},
};
</script>
<style lang="scss" scoped>
@import '~@/modules/pur/common/pur.scss';
</style>
2、正确示例:
将span-method方法修改为下方示例:
vue
// el-table需要通过ref直接获取:
let { tableData } = this.refs.table;
// vxe-table需要通过ref调用API去获取值:
let { tableData } = this.refs.table.getTableData();
vue
spanMethod({ row, column, rowIndex, columnIndex }) {
let notMatchIndex = [];
this.activeTableTitle.forEach((item, i) => {
if (item.extField06 === '0') {
notMatchIndex.push(i + 1);
}
});
if (!notMatchIndex.includes(columnIndex)) {
// 合并行使用的索引方案,排序后,实际渲染数据和原数据不一致,索引也就不对了,所以需要获取实际的表格数据
let { tableData } = this.$refs.table;
let { billId } = row;
let firstIndex = tableData.findIndex((e) => e.billId === billId);
let length = tableData.filter((e) => e.billId === billId).length;
if (firstIndex === rowIndex) {
return {
rowspan: length,
colspan: 1,
};
} else {
return {
rowspan: 0,
colspan: 1,
};
}
}
},
二、vxe-table虚拟滚动
- vxe-table触发虚拟滚动后,实际渲染的数据只有一部分,所以当计算行合并使用原始数据时,合并的行数并不正确,也会导致表格错位。
- 解决方案同上,数据源使用vxe-table的实际渲染的数据即可:
vue
let { tableData } = this.$refs.table.getTableData();