Complex layout structure vue component, tree component integrated table component, each node represents a row

foreword

This article introduces a more complex typesetting method, which is a tree component integrated with a table component. Each node of the tree has a row of configuration.
The comparison product page is as follows:

insert image description here

demand analysis

Looking at this page, the first thing we think of is a tree component and a table component.
If you want to use the right side as a table component to sit up, it is more copying. Because it involves the folding of tree nodes, the corresponding rows are also folded.
So the easiest way is to simulate the table on the right and write each row to a tree node. In this way, when a node is collapsed, the corresponding row is also collapsed accordingly.

Code

The top header is implemented as a single div, offset to the right.

<div class="top-header">
  <div class="col-item">
    Row count
  </div>
  <div class="col-item">
    Column count
  </div>
  <div class="col-item">
    Custom SQL
  </div>
  <div class="col-item">
    Freshness
  </div>
  <div class="col-item">
    Cardinality
  </div>
  <div class="col-item">
    Uniqueness
  </div>
  <div class="col-item">
    Nullness
  </div>
  <div class="col-item">
    Distribution
  </div>
</div>

The tree on the left is displayed using the tree component of element-plus.
Each node should be written using slots, so that a lot of logic can be handled independently, such as using different icons for different levels.
The number of cells for each node can be set individually.
The number of cells is implemented using css classes. For cells that cannot be clicked, the background color is set to transparent, or set to be consistent with the background color.
Whether each cell is displayed or not, and what color is displayed, is related to the data of the node and the cell type.
So when getting the css class of the cell, use the two parameters of cell type 1 and node. as follows
<div :class="showCell(1, node)" class="node-rules-cell" />

Special attention should be paid to:
the tree has different levels, and the indentation distance to the right of each level is different. In order to achieve the width of the book node without affecting the typesetting of the following cells.
We need to align the end of the tree nodes, and the width of all different level nodes is different.
We can write like this.

@levMargin: 16px;
.node-info {
    
    
  width: 340px;
  margin-right: 12px;
  &.node-lev2{
    
    
    width: calc(340px - @levMargin) ;
  }
  &.node-lev3{
    
    
    width: calc(340px - @levMargin * 2) ;
  }
  &.node-lev4{
    
    
    width: calc(340px - @levMargin * 3) ;
  }
}

The width of the first level node is 340px, the width of the second level is 340-16, and the width of the third level node is 340 - 16*2. and so on.

Some detail processing is that we need to do some optimization for the hover and focus of the node.

.el-tree-node__content:hover .node-rules{
  background-color: #fff;
}
.el-tree-node:focus>.el-tree-node__content .node-rules{
  background-color: #fff;
}

Demonstration effect

insert image description here

full code

<script lang="ts" setup>
interface Tree {
      
      
  id: string
  label: string
  children?: Tree[]
}

const props = {
      
      
  value: 'id',
  label: 'label',
  children: 'children',
}

const data = [
  {
      
      
    id: '1',
    label: 'sample_analytics',
    children: [
      {
      
      
        id: '1-1',
        label: 'core',
        children: [
          {
      
      
            id: '1-1-1',
            label: 'customers',
            children: [
              {
      
      
                id: '1-1-1-1',
                label: 'id',
              },
              {
      
      
                id: '1-1-1-2',
                label: 'name',
              },
              {
      
      
                id: '1-1-1-3',
                label: 'email',
              },
            ],
          },
          {
      
      
            id: '1-1-2',
            label: 'order_items',
            children: [
              {
      
      
                id: '1-1-2-1',
                label: 'item_value',
              },
              {
      
      
                id: '1-1-2-2',
                label: 'category',
              },
            ],
          },
          {
      
      
            id: '1-1-3',
            label: 'orders',
            children: [
              {
      
      
                id: '1-1-3-1',
                label: 'id',
              },
              {
      
      
                id: '1-1-3-2',
                label: 'order_value',
              },
              {
      
      
                id: '1-1-3-3',
                label: 'order_type',
              },
            ],
          },
          {
      
      
            id: '1-1-4',
            label: 'revenue',
            children: [
              {
      
      
                id: '1-1-4-1',
                label: 'sales_mtd',
              },
              {
      
      
                id: '1-1-4-2',
                label: 'sales_qtd',
              },
              {
      
      
                id: '1-1-4-3',
                label: 'sales_ytd',
              },
            ],
          },
          {
      
      
            id: '1-1-5',
            label: 'traffic',
            children: [
              {
      
      
                id: '1-1-5-1',
                label: 'visitors_daily',
              },
              {
      
      
                id: '1-1-5-2',
                label: 'visitors_wtd',
              },
              {
      
      
                id: '1-1-5-3',
                label: 'visitors_mtd',
              },
            ],
          },
        ],
      },
      {
      
      
        id: '1-2',
        label: 'hubspot',
      },
      {
      
      
        id: '1-3',
        label: 'jira',
      },

      {
      
      
        id: '1-4',
        label: 'salesforce',
      },
      {
      
      
        id: '1-5',
        label: 'segment',
      },
      {
      
      
        id: '1-6',
        label: 'stripe',
      },
      {
      
      
        id: '1-7',
        label: 'zendesk',
      },
    ],
  },
]

const defaultExpandedKeys = ['1', '1-1']

function showCell(cell: number, node: any) {
      
      
  if ([1, 2].includes(node.level)) {
      
      
    return 'not-select'
  }
  if (node.level === 3) {
      
      
    return [1, 2, 4].includes(cell) ? 'select' : 'not-select'
  }
  if (node.level === 4) {
      
      
    return [5, 7, 8].includes(cell) ? 'select' : 'not-select'
  }
  return 'select'
}
</script>

<template>
  <div class="rules-container">
    <div class="top-header">
      <div class="col-item">
        Row count
      </div>
      <div class="col-item">
        Column count
      </div>
      <div class="col-item">
        Custom SQL
      </div>
      <div class="col-item">
        Freshness
      </div>
      <div class="col-item">
        Cardinality
      </div>
      <div class="col-item">
        Uniqueness
      </div>
      <div class="col-item">
        Nullness
      </div>
      <div class="col-item">
        Distribution
      </div>
    </div>
    <el-tree-v2
      class="table-tree-container"
      :default-expanded-keys="defaultExpandedKeys"
      :data="data"
      :props="props"
      :height="500"
    >
      <template #default="{ node }">
        <div class="node-item-row">
          <div class="node-info" :class="`node-lev${node.level}`">
            <span class="prefix" :class="{ 'is-leaf': node.isLeaf }" />
            <span>{
   
   { node.label }}</span>
          </div>
          <div class="node-rules">
            <div :class="showCell(1, node)" class="node-rules-cell" />
            <div :class="showCell(2, node)" class="node-rules-cell warning" />
            <div :class="showCell(3, node)" class="node-rules-cell" />
            <div :class="showCell(4, node)" class="node-rules-cell" />
            <div :class="showCell(5, node)" class="node-rules-cell" />
            <div :class="showCell(6, node)" class="node-rules-cell" />
            <div :class="showCell(7, node)" class="node-rules-cell error" />
            <div :class="showCell(8, node)" class="node-rules-cell" />
          </div>
        </div>
      </template>
    </el-tree-v2>
  </div>
</template>

<style lang="less">
@levMargin: 16px;
.rules-container{
      
      
  padding: 20px;
  border: 1px solid #ddd;
  height: 100%;
  .node-item-row{
      
      
    width: 100%;
    display: flex;
    // justify-content: space-between;
    padding-right: 20px;
    height: 28px;
    line-height: 28px;
  }
  .node-rules {
      
      
    // display: flex;
    // justify-content: space-between;
    // width: 1000px;
  }
  .node-rules-cell{
      
      
    display: inline-block;
    width: 100px;
    height: 28px;
    background-color: #47b881;
    box-shadow: white 0px 0px 0px 1px inset;
    &.warning{
      
      
      background-color: #f5d60099;
    }
    &.error{
      
      
      background-color: #eb5757;
    }
    &.not-select{
      
      
      background-color: #fff;
    }
  }
  .el-tree-node__content{
      
      
    height: 28px;
  }
  .table-tree-container {
      
      
    border: 1px solid #ddd;
    height: 100%;
    // margin-top: 50px;
  }
  .node-info {
      
      
    width: 340px;
    margin-right: 12px;
    &.node-lev2{
      
      
      width: calc(340px - @levMargin) ;
    }
    &.node-lev3{
      
      
      width: calc(340px - @levMargin * 2) ;
    }
    &.node-lev4{
      
      
      width: calc(340px - @levMargin * 3) ;
    }
  }
  .el-tree-node__content:hover .node-rules{
      
      
    background-color: #fff;
  }
  .el-tree-node:focus>.el-tree-node__content .node-rules{
      
      
    background-color: #fff;
  }
  .top-header{
      
      
    margin-left: 380px;
    .col-item{
      
      
      display: inline-block;
      width: 100px;
      font-size: 13px;
      text-align: center;
    }
  }

}
</style>

Guess you like

Origin blog.csdn.net/github_35631540/article/details/129061623