Bootstrap

vue使有jsPlumb画Dom元素间的连线

文章目录

 

 

   前言

   一、 什么是jsplumb?

   二、jsplumb能干什么?

   三、安装

1.安装jsplumb

2.安装 d3

   完整代码

   Github源码

 


前言

画Dom元素的连线的工具有leader-line-vue,leader-line,G6和我们现在重点要介绍的jsPlumb等等。其中G6提供了丰富的示例代码,且官方文档为中文,功能还是比较强大的。leader-line-vue是用leader-line封装的vue插件,在vue中使用比leader-line友好,安装引入即可使用,可以省略leader-line的繁杂配置。但leader-line-vue存在一个缺陷,那就是leader-line-vue用来画dom元素间连线的svg画布是被加载到页面的body元素里的,导致页面局部滚动时dom元素间的连线位置不变,无法随局部滚动条位置更新。如如需解决这个问题,有两种方法:一、使用d3辅助,在初始化画布时把svg画布移到指定容器内,在该容器内绘制dom元素间连线。二、使用jsPlumb画Dom元素间的连线。

f1dac724100e78a40f79bdca3c865d50.gif


 

 

一、 什么是jsplumb?

你有没有想过在你的网站上展示图表或者甚至在浏览器应用程序中使用它?用jsPlumb你可以!它是完全免费的,并根据MIT许可证提供。您可以直接从jsPlumb github网站下载框架。

该项目主要由Simon Porritt开发,他在澳大利亚西德尼担任网络开发人员。 jsPlumb由他积极开发。作为许多优秀的开发人员,他似乎更喜欢开发代码而不是编写教程,这就是为什么我提供一个简单的入门教程。

二、jsplumb能干什么?

那么如果你应该使用它取决于你想用jsPlumb做什么。该框架适用于必须绘制图表的Web应用程序,例如类似于Visio的应用程序或工作流程设计器等。由于图表项目和连接的所有参数都是非常精细可控的,因此您可以绘制您可以想到的任何类型的图表的!

三、安装

1.安装jsplumb

npm install jsplumb --save 

//在组件中引入jsplumb

 

import { jsPlumb } from "jsplumb";

代码如下(示例):

<template>

  <div class="TestTwo">

    <div class="TestTwoOutbox">

      <div class="line-wrap" style="margin-left: 70px">

        <div id="item-1" class="state-item">State 1</div>

        <div id="item-2" class="state-item">State 2</div>

      </div>

    </div>

  </div>

</template>

 

<script>

import { jsPlumb } from "jsplumb";

 

export default {

  name: "TestTwo",

  mounted() {

    let plumbIns = jsPlumb.getInstance();

    plumbIns.ready(function () {

      plumbIns.connect({

        source: "item-1",

        target: "item-2",

        anchor: [

          "Left",

          "Right"

        ],

        connector: ["StateMachine"],

        endpoint: "Blank",

        overlays: [["Arrow", { width: 8, length: 8, location: 1 }]],

        paintStyle: { stroke: "#909399", strokeWidth: 2 },

      });

    });

  },

};

</script>

 

<style lang="scss" scoped>

.TestTwo {

  width: 100%;

  height: 100%;

  .TestTwoOutbox {

    background: radial-gradient(

      ellipse at top left,

      rgba(255, 255, 255, 1) 40%,

      rgba(229, 229, 229, 0.9) 100%

    );

    height: 100%;

    padding:100px;

    width: 100%;

    position: relative;

    overflow: auto;

    .line-wrap {

      display: flex;

      margin-bottom: 40px;

      .state-item {

        width: 80px;

        height: 40px;

        color: #606266;

        background: #f6f6f6;

        border: 2px solid rgba(0, 0, 0, 0.05);

        text-align: center;

        line-height: 40px;

        font-family: sans-serif;

        border-radius: 4px;

        margin-right: 60px;

      }

    }

  }

}

</style>

 

2.安装 d3

 npm i d3 @6.2.0

//在main.js中引入

 

import * as d3 from "d3";

Vue.prototype.$d3 = d3;

window.d3 = d3;

代码如下(示例):

 if (d3.selectAll(".jtk-connector")) {

        d3.selectAll(".ShowContent").selectAll(".jtk-connector").remove();

      }

      if (d3.selectAll(".jtk-endpoint")) {

        d3.selectAll(".ShowContent").selectAll(".jtk-endpoint").remove();

      }

 

d3.js 清除svg

rave.select(el).html(''); // 不推荐

rave.selectAll("svg > *").remove(); // 移除svg内部节点

rave.selectAll("svg").remove(); // 移除svg节点

注:使用d3作用是切换时清除之前连线

安装成功后如图所示:

780ee0f2ac9521e49273e54100f5c0ed.png


 

完整代码

<template>
  <div class="TestOne">
    <div class="FrameContnet">
      <div class="ShowContent"  v-if="showTree">
        <div class="FirstContent">
          <div
            v-for="(item, index) in FirstContentData"
            :key="index"
            class="Itembox"
            :class="FirstContentNowIndex === index ? 'ActiveItembox' : ''"
            :id="item.Id"
            @click="FirstContentFun(item, index)"
          >
            <!--  <div class="Itembox_PartOne"></div> -->
            <div class="Itembox_PartTwo">
              <div class="ContentOutbox">
                <div class="TopPart">
                  <div class="LeftPart">{{ item.Name }}</div>
                  <div class="RightPart">{{ item.Time }}</div>
                </div>
                <div class="ShowCode">{{ item.ShowCode }}</div>
                <div class="BottomPart">
                  <div
                    v-for="(childitem, childindex) in item.List"
                    :key="childindex"
                    class="childitembox"
                  >
                    <div class="Numbox">{{ childitem.Data }}</div>
                    <div class="Titlebox">{{ childitem.Title }}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="SecondContent">
          <div
            v-for="(item, index) in SecondContentData"
            :key="index"
            class="Itembox"
            :class="SecondContentNowIndex === index ? 'ActiveItembox' : ''"
            :id="item.Id"
            @click="SecondContentFun(item, index)"
          >
            <!--  <div class="Itembox_PartOne"></div> -->
            <div class="Itembox_PartTwo">
              <div class="ContentOutbox">
                <div class="TopPart">
                  <div class="LeftPart">{{ item.Name }}</div>
                  <div class="RightPart">{{ item.Time }}</div>
                </div>
                <div class="ShowCode">{{ item.ShowCode }}</div>
                <div class="BottomPart">
                  <div
                    v-for="(childitem, childindex) in item.List"
                    :key="childindex"
                    class="childitembox"
                  >
                    <div class="Numbox">{{ childitem.Data }}</div>
                    <div class="Titlebox">{{ childitem.Title }}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="ThirdContent">
          <div
            v-for="(item, index) in ThirdContentData"
            :key="index"
            class="Itembox"
            :class="ThirdContentNowIndex === index ? 'ActiveItembox' : ''"
            :id="item.Id"
            @click="ThirdContentFun(item, index)"
          >
            <!-- <div class="Itembox_PartOne"></div> -->
            <div class="Itembox_PartTwo">
              <div class="ContentOutbox">
                <div class="TopPart">
                  <div class="LeftPart">{{ item.Name }}</div>
                  <div class="RightPart">{{ item.Time }}</div>
                </div>
                <div class="ShowCode">{{ item.ShowCode }}</div>
                <div class="BottomPart">
                  <div
                    v-for="(childitem, childindex) in item.List"
                    :key="childindex"
                    class="childitembox"
                  >
                    <div class="Numbox">{{ childitem.Data }}</div>
                    <div class="Titlebox">{{ childitem.Title }}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="FourContent">
          <div
            v-for="(item, index) in FourContentData"
            :key="index"
            class="Itembox"
            :class="FourContentNowIndex === index ? 'ActiveItembox' : ''"
            :id="item.Id"
            @click="FourContentFun(item, index)"
          >
            <!-- <div class="Itembox_PartOne"></div> -->
            <div class="Itembox_PartTwo">
              <div class="ContentOutbox">
                <div class="TopPart">
                  <div class="LeftPart">{{ item.Name }}</div>
                  <div class="RightPart">{{ item.Time }}</div>
                </div>
                <div class="ShowCode">{{ item.ShowCode }}</div>
                <div class="BottomPart">
                  <div
                    v-for="(childitem, childindex) in item.List"
                    :key="childindex"
                    class="childitembox"
                  >
                    <div class="Numbox">{{ childitem.Data }}</div>
                    <div class="Titlebox">{{ childitem.Title }}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import AnimEvent from "anim-event";
import FirstContentDataJson from "./data/FirstContentData.json";
import SecondContentDataJson from "./data/SecondContentData.json";
import ThirdContentDataJson from "./data/ThirdContentData.json";
import FourContentDataJson from "./data/FourContentData.json";
import ListDataJson from "./data/ListData.json";
import { jsPlumb } from "jsplumb";
export default {
  name: "TestOne",
  data() {
    return {
      ListData: ListDataJson.Data,
      FirstContentData: ListDataJson.Data,
      SecondContentData: [],
      ThirdContentData: [],
      FourContentData: [],
      FirstContentNowIndex: 0,
      SecondContentNowIndex: 0,
      ThirdContentNowIndex: 0,
      FourContentNowIndex: 0,
      ///
      // jsPlumb默认配置
      jsPlumbSetting: {
        // 动态锚点、位置自适应
        Anchors: [
          "Top",
          "TopCenter",
          "TopRight",
          "TopLeft",
          "Right",
          "RightMiddle",
          "Bottom",
          "BottomCenter",
          "BottomRight",
          "BottomLeft",
          "Left",
          "LeftMiddle",
        ],
        // 连线的样式 StateMachine、Flowchart,Bezier、Straight
        Connector: ["Bezier", { curviness: 60 }],
        // 鼠标是否拖动删除线
        ConnectionsDetachable: false,
        // 删除线的时候节点不删除
        DeleteEndpointsOnDetach: false,
        // 连线的两端端点类型:矩形 Rectangle;圆形Dot; eight: 矩形的高 ,idth: 矩形的宽
        Endpoints: [
          ["Dot", { radius: 2 }],
          ["Dot", { radius: 2 }],
        ],
        // 线端点的样式
        EndpointStyle: { fill: "skyblue", outlineWidth: 1 },
        // 绘制连线
        PaintStyle: {
          stroke: "#000000",
          strokeWidth: 1,
          outlineStroke: "transparent",
          // 设定线外边的宽,单位px
          outlineWidth: 10,
        },
        // 绘制连线箭头
        Overlays: [
          // 箭头叠加
          [
            "Arrow",
            {
              width: 10, // 箭头尾部的宽度
              length: 8, // 从箭头的尾部到头部的距离
              location: 1, // 位置,建议使用0~1之间
              direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
              foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
            },
          ],
        ],
        // 绘制图的模式 svg、canvas
        RenderMode: "svg",
        DragOptions: { cursor: "pointer", zIndex: 2000 },
        // 鼠标滑过线的样式
        HoverPaintStyle: {
          stroke: "skyblue",
          strokeWidth: 3,
          cursor: "pointer",
        },
      },
      // 连线的配置
      jsPlumbConnectOptions: {
        isSource: true,
        isTarget: true,
        // 动态锚点、提供了4个方向 Continuous、AutoDefault
        anchor: "Continuous",
        overlays: [
          [
            "Arrow",
            {
              width: 10, // 箭头尾部的宽度
              length: 8, // 从箭头的尾部到头部的距离
              location: 1, // 位置,建议使用0~1之间
              direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
              foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
            },
          ],
        ], // overlay
      },
    };
  },
  computed: {
    /**
     * 是否展示树计算属性
     */
    showTree() {
      return this.ListData && this.ListData.length;
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.InitDataFun();
      // this.AddEventListenerSrollFun();
    });
  },
  beforeDestroy() {
     this.RomoveSvgFun();
  },
  methods: {
    FirstContentFun(item, index) {
      this.FirstContentNowIndex = index;
      this.AsyncFun();
    },
    SecondContentFun(item, index) {
      this.SecondContentNowIndex = index;
      this.AsyncFun();
    },
    ThirdContentFun(item, index) {
      this.ThirdContentNowIndex = index;
      this.AsyncFun();
    },
    FourContentFun(item, index) {
      this.FourContentNowIndex = index;
      this.AsyncFun();
    },

    /**
     * 异步执行 获取数据----->清空已有连线--->从新绘制连线
     */
    AsyncFun() {
      new Promise((resolve) => {
        setTimeout(() => {
          resolve("aaa");
        }, 10);
      })
        .then((res) => {
          this.ChangShowDataFun();
          console.log(res, "第一次自己处理别的业务的代码");
          return res + "111";
        })
        .then((res) => {
          this.RomoveSvgFun();
          console.log(res, "第二次自己处理别的业务的代码");
          return res + "222";
        })
        .then((res) => {
          this.InitDrawLine();
          console.log(res, "第三次自己处理别的业务的代码");
        })
        .then((res) => {});
    },
    ChangShowDataFun() {
      this.SecondContentData =
        this.ListData[this.FirstContentNowIndex].Children;
      this.ThirdContentData =
        this.ListData[this.FirstContentNowIndex].Children[
          this.SecondContentNowIndex
        ].Children;
      this.FourContentData =
        this.ListData[this.FirstContentNowIndex].Children[
          this.SecondContentNowIndex
        ].Children[this.ThirdContentNowIndex].Children;
    },
    RomoveSvgFun() {
      if (d3.selectAll(".jtk-connector")) {
        d3.selectAll(".ShowContent").selectAll(".jtk-connector").remove();
      }
      if (d3.selectAll(".jtk-endpoint")) {
        d3.selectAll(".ShowContent").selectAll(".jtk-endpoint").remove();
      }
    },
    /*
     ************
     */
    InitDrawLine() {
      this.$nextTick().then(() => {
        let plumbIns = jsPlumb.getInstance();
        let defaultConfig = {
          anchor: ["Left", "Right"],
          Endpoints: [
            ["Dot", { radius: "50%" }],
            ["Dot", { radius: "50%" }],
          ],
          paintStyle: { stroke: "#75cede", strokeWidth: 3 },
          endpointStyle: {
            fill: "red",
            outlineStroke: "yellow",
            outlineWidth: 5,
          },
          connector: ["Bezier", { curviness: 60 }],
          overlays: [
            [
              "Arrow",
              {
                width: 10, // 箭头尾部的宽度
                length: 8, // 从箭头的尾部到头部的距离
                location: 1, // 位置,建议使用0~1之间
                direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
                foldback: 0.623, // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
              },
            ],
          ],
        };
        let FirstId = this.ListData[this.FirstContentNowIndex].Id;
        if (this.SecondContentData && this.SecondContentData.length) {
          this.SecondContentData.map((item) => {
            plumbIns.ready(() => {
              plumbIns.connect(
                {
                  source: FirstId,
                  target: item.Id,
                },
                defaultConfig
              );
            });
          });
        }
        let SecondId = document.getElementById(
          this.SecondContentData[this.SecondContentNowIndex].Id
        );
        if (this.ThirdContentData && this.ThirdContentData.length) {
          this.ThirdContentData.map((item) => {
            plumbIns.ready(() => {
              plumbIns.connect(
                {
                  source: SecondId,
                  target: item.Id,
                },
                defaultConfig
              );
            });
          });
        }
        let ThirdId = document.getElementById(
          this.ThirdContentData[this.ThirdContentNowIndex].Id
        );
        if (this.FourContentData && this.FourContentData.length) {
          this.FourContentData.map((item) => {
            plumbIns.ready(() => {
              plumbIns.connect(
                {
                  source: ThirdId,
                  target: item.Id,
                },
                defaultConfig
              );
            });
          });
        }
        ///
      });
    },
    /*
     ************
     */
    /**
     * 监听滚动条变化各部分连线跟随变化
     */
    AddEventListenerSrollFun() {
      window.addEventListener(
        "scroll",
        AnimEvent.add(() => {
          //this.ResetPositionDrawLine();
        }),
        false
      );
      let scrollableBox1 = document.getElementsByClassName("FirstContent")[0];
      scrollableBox1.addEventListener(
        "scroll",
        AnimEvent.add(() => {
          // this.ResetPositionDrawLine();
        }),
        false
      );
    },

    /************************以下是生成数据方法***********************************/
    InitDataFun() {
      let ListData = [];
      let FirstContentData = [];
      let SecondContentData = [];
      let ThirdContentData = [];
      let FourContentData = [];
      for (var i = 0; i < 16; i++) {
        if (i < 6) {
          FirstContentData.push({
            Name: "一级水厂" + (i + 1),
            Time: "13:0" + i,
            Id: "Firstid" + (i + 1),
            LID: "Firstlid" + (i + 1),
            List: [
              {
                Title: "浊度",
                Data: 0.23,
              },
              {
                Title: "PH",
                Data: 3,
              },
              {
                Title: "湿度",
                Data: 0.63,
              },
              {
                Title: "COD",
                Data: 0.23,
              },
            ],
          });
          let ListDataSec = [];
          for (var Seci = 0; Seci < 16; Seci++) {
            let ListDatarsd = [];
            for (var Rsdi = 0; Rsdi < 16; Rsdi++) {
              let ListDataFour = [];
              for (var Fouri = 0; Fouri < 16; Fouri++) {
                ListDataFour.push({
                  Name: "四级水厂" + (Fouri + 1),
                  Time: "13:0" + Fouri,
                  Id: "Fourid" + (Fouri + 1),
                  LID: "Fourlid" + (Fouri + 1),
                  ShowCode:
                    "一级---" +
                    (i + 1) +
                    "---二级---" +
                    (Seci + 1) +
                    "---三级---" +
                    (Rsdi + 1) +
                    "---四级---" +
                    (Fouri + 1),
                  Children: [],
                  List: [
                    {
                      Title: "浊度",
                      Data: 0.23,
                    },
                    {
                      Title: "PH",
                      Data: 3,
                    },
                    {
                      Title: "湿度",
                      Data: 0.63,
                    },
                    {
                      Title: "COD",
                      Data: 0.23,
                    },
                  ],
                });
              }
              ListDatarsd.push({
                Name: "三级水厂" + (Rsdi + 1),
                Time: "13:0" + Rsdi,
                Id: "Thirdid" + (Rsdi + 1),
                LID: "Thirdlid" + (Rsdi + 1),
                ShowCode:
                  "一级---" +
                  (i + 1) +
                  "---二级---" +
                  (Seci + 1) +
                  "---三级---" +
                  (Rsdi + 1),
                Children: ListDataFour,
                List: [
                  {
                    Title: "浊度",
                    Data: 0.23,
                  },
                  {
                    Title: "PH",
                    Data: 3,
                  },
                  {
                    Title: "湿度",
                    Data: 0.63,
                  },
                  {
                    Title: "COD",
                    Data: 0.23,
                  },
                ],
              });
            }
            ListDataSec.push({
              Name: "二级水厂" + (Seci + 1),
              Time: "13:0" + Seci,
              Id: "Secondid" + (Seci + 1),
              LID: "Secondlid" + (Seci + 1),
              ShowCode: "一级---" + (i + 1) + "---二级---" + (Seci + 1),
              Children: ListDatarsd,
              List: [
                {
                  Title: "浊度",
                  Data: 0.23,
                },
                {
                  Title: "PH",
                  Data: 3,
                },
                {
                  Title: "湿度",
                  Data: 0.63,
                },
                {
                  Title: "COD",
                  Data: 0.23,
                },
              ],
            });
          }
          ListData.push({
            Name: "一级水厂" + (i + 1),
            Time: "13:0" + i,
            Id: "Firstid" + (i + 1),
            LID: "Firstlid" + (i + 1),
            ShowCode: "一级---" + (i + 1),
            List: [
              {
                Title: "浊度",
                Data: 0.23,
              },
              {
                Title: "PH",
                Data: 3,
              },
              {
                Title: "湿度",
                Data: 0.63,
              },
              {
                Title: "COD",
                Data: 0.23,
              },
            ],
            Children: ListDataSec,
          });
        }
        SecondContentData.push({
          Name: "二级水厂" + (i + 1),
          Time: "13:0" + i,
          Id: "Secondid" + (i + 1),
          LID: "Secondlid" + (i + 1),
          List: [
            {
              Title: "浊度",
              Data: 0.23,
            },
            {
              Title: "PH",
              Data: 3,
            },
            {
              Title: "湿度",
              Data: 0.63,
            },
            {
              Title: "COD",
              Data: 0.23,
            },
          ],
        });
        ThirdContentData.push({
          Name: "三级水厂" + (i + 1),
          Time: "13:0" + i,
          Id: "Thirdid" + (i + 1),
          LID: "Thirdlid" + (i + 1),
          List: [
            {
              Title: "浊度",
              Data: 0.23,
            },
            {
              Title: "PH",
              Data: 3,
            },
            {
              Title: "湿度",
              Data: 0.63,
            },
            {
              Title: "COD",
              Data: 0.23,
            },
          ],
        });
        FourContentData.push({
          Name: "四级水厂" + (i + 1),
          Time: "13:0" + i,
          Id: "Fourid" + (i + 1),
          LID: "Fourlid" + (i + 1),
          List: [
            {
              Title: "浊度",
              Data: 0.23,
            },
            {
              Title: "PH",
              Data: 3,
            },
            {
              Title: "湿度",
              Data: 0.63,
            },
            {
              Title: "COD",
              Data: 0.23,
            },
          ],
        });
      }
      this.FirstContentData = FirstContentData;
      this.SecondContentData = SecondContentData;
      this.ThirdContentData = ThirdContentData;
      this.FourContentData = FourContentData;
      // console.log("ListData",ListData)
      // console.log("JSON.stringify(ListData)", JSON.stringify(ListData));

      this.AsyncFun();
    },
  },
};
</script>
<style lang="scss" scoped>
.TestOne {
  width: 100%;
  height: 100%;
  .FrameContnet {
    display: flex;
    flex-flow: row nowrap;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 80%;
    background: #1f4760;
    overflow: auto;
    margin-top: 60px;
    /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
    &::-webkit-scrollbar {
      width: 5px;
      height: 5px;
      background-color: #f5f5f5;
    }
    /*定义滚动条轨道 内阴影+圆角*/
    &::-webkit-scrollbar-track {
      -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
      border-radius: 2px;
      background-color: #f5f5f5;
    }
    /*定义滑块 内阴影+圆角*/
    &::-webkit-scrollbar-thumb {
      border-radius: 2px;
      -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
      background-color: #eee;
    }
    .ShowContent {
      display: flex;
      flex-flow: row nowrap;
      justify-content: space-between;
      width: calc(100% - 40px);
      height: calc(100% - 40px);
      position: relative;
      .FirstContent {
        width: 20%;
        // height: 100%;
        // overflow: auto;
        /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
        &::-webkit-scrollbar {
          width: 0px;
          height: 0px;
          background-color: #f5f5f5;
        }
        /*定义滚动条轨道 内阴影+圆角*/
        &::-webkit-scrollbar-track {
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-radius: 2px;
          background-color: #f5f5f5;
        }
        /*定义滑块 内阴影+圆角*/
        &::-webkit-scrollbar-thumb {
          border-radius: 2px;
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          background-color: #eee;
        }
        .Itembox {
          display: flex;
          flex-flow: row nowrap;
          width: 100%;
          //height: 190px;
          margin-bottom: 10px;
          background: #235270;
          border-radius: 5px;
          cursor: pointer;
          // .Itembox_PartOne {
          //   background: #63d2e1;
          //   width: 5px;
          //   height: 100%;
          //   border-radius: 5px;
          // }
          .Itembox_PartTwo {
            display: flex;
            flex-flow: column;
            justify-content: center;
            align-items: center;
            // width: calc(100% - 5px);
            width: 100%;
            height: 100%;
            border: 3px solid #396079;
            border-left: 5px solid #63d2e1;
            box-sizing: border-box;
            border-radius: 5px;
            .ContentOutbox {
              width: calc(100% - 20px);
              height: calc(100% - 20px);
              .TopPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .LeftPart {
                  color: #fff;
                  font-size: 20px;
                  font-weight: bold;
                }

                .RightPart {
                  color: #fff;
                  font-size: 16px;
                }
              }
              .ShowCode {
                color: #fff;
                font-size: 16px;
              }
              .BottomPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .childitembox {
                  display: flex;
                  flex-flow: column;
                  justify-content: center;
                  align-items: center;
                  .Numbox {
                    color: #fff;
                    font-size: 16px;
                    font-weight: bold;
                    margin-bottom: 10px;
                  }
                  .Titlebox {
                    color: rgb(213, 213, 213);
                    font-size: 14px;
                  }
                }
              }
            }
          }
        }
        .ActiveItembox {
          background: #112b3a;
        }
      }
      .SecondContent {
        width: 20%;
        // height: 100%;
        // overflow: auto;
        /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
        &::-webkit-scrollbar {
          width: 0px;
          height: 0px;
          background-color: #f5f5f5;
        }
        /*定义滚动条轨道 内阴影+圆角*/
        &::-webkit-scrollbar-track {
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-radius: 2px;
          background-color: #f5f5f5;
        }
        /*定义滑块 内阴影+圆角*/
        &::-webkit-scrollbar-thumb {
          border-radius: 2px;
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          background-color: #eee;
        }
        .Itembox {
          display: flex;
          flex-flow: row nowrap;
          width: 100%;
          //height: 190px;
          margin-bottom: 10px;
          background: #235270;
          border-radius: 5px;
          cursor: pointer;
          // .Itembox_PartOne {
          //   background: #63d2e1;
          //   width: 5px;
          //   height: 100%;
          //   border-radius: 5px;
          // }
          .Itembox_PartTwo {
            display: flex;
            flex-flow: column;
            justify-content: center;
            align-items: center;
            // width: calc(100% - 5px);
            width: 100%;
            height: 100%;
            border: 3px solid #396079;
            border-left: 5px solid #63d2e1;
            box-sizing: border-box;
            border-radius: 5px;
            .ContentOutbox {
              width: calc(100% - 20px);
              height: calc(100% - 20px);
              .TopPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .LeftPart {
                  color: #fff;
                  font-size: 20px;
                  font-weight: bold;
                }
                .RightPart {
                  color: #fff;
                  font-size: 16px;
                }
              }
              .ShowCode {
                color: #fff;
                font-size: 16px;
                margin: 5px 0 5px 0;
              }
              .BottomPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .childitembox {
                  display: flex;
                  flex-flow: column;
                  justify-content: center;
                  align-items: center;
                  .Numbox {
                    color: #fff;
                    font-size: 16px;
                    font-weight: bold;
                    margin-bottom: 10px;
                  }
                  .Titlebox {
                    color: rgb(213, 213, 213);
                    font-size: 14px;
                  }
                }
              }
            }
          }
        }
        .ActiveItembox {
          background: #112b3a;
        }
      }
      .ThirdContent {
        width: 20%;
        // height: 100%;
        // overflow: auto;
        /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
        &::-webkit-scrollbar {
          width: 0px;
          height: 0px;
          background-color: #f5f5f5;
        }
        /*定义滚动条轨道 内阴影+圆角*/
        &::-webkit-scrollbar-track {
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-radius: 2px;
          background-color: #f5f5f5;
        }
        /*定义滑块 内阴影+圆角*/
        &::-webkit-scrollbar-thumb {
          border-radius: 2px;
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          background-color: #eee;
        }
        .Itembox {
          display: flex;
          flex-flow: row nowrap;
          width: 100%;
          //height: 190px;
          margin-bottom: 10px;
          background: #235270;
          border-radius: 5px;
          cursor: pointer;
          // .Itembox_PartOne {
          //   background: #63d2e1;
          //   width: 5px;
          //   height: 100%;
          //   border-radius: 5px;
          // }
          .Itembox_PartTwo {
            display: flex;
            flex-flow: column;
            justify-content: center;
            align-items: center;
            // width: calc(100% - 5px);
            width: 100%;
            height: 100%;
            border: 3px solid #396079;
            border-left: 5px solid #63d2e1;
            box-sizing: border-box;
            border-radius: 5px;
            .ContentOutbox {
              display: flex;
              flex-flow: column;
              justify-content: space-between;
              width: calc(100% - 20px);
              height: calc(100% - 20px);
              .TopPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .LeftPart {
                  color: #fff;
                  font-size: 20px;
                  font-weight: bold;
                }
                .RightPart {
                  color: #fff;
                  font-size: 16px;
                }
              }
              .ShowCode {
                color: #fff;
                font-size: 16px;
              }
              .BottomPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .childitembox {
                  display: flex;
                  flex-flow: column;
                  justify-content: center;
                  align-items: center;
                  .Numbox {
                    color: #fff;
                    font-size: 16px;
                    font-weight: bold;
                    margin-bottom: 10px;
                  }
                  .Titlebox {
                    color: rgb(213, 213, 213);
                    font-size: 14px;
                  }
                }
              }
            }
          }
        }
        .ActiveItembox {
          background: #112b3a;
        }
      }
      .FourContent {
        width: 20%;
        // height: 100%;
        // overflow: auto;
        /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
        &::-webkit-scrollbar {
          width: 0px;
          height: 0px;
          background-color: #f5f5f5;
        }
        /*定义滚动条轨道 内阴影+圆角*/
        &::-webkit-scrollbar-track {
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-radius: 2px;
          background-color: #f5f5f5;
        }
        /*定义滑块 内阴影+圆角*/
        &::-webkit-scrollbar-thumb {
          border-radius: 2px;
          -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          background-color: #eee;
        }
        .Itembox {
          display: flex;
          flex-flow: row nowrap;
          width: 100%;
          //height: 190px;
          margin-bottom: 10px;
          background: #235270;
          border-radius: 5px;
          cursor: pointer;
          // .Itembox_PartOne {
          //   background: #63d2e1;
          //   width: 5px;
          //   height: 100%;
          //   border-radius: 5px;
          // }
          .Itembox_PartTwo {
            display: flex;
            flex-flow: column;
            justify-content: center;
            align-items: center;
            // width: calc(100% - 5px);
            width: 100%;
            height: 100%;
            border: 3px solid #396079;
            border-left: 5px solid #63d2e1;
            box-sizing: border-box;
            border-radius: 5px;
            .ContentOutbox {
              width: calc(100% - 20px);
              height: calc(100% - 20px);
              .TopPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .LeftPart {
                  color: #fff;
                  font-size: 20px;
                  font-weight: bold;
                }
                .RightPart {
                  color: #fff;
                  font-size: 16px;
                }
              }
              .ShowCode {
                color: #fff;
                font-size: 16px;
              }
              .BottomPart {
                display: flex;
                flex-flow: row nowrap;
                justify-content: space-between;
                align-items: center;
                width: 100%;
                margin: 15px 0 15px 0;
                .childitembox {
                  display: flex;
                  flex-flow: column;
                  justify-content: center;
                  align-items: center;
                  .Numbox {
                    color: #fff;
                    font-size: 16px;
                    font-weight: bold;
                    margin-bottom: 10px;
                  }
                  .Titlebox {
                    color: rgb(213, 213, 213);
                    font-size: 14px;
                  }
                }
              }
            }
          }
        }
        .ActiveItembox {
          background: #112b3a;
        }
      }
    }
  }
}
</style>

Github源码

 

 

;