递归组件实现合并单元格组件

626 阅读4分钟

要求:根据提供的数据,完成下图页面。 typeList是动态的数量不定,设计组件时需考虑通用性 不一定用表格实现 设计稿: 微信图片_20200721153443.png 数据:

data() {
      return {
        data: [
          ['JOBS', '2018-01', '2018-01-01', 12, 16],
          ['JOBS', '2018-01', '2018-01-07', 13, 17],
          ['JOBS', '2018-02', '2018-01-08', 14, 18],
          ['JOBS', '2018-02', '2018-01-09', 15, 19],
          ['JOBS', '2018-02', '2018-01-12', 16, 10],
          ['JOBS', '2018-03', '2018-01-15', 8, 11],
          ['JOBS', '2018-03', '2018-01-16', 6, 12],
          ['JOBS', '2018-03', '2018-01-18', 8, 13],
          ['JOBS', '2018-03', '2018-01-21', 9, 14],
          ['JOBS', '2018-04', '2018-01-23', 12, 15],
          ['JOBS', '2018-04', '2018-01-26', 9, 16],
          ['JOBS', '2018-05', '2018-01-30', 12, 17],
          ['JOBS1', '2018-01', '2018-01-01', 12, 18],
          ['JOBS1', '2018-01', '2018-01-07', 12, 19],
          ['JOBS1', '2018-02', '2018-01-08', 12, 20],
          ['JOBS1', '2018-02', '2018-01-09', 12, 21],
          ['JOBS1', '2018-02', '2018-01-12', 12, 22],
          ['JOBS1', '2018-03', '2018-01-15', 12, 23],
          ['JOBS1', '2018-03', '2018-01-16', 12, 24],

        ],
        typeList: ['服务', '周', '日'],
        valueType: ['平均值', '最大值'],
      };
    },

这道题我没写出来,事后总结那时那刻我犯了一个致命错误:急于对给到的数据进行转化,转化成我心目中想象的应该可以渲染成那个样式的数据结构。 在这种心理状态下,我试着转化数据,转的过程中感觉不对,又换思路,。。。,操作猛如虎,组件原地杵,我根本就没有确定什么样的数据结构可以渲染成设计稿的样式。

正常开发组件的流程应该是这样的: 1.分析设计稿的特点,根据设计稿还原样式(纯静态页面); 2.根据已写好的静态页面确定渲染当前页面的数据结构; 3.根据确定的数据结构转化给到的数据;

设计稿特点: 这个页面乍一看是个表格,是个有合并单元格的表格,如果是静态页面可以确定哪个单元格合并,但是数据是动态采用表格table实现我个人觉得有点难度,即使用element-ui表格组件都悬,这个方案pass; 我发现这个表格的结构非常象一个数型结构 大概是酱紫 treeTable-1.png 于是非常顺理成章的想到了我以前封装的公司职务结构树形组件的数据结构: name字段渲染每个节点名称 children表示其下级成员

确定最终数据结构

[
            {
            name: 'JOBS',
            children: [{
                name: '2018-01',
                children: [{
                    name: '2018-01-01',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                  {
                    name: '2018-01-07',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                ]
              },
              {
                name: '2018-02',
                children: [{
                    name: '2018-01-08',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                  {
                    name: '2018-01-09',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                ]
              },
            ]
          }, {
            name: 'JOBS1',
            children: [{
                name: '2018-01',
                children: [{
                    name: '2018-01-01',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                  {
                    name: '2018-01-07',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                ]
              },
              {
                name: '2018-02',
                children: [{
                    name: '2018-01-08',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                  {
                    name: '2018-01-09',
                    children: [{
                      name: 12,
                      children: [{
                        name: 16
                      }]
                    }]
                  },
                ]
              },
            ]
          }
]

页面结构样式

由于树形结构的动态特性,自然而然的需要想到需要用到递归组件来实现了

<!--treeTable.vue-->
<template>
  <div class="tree-table">
    <div v-for="(item,index) in treeData" :key="item.name">
      <div :style="{width:wrapWidth+'px'}">{{item.name}}</div>
      <template v-if="item.children && item.children.length">
        <tree-table :tree-data="item.children" :wrapWidth="wrapWidth"></tree-table>
      </template>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'tree-table',//递归组件必须要有name
    data() {
      return {

      }
    },
    props: {
      //其容器元素的尺寸/列数=每列宽度
      wrapWidth: {
        default: 0
      },
      treeData: {
        default () {
          //最终数据结构预览
          return []
        }
      }
    },
    mounted() {
    }
  }
</script>

<style>
  .tree-table>div {
    width: 100%;
    display: flex;
    align-items: center;
    box-shadow: inset 0 0 0 1px #000;
  }
  .tree-table>div+div {
    margin-top: -1px;
  }

  .tree-table>div>div:first-child {
    /* 显示文字的dom元素 */
    padding:5px 8px;
    box-sizing: border-box;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .tree-table div:nth-child(n+1) {
    /* 剩余项填充满除第一项宽度的剩余宽度 */
    flex: 1
  }
</style>

页面的结构样式,大致是用display:flex实现的有兴趣的可以仔细看看,这里就不过多介绍了 现在想来,在天不时,地不利,人不和的面试环境,我估计实现这个样式都有难度吧 数据结构确定了,接下来就是数据转化的工作了

数据转化

水平有限倒腾好一会才把思路理顺,一图胜千言,我就直接上代码了

created() {
      //合并表头数据
      this.data.unshift(this.headerList)
      //遍历数据
      this.data.forEach((v) => {
        //递归转化数据
        this.recursionFn(v, this.result)
      })
    },
    methods: {
      recursionFn(data, arr) {
        //如果不存在
        if (!arr.find((w, j) => {
            return w.name && w.name === data[0]
          })) {
          let obj = {
            name: data[0],
            children: []
          }
          //如果data.slice(1)中还有元素则一直递归下去
          if (data.slice(1).length) {
            this.recursionFn(data.slice(1), obj.children)
          }
          // 遍历完毕整体填充至arr中
          arr.push(obj)
        } else {//数据中已存在
          let index = arr.findIndex((w, j) => {
            return w.name === data[0]
          })
          if (data.slice(1).length) {
            this.recursionFn(data.slice(1), arr[index].children)
          }
        }
      }

代码地址

完整代码地址:https://github.com/aol121/treeTable

希望能帮到学习中,面试中,工作中的你。

最后

这个组件的实现主要难点有:使用递归组件渲染页面,使用递归函数对数据的转化,当然这个只是我的思路,应该会有更好的方法实现,恳请大家不吝赐教;