尝试在Vue3中使用Element-plus的el-table来实现单元格合并(即rowspan/colspan)功能,在数据量很大的场景下发现有性能问题,于是用原生的方式来实现,另外,生成的表格还能实现表头固定表体滚动的功能,具体代码如下:
.vue组件代码:
<template>
<div
class="custom-table-wrapper"
v-html="mapStore.trajectoryDetailsTable"
v-loading="loading"
element-loading-text="加载中..."
@click="handleClick"
></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import {
getAjaxPersonnelData,
getAjaxPersonnelTableData,
} from "@/api/getAjaxData.js";
import pinia from "@/store/pinia";
import { useMapStore } from "@/store/map";
const mapStore = useMapStore(pinia);
let loading = ref(true);
// 点击单元格时
function handleClick(event) {
const target = event.target;
const obj = JSON.parse(
target.getAttribute("data").replace(/\n/g, "").replace(/%/g, '"')
);
if (obj.hasOwnProperty("personName") && !obj.hasOwnProperty("lon")) {
// ...
} else {
if (obj.lat && obj.lon) {
// ...
} else {
// ...
}
}
}
onMounted(async () => {
// ...
});
onBeforeUnmount(() => {
// ...
});
</script>
<style scoped>
.custom-table-wrapper {
height: calc(100% - 31px) !important;
overflow: auto;
}
.custom-table-wrapper /deep/ .custom-table {
width: 100%;
border-collapse: collapse;
background: linear-gradient(to right,rgba(255,255,255, 0.25),rgba(94, 182, 255, 0.35),rgba(255,255,255,0));
}
.custom-table-wrapper /deep/ .custom-table thead {
position: -webkit-sticky;
/* Safari 支持 */
position: sticky;
top: 0;
/* 距离顶部的位置 */
z-index: 1;
/* 确保表头在其他元素之上 */
}
.custom-table-wrapper /deep/ .custom-table thead tr{
border: 1px solid rgba(255, 255, 255, 0.1);
}
.custom-table-wrapper /deep/ .custom-table thead th {
border: 1px solid rgba(255, 255, 255, 0.1);
background: linear-gradient(to right,rgba(255,255,255,0),rgba(94, 182, 255, 0.35),rgba(255,255,255,0));
padding: 8px;
color: rgba(255, 255, 255, 1);
text-shadow: 1px 1px 1px #000;
text-align: left;
font-size: 15px;
font-weight: bold;
}
.custom-table-wrapper /deep/ tr:nth-child(odd) {
/* background: rgba(78, 93, 110, 1); */
background: linear-gradient(to right,rgba(255,255,255,0),rgba(94, 182, 255, 0.05),rgba(255,255,255,0));
}
.custom-table-wrapper /deep/ tr:nth-child(even) {
/* background: rgba(78, 93, 110, 1); */
background: linear-gradient(to right,rgba(255,255,255,0),rgba(94, 182, 255, 0.15),rgba(255,255,255,0));
}
.custom-table-wrapper /deep/ tr:nth-child(odd):hover {
background: linear-gradient(to right,rgba(255,255,255,0),rgba(94, 182, 255, 0.15),rgba(255,255,255,0));
cursor: pointer;
}
.custom-table-wrapper /deep/ tr:nth-child(even):hover {
background: linear-gradient(to right,rgba(255,255,255,0),rgba(94, 182, 255, 0.05),rgba(255,255,255,0));
cursor: pointer;
}
.custom-table-wrapper /deep/ .custom-table td {
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 5px 10px;
color: rgba(255, 255, 255, 1);
text-shadow: 1px 1px 1px #000;
word-break: break-all;
word-wrap: break-word;
overflow-wrap: break-word;
font-size: 14px;
}
</style>
getAjaxData.js代码:
import axios from "axios";
import {
ElMessage
} from "element-plus";
import "element-plus/theme-chalk/el-message.css";
import personalData from "./data.js";
import pinia from "@/store/pinia";
import {
useMapStore
} from "@/store/map";
const mapStore = useMapStore(pinia);
const getAjaxPersonnelData = async (obj) => {
// 获取异步数据
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(personalData);
}, 1000);
});
};
const getAjaxPersonnelTableData = (obj) => {
getAjaxPersonnelData(obj).then(res => {
let tableString =
'<table class="custom-table"><thead><tr><th width="100">姓名</th><th>身份证号码</th><th width="200">拍摄位置</th><th width="150">拍摄时间</th><th width="150">经度</th><th width="150">纬度</th></tr></thead><tbody>';
for (let n = 0; n < res["personTrack"].length; n++) {
mapStore.trajectoryNameList.push({
value: res["personTrack"][n]["personName"],
label: res["personTrack"][n]["personName"],
});
for (
let m = 0; m < res["personTrack"][n]["trackList"].length; m++
) {
if (res["personTrack"][n]["trackList"].length === 0) {
tableString += `<tr>
<td rowspan="${res["personTrack"][n]["trackList"].length}" data='{%personName%: %${res["personTrack"][n]["personName"]}%}'>${res["personTrack"][n]["personName"]}</td>
<td rowspan="${res["personTrack"][n]["trackList"].length}" data='{%personName%: %${res["personTrack"][n]["personName"]}%}'>${res["personTrack"][n]["personImageUrl"]}</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
`;
} else {
if (m === 0) {
tableString += `<tr>
<td rowspan="${res["personTrack"][n]["trackList"].length}" data='{%personName%: %${res["personTrack"][n]["personName"]}%}'>${res["personTrack"][n]["personName"]}</td>
<td rowspan="${res["personTrack"][n]["trackList"].length}" data='{%personName%: %${res["personTrack"][n]["personName"]}%}'>${res["personTrack"][n]["personImageUrl"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchShotTime"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchLongitude"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchLatitude"]}</td>
</tr>
`;
} else {
tableString += `
<tr>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchShotTime"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchLongitude"]}</td>
<td data='{%personName%: %${res["personTrack"][n]["personName"]}%, %lon%: ${res["personTrack"][n]["trackList"][m]["matchLongitude"]}, %lat%: ${res["personTrack"][n]["trackList"][m]["matchLatitude"]}, %matchShotTime%: %${res["personTrack"][n]["trackList"][m]["matchShotTime"]}%, %matchDevicePosition%: %${res["personTrack"][n]["trackList"][m]["matchDevicePosition"]}%}'>${res["personTrack"][n]["trackList"][m]["matchLatitude"]}</td>
</tr>
`;
}
}
}
}
tableString += "</tbody></table>";
mapStore.trajectoryDetailsTable = tableString;
}).catch(error => {
ElMessage.error(error);
});
}
export {
getAjaxPersonnelData,
getAjaxPersonnelTableData
};
data.js数据格式:
let data = {
personTrack: [
{
personId: 1,
personNumber: "xxx",
personName: "xxx",
personSex: null,
personImageUrl: "xxx.jpg",
trackList: [
{
matchId: 25489,
matchSimilarity: 0.953387975692749,
matchCoordinatesFix: null,
matchDevicePosition: "xxx",
matchLongitude: x,
matchLatitude: x,
matchShotTime: "xxx",
}
],
},
{
personId: 2,
personNumber: "xxx",
personName: "xxx",
personSex: null,
personImageUrl: "xxx.jpg",
trackList: [
{
matchId: 25509,
matchSimilarity: 0.9613999724388123,
matchCoordinatesFix: null,
matchDevicePosition: "xxx",
matchLongitude: x,
matchLatitude: x,
matchShotTime: "xxx",
},
],
}
],
queryInfo: {
nameList: null,
numberList: null,
minSimilarity: 0.9,
maxSimilarity: 1,
startDateTime: "2024-09-14 00:00:00",
endDateTime: "2024-10-14 23:59:59",
},
};
export default data;
Pinia
pinia.js:
import { createPinia } from 'pinia';
const pinia = createPinia();
export default pinia;
map.js
// 引入defineStore用于创建store
import { defineStore } from "pinia";
// 定义并暴露一个store
export const useMapStore = defineStore("defineMap", {
// 动作
actions: {},
// 状态
state() {
return {
trajectoryDetailsTable: "",
};
},
// 计算
getters: {},
});