前端打印表格的换行处理

前言

之前写过一篇关于打印的文章:前端使用print.js实现打印,但是呢其实一直有一个关于表格的打印问题没有解决,最近才有了解决思路,因此打算整理一下。

问题
问题是这样的,很多时候都会有这样的需求:打印时页面上面是固定的表头之类的内容,下面是一个表格。由于表格的数据可能会很多,这就涉及一个分页问题,如何保证表格的最后一条数据不会分割到两张纸上。
这个问题已经困扰很久了,现在采用的一种方式是:

  • 测试一下每页显示多少条数据比较合适,以10条数据为例,超过10条数据就进行分页
  • 对于可能会影响行高的字段,比如备注。通过判断字符串的长度,当长度大于多少值后,就通过设置样式来调整字的大小

但是上面这种方法是治标不治本。

解决

关于什么时候分页的实际问题是:页面剩余的高度知道,但是表格每一行的高度不知道,无法确定多少条数据的时候分页;要想知道每一行的高度,就必须要等dom渲染出来,这就产生了一种矛盾。最近同事的一次分享,才发现自己钻牛角尖了。

我完全可以将表格的数据完全渲染出来,但是我不显示不就可以了吗。由于文档流是按照从上往下的顺序进行渲染的,我可以等表格全部高度都拿到后再进行渲染真正显示的区域。数据也不可能特别多,应该在100条以内,在获取高度的这段时间可以显示一个loading加载,这样也不会显得很奇怪。
在这里插入图片描述

实现代码

<template>
    <div>
        <!-- 这个表格是用来获取高度的,不显示。不能设置display: none;,否则会获取不到高度 -->
        <div class="no-show">
            <table >
            <tr>
                <th>序号</th>
                <th>内容</th>
                <th>来源</th>
            </tr>
            <tr v-for="(item, index) in data" :key="item.id">
                <td>{{ index + 1 }}</td>
                <td>{{ item.content }}</td>
                <td>{{ item.source }}</td>
            </tr>
        </table>
        </div>
        <!-- 真正的打印区域 -->
        <div id="printDom">
            <div class="print-content" v-for="(item,index) in list" :key="index">
                <div>
                    {{`当前页${index+1},总页数${list.length}`}}
                </div>
                <table>
                    <tr>
                        <th>序号</th>
                        <th>内容</th>
                        <th>来源</th>
                    </tr>
                    <tr v-for="(tr, i) in item" :key="tr.id">
                        <td>{{ i + 1 }}</td>
                        <td>{{ tr.content }}</td>
                        <td>{{ tr.source }}</td>
                    </tr>
                </table>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from 'vue';

const loading = ref(false);
// 用于记录行高
let lineHeight = [];
// 表格数据
const data = [
    {
        id: '1',
        content: '也许我的选择最终会被证明是错误。但至少现在,我想这样选,那么我便这样选。',
        source: '将夜'
    },
    {
        id: '2',
        content: '鱼跃此时海,花开彼岸天。',
        source: '将夜'
    },
    {
        id: '3',
        content: '走得慢并不要紧,只要你坚持不停地走,那么总有一天你能走到你想要到达的地方,能超过道旁那些不敢走的人。',
        source: '将夜'
    },
    {
        id: '4',
        content: '君子不行陌路,管它咫尺还是天涯。',
        source: '将夜'
    }
];

// 记录分页的数据
const list = ref([]);

const init = () => {
    loading.value = true;
    lineHeight = [];
    // 获取高度
    let trList = document.querySelectorAll('.no-show table tr');
    console.log(trList);
    trList.forEach((tr, index) => {
        if (index > 0) {
            lineHeight.push(parseInt(tr?.offsetHeight || 0));
        }
    });
    setTimeout(() => {
        console.log('高度', lineHeight);
        // 处理数据,假设每张纸可以打印的表格区域高度为220
        // 这里不考虑特别离谱的情况,一行的高度直接大于一张纸的高度
        // 记录起始位置
        let start = 0;
        // 记录高度和
        let sum = 0;
        lineHeight.forEach((item, index) => {
            sum += item;
            if (index + 1 < lineHeight.length && sum + lineHeight[index + 1] > 220) {
                list.value.push(data.slice(start, index + 1));
                // 更新起始位置和高度和
                start = index + 1;
                sum = 0;
            }
        });
        if (list.value.length != data.length) {
            // 如果最后长度不相等,说明最后剩余的数据高度和不够220
            list.value.push(data.slice(start));
        }
        console.log('最后的数据', list.value);
    }, 500);
};

onMounted(() => {
    init();
});

</script>
<style lang="scss" scoped>
.no-show {
height: 1px; overflow: hidden;opacity: 0;
}

.print-content{
    border: 1px solid #000;
}

table {
    border-collapse: collapse;
    margin: 0 auto;
    width: 300px;
}

table tr {
    background: #EFF3F5;
}

table td,
table th {
    /**文字垂直、水平居中 */
    vertical-align: middle;
    text-align: center;
    /** 基础样式 */
    border: 1px solid black;
    color: #666;
    height: 40px;
    font-size: 18px;
    min-width: 150px;
    line-height: 30px;
}

table thead th {
    background-color: #CCE8EB;
    width: 100px;
}
</style>

效果
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值