d3真实数据---实例

文章展示了基于D3.js的消防安全知识图谱,包括隐患项和相关案例的层次结构,以及力导向布局的模拟。用户可以交互式地查看和搜索节点,点击节点以显示详细信息。系统还提供了控制界面,允许用户调整力模拟参数和显示设置。
摘要由CSDN通过智能技术生成

示例

![在这里插入图片描述](https://img-blog.csdnimg.cn/50f71f5e11464fafb9a9575d7fa9e70b.png
在这里插入图片描述

在这里插入图片描述

目录结构

在这里插入图片描述

testData.js

export const HZData = [
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "易燃可燃液体、可燃气体储罐(区)未按国家工程建设消防技术标准的规定设置固定灭火、冷却、可燃气体浓度报警、火灾报警设施",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "长虹旅馆",
        "XZCFAYYHMC": "",
        "JZKID": "258",
        "ZDHZYHID": "29,34,132",
        "ZLXQYHMC": "5.埋压/圈占/遮挡消火栓,占用防火间距;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 易燃可燃液体、可燃气体储罐(区)未按国家工程建设消防技术标准的规定设置固定灭火、冷却、可燃气体浓度报警、火灾报警设施;\n",
        "ZLXQGZID": "17,18,19,20,21",
        "DWID": "6100484402",
        "XZCFAYID": ""
      }
    ],
    "ID": "132",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "在人员密集场所违反消防安全规定使用、储存或销售易燃易爆危险品",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西乡县第二中学",
        "XZCFAYYHMC": "",
        "JZKID": "302",
        "ZDHZYHID": "29,133,37",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "1. 公共娱乐场所、商店、地下人员密集场所的安全出口数量不足或其总净宽度小于国家工程建设消防技术标准规定值的80%;\n2. 在人员密集场所违反消防安全规定使用、储存或销售易燃易爆危险品;\n",
        "ZLXQGZID": "",
        "DWID": "6100470743",
        "XZCFAYID": ""
      }
    ],
    "ID": "133",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "直接判定",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "长虹旅馆",
        "XZCFAYYHMC": "",
        "JZKID": "258",
        "ZDHZYHID": "29,34,132",
        "ZLXQYHMC": "5.埋压/圈占/遮挡消火栓,占用防火间距;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 易燃可燃液体、可燃气体储罐(区)未按国家工程建设消防技术标准的规定设置固定灭火、冷却、可燃气体浓度报警、火灾报警设施;\n",
        "ZLXQGZID": "17,18,19,20,21",
        "DWID": "6100484402",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "镇巴县中医院",
        "XZCFAYYHMC": "",
        "JZKID": "260",
        "ZDHZYHID": "29,33",
        "ZLXQYHMC": "6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n",
        "ZDHZYHMC": "1. 生产、储存和装卸易燃易爆危险品的工厂、仓库和专用车站、码头、储罐区,未设置在城市的边缘或相对独立的安全地带;\n",
        "ZLXQGZID": "25,22",
        "DWID": "6100471902",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "渭南承诺制测试02",
        "XZCFAYYHMC": "",
        "JZKID": "275",
        "ZDHZYHID": "29,33",
        "ZLXQYHMC": "4.占用/堵塞/封闭疏散通道、安全出口;\n",
        "ZDHZYHMC": "1. 生产、储存和装卸易燃易爆危险品的工厂、仓库和专用车站、码头、储罐区,未设置在城市的边缘或相对独立的安全地带;\n",
        "ZLXQGZID": "15,13",
        "DWID": "6100492868",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "渭南市体育中心",
        "XZCFAYYHMC": "",
        "JZKID": "289",
        "ZDHZYHID": "29,34",
        "ZLXQYHMC": "5.埋压/圈占/遮挡消火栓,占用防火间距;\n6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n",
        "ZLXQGZID": "18,17,23,22",
        "DWID": "6100434845",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西乡县第二中学",
        "XZCFAYYHMC": "",
        "JZKID": "302",
        "ZDHZYHID": "29,133,37",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "1. 公共娱乐场所、商店、地下人员密集场所的安全出口数量不足或其总净宽度小于国家工程建设消防技术标准规定值的80%;\n2. 在人员密集场所违反消防安全规定使用、储存或销售易燃易爆危险品;\n",
        "ZLXQGZID": "",
        "DWID": "6100470743",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "略阳县何家岩加油站",
        "XZCFAYYHMC": "",
        "JZKID": "371",
        "ZDHZYHID": "29,34,35",
        "ZLXQYHMC": "6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n8.使用不符合市场准入/不合格/国家明令淘汰的消防产品;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 城市建成区内的加油站、天然气或液化石油气加气站、加油加气合建站的储量达到或超过GB 50156对一级站的规定;\n",
        "ZLXQGZID": "23,22,24,28,27",
        "DWID": "6100471375",
        "XZCFAYID": ""
      }
    ],
    "ID": "29",
    "TREE_LEVEL": "1",
    "PARENT_ID": ""
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "生产、储存和装卸易燃易爆危险品的工厂、仓库和专用车站、码头、储罐区,未设置在城市的边缘或相对独立的安全地带",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "镇巴县中医院",
        "XZCFAYYHMC": "",
        "JZKID": "260",
        "ZDHZYHID": "29,33",
        "ZLXQYHMC": "6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n",
        "ZDHZYHMC": "1. 生产、储存和装卸易燃易爆危险品的工厂、仓库和专用车站、码头、储罐区,未设置在城市的边缘或相对独立的安全地带;\n",
        "ZLXQGZID": "25,22",
        "DWID": "6100471902",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "渭南承诺制测试02",
        "XZCFAYYHMC": "",
        "JZKID": "275",
        "ZDHZYHID": "29,33",
        "ZLXQYHMC": "4.占用/堵塞/封闭疏散通道、安全出口;\n",
        "ZDHZYHMC": "1. 生产、储存和装卸易燃易爆危险品的工厂、仓库和专用车站、码头、储罐区,未设置在城市的边缘或相对独立的安全地带;\n",
        "ZLXQGZID": "15,13",
        "DWID": "6100492868",
        "XZCFAYID": ""
      }
    ],
    "ID": "33",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "长虹旅馆",
        "XZCFAYYHMC": "",
        "JZKID": "258",
        "ZDHZYHID": "29,34,132",
        "ZLXQYHMC": "5.埋压/圈占/遮挡消火栓,占用防火间距;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 易燃可燃液体、可燃气体储罐(区)未按国家工程建设消防技术标准的规定设置固定灭火、冷却、可燃气体浓度报警、火灾报警设施;\n",
        "ZLXQGZID": "17,18,19,20,21",
        "DWID": "6100484402",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "渭南市体育中心",
        "XZCFAYYHMC": "",
        "JZKID": "289",
        "ZDHZYHID": "29,34",
        "ZLXQYHMC": "5.埋压/圈占/遮挡消火栓,占用防火间距;\n6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n",
        "ZLXQGZID": "18,17,23,22",
        "DWID": "6100434845",
        "XZCFAYID": ""
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "略阳县何家岩加油站",
        "XZCFAYYHMC": "",
        "JZKID": "371",
        "ZDHZYHID": "29,34,35",
        "ZLXQYHMC": "6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n8.使用不符合市场准入/不合格/国家明令淘汰的消防产品;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 城市建成区内的加油站、天然气或液化石油气加气站、加油加气合建站的储量达到或超过GB 50156对一级站的规定;\n",
        "ZLXQGZID": "23,22,24,28,27",
        "DWID": "6100471375",
        "XZCFAYID": ""
      }
    ],
    "ID": "34",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "城市建成区内的加油站、天然气或液化石油气加气站、加油加气合建站的储量达到或超过GB 50156对一级站的规定",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "略阳县何家岩加油站",
        "XZCFAYYHMC": "",
        "JZKID": "371",
        "ZDHZYHID": "29,34,35",
        "ZLXQYHMC": "6.占用/堵塞/封闭消防车通道,妨碍消防车通行;\n8.使用不符合市场准入/不合格/国家明令淘汰的消防产品;\n",
        "ZDHZYHMC": "1. 生产、储存、经营易燃易爆危险品的场所与人员密集场所、居住场所设置在同一建筑物内,或与人员密集场所、居住场所的防火间距小于国家工程建设消防技术标准规定值的75%;\n2. 城市建成区内的加油站、天然气或液化石油气加气站、加油加气合建站的储量达到或超过GB 50156对一级站的规定;\n",
        "ZLXQGZID": "23,22,24,28,27",
        "DWID": "6100471375",
        "XZCFAYID": ""
      }
    ],
    "ID": "35",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  },
  {
    "YH_TYPE": "重大",
    "CODE_DESC": "公共娱乐场所、商店、地下人员密集场所的安全出口数量不足或其总净宽度小于国家工程建设消防技术标准规定值的80%",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西乡县第二中学",
        "XZCFAYYHMC": "",
        "JZKID": "302",
        "ZDHZYHID": "29,133,37",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "1. 公共娱乐场所、商店、地下人员密集场所的安全出口数量不足或其总净宽度小于国家工程建设消防技术标准规定值的80%;\n2. 在人员密集场所违反消防安全规定使用、储存或销售易燃易爆危险品;\n",
        "ZLXQGZID": "",
        "DWID": "6100470743",
        "XZCFAYID": ""
      }
    ],
    "ID": "37",
    "TREE_LEVEL": "2",
    "PARENT_ID": "29"
  }
]

export const CFData = [
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "不履行消防安全职责逾期未改",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西乡县茶镇九年制学校",
        "XZCFAYYHMC": "不履行消防安全职责逾期未改;消防技术服务机构出具失实文件",
        "JZKID": "378",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100470707",
        "XZCFAYID": "99,102"
      }
    ],
    "ID": "99",
    "TREE_LEVEL": "3",
    "PARENT_ID": "17"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防产品、电气、燃气用具类",
    "YHRWXX": [],
    "ID": "16",
    "TREE_LEVEL": "2",
    "PARENT_ID": "9"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防安全标志未保持完好有效",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "天伊阁住宅小区1-4#楼、商业A地下车库",
        "XZCFAYYHMC": "消防设施、器材配置、设置不符合标准;消防安全标志未保持完好有效",
        "JZKID": "352",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100002275",
        "XZCFAYID": "35,38"
      }
    ],
    "ID": "38",
    "TREE_LEVEL": "3",
    "PARENT_ID": "12"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防安全管理手册",
    "YHRWXX": [],
    "ID": "154",
    "TREE_LEVEL": "2",
    "PARENT_ID": "0"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防技术服务机构出具失实文件",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西乡县茶镇九年制学校",
        "XZCFAYYHMC": "不履行消防安全职责逾期未改;消防技术服务机构出具失实文件",
        "JZKID": "378",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100470707",
        "XZCFAYID": "99,102"
      }
    ],
    "ID": "102",
    "TREE_LEVEL": "3",
    "PARENT_ID": "18"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防行政处罚案由代码",
    "YHRWXX": [],
    "ID": "9",
    "TREE_LEVEL": "1",
    "PARENT_ID": "0"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "消防设施、器材配置、设置不符合标准",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "陕西长城投资置业有限公司.",
        "XZCFAYYHMC": "消防设施、器材配置、设置不符合标准",
        "JZKID": "344",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100510988",
        "XZCFAYID": "35"
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "陕西长城投资置业有限公司.",
        "XZCFAYYHMC": "消防设施、器材配置、设置不符合标准",
        "JZKID": "344",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100510988",
        "XZCFAYID": "35"
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "西安飞机工业(集团)有限责任公司西飞技术学院实训综合楼",
        "XZCFAYYHMC": "消防设施、器材配置、设置不符合标准",
        "JZKID": "353",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100002388",
        "XZCFAYID": "35"
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "陕西长城投资置业有限公司.",
        "XZCFAYYHMC": "消防设施,器材配置,设置不符合标准",
        "JZKID": "344",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100510988",
        "XZCFAYID": "35"
      },
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "天伊阁住宅小区1-4#楼、商业A地下车库",
        "XZCFAYYHMC": "消防设施、器材配置、设置不符合标准;消防安全标志未保持完好有效",
        "JZKID": "352",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100002275",
        "XZCFAYID": "35,38"
      }
    ],
    "ID": "35",
    "TREE_LEVEL": "3",
    "PARENT_ID": "12"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "社会消防技术服务管理规定",
    "YHRWXX": [],
    "ID": "163",
    "TREE_LEVEL": "2",
    "PARENT_ID": "9"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "非人员密集场所使用国家明令淘汰的消防产品逾期未改",
    "YHRWXX": [
      {
        "ZLLJYHMC": "",
        "ZLLJGZID": "",
        "DWMC": "延长壳牌石油有限公司汉中西乡十天高速引道加油站",
        "XZCFAYYHMC": "非人员密集场所使用国家明令淘汰的消防产品逾期未改;",
        "JZKID": "375",
        "ZDHZYHID": "",
        "ZLXQYHMC": "",
        "ZDHZYHMC": "",
        "ZLXQGZID": "",
        "DWID": "6100470820",
        "XZCFAYID": "85"
      }
    ],
    "ID": "85",
    "TREE_LEVEL": "3",
    "PARENT_ID": "16"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "高层民用建筑消防安全管理规定",
    "YHRWXX": [],
    "ID": "104",
    "TREE_LEVEL": "2",
    "PARENT_ID": "9"
  },
  {
    "YH_TYPE": "处罚案由",
    "CODE_DESC": "高层民用建筑消防安全管理规定",
    "YHRWXX": [],
    "ID": "162",
    "TREE_LEVEL": "2",
    "PARENT_ID": "9"
  }
]

d3ZSTP.vue


<!-- 知识图谱 -->
<!-- d3知识图谱 -->
<template>
  <div class="d3_container">
    <d3-search class="d3_search" @getSearch="getSearch" @toggleCollapse="toggleCollapse"></d3-search>
    <d3-content class="d3_content" :data="data" :config="config"></d3-content>
  </div>
</template>

<script>
import d3Search from './d3Search'
import d3Content from './d3Content'
import { HZData, CFData } from './testData.js' //测试数据
import { link } from 'fs';
export default {
  components: { d3Search, d3Content },
  data() {
    return {
      search: {
        keyword: '',
        startTime: '',
        endTime: '',
        YHLX: 'ZDHZ',
      },
      nodes: [],
      links: [],//
      data: {},//处理后的数据
      config: {//配置
        control: true,//控件是否显示
        isCollapse: false,
        textDisplay: true,//控制关联文字显隐
        colors: ['#FFC0CB', '#55cccc', '#aaaaff', '#ca635f', '#BA55D3', '#1E90FF', '#4e88af'],//控制各级节点的背景颜色
        nodeSize: 50,//node节点大小
        textLineDisplay: true,//节点之间的关系内容是否默认显示
        node_category: [{ value: true, label: '隐患项', color: '#7FFFD4' }, { value: true, label: '相关案例', color: '#4e88af' }],//分类------(根据自己接口返回的数据进行调整)
      },

    }
  },
  mounted() { },
  methods: {
    //切换折叠
    toggleCollapse(isCollapse) {
      this.config.isCollapse = isCollapse
    },
    //获取搜索条件
    getSearch(query) {
      // console.log(query,'getSearch')
      this.search = query
      this.getData()
    },

    //获取数据---在这儿调用接口获取后台数据
    async getData() {
      try {
        let rulest
        //在这儿调用接口获取后台数据

        // rulest = await post('/api/xxxx', { keyword: this.search.keyword, type: this.search.type })
        // console.log(rulest, 'rulest')
        /**----------------------------------------获取死数据-树结构------------------------------------------- */
        let timer = setTimeout(() => {
          if (this.search.type == 'HZ') {
            rulest = HZData
          } else {
            rulest = CFData
          }
          this.formateData(rulest)
          clearTimeout(timer)
        }, 1000)
      } catch {

      }
    },

    //处理接口返回来的数据
    formateData(data) {
      console.log(data, 'data')
      let nodes = []
      let links = []

      data.forEach((item, index) => {
        //存在相关案例
        if (item.YHRWXX && item.YHRWXX.length) {
          item.YHRWXX.forEach(ite => {
            if (nodes.length) {//判断是否第一次(第一次nodes为空)
              if (!nodes.some(it => it.id == ite.DWID)) { nodes.push({ ...ite, id: ite.DWID, name: ite.DWMC, pid: item.ID, pids: [item.ID], level: parseInt(item.TREE_LEVEL) + 1 + '', relation: '相关案例' }); }
              else {
                nodes.forEach((a, index) => {
                  if (a.id == ite.DWID) {
                    // debugger
                    if (a.level < item.level) { a.level = parseInt(item.level) + 1 }
                    if (!a.pids.includes(item.ID)) { a.pids.push(item.ID) }
                  }
                })
              }
            } else {
              nodes.push({ ...ite, id: ite.DWID, name: ite.DWMC, pid: item.ID, pids: [item.ID], level: parseInt(item.TREE_LEVEL) + 1 + '', relation: '相关案例' });
            }

          })
        }

        nodes.push({ ...item, id: item.ID, name: item.CODE_DESC, pid: item.PARENT_ID, level: item.TREE_LEVEL, relation: '隐患项' })
      })
      console.log(nodes, 'nodes-----')

      for (let i = 0; i < nodes.length - 1; i++) {
        // for (let j = i + 1; j < nodes.length; j++) {

        if (nodes[i].pids && nodes[i].pids.length) {
          for (let a = 0; a < nodes[i].pids.length; a++) {
            links.push({ source: nodes[i].pids[a], target: nodes[i].id, relation: nodes[i].relation })
          }
        } else {
          // if(nodes.some(it => it.id == nodes[i].pid)) {links.push({ source: nodes[i].pid, target: nodes[i].id, relation: nodes[i].relation })}
          for (let j = i + 1; j < nodes.length; j++) {
            if (nodes[i].id == nodes[j].pid) {
              links.push({ source: nodes[i].id, target: nodes[j].id, relation: nodes[j].relation })
            } else if (nodes[i].pid == nodes[j].id) {
              links.push({ source: nodes[j].id, target: nodes[i].id, relation: nodes[j].relation })
            }
          }
        }

      }
      console.log(nodes, links, 'linkslinkslinks')
      this.data = { nodes, links }
    },






  }
}
</script>

<style lang="scss" scoped>
.d3_container {
  overflow: hidden;
  background: #fff;
}
</style>

d3Content.vue

<!-- d3内容组件 -->
<template>
  <div class="d3_content" style="display: flex; position: relative;">
    <div class="d3_control" v-if="option.control" :class="{collapse: option.isCollapse}">
      <div class="control_auto">
        <div class="control_wrap">
          <!-- <div class="control_force">
        <button @click="restartForce">重新启动仿真:restart()</button>
        <button @click="stopForce">重新启动仿真:stop()</button>
      </div> -->
          <!-- 力模拟控制 -->
          <div class="control_force">
            <el-collapse v-model="activeNames">
              <el-collapse-item title="电荷" name="1">
                电荷力:<el-slider v-model="forceOption.chargeStrength" :min="-300" :max="300" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
              </el-collapse-item>
              <el-collapse-item title="链接对象" name="2">
                弹簧距离: <el-slider v-model="forceOption.linkDistance" :min="0" :max="800" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
                弹簧强度: <el-slider v-model="forceOption.linkStrength" :step="0.1" :min="0" :max="1" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
              </el-collapse-item>
              <el-collapse-item title="碰撞检测" name="3">
                碰撞半径:<el-slider v-model="forceOption.collisionRadius" :min="0" :max="800" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
                碰撞强度:<el-slider v-model="forceOption.collisionStrength" :step="0.1" :min="0" :max="1" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
                迭代次数:<el-slider v-model="forceOption.collisionIterations" :step="0.01" :min="0" :max="1" @change="forceChange($event, 'forceOption.chargeStrength')"></el-slider>
              </el-collapse-item>
            </el-collapse>
          </div>
          <!-- 控制关联文字显隐 -->
          <div class="control_item d3_text">
            <h3>是否显示关联内容</h3>
            <div><el-radio-group v-model="option.textDisplay" size="mini"><el-radio-button :label="true">显示</el-radio-button><el-radio-button :label="false">隐藏</el-radio-button></el-radio-group></div>
          </div>

          <!-- 搜索节点 -->
          <div class="control_item d3_nativeSearch">
            <h3>搜索节点</h3>
            <div><el-input @input="searchKeyWords" v-model="option.keywords" clearable placeholder="请输入内容" /></div>
            <p class="font-sky" style="text-align: left;">
              <strong>节点个数:{{ nodes.length }}</strong>
              <br>
              <strong>关系个数:{{ links.length }}</strong>
              <br>
              <strong>平均度数:{{ gDegree }}</strong>
              <br>
              <strong>图密度:{{ gDensity }}</strong>
              <br>
              <strong>稀疏度:{{ gSparsity }}</strong>
            </p>
          </div>
          <!-- 切换显示分类 -->
          <div class="control_item d3_category">
            <h3>切换显示分类</h3>
            <!-- 节点分类 -->

            <div class="node_category">
              <div v-for="(item, index) in option.node_category" :key="index" style="cursor: pointer;">
                <div style="display: flex; margin: 10px 0;" @click="toggleCategory(item)">
                  <span style="width: 50px; height: 25px; margin-right: 10px;" :style="{ backgroundColor: item.value ? item.color : '#aaa' }"></span>
                  <span style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; flex: 1;" :style="{ color: item.value ? '#000' : '#aaa' }">{{ item.label }}</span>
                </div>
              </div>
            </div>

            <!-- 关系分类 -->
            <div class="relative_category"></div>

          </div>
        </div>
      </div>
    </div>
    <div class="svg_wrap" style="flex: 1"></div>

    <!-- 右边提示信息 -->
    <!-- 选择倒数第二级别 -->
    <el-card class="node-card info_yhx" v-if="selectCurrentYH.ID">
      <div slot="header" class="clearfix">
        <span>隐患项:{{ selectCurrentYH.name }}</span>
      </div>
      <div class="content">
        <!-- <div><span class="title">隐患名称:</span><span></span></div> -->
        <span class="title">单位名称:</span>
        <div v-for="(item,index) in selectCurrentYH.YHRWXX">
          <div class="rw_title"><span type="text" @click="dwmcClick(item)" style="color: #66b1ff; cursor:pointer">{{index + 1}}{{ item.DWMC }}</span></div>
        </div>
      </div>
    </el-card>

    <el-card class="node-card info_yhx" v-if="selectCurrentRWMC.DWID">
      <div slot="header" class="clearfix">
        <span style="color: black; cursor:pointer; font-size: 15px;margin-right: 10px;" v-if="selectCurrentYHCopy.ID" @click="backYHList"><i class="el-icon-back"></i>返回</span><span>{{ selectCurrentRWMC.DWMC }}{{ selectCurrentYHCopy.ID }}</span>
      </div>
      <div class="content">
        <div><span class="title">隐患项:</span><span v-html="selectCurrentRWMC.ZLXQYHMC"></span></div>
        <div v-for="item in shdwXgrwData" :key="item.RWID">
          <div class="rw_title" @click="rwmcClick(item)"><span class="title">任务名称:</span> <span type="text" style="color: #66b1ff; cursor:pointer">{{ item.RWMC }}</span></div>
        </div>
      </div>
    </el-card>
  </div>
</template>
<script>
import * as d3 from 'd3'
// import install from '@/plugins/d3-context-menu'
// install(d3) // 为d3注册右键菜单插件
export default {
  props: {
    data: { type: Object, required: true },//数据
    config: { type: Object, required: true },//配置
    // node_category: { type: Array, default() { return [] } }
  },
  computed: {
    gDegree() { return this.links.length && this.nodes.length ? (this.links.length / this.nodes.length).toFixed(2) : '0' },//平均度数
    gDensity() { return this.nodes.length <= 1 ? 0 : (this.links.length / (this.nodes.length * (this.nodes.length - 1))).toFixed(2) },//图密度
    gSparsity() { return this.links.length && this.nodes.length ? (this.links.length / (this.nodes.length * Math.log(this.nodes.length))).toFixed(2) : '0' },//稀疏度:
  },
  data() {
    return {
      option: {},

      /** data 数据 */
      nodes: [],
      links: [],

      /**node节点和文本 */
      nodeWrap: null,
      node: null,//node节点
      nodeText: null, //node文本



      forceSimulation: null,//力-实例
      svg: null,//元素
      forceOption: {
        chargeStrength: -300, //电荷力 强度

        //弹簧
        linkDistance: 400,//弹簧距离
        linkStrength: .5,//弹簧强度

        //碰撞检测
        collisionRadius: 0,
        collisionStrength: .3,
        collisionIterations: 0.15

      },


      /**线、文本、文本框 */
      lineWrap: null,
      line: null,//线条
      textWrap: null,/**文本、文本框 */
      lineText: null,//线条文字
      lineRect: null,//文本框


      isclickNode: false,//是否点击了node
      currentNodeData: {},//当前节点数据

      //控件配置
      activeNames: '0',

      /**右侧数据 */
      selectCurrentYH: {},//二级隐患node
      selectCurrentYHCopy: {},//二级隐患node 备用

      //点击社会单位
      selectCurrentRWMC: {},//任务名称node
      shdwXgrwData: [],//单位的相关任务
    }
  },
  watch: {
    data: {
      handler(newValue, oldValue) {
        console.log(newValue, 'newVlaue')
        this.option.textDisplay = true
        this.option.node_category.forEach(item => { item.value = true })
        this.nodes = newValue.nodes
        this.links = newValue.links
        this.init()
      },
      deep: true,
    },
    config: {
      handler(newValue, oldValue) {
        let obj = {
          control: newValue.control || true,
          textDisplay: newValue.textDisplay || true,
          keywords: newValue.keywords || '',
          control: newValue.control || true,
          node_category: newValue.node_category || [],
        }
        this.forceOption.collisionRadius = newValue.nodeSize + 50,
          this.option = { ...newValue, ...obj }

      },
      deep: true,
      immediate: true
    },
    'option.textDisplay'(newValue, oldValue) {
      this.toggleTextStatus()
    },
    selectCurrentRWMC(newValue, oldValue) {
      if (newValue.DWID) {
        this.getRWLB(newValue.DWID)
      }
    }
  },
  methods: {
    forceChange(value, type) {
      console.log(value, type, '--value-type')
      this.init()
    },
    //清空选中的对象
    clearSelectNode() {
      this.option.keywords = ''
      this.selectCurrentYH = {}
      this.selectCurrentYHCopy = {}
      this.selectCurrentRWMC = {}
    },

    //初始化
    init() {
      d3.select('.svg_wrap').selectAll('*').remove()// 渲染前清空svg内的元素
      this.initRender()//创建画布
      this.addMarkers()//创建数据
      this.d3SelectData()
      this.initForceSimulation()//初始化力学模拟
    },
    //初始化渲染
    initRender() {
      this.svg = d3.select('.svg_wrap').append('svg').attr('width', '100%').attr('height', '680px')
        .on('click', () => { console.log(this.isclickNode); this.isclickNode = false; this.clearSelectNode(); this.mouseleaveNode() })
        // 给画布绑定zoom事件(缩放、平移)
        .call(d3.zoom().on('zoom', event => {
          var scale = event.transform.k, translate = [event.transform.x, event.transform.y]
          // this.svg.attr('transform', 'translate(' + translate[0] + ', ' + translate[1] + ') scale(' + scale + ')');
          this.svg.attr("transform", event.transform)
        })).append('g').attr('width', '100%').attr('height', '100%')
    },

    /**
     * 处理数据工具
     * @param level 层级
     * @return size 大小
     */
    //处理node节点大小
    handleNodeSize(level) { let size = this.config.nodeSize; level = (level && parseInt(level)) || 1; return size - ((level - 1) * 3) },


    //d3 select data
    d3SelectData() {
      let _this = this
      //绘制边上的文字
      this.lineWrap = this.svg.selectAll('g.lineText_wrap').data(this.links).enter().append('g').attr("class", "lineText_wrap")
      //连线
      this.line = this.lineWrap.append('g').attr('class', 'line_wrap').append('line').attr('stroke', '#888').attr('stroke-width', 2).attr('opacity', 1).attr("marker-end", "url(#posMarker)")

      this.textWrap = this.lineWrap.append('g').attr('class', 'line_rect_text')
      //连线-矩形
      this.lineRect = this.textWrap.append('rect').attr('width', function (d) { return d.relation ? d.relation.length * 20 : 50 }).attr('height', 20).attr('y', -10).attr('fill', '#fff').attr('stroke', '#888').style('display', (d) => { return !d.relation ? 'none' : 'unset' })
      //连线-内容
      this.lineText = this.textWrap.append('text').attr('text-anchor', 'middle').attr('fill', '#333').text(function (d, i) { return d.relation || ''; });


      //节点、文本
      this.nodeWrap = this.svg.selectAll('g.node_wrap').data(this.nodes).enter().append('g').attr('class', 'node_wrap').style("cursor", "pointer").on("click", this.nodeClick).on('mouseover', this.mouseoverNode).on('mouseleave', this.mouseleaveNode).call(this.drag())//拖拽

      //节点
      this.node = this.nodeWrap.append('circle').attr('r', d => this.handleNodeSize(d.level)).attr("fill", d => { return this.config.colors[d.level ? d.level : this.config.colors.length - 1] }).attr("stroke", "none").attr("name", d => d.name).attr("id", d => d.id).exit().remove()

      //node中的文本
      this.nodeText = this.nodeWrap.append('text').attr('class', 'node_text').attr('font-size', () => 13).attr('fill', '#fff').attr('name', d => d.name).attr('text-anchor', 'middle')
        .attr('x', function (d) { return _this.formateNodeText(d3.select(this), d.name) })
    },

    //处理node 中的 文本
    formateNodeText(el, text) {
      let len = text.length
      text = text || ''
      if (len <= 3) { el.append('tspan').attr('x', 0).attr('y', 6).text(text) }
      else {
        const topText = text.substring(0, 3)
        const midText = text.substring(3, 7)
        let botText = text.substring(7, len)
        let topY = -16, midY = 6, botY = 26
        if (len <= 7) { topY = -10; midY = 20 } else if (len > 10) { botText = text.substring(7, 9) + '...' }
        el.text('')
        el.append('tspan').attr('x', 0).attr('y', topY).text(function () { return topText })
        el.append('tspan').attr('x', 0).attr('y', midY).text(function () { return midText })
        el.append('tspan').attr('x', 0).attr('y', botY).text(function () { return botText })
      }
    },


    //力学模拟
    initForceSimulation() {
      // 定义碰撞检测模型
      this.forceCollide = d3.forceCollide()
        .radius(this.forceOption.collisionRadius)//半径访问器-将节点视为具有一定 radius 的圆
        .strength(this.forceOption.collisionStrength)//碰撞力的强度
        .iterations(this.forceOption.collisionIterations) //迭代次数


      //力
      this.forceSimulation = d3.forceSimulation(this.nodes)//创建 力模拟实力
        //为力模型新加某种力
        .force("link", d3.forceLink(this.links).id(d => d.id).distance(this.forceOption.linkDistance).strength(this.forceOption.linkStrength))//弹簧力-弹簧模型,力的强度与被链接两个节点的距离成比例
        .force("charge", d3.forceManyBody().strength(this.forceOption.chargeStrength))//电荷力
        .force("center", d3.forceCenter(this.svg.node().parentElement.clientWidth / 2, this.svg.node().parentElement.clientHeight / 2))//向心力-用指定的x坐标和y坐标创建一个居中力
        .force("collision", this.forceCollide)//碰撞检测
        // .force("positioning", d3.forceRadial(200, this.svg.node().parentElement.clientWidth / 2, this.svg.node().parentElement.clientHeight / 2))//碰撞检测
        .on("tick", this.ticked)//监听力学
    },

    getTransform(source, target, _dis) {
      var r;
      if (target.x > source.x) {
        if (target.y > source.y) {
          r = Math.asin((target.y - source.y) / _dis)
        } else {
          r = Math.asin((source.y - target.y) / _dis);
          r = -r;
        }
      } else {
        if (target.y > source.y) {
          r = Math.asin((target.y - source.y) / _dis);
          r = Math.PI - r;
        } else {
          r = Math.asin((source.y - target.y) / _dis);
          r -= Math.PI;
        }
      }
      r = r * (180 / Math.PI);
      return "translate(" + source.x + "," + source.y + ")rotate(" + r + ")";
    },
    // 求两点间的距离
    getDis(s, t) { return Math.sqrt((s.x - t.x) * (s.x - t.x) + (s.y - t.y) * (s.y - t.y)) },
    // ticked()函数确定link线的起始点x、y坐标 node确定中心点 文本通过translate平移变化
    ticked() {

      //lineWrap
      this.lineWrap.attr("transform", d => { return this.getTransform(d.source, d.target, this.getDis(d.source, d.target)) });
      //连线
      this.line.attr("x1", d => this.handleNodeSize(d.source.level)).attr("y1", 0).attr("x2", d => this.getDis(d.source, d.target) - this.handleNodeSize(d.source.level) - 10).attr("y2", 0)
      //连线-矩形
      this.lineRect.attr("x", d => this.getDis(d.source, d.target) / 2 - (d.relation ? d.relation.length * 20 / 2 : 25))
      //连线-文字
      this.lineText.attr("x", (d, i, el) => { return this.getDis(d.source, d.target) / 2 })
        .attr("y", 5).attr("transform", (d) => {
          // 7.3.3 更新文本反正
          if (d.target.x < d.source.x) {
            var x = this.getDis(d.source, d.target) / 2;
            return 'rotate(180 ' + x + ' ' + 0 + ')';
          } else { return 'rotate(0)'; }
        });

      // 节点
      this.nodeWrap.attr("transform", d => { return `translate(${d.x}, ${d.y})` });
      return;

    },
    //点击事件
    nodeClick(event, data) {
      event.stopPropagation()
      this.isclickNode = true
      this.mouseoverNode(event, data)
      this.clearSelectNode();
      if (data.YHRWXX && data.YHRWXX.length) { this.selectCurrentYH = data }
      else if (data.DWID) { this.selectCurrentRWMC = data }
    },
    // 绘制关系箭头
    addMarkers() {
      // 定义箭头的标识
      var defs = this.svg.append("defs")
      const posMarker = defs.append("marker").attr("id", "posMarker").attr("orient", "auto").attr("stroke-width", 2).attr("markerUnits", "strokeWidth").attr("markerUnits", "userSpaceOnUse").attr("viewBox", "0 -5 10 10").attr("markerWidth", 12).attr("markerHeight", 12).append("path").attr("d", "M 0 -5 L 10 0 L 0 5").attr('fill', '#888').attr("stroke-opacity", 0.6);
      const posActMarker = defs.append("marker").attr("id", "posActMarker").attr("orient", "auto").attr("stroke-width", 2).attr("markerUnits", "strokeWidth").attr("markerUnits", "userSpaceOnUse").attr("viewBox", "0 -5 10 10").attr("markerWidth", 12).attr("markerHeight", 12).append("path").attr("d", "M 0 -5 L 10 0 L 0 5").attr('fill', '#1E90FF').attr("stroke-opacity", 0.6);
    },
    //node 拖拽事件
    drag() {
      let _this = this
      function dragsubject(event) {
        console.log(event, 'dragsubject')
        return _this.forceSimulation.find(event.x, event.y);
      }

      function dragstarted(event) {
        // console.log(event, 'dragstarted')
        if (!event.active) _this.forceSimulation.alphaTarget(0.3).restart();
        event.subject.fx = event.subject.x;
        event.subject.fy = event.subject.y;
      }

      function dragged(event) {
        // console.log(event, 'dragged')
        event.subject.fx = event.x;
        event.subject.fy = event.y;
      }

      function dragended(event) {
        // console.log(event, 'dragended')
        if (!event.active) _this.forceSimulation.alphaTarget(0);
        // 注释以下代码,使拖动结束后固定节点
        event.subject.fx = null;//如果想要某个节点解除固定,则将 node.fx 和 node.fy 设置为 null 或者删除这两个属性。
        event.subject.fy = null;//如果想要某个节点解除固定,则将 node.fx 和 node.fy 设置为 null 或者删除这两个属性。
      }

      return d3.drag()
        .subject(dragsubject)
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended)
    },
    //鼠标移入node节点上
    mouseoverNode(event, data) {
      //节点
      this.nodeWrap.attr('class', (d) => { if (data.id == d.id || data.id == d.pid || data.pid == d.id || (data.pids && data.pids.includes(d.id)) || (d.pids && d.pids.includes(data.id))) { return 'node_wrap actived' } else { return this.isclickNode ? 'node_wrap disactived' : 'node_wrap' } })
      //line
      this.lineWrap.attr('class', (d) => { if (d['source'].id == data.id || d['target'].id == data.id) { return 'lineText_wrap actived' } else { return this.isclickNode ? 'lineText_wrap disactived' : 'lineText_wrap' } })
    },
    //鼠标离开节点
    mouseleaveNode(event, data) {
      // 移除所有样式
      if (!this.isclickNode) {
        this.nodeWrap.attr('class', 'node_wrap')
        this.lineWrap.attr('class', 'lineText_wrap')
      }
    },



    /**----------------------------------------------------------------------控制器------------------------------------------------------------------- */
    restartForce() { console.log('this.forceSimulation.restart()'); this.forceSimulation.restart() },//重新启动仿真
    stopForce() { console.log('this.forceSimulation.stop()'); this.forceSimulation.stop() },//暂停仿真
    //切换文本显隐
    toggleTextStatus() { this.option.textDisplay ? this.textWrap.attr('display', 'block') : this.textWrap.attr('display', 'none') },
    //搜索节点
    searchKeyWords(value) {
      // 如果Input值是空的显示所有的圆和线(没有进行筛选)
      if (this.option.keywords === '') {
        this.nodeWrap.attr('class', 'node_wrap')
        this.lineWrap.attr('class', 'lineText_wrap')
      }
      // 否则判断判断三个元素是否等于name值,等于则显示该值
      else {
        this.isclickNode = true
        this.nodeWrap.attr('class', 'node_wrap disactived')
        this.lineWrap.attr('class', 'lineText_wrap disactived')


        let nodeArr = []
        this.nodeWrap.each(d => {
          if (d.name.indexOf(this.option.keywords) != -1) {
            console.log(d.name, 'd')
            if (d.id && !nodeArr.includes(d.id)) { nodeArr.push(d.id) }
            if (d.pid && !nodeArr.includes(d.pid)) { nodeArr.push(d.pid) }
            if (d.pids) {
              d.pids.forEach(it => {
                if (!nodeArr.includes(it)) { nodeArr.push(it) }
              })
            }
          }
        })

        // 搜索所有的节点
        this.nodeWrap.attr('class', d => { return nodeArr.includes(d.id) ? 'node_wrap actived' : 'node_wrap disactived' })

        //搜索关联line
        this.lineWrap.attr('class', (d) => { if (nodeArr.includes(d['source'].id) && nodeArr.includes(d['target'].id)) { return 'lineText_wrap actived' } else { return 'lineText_wrap disactived' } })
      }
    },
    //切换分类
    toggleCategory(item) {
      item.value = !item.value
      let arr = this.option.node_category.filter(item => item.value).map(item => item.label)
      this.nodes = this.data.nodes.filter(node => { return arr.includes(node.relation) })
      // 遍历删除关系
      this.links = this.data.links.filter(link => {
        console.log(arr.includes(link.source) && arr.includes(link.target), 'link')
        return arr.includes(link.source.relation) && arr.includes(link.target.relation)
      })
      this.init()
    },



    /**----------------------------------------------------右侧详情栏功能------------------------------------------------------------------------------- */
    // 点击任务名称
    rwmcClick(row) {
      if (row.RWID) {
        let path = ''
        if (row.RWLX && row.RWLX.indexOf('[火]') != -1) { path = '/xfjd/hzdc/fireDetail' } else { path = '/xfjd/projectDetail' }
        this.$router.push({ path: path, query: { runId: this.$route.query.runId, RWID: row.RWID, path: this.$route.path, pagetype: 2, parentName: "综合数据数据识别推荐", } });
      } else {
        this.$message.warning("任务ID不存在");
      }
    },
    //点击 隐患项-单位
    dwmcClick(item) {
      this.selectCurrentRWMC = item
      this.selectCurrentYHCopy = JSON.parse(JSON.stringify(this.selectCurrentYH))
      this.selectCurrentYH = {}
    },
    //backYHList
    backYHList() {
      this.selectCurrentRWMC = {}
      this.shdwXgrwData = []
      this.selectCurrentYH = this.selectCurrentYHCopy
      this.selectCurrentYHCopy = {}
    },
    //点击社会单位节点
    async getRWLB(DWID) {
      try {
        //这儿调接口
        // let res = await post('/api/blade-xfjd/api/zstp/zhsj_gjdwckrw', { DWID: DWID, runId: this.$route.query.runId })
        this.shdwXgrwData = res.data.data || [{RWMC:'这儿的数据可通过调用接口获取'}]
      } catch (error) {

      }
    },
  },

  mounted() {
    this.init()
  }
}
</script> 

<style lang="scss" >
// node节点样式
.svg_wrap {
  svg {
    margin: 0 !important;
  }
  user-select: none;
}

// node 包裹
.node_wrap {
  circle {
    border: 10px #ff6a6a solid;
  }
  //失效
  &.disactived {
    circle {
    }
  }
  &.actived {
    circle {
      stroke: #ffc0cb;
      stroke-width: 14px;
      stroke-opacity: 0.15 + 0.3;
      fill: red;
      border: 10px #ff6a6a solid;
    }
  }
}

//线条 包裹
.lineText_wrap {
  &.actived {
    //线条
    .line_wrap {
      //线条
      text {
        &.actived {
          stroke: #ffc0cb;
          stroke-width: 14px;
          stroke-opacity: 0.15 + 0.3;
          background: red;
          border: 10px #ff6a6a solid;
        }
      }
    }
  }
}

.node_wrap.disactived,
.lineText_wrap.disactived {
  opacity: 0.1;
}

/**控件样式 */
.d3_content {
  .el-collapse-item__wrap,
  .el-collapse-item__header {
    background: transparent;
    overflow: unset;
  }

  display: flex;
  // 控制wrap
  .d3_control {
    transition: 0.2s;
    width: 240px;
    height: 686px;
    overflow: auto;
    box-shadow: 1px 10px 10px #aaa;
    .control_auto {
      width: 100%;
      .control_wrap {
        width: 200px;
        padding: 20px 10px 20px 20px;
        margin-right: 10px;

        .control_item {
          margin-bottom: 40px;
        }
        .d3_text {
          h3 {
            line-height: 50px;
          }
        }
      }
    }
    &.collapse {
      width: 0px;
    }
  }

  .svg_wrap {
    flex: 1;
  }
}
//右边提示信息
.info_yhx {
  position: absolute;
  right: 5px;
  top: 40px;
  width: 270px !important;
}
.node-card {
  border: 1px solid #9faecf;
  background-color: #fff;
  color: black;
  text-align: left;

  .el-card__header {
    border-bottom: 1px solid #50596d;
    padding: 6px 5px;
  }
  .el-card__body {
    max-height: 500px;
    overflow: auto;
  }
}
</style>


d3Search.vue

<!-- d3 搜索组件 -->
<template>
  <div class="search_wrap" :class="{collapse: isCollapse}">
    <div class="control_collapse" :class="{collapse: isCollapse}" @click="toggleCollapse" title="控制栏">
      <div class="collapse_wrap">
        <span v-if="!isCollapse" style="font-size: 24px;font-weight: 700;">控制栏</span><span><i class="el-icon-s-fold" v-if="!isCollapse"></i><i class="el-icon-s-unfold" v-if="isCollapse"></i></span>
      </div>
    </div>
    <div class="search_content" :class="{collapse: isCollapse}">
      <el-input style="width: 600px" v-model="search.keyword" placeholder="请输入内容" clearable title="隐患项搜索">
        <el-select v-model="search.type" slot="prepend" placeholder="类型" style="width: 130px" title="类型切换">
          <el-option :label="item.value" :value="item.key" v-for="item in dictList" :key="item.key"></el-option>
        </el-select>
        <el-button slot="append" type="success" icon="el-icon-search" @click="query">搜索</el-button>
      </el-input>
    </div>
  </div>
</template>

<script>
export default {
  name: 'd3Search',
  data() {
    return {
      search: {
        keyword: '', type: 'HZ',//默认显示火灾类型的知识图谱
      },
      isCollapse: false,
      dictList: [{ key: 'HZ', value: '火灾' }, { key: 'CF', value: '处罚' }],//数据字典功能
    }
  },
  mounted() {
    this.$emit('getSearch', this.search)
  },
  methods: {
    //搜索功能---调用搜索接口
    query() { this.$emit('getSearch', this.search) },
    //切换控制栏折叠
    toggleCollapse() { this.isCollapse = !this.isCollapse; this.$emit('toggleCollapse', this.isCollapse) }
  }
}
</script>

<style lang='scss' scoped>
.search_wrap {
  position: relative;
  z-index: 9;
  &.collapse {
    box-shadow: 0 1px 10px #aaa;
  }
  display: flex;
  .control_collapse {
    .collapse_wrap {
      padding: 0 20px;
      width: 200px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    line-height: 60px;
    font-size: 30px;
    transition: 0.2s;
    &:hover {
      cursor: pointer;
    }

    &.collapse {
      width: 60px;
    }
  }
  .search_content {
    flex: 1;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    box-shadow: 10px 1px 10px #aaa;
    &.collapse {
      box-shadow: unset;
    }
  }
  .rigth_search_control {
    width: 60px;
    display: flex;
    justify-content: center;
    align-items: center;
    line-height: 60px;
    font-size: 30px;
    transition: 0.2s;
  }
}
</style>


</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RxnNing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值