<div class="g-bd" ref="boxRef">
<t-table class="m-table" row-key="index" :data="data" :columns="columns" :row-class-name="rowClassName"
:pagination="pagination" bordered resizable lazy-load v-model:displayColumns="displayColumns"
v-model:columnControllerVisible="columnControllerVisible">
</t-table>
</div>
Tdesign 表格实现自动撑满方法
1、利用CSS 撑满
.m-table {
height: calc(100% - 64px);
.t-table__content {
height: 100%;
}
}
2、使用js 设置高度
const boxRef = ref(null);
const boxHeight = computed(() => boxRef.value?.offsetHeight);
const updateContainer = () => {
console.log('Box height:', boxHeight.value)
}
效果:
简单版参考组件,未做封装,整理 index.vue
<template>
<div class="g-doc-wrapper" ref="docRef">
<div class="g-doc">
<div class="g-hd" ref="headerRef">
<t-list>
<t-list-item class="list-item" style="padding: 0">
<span class="page-title">产品信息维护 </span>
<template #action>
<t-space>
<t-checkbox class="item-tips" @change="onChangeFilter">隐藏筛选 {{ boxHeight }}</t-checkbox>
<FilterDrawer></FilterDrawer>
</t-space>
</template>
</t-list-item>
</t-list>
<div class="m-form" v-if="!HideFilter">
<t-form ref="form" :data="formData" :layout="formData.layout" scroll-to-first-error="smooth" @reset="onReset"
@submit="onSubmit" layout="inline" :label-width="0">
<t-form-item label="" name="name">
<t-input v-model="formData.name">
<template #prefixIcon>
<span class="m-prefix">名字:</span>
</template>
</t-input>
</t-form-item>
<t-form-item label="" name="password">
<t-input v-model="formData.password">
<template #prefixIcon>
<span class="m-prefix">密码:</span>
</template>
</t-input>
</t-form-item>
<t-form-item>
<t-space size="small">
<t-button theme="primary" type="submit">查询</t-button>
<t-button theme="default" variant="base" type="reset">重置</t-button>
</t-space>
</t-form-item>
</t-form>
</div>
<t-divider />
<t-list>
<t-list-item class="list-item" style="padding-top: 0;">
<t-space size="24px">
<t-button theme="primary">
<template #icon><add-icon /></template>
新建
</t-button>
</t-space>
<template #action>
<t-space>
<t-link class="item-tips" theme="default" @click="columnControllerVisible = true">
<AdjustmentIcon />
列表项 {{ columnControllerVisible }}
</t-link>
</t-space>
</template>
</t-list-item>
</t-list>
</div>
<div class="g-bd" ref="boxRef">
<t-table :height="boxHeight" class="table-header-class m-table" row-key="index" :data="data" :columns="columns"
:row-class-name="rowClassName" :pagination="pagination" bordered resizable lazy-load
v-model:displayColumns="displayColumns" v-model:columnControllerVisible="columnControllerVisible">
</t-table>
</div>
</div>
</div>
</template>
<script lang="jsx">
export default {
name: 'DashboardBase',
};
</script>
<script setup lang="jsx">
import { ref, watch, h, computed, onMounted, onUnmounted, onBeforeUnmount, reactive, nextTick } from 'vue';
import { AdjustmentIcon, AddIcon } from 'tdesign-icons-vue-next';
import { ErrorCircleFilledIcon, CheckCircleFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-vue-next';
// import { AddIcon, CloudUploadIcon, SearchIcon, CloudDownloadIcon, DiscountIcon } from 'tdesign-icons-vue-next';
import { MessagePlugin } from 'tdesign-vue-next';
import FilterDrawer from "@/components/FilterDrawer/index.vue"
const HideFilter = ref(false);
const docRef = ref(null);
const headerRef = ref(null);
const boxRef = ref(null);
const boxHeight = ref(500)
const updateViewportHeight = () => {
let height = boxRef.value?.offsetHeight - 64
boxHeight.value = height >= 0 ? height : 0
};
onMounted(() => {
updateViewportHeight(); // 初始化视口高度
window.addEventListener('resize', updateViewportHeight, false); // 监听窗口大小变化
});
onUnmounted(() => {
window.removeEventListener('resize', updateViewportHeight); // 移除监听
});
const onChangeFilter = (value) => {
HideFilter.value = value;
}
const columnControllerVisible = ref(false)
const columnControllerConfig = computed(() => ({
// 列配置按钮位置
// placement: placement.value,
// 用于设置允许用户对哪些列进行显示或隐藏的控制,默认为全部字段
// fields: ['channel', 'detail.email', 'createTime', 'data1', 'data2', 'data3', 'data4'],
// 弹框组件属性透传
dialogProps: { preventScrollThrough: true },
// 列配置按钮组件属性透传
buttonProps: {
content: '显示列控制', default: <div>
上传文件 你看看
</div>
}
// 数据字段分组显示
// groupColumns: groupColumn.value
// ? [
// {
// label: '指标维度',
// value: 'index',
// columns: ['applicant', 'status', 'channel'],
// },
// {
// label: '次要维度',
// value: 'secondary',
// columns: ['detail.email', 'createTime'],
// },
// {
// label: '数据维度',
// value: 'data',
// columns: ['data1', 'data2', 'data3', 'data4'],
// },
// ]
// : undefined,
}));
const visibleFilter = ref(false)
const clickLink = () => {
console.log(123)
visibleFilter.value = !visibleFilter.value
}
// 左侧固定列发生变化时
watch(
HideFilter,
(val) => {
console.log("变化啊", headerRef.value?.offsetHeight)
nextTick(() => {
updateViewportHeight();
})
},
{ immediate: true },
);
const formData = reactive({
layout: 'inline',
name: '',
password: '',
});
const onReset = () => {
MessagePlugin.success('重置成功');
};
const onSubmit = ({ validateResult, firstError }) => {
if (validateResult === true) {
MessagePlugin.success('提交成功');
} else {
console.log('Validate Errors: ', firstError, validateResult);
MessagePlugin.warning(firstError);
}
};
const displayColumns = ref(["index", "applicant", "status", "channel", "detail", "matters", "time", "createTime"]);
const statusNameListMap = {
0: { label: '审批通过', theme: 'success', icon: <CheckCircleFilledIcon /> },
1: { label: '审批失败', theme: 'danger', icon: <CloseCircleFilledIcon /> },
2: { label: '审批过期', theme: 'warning', icon: <ErrorCircleFilledIcon /> },
};
function getData(count) {
const data = [];
for (let i = 0; i < count; i++) {
data.push({
index: i + 1,
applicant: ['贾明', '张三', '王芳'][i % 3],
status: i % 3,
channel: ['电子签署', '纸质签署', '纸质签署'][i % 3],
detail: {
email: ['w.cezkdudy@lhll.au', 'r.nmgw@peurezgn.sl', 'p.cumx@rampblpa.ru'][i % 3],
},
matters: ['宣传物料制作费用', 'algolia 服务报销', '相关周边制作费', '激励奖品快递费'][i % 4],
time: [2, 3, 1, 4][i % 4],
createTime: ['2022-01-01', '2022-02-01', '2022-03-01', '2022-04-01', '2022-05-01'][i % 4],
});
}
return data;
}
const TOTAL = 38;
function getColumns(h, { fixedLeftColumn, fixedRightColumn }) {
return [
{
align: 'left',
colKey: 'applicant',
title: '申请人',
foot: () => <b style="font-weight: bold">表尾信息</b>,
width: '120',
fixed: fixedLeftColumn ? 'left' : undefined,
},
{
colKey: 'status',
title: '申请状态',
width: '150',
cell: (h, { row }) => {
return (
<t-tag shape="round" theme={statusNameListMap[row.status].theme} variant="light-outline">
{statusNameListMap[row.status].icon}
{statusNameListMap[row.status].label}
</t-tag>
);
},
},
{ colKey: 'channel', title: '签署方式', width: '120' },
{ colKey: 'detail.email', title: '邮箱地址', width: '180', ellipsis: true },
{ colKey: 'matters', title: '申请事项', width: '180', ellipsis: true },
{ colKey: 'createTime', title: '申请时间', width: '120' },
{
colKey: 'operation',
title: '操作',
cell: (h, { row }) => (
<t-link hover="color" theme="primary">
{row.status === 0 ? '查看详情' : '再次申请'}
</t-link>
),
width: 120,
foot: '-',
fixed: fixedRightColumn ? 'right' : undefined,
},
];
}
const data = getData(TOTAL);
// 表尾有一行数据
const footData = [{ index: 'footer-row-1', type: '全部类型', description: '-' }];
const columns = ref([]);
// 重要:如果在预渲染场景下,初次渲染的表格宽度和最终呈现宽度不一样,请异步设置表头吸顶
// type 可选值:foot 和 body
function rowClassName({ type }) {
if (type === 'foot') return 't-tdesign__custom-footer-tr';
return 't-tdesign__custom-body-tr';
}
function onDragSortChange({ newData }) {
columns.value = newData;
}
const pagination = ref({ defaultCurrent: 1, defaultPageSize: 5, total: TOTAL });
const fixedLeftColumn = ref(true);
const fixedRightColumn = ref(true);
// 左侧固定列发生变化时
watch(
fixedLeftColumn,
(val) => {
columns.value = getColumns(h, {
fixedLeftColumn: val,
fixedRightColumn: fixedRightColumn.value,
});
},
{ immediate: true },
);
// 右侧固定列发生变化时
watch(
fixedRightColumn,
(val) => {
columns.value = getColumns(h, {
fixedLeftColumn: fixedLeftColumn.value,
fixedRightColumn: val,
});
},
{ immediate: true },
);
</script>
<style scoped lang="less">
.row-container:not(:last-child) {
margin-bottom: 16px;
}
.g-doc-wrapper {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
border: solid 16px transparent;
}
.g-doc {
width: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 100%;
.g-hd {
height: auto;
}
.g-bd {
flex: 1;
height: 100%;
overflow-y: auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
position: relative;
box-sizing: border-box;
&::-webkit-scrollbar {
width: 8px;
background: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 6px;
border: 2px solid transparent;
background-clip: content-box;
background-color: var(--td-scrollbar-color);
}
}
}
.page-title {
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 18px;
color: #303133;
}
.m-form {
margin-top: 24px;
}
.list-item {
padding-left: 0;
padding-right: 0;
.item-tips {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #606266;
}
}
.m-prefix {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #606266;
}
</style>
<style lang="less">
.m-table {
height: calc(100% - 64px);
.t-table__content {
height: 100%;
}
}
</style>
实战示意图