1、需求如图:展开行里的数据要保持对齐表头,而且表头是可以自定义的(可自定义设置显示或者隐藏哪些字段),最好先看另一篇文章:antdv table组件封装成全局组件以及携带自定义表头展示
2、组件里使用公用表格组件,思路:表格嵌套表格,展开行里的表格展示的字段与外面表格保持一致,且不展示表头,其它不展示的内容赋值为空值,并且只显示一条数据即可!
// index.vue
<table-box
v-if="isShow"
:columns="state.columns"
:dataSource="state.tableData"
:pagination="pagination"
:routerName="routerName"
:rowSelection="rowSelection"
@change="handleTableChange"
@expand="handleExpanded"
:expandedRowRender="expandedRowRender"
@getTableColumns="getTableColumns"
>
<template #showPostLabelList="{ record }">
<span v-if="record.showPostLabelList && record.showPostLabelList.length > 0">
<span>{{ record.showPostLabelList[0].name }}</span>
</span>
<span v-else>--</span>
</template>
<template #showIssueItem="{ record }">
<span v-if="record.showIssueItem && record.showIssueItem.length > 0">
<span>{{ record.showIssueItem[0].name }}</span>
</span>
<span v-else>--</span>
</template>
<template v-slot:expandedRowRender="{ record }">
<div class="expand-box" v-if="record.tableShow">
<a-table
:showHeader="false"
:columns="state.rowColumns"
:data-source="record.innerData"
:pagination="false"
:row-key="(record) => record.id"
>
<template #showPostLabelList="{ record }">
<div v-if="record.showPostLabelList && record.showPostLabelList.length > 1" class="row-item-box">
<span class="row-item" v-for="item in record.showPostLabelList.slice(1, record.showPostLabelList.length)" :key="item">{{ item.name }}</span>
</div>
<span v-else>--</span>
</template>
<template #showIssueItem="{ record }">
<div v-if="record.showIssueItem && record.showIssueItem.length > 1" class="row-item-box">
<span class="row-item" v-for="item in record.showIssueItem.slice(1, record.showIssueItem.length)" :key="item">{{ item.name }}</span>
</div>
<span v-else>--</span>
</template>
</a-table>
</div>
</template>
<template #action="{ record }">
<a-popover title="">
<template #content>
<div
class="action-box"
style="display: flex; flex-direction: column"
>
<a @click="handleEdit(true, record)">修改</a>
<a @click="handleDel(record.id)" href="javascript:void(0)"
>删除</a
>
</div>
</template>
<MenuOutlined />
</a-popover>
</template>
</table-box>
<script setup>
const state = reactive({
tableData: [],
selectedRowKeys: [],
columns: [
{
title: "类型名称",
dataIndex: "typeName",
key: "typeName",
ellipsis: true,
},
{
title: "简称",
dataIndex: "abbreviation",
key: "abbreviation",
ellipsis: true,
},
{
title: "协议接入",
dataIndex: "agreement",
key: "agreement",
ellipsis: true,
},
{
title: "标签类型",
dataIndex: "labelType",
key: "labelType",
ellipsis: true,
},
{
title: "上报监测项",
dataIndex: "showPostLabelList",
key: "showPostLabelList",
slots: {
customRender: "showPostLabelList",
},
ellipsis: true,
width: 200
},
{
title: "下发监测项",
dataIndex: "showIssueItem",
slots: {
customRender: "showIssueItem",
},
key: "showIssueItem",
},
{
key: "action",
isSlot: true,
slots: {
customRender: "action",
},
}
],
rowColumns: []
});
// 获取表格数据
const getTableData = (formData) => {
for (let i = 0; i < 7; i++) {
state.tableData.push({
id: i,
key: i,
typeName: "窨井水位计",
abbreviation: "窨井",
agreement: "s651_5",
showPostLabelList: [
{
id: 1,
name: '泵机状态'
},
{
id: 2,
name: 'TOC'
},
{
id: 3,
name: '开机台数'
}
],
labelType: '泵机状态',
showIssueItem: [
{
id: 1,
name: '水温'
},
{
id: 2,
name: '水压'
},
{
id: 3,
name: '电压'
}
],
});
}
loading.value = false;
};
// 点击展开行
const handleExpanded = async (obj) => {
if (obj.expanded) {
let index = 0;
state.tableData.forEach((t, i) => {
t.id == obj.record.id ? (index = i) : "";
});
obj.record.innerData = await getDeviceData(); // 获取当前展开行表格数据,除了上报和下发监测项外,其它为空,且只有一行数据
obj.record.tableShow = true;
}
};
// 获取展开行的表格的表头数据
const getTableColumns = (data) => {
state.rowColumns = data;
};
const getDeviceData = () => {
let arr = [];
for (let i = 1; i < 2; i++) {
arr.push({
id: i,
key: i,
typeName: "",
abbreviation: "",
agreement: "",
showPostLabelList: [
{
id: 1,
name: '泵机状态'
},
{
id: 2,
name: 'TOC'
},
{
id: 3,
name: '开机台数'
}
],
labelType: '',
showIssueItem: [
{
id: 1,
name: '水温'
},
{
id: 2,
name: '水压'
},
{
id: 3,
name: '电压'
}
],
});
}
return arr;
};
onMounted(async () => {
if (
sessionStorage.tableHeadObj &&
JSON.parse(sessionStorage.tableHeadObj)[routerName.value]
) {
let arr = JSON.parse(sessionStorage.tableHeadObj)[routerName.value];
for (let i = 0; i < state.columns.length; i++) {
if (arr.indexOf(state.columns[i].key) == -1) {
state.columns[i].isShow = false;
}
}
isShow.value = true;
} else {
isShow.value = true;
}
getTableData(formData);
});
</script>
3、表格公用组件修改: 把表头数据修改后传给父组件渲染展开行里的表格
// tableBox.vue
const emit = defineEmits(["change", "expand", "getTableColumns"]);
// 监听表头数据的变化,响应到展开行里的表头数据
watchEffect(() => {
emit('getTableColumns', tableColumns.value);
})
const getTableColumns = () => {
emit('getTableColumns', tableColumns.value)
}
let showColumns = createColumns();
showColumns = showColumns.filter((i) => i.isShow == true);
tableColumns.value = showColumns; // 表格显示的所有项,isShow 为true时
getTableColumns();
4、因为有展开行和选中的宽度,所以展开行里表格样式要稍微改一改哟
// index.vue
<style lang="less" scoped>
.deviceTypeManage {
// 展开行表格的样式
.expand-box {
margin-left: 68px;
.row-item-box {
.row-item {
display: flex;
flex-direction: column;
margin-bottom: 16px;
&:last-child {
margin-bottom: 0px;
}
}
}
}
:deep(.ant-table-expanded-row td:nth-child(2)) {
padding: 0px 16px;
border: none;
}
:deep(.ant-table-expanded-row .ant-table) {
background: #fbfbfb;
}
}
</style>
5、完成,下次再也不用担心这些骚设计了,效果如图:
6、注意一点儿:后台返回的数据一般没有key属性的,因为在表格子组件展开行有使用到key值,所以请求回来的数据手动加key属性:
// 获取表格数据
const getTableData = (formData) => {
loading.value = true;
request(url.GET_DEVICE_STATUS_LIST, "get", {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...formData,
})
.then(res => {
state.selectedRowKeys = [];
totalCount.value = res.count.total;
inlineCount.value = res.count.onlineCount;
offlineCount.value = res.count.offLineCount;
state.tableData = res.page.rows.map(i => ({...i, key: i.id}));
pagination.total = res.page.total;
})
.finally(() => {
loading.value = false;
})
};