【table组件】结合【渲染函数 && JSX】的改造封装,动态生成表格

前言

在使用so-ui(类似于element-ui)组件库中的table的过程中,想要通过走动态配置生成表格,并将其封装成简单的通用组件。
实现步骤:

  1. 将每一列的属性提取写入js变量中,再通过遍历生成表格的每一列。
  2. 将表格的单元格,通过组件的渲染函数动态载入。
  3. 单元格组件通过JSX方式书写。

接下去的文章会沿着我针对几个不同项目需求对表格进行一步又一步的改造。

一、表格(表头、单元格)的动态配置

【原本】

先说一下,table组件使用方式和我认为需要该进的方面。
  • 原本使用table组件,每一列的配置都是在html中写死的(有多少列就要写多少个,html代码特别冗长)。
  • 如果遇到如“操作”这种特殊的单元格内容的,可以使用组件的作用域插槽(改造难点)。
    <so-table
        :data="tableData"
        stripe
        style="width: 100%">

        <so-table-column prop="date" label="日期" width="180">so-table-column>

        <so-table-column label="操作" width="180">
            <template slot-scope="scope">
                <span class="span" @click="handleClick">查看span>
                <span class="span">编辑span>
            template>
        so-table-column>

    so-table>

【改造过程】

我的经历过程(不同的项目需求)如下:
    1. 没有自定的单元格。(每个单元格显示的里都是文本内容)
    2. 有自定的单元格。(显示图片,换行,操作)
    3. 可通用的表格组件。(封装)

1、没有自定的单元格

因为,每一个 都是由固定的列属性值(比如:表头名字,宽度等)组成的。

所以,我就写了一个简单的 tableColumn 数据 。 然后对其进行遍历,生成表格。


<so-table :data="tableData" border style="width: 100%" empty-text="暂无数据">
    <so-table-column
        v-for="(col, index) in tableColumn" :key="index"
        :fixed="col.fixed"
        :prop="col.prop"
        :label="col.label"
        :width="col.width || ''"
        :align="col.align || 'left'"
        :header-align="col.headerAlign || 'left'"
        :className="col.class"
        :min-width="col.minWidth"
    >
        <template slot-scope="scope">
            <span>
                {{ formatData(scope.row[col.prop], col.handleType) }}
            span>
        template>
    so-table-column>
so-table>
// js

/**
 * tableColumn包含如下属性:
 *
 *      @parmas prop 对应后台的字段
 *      @parmas label 表头字段
 *      @parmas width 宽度
 *      @parmas class 样式
 *      @parmas handleType 数据处理类型(与函数formatData结合使用)
 *      @parmas fixed 是否固定
 *      @parmas align 表头对其齐方式
 * 
 */

tableColumn: [{
    prop: 'time',
    label: '时间',
    width: '120',
    class: 'table-cell-wrap-lj',
    handleType: 'time',
    fixed: true
},
{
    prop: 'remainMoney',
    label: '金额(元)',
    width: '120',
    align: 'right',
    handleType: 'awaitShowMoney'
}]

formatData(data, type) { // 对数据进行处理
    if (type === 'money') {
        return data ? format.moneyFixed2(data) : '0.00';
    }else if (type === 'time') {
        return data ? moment(data.split('.').join('/')).format('YYYY/MM/DD HH:mm') : '-';
    }
    return data || '-';
}

自认为的缺点:单元格内的数据处理,我用了 函数 formatData 结合 属性 handleType 的方式,感觉有一点麻烦。建议在给表格的tableData赋值前就直接将数据处理好

2、有自定的单元格

在表格插槽作用域中定义多个 template,然后根据 属性type 显示对应的内容。


<so-table :data="tableData" border style="width: 100%" empty-text="暂无数据">
    <so-table-column ...>
        
        
        <template v-if="item.type=='btns'">
            <span class="span" @click="handleClick">查看span>
            <span class="span">编辑span>
        template>
        
        <template v-else-if="item.type =='image'">
            <img :src="scope.row.mainPicture" />
        template>
        
        <template v-else>
            {{ formatValue(scope.row[item.prop], item.handleType)}}
        template>
    so-table-column>
so-table>

自认为的缺点:这样走动态配置的表格只能用于一个业务场景(局限性),针对每一个业务场景都要重新写一遍这个表格组件。(好麻烦的说~比如,上年省去的好几行表格配置的代码)

3、可通用的表格组件

那么如何封装表格组件呢?使之能够一劳永逸。

我一开始想,是不是可以将 template 中的通过配置动态写入。

尝试1:

我尝试直接返回 html代码片段 然后使用 v-html 显示。然而,这样一方面会导致 组件库 的组件无法被渲染。还有一方面,函数的定义使用也是难点。


<template v-else-if="item.type == 'special'">
    <div v-html="item.config.way(scope.row[item.prop])">div>
template>


res = `
    <button onclick="click">普通(正常显示,函数找不到)button>
    <span onclick="console.log(1111)">普通(正常显示,函数正常)span>
    <so-button @click="item.config.otherWay(scope.row && scope.row[item.prop])">组件失效so-button>
`;
尝试2:

我还尝试了用 渲染函数 VNodes,看了好几遍vue的官网中的 渲染函数 & JSX,看的都是一知半解(脑阔疼~)。

因为我不是很懂 JSX ,所以真的让我用纯的VNodes写复杂的单元格内容就让人有些头疼。

最后,还是要感谢我同事写的组件代码,使得我对 JSX 这个知识点有了新的认识和顿悟。

所以,我干了什么惊人之举呢?

我将写在 html 中 template 都提取出来放在 js 中,用 组件 (table-cell-content) 的形式动态渲染。


<so-table
    :data="tableData"
    border style="width: 100%"
    empty-text="暂无数据"
    class="table-bar"
    v-if="tableData && tableColumns">
    <so-table-column
        v-for="(col, index) in tableColumns" :key="index"
        :fixed="col.fixed"
        :prop="col.prop"
        :label="col.label"
        :width="col.width || ''"
        :align="col.align || 'left'"
        :header-align="col.headerAlign || 'left'"
        :className="col.class"
        :min-width="col.minWidth"
    >
        <template slot-scope="scope">
            
            <table-cell-content
                :rowData="scope.row"
                :col="col"
                :handleC_oPrint="handleC_oPrint" :handleC_oDetail="handleC_oDetail"
            >table-cell-content>
        template>
    so-table-column>
so-table>
// js
/* 单元格组件 */
export default {
    components: {
        /* 表格-单元格 【组件】 */
        tableCellContent: {
            /**
             * table-cell-content 组件包含如下参数:
             *
             *      @parmas rowData 该行所有数据字段
             *      @parmas col 改行的配置属性
             * 
             *      @funct handleC_oPrint 自定义函数
             *      @funct handleC_oDetail 自定义函数
             * 
             */
            props: {
                rowData: Object,
                col: Object,
                handleC_oPrint: Function,
                handleC_oDetail: Function
            },
            data() {
                return {
                    /* 表格-单元格 显示的内容 【组件】 */
                    /* 文字 */
                    normal:
                        <span>{this.rowData[this.col.prop]}</span>,
                    /* 车型信息 */
                    carInfo:
                        <div class="carInfo">
                            <div class="title">
                                {this.rowData.c_Name}显示图片OK
                            </div>
                            <div class="content">
                                <img src={this.rowData.c_Img} alt="" />
                                <div class="text">
                                    <span>第一行 {this.rowData.one}</span>
		                            <so-tooltip content="提示信息" placement="top">
		                                <span>引用组件库组件ok</span>
		                            </so-tooltip>
                                </div>
                            </div>
                        </div>,
                    /* 操作 */
                    operation:
                        <div class="operation">
                            {this.rowData.operation_print ? <p onClick={this.handleC_oPrint.bind(this, this.rowData)}>打印</p> : ''}
                            {this.rowData.operation_detail ? <p onClick={this.handleC_oDetail.bind(this, this.rowData)}>订单详情</p> : ''}
                        </div>
                };
            },
            render() {
                return (
                    (this.col.dataComponent && this[this.col.dataComponent]) ? this[this.col.dataComponent] : this.normal
                );
            }
        }
    }
}

然后,我就很顺利的用上了表格组件,同时支付样式组件库的组件的动态插入使用(比如:so-tooltip)。
但是,表格组件中添加函数还是需要手动添加修改。(值得继续反思)

jsx相关问题

在使用jsx的过程中遇到的问题(看着上面的代码可以思考一下):

  1. jsx如何安装、使用。
  2. jsx中不用能vue的指令(除了v-show)。
  3. jsx中的点击事件,及传参数(vue中@click)。
  4. jsx中标签属性要动态绑定值(vue中用v-bind)。

你可能感兴趣的:(vue,前端)