当前位置:   article > 正文

jsplumb实现连线,表格之间实现批量字段映射_jsplumb连线

jsplumb连线

jsplumb + vue2

需求分析:两个表格分别展示需要拼接的数据,通过拖拽连线建立数据绑定关系。
实现步骤:
一、渲染表格数据
二、初始化节点数据
三、绑定事件
四、获取链接关系数据
1、根据已有数据初始化连线关系
2、新增数据不改变原连线添加节点
五、优化功能三:数据量大,表格滚动,滚动时连线跟随移动

#一、渲染数据
根据你的数据渲染出表格,注意绑定唯一字段,jsplumb使用

<template>
  <div class="app-container">
    <el-row id="container" :gutter="20">
      <el-col :span="11">
        <div id="leftTable" style="display: inline-block">
          <div style="text-indent: 0px; font-size: 16px;text-align: center;margin-bottom:5px">数据对象</div>
          <table style="height: 535px; overflow: scroll; display: inline-block"  @scroll="connect">
            <thead>
              <tr>
                <th
                  v-for="col in sourceColumn"
                  :key="col.dataIndex"
                  style=" text-align: left"
                  :style="{minWidth:col.width}"
                >
                  {{ col.title }}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr
              style="overflow:hidden;"
                v-for="code in sourceList"
                :id="code[sourceKey]"
                :key="code"
                name="joint"
              >
                <td
                  v-for="col in sourceColumn"
                  :key="index + col.dataIndex"
                  style="text-align: left;"
                  :style="{
                    textIndent:
                      col.dataIndex == sourceKey
                        ? code.indent * 20 + 'px'
                        : '0px',
                        minWidth:col.width,
                  }"
                >
                  {{ code[col.dataIndex] }}
                </td>
                <span
                  v-if="code.data_type != 'Object' && code.data_type != 'Array' && !code.disabled && code.dict_type"
                  style="
                    display: inline-block;
                    width: 14px;
                    height: 14px;
                    border-radius: 50%;
                    background: #ccc;
                    margin-left: -15px;
                    transform: translateY(5px);
                  "
                  :style="{transform:'translateY('+code.top + ')'}"
                >
              </span>
              </tr>
            </tbody>
          </table>
        </div>
      </el-col>
      <el-col :span="12" :offset="1">
        <div
          id="rightTable"
          style="
            height: 555px;
            overflow-y: auto;
            display: inline-block;
            position: relative;
            padding-left: 50px;
          "
          @scroll="connect"
        >
          <div
            v-for="target in targetList"
            :key="target.title"
            style="position: relative; margin-bottom: 10px"
          >
            <div style="text-indent: 15px; font-size: 16px;text-align: center;margin-bottom:5px">
              {{ target.title }}
            </div>
            <table style="margin-left: 15px">
              <thead style="width: 100%">
                <tr>
                  <th
                    v-for="col in targetColumn"
                    :key="col.dataIndex"
                    style="
                      min-width: 180px;
                      text-align: left;
                      text-indent: 40px;
                    "
                  >
                    {{ col.title }}
                  </th>
                </tr>
              </thead>
              <tbody style="width: 100%">
                <tr
                  v-for="(data, index) in target.list"
                  :key="index"
                  :id="'right' + (data[targetKey] ? data[targetKey] : data)"
                  name="data"
                  style="position: relative"
                >
                  <td
                    style="text-align: left; text-indent: 40px"
                    v-for="col in targetColumn"
                    :key="index + col.dataIndex"
                  >
                    {{ data[col.dataIndex] }}
                  </td>
                  <span
                    style="
                      display: inline-block;
                      width: 14px;
                      height: 14px;
                      border-radius: 50%;
                      background: #ccc;
                      margin-top: 5px;
                      margin-left: -0px;
                      position: absolute;
                      left: 0;
                      top: 0;
                      z-index: 999;
                    "
                  ></span>
                </tr>
              </tbody>
            </table>
          </div>
          <div
            v-if="targetList.length == 0"
            style="
              color: #ffffff88;
              text-align: center;
              font-size: 14px;
              margin-top: 80px;
            "
          >
            请选择字典
          </div>
        </div>
      </el-col>
    </el-row>
  </div>
</template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146

#二、初始化数据节点

<script>
//局部引入
import jsPlumb from "jsplumb";
//jsplumb使用
let $jsPlumb = jsPlumb.jsPlumb;
let jsPlumb_instance = null; // 缓存实例化的jsplumb对象
export default {
  data() {
    return {
      // 左侧对照数据
      // sourceList: [
      //   { name: "表字段1" },
      //   { name: "表字段2" },
      //   { name: "表字段3" }
      // ],
      // 右侧标准字典数据
      // targetList: [
      //   { name: "表字段1" },
      //   { name: "字段2" },
      //   { name: "字段3" },
      //   { name: "字段4" }
      // ],
      pointList: null, //向后端传递的map映射关系
      diffList: [],
      count: 0
    };
  },
  props: [
    "sourceList",
    "targetList",
    "targetColumn",
    "targetKey",
    "sourceColumn",
    "sourceKey"
  ],
  mounted() {
    setTimeout(() => {
      this.showPlumb();
    }, 100);
  },
  watch: {
    targetList(newval, oldval) {
      let oldList = oldval.map((val) => val.title);
      this.diffList = newval.filter((val) => !oldList.includes(val.title));
      setTimeout(() => {
        this.showDiff();
      }, 100);
    }
  },
  methods: {
    showPlumb() {
      let self = this;
      // 清除端点、连接
      jsPlumb_instance?.reset();
      this.pointList = new Map();
      this.$forceUpdate();
      jsPlumb_instance = $jsPlumb.getInstance({
        Container: "container", // 选择器id
        EndpointStyle: { radius: 0.11, fill: "#00c1de" }, // 端点样式
        PaintStyle: { stroke: "#00c1de", strokeWidth: 4 }, // 绘画样式,默认8px线宽  #456
        HoverPaintStyle: { stroke: "#1E90FF" }, // 默认悬停样式  默认为null
        ConnectionOverlays: [
          // 此处可以设置所有箭头的样式,因为我们要改变连接线的样式,故单独配置
          // Arrow-箭头  Label-标签  PlainArrow-平头箭头  Diamond-菱形  Diamond(钻石)-钻石箭头  Custom-自定义
          //   [
          //     "Arrow",
          //     {
          //       // 设置参数可以参考中文文档
          //       location: 1,
          //       length: 8,
          //       paintStyle: {
          //         // stroke: "#409EFF",
          //         // fill: "#409EFF"
          //         fill: '#7AB02C',
          // radius: 10
          //       }
          //     }
          //   ]
        ],
        Connector: ["Straight"], // 连接器的类型:直线Straight,流程图flowchart,状态机state machine,贝塞尔曲线Bezier等 默认贝塞尔曲线
        DrapOptions: { cursor: "crosshair", zIndex: 2000 }
        //     connectorStyle:{
        //       paintStyle: {
        //     fill: '#7AB02C',
        //     radius: 10
        // },
        //     }
      });
	//调用初始化节点
      jsPlumb_instance.batch(() => {
        for (let i = 0; i < this.sourceList.length; i++) {
        //根据条件筛选哪些数据可以连接
          if (
            this.sourceList[i].data_type != "Object" &&
            this.sourceList[i].data_type != "Array" && !this.sourceList[i].disabled && this.sourceList[i].dict_type
          ) {
            this.initLeaf(this.sourceList[i][this.sourceKey], "joint");
          }
        }
        for (let j = 0; j < this.targetList.length; j++) {
          for (let i = 0; i < this.targetList[j].list.length; i++) {
            this.initLeaf(
              "right" +
                (this.targetList[j].list[i][this.targetKey] ?
                  this.targetList[j].list[i][this.targetKey] :
                  this.targetList[j].list[i]),
              "data"
            );
          }
        }
      });

      const joint = document.getElementsByName("joint");
      const data = document.getElementsByName("data");

      jsPlumb_instance.setSourceEnabled(joint, true);
      jsPlumb_instance.setTargetEnabled(data, true);
      jsPlumb_instance.setDraggable(joint, true); // 是否支持拖拽
      jsPlumb_instance.setDraggable(data, true); // 是否支持拖拽
      jsPlumb_instance.bind("dblclick", (conn, originalEvent) => {
        // this.$confirm("确认删除映射么?", "提示", {
        //   confirmButtonText: "确定",
        //   cancelButtonText: "取消",
        //   closeOnClickModal: false,
        //   type: "warning"
        // })
        //   .then(() => {
            jsPlumb_instance.deleteConnection(conn);
          // })
          // .catch(() => {});
      });
      ///两个表进行关联时 连线
      jsPlumb_instance.bind("connection", function (info) {
      //连线发盛变化时,处理自己需要的数据
        self.pointList.set(info.sourceId, info.targetId.split("right")[1]);
        self.$emit("change", self.pointList);
      });
      // 删除连线
      jsPlumb_instance.bind("connectionDetached", function (evt) {
        if (self.pointList.has(evt.sourceId)) {
          self.pointList.delete(evt.sourceId);
          self.$emit("change", self.pointList);
        }
      });
    },
    // 初始化具体节点
    initLeaf(key, type) {
      const ins = jsPlumb_instance;
      const elem = document.getElementById(key);
      // console.log(key, elem, 99);
      if (type == "joint") {
        ins.makeSource(elem, {
          anchor: [1, 0.5, 0, 0], // 左 上 右 下
          allowLoopback: false,
          maxConnections: 1,
          endpoint: "Dot"
        });
      } else {
        ins.makeTarget(elem, {
          anchor: [0, 0.5, 0, 0],
          allowLoopback: false,
          maxConnections: 100
          // endpoint: "Dot",
          // paintStyle: {
          //   fill: "blue",
          //   strokeWidth: 1
          // }
        });
      }
    },
    initLeft() {
      for (let i = 0; i < this.sourceList.length; i++) {
        if (
          this.sourceList[i].data_type != "Object" &&
          this.sourceList[i].data_type != "Array"
        ) {
          this.initLeaf(this.sourceList[i][this.sourceKey], "joint");
        }
      }
    }
  }
};
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183

我这里用的props的数据,可以用自己的数据,注意数据结构即可
绑定事件上面方法以实现

#四、初始化已有的连接数据及右侧数据变化时将节点渲染为可连接节点

// 初始化字典连线
    initLines () {
      this.sourceList.forEach(item => {
        if(item.dict_data){
          console.log("right" + item.dict_data_path + "__" + item.dict_data + "__" +item.dict_data_type);
          
          jsPlumb_instance.connect({
            source: item[this.sourceKey],
            target:"right" + item.dict_data_path + "__" + item.dict_data + "__" +item.data_type
          });
        }
      })
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

遍历需要连接的数据,调用实例的connect方法连接


watch: {
    targetList(newval, oldval) {
      let oldList = oldval.map((val) => val.title);
      this.diffList = newval.filter((val) => !oldList.includes(val.title));
      setTimeout(() => {
        this.showDiff();
      }, 100);
    }
  },
methods: {
    showDiff() {
      for (let j = 0; j < this.diffList.length; j++) {
        for (let i = 0; i < this.diffList[j].list.length; i++) {
          this.initLeaf(
            "right" +
              (this.diffList[j].list[i][this.targetKey] ?
                this.diffList[j].list[i][this.targetKey] :
                this.diffList[j].list[i]),
            "data"
          );
        }
      }
    },
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

监听数据变化,将新增的数据渲染节点

#五、页面滚动,连线跟随变化
触发滚动事件时,将现有的连接关系保存,将原有的连线删除,重新简历连线,这里设置滚动三次触发一次方法

    connect() {
      this.count++;
      if (this.count >= 3) {
        let connections = jsPlumb_instance.getAllConnections();
        let linePoints = [];
        for (let i = connections.length - 1; i >= 0; i--) {
          linePoints.push([connections[i].sourceId, connections[i].targetId]);
          jsPlumb_instance.deleteConnection(connections[i]);
        }
        for (let i = 0; i < linePoints.length; i++) {
          jsPlumb_instance.connect({
            source: linePoints[i][0],
            target: linePoints[i][1]
          });
        }
        this.count = 0;
      }
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/117139
推荐阅读
相关标签
  

闽ICP备14008679号