序文
この記事では、テーブル コンポーネントと統合されたツリー コンポーネントである、より複雑な組版方法を紹介します. ツリーの各ノードには構成の行があります.
比較商品ページは以下の通りです。
需要分析
このページを見て最初に思いつくのは、ツリー コンポーネントとテーブル コンポーネントです。
右側を座るテーブル部品として使いたい場合は、よりコピーです。ツリー ノードの折り畳みを伴うため、対応する行も折り畳まれます。
したがって、最も簡単な方法は、右側のテーブルをシミュレートし、各行をツリー ノードに書き込むことです。このように、ノードが折りたたまれると、それに応じて対応する行も折りたたまれます。
コード
上部ヘッダーは、右にオフセットされた単一の div として実装されます。
<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>
左側のツリーは、element-plus のツリー コンポーネントを使用して表示されます。
各ノードはスロットを使用して作成する必要があります。これにより、レベルごとに異なるアイコンを使用するなど、多くのロジックを個別に処理できます。
各ノードのセル数は個別に設定できます。
セルの数は css クラスを使用して実装されます。クリックできないセルについては、背景色が透明に設定されるか、背景色と一致するように設定されます。
各セルを表示するかしないか、何色で表示するかは、ノードのデータとセルの種類に関係します。
したがって、セルの css クラスを取得するときは、セル タイプ 1 とノードの 2 つのパラメーターを使用します。次のように
<div :class="showCell(1, node)" class="node-rules-cell" />
特別な注意を払う必要があります:
ツリーにはさまざまなレベルがあり、各レベルの右側のくぼみの距離が異なります。後続のセルの組版に影響を与えずにブック ノードの幅を実現するため。
ツリー ノードの端を揃える必要があり、すべての異なるレベル ノードの幅は異なります。
このように書くことができます。
@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) ;
}
}
第 1 レベル ノードの幅は 340px、第 2 レベル ノードの幅は 340-16、第 3 レベル ノードの幅は 340 - 16*2 です。等々。
いくつかの詳細な処理は、ノードのホバーとフォーカスの最適化を行う必要があることです。
.el-tree-node__content:hover .node-rules{
background-color: #fff;
}
.el-tree-node:focus>.el-tree-node__content .node-rules{
background-color: #fff;
}
実証効果
完全なコード
<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>