尝试在Vue3中使用Element-plus的el-table来实现单元格合并(即rowspan/colspan)功能,在数据量很大的场景下发现有性能问题,于是用原生的方式来实现,另外,生成的表格还能实现表头固定表体滚动的功能,具体代码如下:
.vue组件代码:
<template><divclass="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: {},
});