iView Table 中 Progress 进度条换行问题解决方案
一、问题背景
在 iView(View UI)Table 组件中,通过 render 函数渲染 Progress 进度条时,当进度值较大(如超过 100%)或列宽有限时,进度条与百分比文字发生换行,导致单元格布局错乱、视觉异常。
问题现象:
进度条与百分比文字上下换行显示,而非同一行水平排列
进度值超过 100% 时,iView Progress 组件渲染异常(percent 属性最大只接受 100)
单元格高度在数据加载后突变,页面出现"抖动"
二、问题根因分析
2.1 直接在 render 中返回 Progress 组件
典型问题写法:
render (h, { row }) {
const rate = (row.actual / row.plan) * 100
row.rate = parseFloat(rate.toFixed(2))
const color = row.rate < 70 ? '#ff2239' : row.rate < 90 ? '#ff9e00' : '#00bf00'
return h('Progress', { props: { percent: row.rate, 'stroke-color': color, 'hide-info': false, vertical: false }})
}问题在于:
无外层容器约束 — 直接返回
ProgressVNode,没有 flex 容器控制进度条和文字的排列方式iView Progress 内置百分比文字与进度条争空间 —
hide-info: false时,百分比文字由 Progress 组件内部渲染,与进度条共享同一行空间;当列宽不足且百分比值较长(如105.04%)时,文字被挤到下一行percent 超过 100 的边界问题 — iView Progress 的
percent属性设计上限为 100,传入超过 100 的值会导致进度条溢出或渲染异常
2.2 iView Table 渲染流程的时序问题
iView Table 的渲染流程:
初始渲染 → 列宽计算 → 行高测量 → 异步数据更新触发重渲染当 Progress 组件嵌套在 render(h, params) 中时:
Progress 内部包含相对定位和弹性布局
外层容器未明确设置对齐方式和尺寸约束
Vue 的异步批量更新机制可能导致
$nextTick执行前 DOM 尚未完成挂载,造成行高测量偏差
三、解决方案
采用 "封装组件 + Flex 布局 + $nextTick 控制 + 自定义百分比文字" 的综合策略。
3.1 封装 ProgressCell 组件
将进度条封装为独立组件,拥有完整的生命周期控制:
const ProgressCell = {
props: {
percent: {
type: Number,
default: 0
},
strokeColor: {
type: String,
default: '#2db7f5'
}
},
data() {
return {
localPercent: 0
}
},
watch: {
percent: {
handler(newVal) {
this.localPercent = newVal
},
immediate: true
}
},
mounted() {
this.$nextTick(() => {
// 确保父级表格完成布局后再赋值,避免行高计算偏差
this.localPercent = this.percent
})
},
render(h) {
// iView Progress 的 percent 最大 100,超过 100 时用 100 渲染条,文字显示真实值
const displayPercent = Math.min(this.localPercent, 100)
const status = this.localPercent >= 100 ? 'success' : 'normal'
const text = this.localPercent.toFixed(2) + '%'
return h('div', {
class: 'progress-cell'
}, [
h('Progress', {
class: 'progress-bar',
props: {
percent: displayPercent,
strokeWidth: 10,
'stroke-color': this.strokeColor,
'hide-info': true, // 隐藏内置百分比,使用自定义文字避免换行
vertical: false,
status: status
}
}),
h('span', {
class: 'progress-text'
}, text)
])
}
}3.2 Flex 布局 CSS
.progress-cell {
display: flex;
align-items: center;
width: 100%;
height: 100%;
min-height: 28px;
padding: 2px 0;
.progress-bar {
flex: 1;
min-width: 0; // 允许 flex 子项收缩
/deep/ .ivu-progress-outer {
padding-right: 0;
}
/deep/ .ivu-progress-show-info .ivu-progress-outer {
padding-right: 50px; // 为百分比文字预留空间
}
/deep/ .ivu-progress-inner {
background-color: #f3f3f3;
}
/deep/ .ivu-progress-bg {
border-radius: 3px;
}
}
.progress-text {
flex-shrink: 0;
width: 50px;
text-align: right;
font-size: 12px;
color: #515a6e;
margin-left: 4px;
}
}3.3 列定义中使用封装组件
{
title: '完成率',
key: 'rate',
width: 220,
align: 'center',
render (h, { row }) {
const rate = (row.actual / row.plan) * 100
row.rate = parseFloat(rate.toFixed(2))
const color = row.rate < 70 ? '#ff2239' : row.rate < 90 ? '#ff9e00' : '#00bf00'
return h(ProgressCell, {
props: {
percent: row.rate,
strokeColor: color
}
})
}
}四、关键设计决策说明
五、方案对比
六、渲染流程图
Table 数据更新
│
├─ 首次渲染 → Vue 编译模板 → 执行 render 函数
│ │
│ ▼
│ 创建 ProgressCell VNode
│ │
│ ▼
│ 挂载组件到 DOM
│ │
│ ▼
│ 调用 $nextTick 延迟赋值
│ │
│ ▼
│ Progress 正常显示 + 自定义文字
│ │
│ ▼
│ 表格布局稳定(flex 约束无换行)
│
└─ 数据更新 → 触发响应式更新 → watch 重新赋值 localPercent
│
▼
Progress 重绘(flex 布局保持稳定)七、复用指引
若其他页面也需在 iView Table 中渲染进度条,可将 ProgressCell 提取为公共组件:
将
ProgressCell定义移至src/components/目录下独立文件(如ProgressCell.js)全局注册或按需引入
在任意 Table 列的
render函数中使用h(ProgressCell, { props: { percent, strokeColor } })
注意事项:
百分比文字宽度
50px可容纳999.99%,若需显示更大值需调整宽度颜色阈值(70/90)为业务约定,可根据实际需求调整
strokeWidth: 10为当前视觉设定,可根据设计稿调整