Element UI:如何精确控制 el-descriptions 组件的列宽

Element UI 的 el-descriptions 在少数情况下会出现内容挤压形变,不美观的问题

在基于 Vue.js 和 Element UI 的后台管理系统开发中,el-descriptions 组件是我们展示详情数据的得力助手。然而,在实际项目中,我们经常会遇到一个痛点:默认的列宽分配不够理想,导致某些字段内容显示不全,或者布局不够美观

本文将分享一种实用的方法,通过 DOM 操作精确控制 el-descriptions 的列宽,帮助你在类似场景中轻松应对列宽调整的需求。

一、问题的起源

el-descriptions 组件默认采用表格布局,其列宽由浏览器根据内容自动计算。这种自适应布局在大多数情况下表现良好,但在以下场景中可能不尽如人意:

  • 标签宽度不一致:某些字段的 label 较长,导致内容区域被挤压
  • 特定列需要固定宽度:如“操作”列需要固定宽度,内容列需要自适应
  • 多列布局下显示不均衡:3 列布局时,某些列内容过多,破坏整体美观

以我最近遇到的实际需求为例:一个宣传品申请详情页面,包含近 30 个字段,采用 3 列布局。业务方要求标签列宽度适中,内容区域能够完整展示长文本,同时保持各列之间的视觉平衡。

二、解决方案思路

Element UI 的 el-descriptions 最终渲染为原生的 <table> 元素。我们可以利用这个特点,通过以下步骤实现列宽控制:

  1. 获取组件的 DOM 引用
  2. 找到内部的 <table> 元素
  3. 创建 <colgroup> 并定义每列的宽度
  4. 设置表格为固定布局

这种方法绕过了组件内部的样式限制,直接控制原生表格的列宽,灵活且可靠。

三、代码实现详解

3.1 模板结构

首先,在模板中为 el-descriptions 添加 ref 属性,以便在组件中获取其引用:

1
2
3
4
5
<template>
<el-descriptions ref="table" :column="3" border>
<!-- 描述项内容 -->
</el-descriptions>
</template>

3.2 列宽调整方法

在组件的 methods 中定义 adjustTableWidth 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
methods: {
adjustTableWidth() {
// 1. 获取 el-descriptions 组件的 DOM 引用
const desc = this.$refs.table;
if (!desc) return;

// 2. 找到内部的 table 元素
const table = desc.$el.querySelector('.el-descriptions__body table');
if (!table) return;

// 3. 创建 colgroup 元素
const colgroup = document.createElement('colgroup');

// 4. 定义每列的宽度(根据实际的列数配置)
// 假设 column=3,则需要定义 6 列(每行包含 label 和 content 各一列)
const colWidths = ['230px', 'auto', '230px', 'auto', '230px', 'auto'];

// 5. 创建并添加 col 元素
colWidths.forEach(width => {
const col = document.createElement('col');
col.style.width = width;
colgroup.appendChild(col);
});

// 6. 将 colgroup 插入到 table 的最前面
table.insertBefore(colgroup, table.firstChild);

// 7. 设置为固定布局,确保宽度精确生效
table.style.tableLayout = 'fixed';
}
}

3.3 在适当时机调用

列宽调整需要在组件渲染完成后进行,因此我们在 mounted 生命周期中调用,并使用 $nextTick 确保 DOM 已完全更新:

1
2
3
4
5
6
7
8
9
mounted() {
// 加载附件等其他逻辑...
this.loadAttaches();

// 调整表格列宽
this.$nextTick(() => {
this.adjustTableWidth();
});
}

四、关键技术点解析

4.1 列数计算规则

el-descriptions 的列数与 column 属性有关,实际渲染的表格列数为 column × 2。这是因为每个描述项由“标签列”和“内容列”组成,如果 column=3,则实际表格有 6 列。

因此,定义宽度数组时,需要按照 [标签列1, 内容列1, 标签列2, 内容列2, ...] 的顺序设置。

4.2 宽度单位的灵活使用

  • 固定宽度:使用 px 单位,适合标签列、操作列等需要固定宽度的场景
  • 自适应宽度:使用 auto,让内容列自动占据剩余空间
  • 百分比宽度:也可以使用百分比,如 20%,实现响应式布局

4.3 table-layout: fixed 的作用

设置 tableLayout: 'fixed' 后,表格的列宽将由 <colgroup> 定义的宽度决定,不再受单元格内容影响。这对于精确控制布局非常关键,但需要注意长文本可能会溢出,需要配合 CSS 的 word-breakoverflow-wrap 处理。

五、完整示例

以下是一个完整的组件示例,展示了上述技术的实际应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<template>
<div>
<el-descriptions ref="table" :column="3" border>
<el-descriptions-item label="申请编码">{{ detail.applicationCode }}</el-descriptions-item>
<el-descriptions-item label="许可证编码">{{ detail.licenseCode }}</el-descriptions-item>
<el-descriptions-item label="办理对象">{{ detail.objectType }}</el-descriptions-item>
<el-descriptions-item label="数据来源">{{ detail.dataSource }}</el-descriptions-item>
<el-descriptions-item label="事项所属区划编码">{{ detail.regionCode }}</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ detail.applicationTime }}</el-descriptions-item>
<!-- 更多字段... -->
</el-descriptions>
</div>
</template>

<script>
export default {
data() {
return {
detail: {
// 基本信息
applicationCode: 'SQ202312150001',
licenseCode: 'XKZ202312150001',
objectType: '单位',
dataSource: '政务服务平台',
eventRegionCode: '110101001',
applicationTime: '2023-12-15 10:30:00'
}
};
},
mounted() {
this.$nextTick(() => {
this.adjustTableWidth();
});
},
methods: {
adjustTableWidth() {
const desc = this.$refs.table;
if (!desc) return;

const table = desc.$el.querySelector('.el-descriptions__body table');
if (!table) return;

// 移除已有的 colgroup 避免重复添加
const existingColgroup = table.querySelector('colgroup');
if (existingColgroup) {
existingColgroup.remove();
}

const colgroup = document.createElement('colgroup');
// 3列布局,共6列
const colWidths = ['180px', 'auto', '180px', 'auto', '180px', 'auto'];

colWidths.forEach(width => {
const col = document.createElement('col');
col.style.width = width;
colgroup.appendChild(col);
});

table.insertBefore(colgroup, table.firstChild);
table.style.tableLayout = 'fixed';

// 处理长文本换行
const cells = table.querySelectorAll('td');
cells.forEach(cell => {
cell.style.wordBreak = 'break-word';
});
}
}
};
</script>

六、注意事项与最佳实践

6.1 避免重复添加 colgroup

在每次调整前,最好先检查并移除已存在的 colgroup,防止多次调用导致重复插入:

1
2
3
4
const existingColgroup = table.querySelector('colgroup');
if (existingColgroup) {
existingColgroup.remove();
}

6.2 长文本处理

固定布局下,长文本可能溢出单元格,建议配合以下 CSS 处理:

1
2
3
4
.el-descriptions__body td {
word-break: break-word;
overflow-wrap: break-word;
}

6.3 封装为指令或 Mixin

如果多个页面都需要调整 el-descriptions 列宽,可以考虑封装为自定义指令或 Mixin,提高代码复用性。

七、结语

通过直接操作底层 <table> 元素并注入 <colgroup>,我们可以轻松实现对 el-descriptions 组件列宽的精确控制。这种方法虽然绕过了组件的 API,但胜在简单直接、稳定可靠,特别适合对布局有精细化要求的业务场景。