需求:表格的数据结构并不是固定不变的,表头,表体和tip都是根据配置,去请求接口动态生成,废话不多说,直接上代码。
效果图:
实现逻辑梳理:
表头数据获取需要单独调一个接口,这个接口的数据包含表头名称和对应的值,表体部分,则需要调取另外一个接口,这个接口数据包含表头名称,tips提示,类型,长度等,但不包含表头对应的值,所以需要将两个数组进行对比,然后进行动态赋值:
表头数据结构:
表体数据结构:
DOM结构:
<!-- 数据表格开始 -->
<div class="tableData" v-if="attrListArr.length > 0">
<!-- 表格列表,新增 type="selection" 列 -->
<el-table
v-if="showTable"
class="customer-table-style"
v-model="multipleSelection"
:data="tableData"
v-loading="loading"
height="6.4rem"
style="width: 100%"
border
@selection-change="handleSelectionChange"
>
<el-table-column fixed type="selection" width="55"></el-table-column>
<el-table-column fixed prop="ts" label="ts" width="230rem" showOverflowTooltip></el-table-column>
<el-table-column
v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label" :min-width="column.width">
<template slot="header" slot-scope="scope">
<el-tooltip v-if="column.description" :content="column.description" placement="top">
<span>{{ column.prop }}</span>
</el-tooltip>
<span v-else>{{ column.prop }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" v-if="tableData.length > 0" label="操作" width="110">
<template slot-scope="scope">
<icon-button title="修改" type="xiugai" @click="getOperateDialog(scope.row, 'edit')"></icon-button>
<icon-button class="last-icon-btn" title="删除" type="shanchu" @click="deleteAll('table-select', scope.row)"></icon-button>
</template>
</el-table-column>
</el-table>
<pagination v-if="total>0" :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="total"
@pagination="getList"/>
</div>
js数据结构:
//查询列表
async getList() {
this.loading = true;
// 重新渲染表格DOM树,防止表格样式错乱
this.showTable = false
this.listQuery.id = this.detailData.id
let newArr = []
// 获取表头数据结构
const {data:res} = await getMetaDataAttrByMetaDataId(this.detailData.metadataId)
this.attrListArr = res.data
if (this.attrListArr.length == 0) {
// 如果表头没有数据,则表格也没存在意义,直接置为空
this.tableData = []
return
}
// 获取数据集新增过的表体属性列表
const {data} = await getMetaDataAttrList(this.listQuery)
if (data.data && data.code == '0') {
// 构造新数组:数据集列表顺序和元数据属性列表保持一致
newArr = data.data.records.map(attr => {
// 单独构造ts属性数据,并进行数据回显
const newObj = { ts: attr.ts || '' };
this.attrListArr.forEach(item => {
// 过滤表格数据,将属性值为undefined,转换为--
newObj[item.name] = attr[item.name] !== undefined ? attr[item.name] : '--';
});
return newObj;
});
// 处理表体数据,将boolean值,转换为字符串
this.tableData = newArr.map(record => {
return Object.keys(record).reduce((acc, key) => {
acc[key] = typeof record[key] === 'boolean' ? record[key].toString() : record[key];
return acc;
}, {});
});
// 重新构造表体数据,进行组织各种所需要数据
this.columns = [
...this.attrListArr.map(column => ({
prop: column.name,
label: column.label,
width: '230rem',
align: column.align,
sortable: column.sortable,
description: column.description, // 确保 description 被正确赋值
showOverflowTooltip: column.showOverflowTooltip
}))
];
this.total = data.data.total
} else {
this.tableData = []
this.total = 0
this.columns = [] // 清空表头
}
this.loading = false;
this.showTable = true
},
完整代码:index.vue
<template>
<div class="content-list api-store-box">
<div class="detail-title" style="width: calc(100% - 2rem);">
<div>
<span>数据集 - </span>
<span>{{ detailData.name }}</span>
</div>
<div><img @click="goBack" src="@/assets/images/back.png"></div>
</div>
<div style="height:.36rem"></div>
<div class="card">
<!-- 表单开始 -->
<div class="form">
<el-form ref="ruleForm" :inline="true" label-width="0.7rem" size="small" @submit.native.prevent>
<el-form-item label="属性时间">
<el-date-picker v-model.trim="dateTime" @change="pickerChange" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" size="small" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button class="btn-onelevel" type="primary" size="small" @click="search" style="margin-right:10px">
搜索
</el-button>
<el-button type="warning" class="btn-clear" size="small" @click="clear" style="margin-right:10px">
清除
</el-button>
<el-button class="btn-onelevel" type="primary" @click="getOperateDialog('', 'create')">新增</el-button>
<el-button class="btn-onelevel" type="primary" size="small" @click="getExportExcel" style="margin-right:10px">
从表格文件导入
</el-button>
<el-button class="btn-onelevel" type="primary" size="small" @click="getExportService" style="margin-right:10px">
从数据服务导入
</el-button>
<el-button type="warning" class="btn-clear" size="small" @click="getCreateVersion" style="margin-right:10px">
创建数据集版本
</el-button>
<!-- 删除数据按钮替换为下拉菜单 -->
<el-dropdown @command="handleDeleteCommand">
<el-button type="primary" class="btn-clear" size="small">
删除数据<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="selected">删除选中数据</el-dropdown-item>
<el-dropdown-item command="all">删除查询结果</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-form-item>
</el-form>
</div>
<!-- 表单结束 -->
<!-- 数据表格开始 -->
<div class="tableData" v-if="attrListArr.length > 0">
<!-- 表格列表,新增 type="selection" 列 -->
<el-table
v-if="showTable"
class="customer-table-style"
v-model="multipleSelection"
:data="tableData"
v-loading="loading"
height="6.4rem"
style="width: 100%"
border
@selection-change="handleSelectionChange"
>
<el-table-column fixed type="selection" width="55"></el-table-column>
<el-table-column fixed prop="ts" label="ts" width="230rem" showOverflowTooltip></el-table-column>
<el-table-column
v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label" :min-width="column.width">
<template slot="header" slot-scope="scope">
<el-tooltip v-if="column.description" :content="column.description" placement="top">
<span>{{ column.prop }}</span>
</el-tooltip>
<span v-else>{{ column.prop }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" v-if="tableData.length > 0" label="操作" width="110">
<template slot-scope="scope">
<icon-button title="修改" type="xiugai" @click="getOperateDialog(scope.row, 'edit')"></icon-button>
<icon-button class="last-icon-btn" title="删除" type="shanchu" @click="deleteAll('table-select', scope.row)"></icon-button>
</template>
</el-table-column>
</el-table>
<pagination v-if="total>0" :limit.sync="listQuery.pageSize" :page.sync="listQuery.pageNo" :total="total"
@pagination="getList"/>
</div>
<div class="on-data">暂无数据</div>
</div>
<formDialog ref="formRefDialog" @getInit="getList"></formDialog>
<VersionFormDialog ref="formVersionDialog"></VersionFormDialog>
<exportExcelDialog ref="exportExcelRefDialog" @getInit="getList"></exportExcelDialog>
<exportServiceDialog ref="exportRefServiceDialog" @getInit="getList"></exportServiceDialog>
</div>
</template>
<script>
import formDialog from "./formDialog.vue";
import { dictitemList } from "@/api/myapi";
import VersionFormDialog from "./VersionFormDialog.vue";
import exportExcelDialog from "./exportExcelDialog.vue";
import exportServiceDialog from "./exportServiceDialog.vue";
import Pagination from "@/components/Pagination/index.vue";
import {sortMixin, tableScrollTop} from "@/utils/mixin";
import {deleteSimulatorListPage, simulatorListPage} from "@/api/myapi";
import { deleteDataAttribute, getMetaDataAttrByMetaDataId, getMetaDataAttrList, getDataSetDetail } from "@/api/AIModel";
export default {
name: "APIStore",
components: { Pagination, formDialog, exportServiceDialog, exportExcelDialog, VersionFormDialog },
mixins: [sortMixin, tableScrollTop],
data() {
return {
showTable: false,
touchTableDesign: false, // 记录是否初始化
columnsBak: '',
columnsConfig: {
auto: ['name'], // 宽度auto
enabled: ['opts'], // 不可设宽 huashuoqueshi xiangdangnvren guangpgjiu
isDisabled: ['name', 'opts'], // 不可更改的config: auto & enabled
},
loading: false,
multipleSelection: [],
selectedTsValues: [], // 新增数组,用于存储选中的 ts 值
tableData: [],
total: 0,
listQuery: {
pageNo: 1,
pageSize: 15,
startTime: '',
endTime: ''
},
// 栏位
columns: [], // 初始化为空数组,动态生成
dateTime: [],
ruleForm: {
name: '',
},
detailData: {},
attrListArr: []
}
},
created() {},
activated() {
this.setBread()
this.getDetail()
},
deactivated() {
this.touchTableDesign = false
},
beforeDestroy() {
this.$tableDesign.clear()
this.$tableExport.clear()
},
methods: {
setBread() {
let arr = [{name: '数据开发', isNode: true}, {name: '数据集', isNode: true}, {name: '详情', isNode: true}]
this.$store.commit("setBreadList", arr)
},
getInitTable(data) {
this.listQuery.metadataId = data.metadataId
this.getList()
},
getExportData() {
let searchInfo = {
pageNo: this.listQuery.pageNo,
pageSize: this.listQuery.pageSize,
name: this.ruleForm.name
}
let paginationTotalInfo = [this.listQuery.pageNo, this.total]
let paginationLimitInfo = [this.listQuery.pageNo, this.listQuery.pageSize]
return {searchInfo, paginationTotalInfo, paginationLimitInfo, callback: simulatorListPage, noSearchParams: true}
},
// 时间查询
//时间选择
pickerChange() {
if (this.dateTime) {
this.listQuery.startTime = this.dateTime[0]
this.listQuery.endTime = this.dateTime[1]
} else {
this.listQuery.startTime = ''
this.listQuery.endTime = ''
}
},
//查询列表
async getList() {
this.loading = true;
// 重新渲染表格DOM树,防止表格样式错乱
this.showTable = false
this.listQuery.id = this.detailData.id
let newArr = []
// 获取表头数据结构
const {data:res} = await getMetaDataAttrByMetaDataId(this.detailData.metadataId)
this.attrListArr = res.data
if (this.attrListArr.length == 0) {
// 如果表头没有数据,则表格也没存在意义,直接置为空
this.tableData = []
return
}
// 获取数据集新增过的表体属性列表
const {data} = await getMetaDataAttrList(this.listQuery)
if (data.data && data.code == '0') {
// 构造新数组:数据集列表顺序和元数据属性列表保持一致
newArr = data.data.records.map(attr => {
// 单独构造ts属性数据,并进行数据回显
const newObj = { ts: attr.ts || '' };
this.attrListArr.forEach(item => {
// 过滤表格数据,将属性值为undefined,转换为--
newObj[item.name] = attr[item.name] !== undefined ? attr[item.name] : '--';
});
return newObj;
});
// 处理表体数据,将boolean值,转换为字符串
this.tableData = newArr.map(record => {
return Object.keys(record).reduce((acc, key) => {
acc[key] = typeof record[key] === 'boolean' ? record[key].toString() : record[key];
return acc;
}, {});
});
this.columns = [
...this.attrListArr.map(column => ({
prop: column.name,
label: column.label,
width: '230rem',
align: column.align,
sortable: column.sortable,
description: column.description, // 确保 description 被正确赋值
showOverflowTooltip: column.showOverflowTooltip
}))
];
this.total = data.data.total
} else {
this.tableData = []
this.total = 0
this.columns = [] // 清空表头
}
this.loading = false;
this.showTable = true
},
async getDetail() {
const { data: res } = await getDataSetDetail(this.$route.query.attributeId)
if (res.code == '0' && res.data) {
this.detailData = res.data
this.getList();
}
},
async getDictionaryAttributeList(enumId) {
const { data: res } = await dictitemList({ dictId: enumId })
if (res.code == '0' && res.data) {
return res.data.map(item => ({
id: item.itemValue,
dictName: item.itemText
}))
} else {
return []
}
},
// 表格勾选操作
handleSelectionChange(rows) {
this.multipleSelection = rows;
this.selectedTsValues = rows.map(row => row.ts); // 提取选中的 ts 值并推入数组
},
search() {
this.listQuery.pageNo = 1;
this.getList();
},
clear() {
this.dateTime = []
this.listQuery.startTime = ''
this.listQuery.endTime = ''
this.listQuery.pageNo = 1
this.getList();
},
//新增
getOperateDialog(data, type) {
this.$refs.formRefDialog.openDialog(data, type, this.listQuery.metadataId)
},
// 从数据服务导入
getExportService() {
this.$refs.exportRefServiceDialog.openDialog(this.listQuery.attributeId)
},
// 从表格中导入
getExportExcel() {
this.$refs.exportExcelRefDialog.openDialog(this.listQuery.attributeId)
},
// 创建数据集版本
getCreateVersion(){
let apiParams = {
startTime: this.listQuery.startTime,
endTime: this.listQuery.endTime
}
this.$refs.formVersionDialog.openDialog(apiParams)
},
// 查看数据属性
handleDetailsClick(row) {
const scrollTop = this.$refs.tablelist.$refs.table.bodyWrapper.scrollTop
this.$router.push({
path: '/index/emindex/queryservice/coolinghome/coolingdetails',
query: {
projectId: row.projectId,
id: row.id,
gateWayId:row.gateWayId,
gateWayName:row.gateWayName,
scrollTop: scrollTop,
formCache: encodeURIComponent(JSON.stringify(this.ruleForm)),
listQueryCache: encodeURIComponent(JSON.stringify(this.listQuery))
}
})
},
// 新增处理删除命令的方法
handleDeleteCommand(command) {
if (command === 'selected') {
if (this.multipleSelection.length === 0) {
this.$message({
message: "请先勾选数据",
type: "warning",
offset: 120
});
return;
}
this.deleteAll('select');
} else if (command === 'all') {
if (this.listQuery.startTime === '' && this.listQuery.endTime === '') {
this.$message({
message: "请先查询数据",
type: "warning",
offset: 120
});
return;
}
this.deleteAll('search');
}
},
// 删除查询结果的方法
async deleteAll(type, row) {
let apiParams = []
let confirmMessage = ''
if (type === 'select') {
this.listQuery.pageNo = 1;
apiParams = this.selectedTsValues
confirmMessage = '此操作将删除选中的数据, 是否继续?'
} else if (type === 'search') {
if (this.listQuery.startTime && this.listQuery.endTime) {
apiParams = [this.listQuery.startTime, this.listQuery.endTime]
confirmMessage = `此操作将删除 ${this.listQuery.startTime} 至 ${this.listQuery.endTime} 区间的所有数据, 是否继续?`
} else {
confirmMessage = '此操作将清空当前数据集所有数据,是否继续?'
}
} else {
apiParams = [row.ts]
confirmMessage = '确认删除所选内容吗?'
}
this.$confirm(confirmMessage, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
const {data} = await deleteDataAttribute(this.$route.query.attributeId, apiParams)
if (data.code == '0') {
this.$message({
message: "删除成功",
type: "success",
offset: 120
})
if (type === 'search') {
// 仅清空请求参数,不影响dateTime回显
this.dateTime = ''
this.listQuery.startTime = '';
this.listQuery.endTime = '';
}
this.getList()
} else {
this.$message({
message: data.message,
type: "error",
offset: 120
})
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
goBack() {
if (this.$route.query.pageType == 'otherPage') {
this.$router.back()
} else {
this.$router.push({
path: '/index/aiindex/dataservicebuild/datacollect',
query: {
metadataId: this.$route.query.metadataId,
pageNo: this.$route.query.pageNo,
handelTreeFlag: this.$route.query.handelTreeFlag
}
})
}
},
}
}
</script>
<style lang="scss" scoped>
.api-store-box{
height: calc(100% - 1.4rem);
}
/deep/ .iconbutton {
margin-right: 0.1rem;
}
/deep/.last-icon-btn {
margin-right: 0!important;
}
.on-data {
margin-top: 3.3rem;
color: #909399;
text-align: center;
}
</style>
anchor.vue
同上anchor.vue
END...