vue-ant-design实现<a-table>表格行合并

本文介绍如何使用 AntDesign Vue 在表格中实现单元格的合并功能,并提供了具体的代码示例。通过自定义渲染函数,可以实现按指定列进行行合并,适用于展示分组数据的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考Ant design Vue官网关于行和列合并api

Ant Design Vue

<template>
    <a-locale-provider :locale="locale">
        <div id="app" class="regulationId">
            <div style="">
                <a-card :bordered="false">
                    <div style="display: inline-block;height: 64px;text-align: left;padding-left: 40%;font-size: 15px;color: #007df9;margin-bottom: 20px;">
                        <span style="color: rgb(0, 0, 0);display: block;font-size: 21px;margin-left: -9%;line-height: 43px;">{{pageTitleDate}}</span>
                        <div style="margin-left: -9%;text-align: center;">
                            <span @click="fetchList('0','prev')" v-show="hideTeamModule"
                                  style="cursor: pointer;">上周</span>
                            <span @click="fetchList('prev')" v-show="!hideTeamModule" style="cursor: pointer;">上周</span>
                            <span @click="fetchList('0','now1')" v-show="showCurWeek" style="cursor: pointer;">&nbsp&nbsp&nbsp本周</span>
                            <span @click="fetchList('0','now')" v-show="showAllWeek" style="cursor: pointer;">&nbsp&nbsp&nbsp本周</span>
                            <span @click="fetchList('now2')" v-show="!hideTeamModule" style="cursor: pointer;">&nbsp&nbsp&nbsp本周</span>
                            <span @click="fetchList('0','next')" v-show="hideTeamModule" style="cursor: pointer;">&nbsp&nbsp&nbsp下周</span>
                            <span @click="fetchList('next')" v-show="!hideTeamModule" style="cursor: pointer;">&nbsp&nbsp&nbsp下周</span>
                        </div>
                    </div>
                    <a-spin :spinning="spinning">
                        <div style="margin-bottom: 10px;text-align: right;">
                            <a-checkbox @change="switchViewText">
                                仅显示不足工时
                            </a-checkbox>
                        </div>
                        <a-table
                                size="middle"
                                rowKey="id"
                                bordered
                                :columns="columns"
                                :data-source="data"
                                :pagination="false"
                                :scroll="{y: scrollY}"
                        >
                        </a-table>
                    </a-spin>
                </a-card>
            </div>
        </div>
    </a-locale-provider>
</template>
<script>
    import {postAction} from "../../api/manage.js";
    import JDate from "../../components/JDate";
    import zhCN from 'ant-design-vue/es/locale/zh_CN'

    // 动态计算需要合并的单元格的行
    const temp = {}; // 当前重复的值,支持多列
    const mergeCells = (text, array, columns) => {
        let i = 0;
        if (text !== temp[columns]) {
            temp[columns] = text;
            array.forEach((item) => {
                if (item.teamName === temp[columns]) {
                    i += 1;
                }
            });
        }
        return i;
    };
    export default {
        name: "WeeklyWorkingHours",
        components: {
            JDate,
        },
        created: function () {
            this.fetchList('0', 'now1');// 初始化数据
            this.scrollY = window.innerHeight - 200;
        },

        mounted() {
        },

        methods: {
            // 初始化加载列表数据
            fetchList(flg, typeVal) {
                let that = this;
                that.hideTeamModule = true;
                if (typeVal == 'now') {
                    that.showCurWeek = false;
                    that.showAllWeek = true;
                }
                if (typeVal == 'now1') {
                    that.showCurWeek = true;
                    that.showAllWeek = false;
                }
                if (typeVal == 'now' || typeVal == 'prev' || typeVal == 'next') {//全部
                    that.type = typeVal;
                } else if (typeVal == 'now1' || typeVal == 'prev' || typeVal == 'next') {//我的
                    if (typeVal == 'now1') {
                        that.type = 'now';
                    } else {
                        that.type = typeVal;
                    }
                }
                let params = {
                    personId: that.queryPersonId,// 人员Id(查询条件)
                    type: that.type,// 查询标记位
                    pageTitleDate: that.pageTitleDate,
                    checkStatus: this.checkFlag,// 仅显示不足工时标记位
                };
                postAction(that.url.listUrl, params).then((res) => {
                    that.spinning = true;
                    if (res.status == 200) {
                        that.data = res.data.json.keys.rsList;// 返回的数据
                        that.storeDatas = res.data.json.keys.rsList;// 返回的数据
                        that.pageTitleDate = res.data.json.keys.pageTitleDate;// 时间段,以周为单位
                        if(that.checkFlag){
                            let newDatas = [];
                            for (let i = 0; i < that.data.length; i++) {
                                let actualHours = that.data[i].actualWeeklyWorkingHours;// 个人实际工时
                                let planHours = that.data[i].planWeeklyWorkingHours;// 个人计划工时
                                if(Number(actualHours) < Number(planHours)){
                                    newDatas.push(that.data[i]);
                                }
                            }
                            that.data = newDatas;
                        }else{
                            that.data = that.storeDatas;
                        }
                    } else {
                        that.$message.info("数据获取失败!");
                    }
                    that.spinning = false;
                })
            },
            // 查看不足工时和所有工时
            switchViewText(val){
                this.spinning = true;
                let checkStatus = val.target.checked;// 选中的状态 true 或 false
                if(checkStatus){// 仅显示不足工时
                    this.checkFlag = checkStatus;
                    let newDatas = [];
                    for (let i = 0; i < this.data.length; i++) {
                        let actualHours = this.data[i].actualWeeklyWorkingHours;// 个人实际工时
                        let planHours = this.data[i].planWeeklyWorkingHours;// 个人计划工时
                        if(Number(actualHours) < Number(planHours)){
                            newDatas.push(this.data[i]);
                        }
                    }
                    this.data = newDatas;
                    this.spinning = false;
                }else{// 显示所有工时
                    this.checkFlag = checkStatus;
                    this.data = this.storeDatas;
                    this.spinning = false;
                }
            },
        },

        data() {
            return {
                spinning: false,
                teamTreeData: [],// 班组树菜单
                replaceFields: {
                    title: 'label',
                    key: 'value',
                },
                locale: zhCN,
                pageTitleDate: '',
                hideTeamModule: false,
                showCurWeek: true,
                showAllWeek: false,
                queryPersonId: '',
                checkFlag: false,// 仅显示不足工时标记位
                type: 'now',
                scrollY: '',
                url: {
                    listUrl: 'weeklyWorkingHoursBLH_list.do',// 初始化列表
                },
                columns: [
                    {
                        title: '序号',
                        dataIndex: 'index',
                        align: 'center',
                        width: '5%',
                        customRender: function (text, row, index) {
                            return parseInt(index) + 1;
                        }
                    },
                    {
                        title: '班组',
                        dataIndex: 'teamName',
                        align: "center",
                        customRender: (text, row, index) => {
                            if(text != ''){
                                const obj = {
                                    children: text + "组",
                                    attrs: {},
                                };
                                obj.attrs.rowSpan = mergeCells(row.teamName, this.data, 'teamName');
                                return obj;
                            }
                        },
                    },
                    {
                        title: '姓名',
                        dataIndex: 'userName',
                        align: "center",
                    },
                    {
                        title: '额外工时(h)',
                        dataIndex: 'extraWorkingHours',
                        align: "center",
                    },
                ],
                commLabelCol: {
                    xs: {span: 8}
                },
                commWrapperCol: {
                    xs: {span: 15}
                },
                data: [],
                storeDatas: [],
            }
        }
    };
</script>

<style scoped>
</style>

主要关注这段合并单元格的代码

最终效果图:

<think>好的,用户想在Vue 3.0和Ant Design Vue的<a-table>组件中实现自适应列宽,根据列标题或内容调整宽度。我需要结合提供的引用资料来找出解决方法。 首先,引用[2]提到在a-table中使用scroll属性设置{x:'max-content'}可以让水平滚动适应内容宽度,同时保持其他列固定宽度。这可能是一个关键点。但用户想要的是自适应,可能需要动态计算列宽。 然后,引用[1]中提到一个npm包aotu-layout-col,用于自动调整列宽。虽然这个包是针对el-table的,但或许类似的思路可以应用到a-table上,比如手动计算每列的宽度。 接下来,用户可能需要监听数据变化或内容变化,动态调整列宽。例如,在updated生命周期钩子中执计算函数,获取头的DOM元素,测量文字宽度,然后设置对应的列宽。 需要注意的是,Ant Design Vue表格列配置是通过columns数组定义的,每个列对象可以设置width属性。因此,动态修改columns中的width值应该可以触发重新渲染,调整列宽。 另外,引用[3]和[4]提到了高度自适应的内容,但用户的问题是关于宽度自适应,所以可能需要忽略高度相关的部分,专注于宽度调整的方法。 可能的步骤包括:使用scroll设置最大内容宽度,监听表格内容变化,计算每列最大宽度(包括标题和内容),然后更新columns配置的width值。同时,可能需要处理表格容器的响应式布局,比如使用CSS的overflow-auto或max-width来允许滚动。 还需要考虑性能问题,比如防抖处理,避免频繁计算影响性能。此外,对于包含大量数据的表格,动态计算可能会比较耗时,需要优化计算方法,比如只计算可见区域的内容。 总结可能的解决方案:结合使用scroll设置、动态计算列宽并更新columns配置,可能借助第三方库或自定义函数来实现测量文本宽度,最后应用样式和配置使表格列自适应内容。</think>以下是在Vue 3.0与Ant Design Vue的$<a-table>$组件中实现自适应列宽的解决方案: ### 一、基础实现方法 1. **响应式容器设置** ```vue <template> <div class="table-container"> <a-table :columns="columns" :data-source="data" :scroll="{ x: 'max-content' }" /> </div> </template> <style> .table-container { overflow-x: auto; max-width: 100vw; } </style> ``` 通过设置$scroll="{ x: 'max-content' }$"使表格水平滚动条适应内容宽度[^2],结合容器CSS实现响应式布局 2. **动态列宽计算** ```javascript const calculateColumnWidth = (dataIndex, title) => { const contentWidths = data.map(item => measureTextWidth(item[dataIndex]) ); const titleWidth = measureTextWidth(title); return Math.max(...contentWidths, titleWidth) + 32; // 增加32px留白 }; const measureTextWidth = (text) => { const span = document.createElement('span'); span.style.visibility = 'hidden'; span.style.whiteSpace = 'nowrap'; span.innerText = text; document.body.appendChild(span); const width = span.offsetWidth; document.body.removeChild(span); return width; }; ``` ### 二、进阶优化方案 1. **自动布局指令(基于引用[1]原理)** ```javascript import { createApp } from 'vue' const autoWidth = { updated(el, { value }) { const table = el.querySelector('.ant-table') const headers = table.querySelectorAll('th') headers.forEach((header, index) => { const cells = table.querySelectorAll(`td:nth-child(${index + 1})`) const contentWidths = [...cells].map(cell => cell.scrollWidth ) const headerWidth = header.scrollWidth header.style.minWidth = `${Math.max(...contentWidths, headerWidth)}px` }) } } // 全局注册 createApp().directive('auto-width', autoWidth) // 组件使用 <template> <a-table v-auto-width /> </template> ``` 2. **性能优化方案** ```javascript // 使用ResizeObserver监听容器变化 const observer = new ResizeObserver(entries => { entries.forEach(entry => { if (entry.contentRect.width !== prevWidth) { calculateColumnsWidth() } }) }) onMounted(() => { observer.observe(document.querySelector('.table-container')) }) onUnmounted(() => { observer.disconnect() }) // 添加防抖处理 const debouncedCalculate = _.debounce(calculateColumnsWidth, 300) ``` ### 三、混合方案实践 ```vue <template> <a-table :columns="dynamicColumns" :scroll="{ x: 'max-content' }" :components="{ header: { cell: ResizableHeader } }" /> </template> <script> // 实现可拖拽调整列宽 const ResizableHeader = { // 拖拽处理逻辑... } // 响应式columns配置 const dynamicColumns = computed(() => { return columns.map(col => ({ ...col, width: calculateColumnWidth(col.dataIndex, col.title) })) }) </script> ``` ### 四、注意事项 1. 对于大数据量表格,建议使用虚拟滚动技术 2. 固定列与自适应列并存时,需明确指定固定列宽度 3. 复杂头建议采用手动指定$width$与自动计算结合的方式 4. 表格内容动态更新时需触发重新计算: ```javascript watch(data, () => { nextTick(() => { calculateColumnsWidth() }) }) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦想天涯~路在脚下

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

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

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

打赏作者

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

抵扣说明:

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

余额充值