【界面设计学习笔记】table表格设计

文章详细介绍了HTML表格的创建、边框设置、合并单元格等方法,并展示了如何使用ExcelJS库进行表格数据导出,包括设置表格样式、合并单元格等功能,以及在前端实现Excel文件下载的示例代码。

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

标签用法
< table >定义表格
< caption>定义表格标题
< tr>定义表格的行
< td>定义表格中的单元格
< th>定义表格中的表头单元格
< thead>定义表格中的表头内容 --相当于给表格增加了一些意义,不在是单纯的格子
< tbody>定义表格中的主体内容
< tfoot>定义表格中的表注内容(脚注)

干巴的表格

<table class="tableStyle">
          <tr>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
          </tr>
          <tr>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
          </tr>
          <tr>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
          </tr>
      </table>

在这里插入图片描述

加上边框的表格

<table class="tableStyle">
          <tr>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
          </tr>
          <tr>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
          </tr>
          <tr>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
          </tr>
      </table>
.tableStyle{
        border: 1px solid #151515;
    }

在这里插入图片描述

加上内边框的表格

<table class="tableStyle">
          <tr>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
          </tr>
          <tr>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
          </tr>
          <tr>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
          </tr>
      </table>
.tableStyle{
      border: 1px solid #151515;
  }
  .tableStyle td{
      border: 1px solid #151515;
  }

在这里插入图片描述

去掉外边框和内边框的空隙

<table class="tableStyle">
          <tr>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
              <td>第一行</td>
          </tr>
          <tr>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
              <td>第二行</td>
          </tr>
          <tr>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
              <td>第三行</td>
          </tr>
      </table>
.tableStyle{
   border-collapse: collapse;
    border: 1px solid #151515;
 }
 .tableStyle td{
     border: 1px solid #151515;
 }

在这里插入图片描述

正经表格

      <table class="tableStyle">
          <caption>我是标题</caption>

          <thead>
              <tr>
                  <th>表头</th>
              </tr>
          </thead>

          <tbody>
              <tr>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
              </tr>
              <tr>
                  <td>第二行</td>
                  <td>第二行</td>
                  <td>第二行</td>
                  <td>第二行</td>
              </tr>
              <tr>
                  <td>第三行</td>
                  <td>第三行</td>
                  <td>第三行</td>
                  <td>第三行</td>
              </tr>
          </tbody>

          <tfoot>
              <tr>
                  <td>表尾</td>
              </tr>
          </tfoot>
      </table>
.tableStyle{
        border-collapse: collapse;
        border: 1px solid #151515;
    }
    .tableStyle td{
        border: 1px solid #151515;
    }

在这里插入图片描述

合并单元格

合并行

      <table class="tableStyle">

          <caption>我是标题</caption>

          <thead>
              <tr>
                  <th>表头</th>
              </tr>
          </thead>

          <tbody>
              <tr>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
              </tr>
              <tr>
                  <td rowspan="2">合并二三行</td>
                  <td>第二行</td>
                  <td>第二行</td>
                  <td>第二行</td>
              </tr>
              <tr>
                  <td>第三行</td>
                  <td>第三行</td>
                  <td>第三行</td>
              </tr>
          </tbody>

          <tfoot>
              <tr>
                  <td>表尾</td>
              </tr>
          </tfoot>
      </table>
      
    .tableStyle{
        border-collapse: collapse;
        border: 1px solid #151515;
    }
    .tableStyle td{
        border: 1px solid #151515;
    }

在这里插入图片描述

合并列

      <table class="tableStyle">

          <caption>我是标题</caption>

          <thead>
              <tr>
                  <th>表头</th>
              </tr>
          </thead>

          <tbody>
              <tr>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
              </tr>
              <tr>
                  <td colspan="2">合并两个二行</td>
                  <td>第二行</td>
                  <td>第二行</td>
              </tr>
              <tr>
                  <td>第三行</td>
                  <td>第三行</td>
                  <td>第三行</td>
                  <td>第三行</td>
              </tr>
          </tbody>

          <tfoot>
              <tr>
                  <td>表尾</td>
              </tr>
          </tfoot>
      </table>
    .tableStyle{
        border-collapse: collapse;
        border: 1px solid #151515;
    }
    .tableStyle td{
        border: 1px solid #151515;
    }

在这里插入图片描述

合并行又合并列

      <table class="tableStyle">

          <caption>我是标题</caption>

          <thead>
              <tr>
                  <th>表头</th>
              </tr>
          </thead>

          <tbody>
              <tr>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
                  <td>第一行</td>
              </tr>
              <tr>
                  <td colspan="2" rowspan="2">合并两个二、三行</td>
                  <td>第二行</td>
                  <td>第二行</td>
              </tr>
              <tr>
                  <td>第三行</td>
                  <td>第三行</td>
              </tr>
          </tbody>

          <tfoot>
              <tr>
                  <td>表尾</td>
              </tr>
          </tfoot>
      </table>
    .tableStyle{
        border-collapse: collapse;
        border: 1px solid #151515;
    }
    .tableStyle td{
        border: 1px solid #151515;
    }

在这里插入图片描述

打印表格

参考博客:https://www.jianshu.com/p/27e8a5cfc420

添加两个依赖

cnpm install exceljs
cnpm install file-saver

在这里插入图片描述

在用的页面导入依赖

在这里插入图片描述

引入element-ui插件

安装cnpm i element-ui -S
在这里插入图片描述

package.json中看见红框的内容说明安装成功
在这里插入图片描述在main.js中引入插件
在这里插入图片描述做完以上操作后element-ui就可以用了

在创建exportExcel.js

在这里插入图片描述

/**
 *  exceljs 封装
 *  author:yf
 *  time:20220323
 *  博客:https://www.jianshu.com/p/27e8a5cfc420
 * */
import ExcelJS from 'exceljs'
import FileSaver from 'file-saver'

function isNullFn(val) {
    return val === null || val === void 0 || val === '' || (val).toString() === 'NaN'
}

/**
 *  将chart与index计算,原理是使用ascii计算
 *  @chart:参与计算的字母,这里代表excel的列A ->Z
 *
 * */
function chartCalFn(chart, index) {
    return String.fromCharCode(chart.charCodeAt() + index)
}

// 获取换算后行的位置。 如 2、25等
function getRowFn(tableRef, index) {
    let startRow = parseInt(tableRef.replace(/[A-Z]/g, '')) // 表格开始的行位置
    return startRow + parseInt(index)
}

// 获取换算后列的位置, 如:B、BC等
function getColumnFn(tableRef, index) {
    // tableRef可能为 AA等
    let startCol = tableRef.replace(/[0-9]/g, '')    // 表格开始的列位置

    let before = startCol.substr(0, startCol.length - 1) // 前面的字母
    let after = startCol.substr(startCol.length - 1, 1) // 最后一位字母

    return before + '' + chartCalFn(after, index)
}

/**
 *  获取换算后的单元格位置
 *  @tableRef: 表格左上角位置
 *  @coords: 需要平移的坐标,格式:[需要横向平移的个数,需要纵向平移的个数]
 *  return String 返回平移行列后的单元格位置
 * */
function getCellFn(tableRef, coords) {
    if (!coords || coords.length !== 2) {
        console.error('坐标错误')
        return tableRef
    }
    return getColumnFn(tableRef, coords[0]) + '' + getRowFn(tableRef, coords[1])
}

/**
 *  excel 导出表格
 *  @tableConfigArr: 同exceljs  sheet.addTable参数 参考 https://github.com/exceljs/exceljs/blob/master/README_zh.md#%E8%A1%A8%E6%A0%BC
 *      注意:1.columns中的_width属性是为了这里便于封装使用的,用于设置表格列宽,exceljs 官网没有此属性,别混淆
 *            2._tableBorder?: boolean 可选封装参数,true 则导出的表格有边框 false 则看表格的theme
 *            3._mergeCells?: [Array<string>] 可选封装参数,需要合并的单元格. eg: [['C3:C5']]
 *            4._cellStyle?: [{   // 单元格样式设置
 *              coords:[0,2], // 如表格ref为C3,则这里表示的是C5 [横坐标,纵坐标],可以理解为以表格左上角为原点
 *              style:{} // 同exceljs 单元格样式 参考https://github.com/exceljs/exceljs/blob/master/README_zh.md#%E6%A0%B7%E5%BC%8F
 *             }]
 *            5._sheetRefName?: string 注意:当customConfig.sheetName 设置为数组的时候,也就是有多个选项卡的时候,不写此参数则,默认加入第一张sheet
 *
 *  @customConfig: {
 *     sheetName: string || Array 表格选项卡名称
 *               当为数组sheetName时,sheetName 单项可为string代表名称,可为对象,结构如下:
 *                                    {"name": "sheet1",
 *                                    "option": { // option同exceljs 工作表 参考:https://github.com/exceljs/exceljs/blob/master/README_zh.md#%E5%B7%A5%E4%BD%9C%E8%A1%A8%E5%B1%9E%E6%80%A7
 *                                      "properties": {"tabColor": {"argb": "FFC0000"}}}
 *                                    }
 *     fileName: string 导出的excel文件名,如:test
 *  }
 * */
// 参数如下:
// 参数说明:为了更友好的参考官网,这里封装的结构基本和exceljs官网一致,但某些为了方便调用的配置的参数,都以 "_"开头,如:_width,_cellStyle,_mergeCells,_tableBorder
// tableConfigArr 结构如下:
// [
//   {
//     "name": "MyTestTable",
//     "ref": "C3",
//     "headerRow": false,
//     "totalsRow": false,
//     "style": {"theme": "TableStyleLight1"},
//     "columns": [
//       {"name": "日期", "_width": 30},
//       {"name": "姓名", "_width": 30},
//       {"name": "省份", "_width": 30},
//     ],
//     "rows": [
//       ["2016-05-02", "王小虎", "上海"],
//       ["2016-05-03", "王小虎", "上海"],
//     ],
//     "_tableBorder": true,
//     "_mergeCells": [
//       ["C3:C5"],
//       ["D3:H3"],
//     ],
//     "_cellStyle": [ // 单元格样式设置,代表表格左上角平移1列2行后的单元格,如表格ref为C3,则这里表示的是D5 [横坐标,纵坐标],可以理解为以表格左上角为原点,
//       {
//         "coords": [0, 2],
//         "style": {"alignment": {"vertical": "middle", "horizontal": "left"}}
//       },
//     ],
//     "_sheetRefName": "sheet2"
//   }
// ],
// customConfig 结构如下:
// {
//     "sheetName": [
//       {"name": "sheet1", "option": {"properties": {"tabColor": {"argb": "FFC0000"}}}},
//       "sheet2"
//     ],
//     "fileName": "合并表头的表格导出"
// }
export function $export(tableConfigArr, customConfig) {
    let {sheetName, fileName} = customConfig
    const workbook = new ExcelJS.Workbook();
    workbook.creator = 'admin';
    workbook.lastModifiedBy = 'admin';
    workbook.created = new Date();
    workbook.modified = new Date();
    workbook.lastPrinted = new Date();

    // 创建带有红色标签颜色的工作表
    // const sheet = workbook.addWorksheet(sheetName, {properties: {tabColor: {argb: 'FFC0000'}}});

    // 生成 sheet start ----------
    let sheetname = null
    if (Array.isArray(sheetName)) { // 多个工作表
        sheetname = sheetName[0].name? sheetName[0].name: sheetName[0]
        sheetName.map(item => {
            workbook.addWorksheet(item.name ? item.name : item, item.option || {});
        })

    } else { // 只有一个工作表
        if (typeof sheetName === 'string') {
            sheetname = sheetName
            workbook.addWorksheet(sheetName);
        } else {
            sheetname = sheetName.name
            workbook.addWorksheet(sheetname, sheetName.option || {});
        }
    }
    // 生成 sheet end ----------

    let sheet = null;
    tableConfigArr.map(tableConfig => {

        let {ref: tableRef, _sheetRefName} = tableConfig // 因为tableRef可能为 AE:2 这种组合

        if (_sheetRefName) { // 指定了插入哪个sheet
            // 按 name 提取工作表
            sheet = workbook.getWorksheet(_sheetRefName);
        } else {
            sheet = workbook.getWorksheet(sheetname);
        }

        // table-列宽设置 start -------
        let {columns} = tableConfig
        columns.map((item, index) => {
            if (isNullFn(item._width)) return // 没有设置宽度使用exceljs默认宽
            let currentChart = sheet.getColumn(getColumnFn(tableRef, index))
            currentChart.width = item._width // 设置列宽度
        })
        // table-列宽设置 end -------

        // 单元格合并 start --------
        let {_mergeCells} = tableConfig
        _mergeCells && _mergeCells.length && _mergeCells.map(item => {
            sheet.mergeCells(item);
        })
        // 单元格合并 end --------

        // 单元格样式设置 start ------
        let {_cellStyle} = tableConfig
        _cellStyle && _cellStyle.length && _cellStyle.map(item => {
            let cell = sheet.getCell(getCellFn(tableRef, item.coords))
            item.style && Object.keys(item.style).map(sub => { // 设置单元格样式
                cell[sub] = item.style[sub]
            })
        })
        // 单元格样式设置 end ------

        // 表格设置边框 start -------
        let {_tableBorder} = tableConfig
        if (_tableBorder) {
            let {columns, rows, headerRow, totalsRow} = tableConfig
            let rowMaxIndex = rows.length
            headerRow && (rowMaxIndex++); // 有标题行则+1
            totalsRow && (rowMaxIndex++); // 有合计行则+1
            let colMaxIndex = columns.length

            for (let rowIndex = 0; rowIndex < rowMaxIndex; rowIndex++) {
                for (let colIndex = 0; colIndex < colMaxIndex; colIndex++) {
                    let cell = sheet.getCell(getCellFn(tableRef, [colIndex, rowIndex]))
                    cell.border = {
                        top: {style: 'thin'},
                        left: {style: 'thin'},
                        bottom: {style: 'thin'},
                        right: {style: 'thin'}
                    }
                }
            }
        }
        // 表格设置边框 end -------

        sheet.addTable(tableConfig)
    })

    // workbook.xlsx.writeFile('test.xlsx') // node端才能使用
    workbook.xlsx.writeBuffer().then(buffer => {
        FileSaver.saveAs(new Blob([buffer], {type: 'application/octet-stream'}), `${fileName || 'excel'}.xlsx`);
    })
}


下面是完整代码

<!-- 纯前端导出excel案例 -->
<template>
    <div class="wrap">


        <h1>复杂表格</h1>
        <p>
            <el-button type="primary" @click="exportXlsx4('导出excel-表头合并')">导出excel-表头合并</el-button>
        </p>


        <div style="width:1000px;margin: 20px auto;">
            <el-table
                    :data="tableData2"
                    style="width: 100%">
                <el-table-column
                        prop="date"
                        label="日期"
                        width="150">
                </el-table-column>
                <el-table-column label="配送信息">
                    <el-table-column
                            prop="name"
                            label="姓名"
                            width="120">
                    </el-table-column>
                    <el-table-column label="地址">
                        <el-table-column
                                prop="province"
                                label="省份"
                                width="120">
                        </el-table-column>
                        <el-table-column
                                prop="city"
                                label="市区"
                                width="120">
                        </el-table-column>
                        <el-table-column
                                prop="address"
                                label="地址"
                                width="300">
                        </el-table-column>
                        <el-table-column
                                prop="zip"
                                label="邮编"
                                min-width="120">
                        </el-table-column>
                    </el-table-column>
                </el-table-column>
            </el-table>
        </div>

    </div>
</template>
<script>
    import * as exportExcel from  './exportExcel.js'

    export default {
        name: 'exceljs-demo',
        data(){
            return {
                tableHeader: [
                    {
                        prop: 'date',
                        label: '日期'
                    },
                    {
                        prop: 'name',
                        label: '姓名'
                    },
                    {
                        prop: 'address',
                        label: '地址'
                    }
                ],

                tableData2: [{
                    date: '2016-05-03',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-02',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-04',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-01',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-08',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-06',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }, {
                    date: '2016-05-07',
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333
                }]
            }
        },
        methods: {
            // 是否为空
            $isNull(val){
                return val === null || val === void 0 || val === '' || (val).toString() === 'NaN'
            },


            // 底部表格
            getTableExportData2(){
                // 表头数据,这里拆成三行,方便演示这里没有放级联数组里,直接写死的
                let header1 = ['', '', '', '', '', '配送信息'] // 注意:因为要合并,文本要放最后一个待合并单元格,不然合并后显示后为空
                let header2 = ['', '', '', '', '', '地址']
                let header3 = [
                    {
                        prop: 'date',
                        label: '日期',
                    },
                    {
                        prop: 'name',
                        label: '姓名',
                    },
                    {
                        prop: 'province',
                        label: '省份',
                    },
                    {
                        prop: 'city',
                        label: '市区',
                    },
                    {
                        prop: 'address',
                        label: '地址',
                    },
                    {
                        prop: 'zip',
                        label: '邮编',
                    },
                ]

                // 生成columns - 因为列必须有列名,这里使用的header3,其他的也行,反正后面不会显示,后面会隐藏表头
                let columns = []
                header3.map(item => {
                    columns.push({
                        name: item.label,
                        _width: 30// 注意:这里是封装后才能这样写,exceljs table columns 没有width
                    })
                })

                // 生成rows
                let rows = []
                rows.push(header1) // 前面是表头1
                rows.push(header2) // 前面是表头2
                rows.push(header3.map(item => item.label)) // 前面是表头3
                this.tableData2.map(item => { // 这里才是表格的数据
                    let arr = []
                    header3.map(sub => {
                        arr.push(!this.$isNull(item[sub.prop]) ? item[sub.prop] : '')
                    })
                    rows.push(arr)
                })


                return {
                    columns,
                    rows
                }
            },

            exportXlsx4(filename){
                let {rows, columns} = this.getTableExportData2()
                exportExcel.$export([
                    {
                        name: 'MyTestTable',
                        ref: 'C3',//起始点
                        headerRow: false, // 【注意:这里headerRow是false】
                        totalsRow: false,
                        style: {
                            theme: 'TableStyleLight1',
//            showRowStripes: true,
                        },
                        columns: columns,
                        rows: rows,

                        _tableBorder: true, // 表格设置边框
                        _mergeCells: [ // 这里是需要合并的表头 - 可以等表格导出后再一个一个看哪些需要合并,这种配置是目前想到的最简单的方式了
                            ['C3:C5'],
                            ['D3:H3'],
                            ['D4:D5'],
                            ['E4:H4'],
                        ],
                        // 表头部分单元格居中
                        _cellStyle: [
                            {
                                coords: [0, 2], // 代表表格第一行第一列 [横坐标,纵坐标]
                                style: {
                                    alignment: {vertical: 'middle', horizontal: 'left'}
                                },
                            },
                            {
                                coords: [1, 2], // 代表表格第一行第一列 [横坐标,纵坐标]
                                style: {
                                    alignment: {vertical: 'middle', horizontal: 'left'}
                                },
                            }
                        ]
                    }
                ], {
                    sheetName: 'My Sheet',
                    fileName: filename
                })
            },

        }
    }
</script>
<style >

</style>

在这里插入图片描述

导出效果图

在这里插入图片描述

对打印的表格进行自定义样式

ExcelJS样式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IsQiya

很庆幸我的文章对您有帮助

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

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

打赏作者

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

抵扣说明:

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

余额充值