GraphSpace.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. <template>
  2. <div class="node-box" style="position:relative;" v-loading="graphLoading">
  3. <transition name="el-zoom-in-center">
  4. <div v-show="infoVisible" class="graph-info-box">
  5. <div class="graph-info-child">
  6. <div class="info-item" style="margin-bottom: 15px">
  7. <div class="info-item-title">
  8. <label>{{ comment }}</label>
  9. <!--<div class="add-attribute-box" style="float: right" @click="showAddGraphSpaceDialog({Name:''})">新建-->
  10. <!--</div>-->
  11. <!--<div class="add-attribute-box" style="float: right;margin-right: 10px" @click="removeGraphSpace()">-->
  12. <!--删除-->
  13. <!--</div>-->
  14. </div>
  15. <div class="info-item-content">
  16. <!--<el-select v-model="space" filterable placeholder="请选择" @change="handleSpaceChange">-->
  17. <!--<el-option v-for="item in spaceList"-->
  18. <!--:key="item.name"-->
  19. <!--:label="item.comment"-->
  20. <!--:value="item.name">-->
  21. <!--</el-option>-->
  22. <!--</el-select>-->
  23. <div style="margin-top: 20px;font-size: 14px;color: #606266">本体名称:{{ ontologyName }}</div>
  24. </div>
  25. </div>
  26. <el-tabs v-model="activeTab" class="el-tabs-item">
  27. <el-tab-pane label="概念" name="tag">
  28. <el-table
  29. class="tab-table-item"
  30. @row-click="modifyTag"
  31. :data="tagList"
  32. style="width: 100%">
  33. <el-table-column
  34. prop="Name"
  35. label="名称">
  36. <template slot-scope="scope">
  37. <div :style="scope.row.Name === activeSelectType ? 'color: #409EFF':''">
  38. <label>{{ scope.row.comment }}</label>
  39. <!-- <label>({{ scope.row.count }})</label>-->
  40. </div>
  41. </template>
  42. </el-table-column>
  43. </el-table>
  44. <!-- <div class="add-attribute-box" @click="modifyTag({Name:''})">+新建TAG</div>-->
  45. </el-tab-pane>
  46. <el-tab-pane label="关系" name="edge" class="el-tabs-item">
  47. <el-table
  48. class="tab-table-item"
  49. @row-click="modifyEdge"
  50. :data="edgeList"
  51. style="width: 100%">
  52. <el-table-column
  53. prop="Name"
  54. label="名称">
  55. <template slot-scope="scope">
  56. <div :style="scope.row.Name === activeSelectType ? 'color: #409EFF':''">
  57. <label>{{ scope.row.comment }}</label>
  58. <!-- <label>({{ scope.row.count }})</label>-->
  59. </div>
  60. </template>
  61. </el-table-column>
  62. </el-table>
  63. <!-- <div class="add-attribute-box" @click="modifyEdge({Name:''})">+新建EDGE</div>-->
  64. </el-tab-pane>
  65. </el-tabs>
  66. </div>
  67. </div>
  68. </transition>
  69. <transition name="el-zoom-in-center">
  70. <div v-show="propsInfoVisible" class="tree-graph-info">
  71. <div class="graph-info-child">
  72. <div v-show="tabModifyCmdType === 'tag'">
  73. <tag-modify ref="tagModify"></tag-modify>
  74. </div>
  75. <div v-show="tabModifyCmdType === 'edge'">
  76. <edge-type-modify ref="edgeTypeModify"></edge-type-modify>
  77. </div>
  78. <div v-show="tabModifyCmdType === 'nodeDetail'">
  79. <node-detail ref="nodeDetail"></node-detail>
  80. </div>
  81. <div v-show="tabModifyCmdType === 'edgeDetail'">
  82. <edge-detail ref="edgeDetail"></edge-detail>
  83. </div>
  84. <div v-show="tabModifyCmdType === 'edgeAdd'">
  85. <edge-add ref="edgeAdd"></edge-add>
  86. </div>
  87. <i class="el-icon-d-arrow-right" @click="propsInfoVisible = false;"></i>
  88. </div>
  89. </div>
  90. </transition>
  91. <!-- 为图谱准备一个具备大小(宽高)的Dom -->
  92. <div class="tree-container">
  93. <div class="icon-box">
  94. <div v-if="showFlag" style="display: inline-block;margin-right: 50px">
  95. <el-input placeholder="请输入关键词" v-model="keyword" style="width: 550px">
  96. <el-select v-model="searchTag" slot="prepend" placeholder="请选择概念" style="width: 150px" filterable>
  97. <el-option :label="item.comment" :value="item.Name" v-for="(item,index) in tagList"
  98. v-if="index != 0"></el-option>
  99. </el-select>
  100. <el-button slot="append" icon="el-icon-search" @click="searchBtn()"></el-button>
  101. </el-input>
  102. </div>
  103. <div class="icon icon-search" @click="showFlag = !showFlag">
  104. <i title="搜索"></i>
  105. </div>
  106. <div class="icon icon-add" @click="showAddNodeDialog">
  107. <i title="新增类"></i>
  108. </div>
  109. <div title="新增关系" class="icon icon-arrow"
  110. @click="showAddEdgeDialog">
  111. <i title="新增关系"></i>
  112. </div>
  113. <div class="icon icon-save" @click="backGraph">
  114. <i title="返回"></i>
  115. </div>
  116. <!--<div title="按住shift点选两节点可新增关系" :class="['icon icon-arrow', createEdgeModelFlag ? 'active' : '']"-->
  117. <!--@click="changeCreateEdgeModel">-->
  118. <!--<i title="按住shift点选两节点可新增关系"></i>-->
  119. <!--</div>-->
  120. <!-- <div class="icon icon-remove" @click="removeNode">-->
  121. <!-- <i title="删除"></i>-->
  122. <!-- </div>-->
  123. </div>
  124. <div id="mountNode" style="width: 100%; height: 1000px"></div>
  125. </div>
  126. <graph-space-add ref="graphSpaceAdd"></graph-space-add>
  127. </div>
  128. </template>
  129. <script>
  130. import request from '@/utils/request';
  131. import {config} from '@/components/graph/config';
  132. import G6 from '@antv/g6';
  133. import tagModify from '@/components/dialog/TagModify';
  134. import edgeTypeModify from '@/components/dialog/EdgeTypeModify';
  135. import nodeDetail from '@/components/dialog/NodeDetail';
  136. import edgeAdd from '@/components/dialog/EdgeAdd';
  137. import edgeDetail from '@/components/dialog/EdgeDetail';
  138. import {getFirstStringProperty} from '@/utils/common';
  139. import graphSpaceAdd from '@/components/dialog/GraphSpaceAdd';
  140. G6.registerBehavior('click-add-edge', {
  141. // Set the events and the corresponding responsing function for this behavior
  142. getEvents() {
  143. return {
  144. 'node:click': 'onClick', // The event is canvas:click, the responsing function is onClick
  145. mousemove: 'onMousemove', // The event is mousemove, the responsing function is onMousemove
  146. 'edge:click': 'onEdgeClick',
  147. };
  148. },
  149. // The responsing function for node:click defined in getEvents
  150. onClick(ev) {
  151. const self = this;
  152. const node = ev.item;
  153. const graph = self.graph;
  154. // The position where the mouse clicks
  155. const point = {x: ev.x, y: ev.y};
  156. const model = node.getModel();
  157. if (self.addingEdge && self.edge) {
  158. graph.updateItem(self.edge, {
  159. target: model.id,
  160. });
  161. // 打开编辑新建关系的信息弹窗
  162. vm.showAddEdgeDialog(self.edge._cfg.model);
  163. self.edge = null;
  164. self.addingEdge = false;
  165. } else {
  166. // Add anew edge, the end node is the current node user clicks
  167. self.edge = graph.addItem('edge', {
  168. source: model.id,
  169. target: model.id,
  170. });
  171. self.addingEdge = true;
  172. }
  173. },
  174. // The responsing function for mousemove defined in getEvents
  175. onMousemove(ev) {
  176. const self = this;
  177. // The current position the mouse clicks
  178. const point = {x: ev.x, y: ev.y};
  179. if (self.addingEdge && self.edge) {
  180. // Update the end node to the current node the mouse clicks
  181. self.graph.updateItem(self.edge, {
  182. target: point,
  183. });
  184. }
  185. },
  186. // The responsing function for edge:click defined in getEvents
  187. onEdgeClick(ev) {
  188. const self = this;
  189. const currentEdge = ev.item;
  190. if (self.addingEdge && self.edge === currentEdge) {
  191. self.graph.removeItem(self.edge);
  192. self.edge = null;
  193. self.addingEdge = false;
  194. }
  195. },
  196. });
  197. var vm;
  198. var graph;
  199. var graphData;
  200. var _ = require('lodash');
  201. var nodeProps = {
  202. id: 'vid',
  203. };
  204. var edgeProps = {
  205. source: 'srcId',
  206. target: 'dstId',
  207. }
  208. export default {
  209. components: {
  210. 'tag-modify': tagModify,
  211. 'edge-type-modify': edgeTypeModify,
  212. 'node-detail': nodeDetail,
  213. 'edge-detail': edgeDetail,
  214. 'graph-space-add': graphSpaceAdd,
  215. 'edge-add': edgeAdd
  216. },
  217. data() {
  218. return {
  219. space: '', // 当前展示的图空间(实例)
  220. activeSelectType: 'All',
  221. spaceList: [], // 所有的图空间列表
  222. tagList: [], // 图空间的TAG列表
  223. edgeList: [], // 图空间的EDGE列表
  224. activeTab: 'tag',
  225. graphLoading: false,
  226. tabModifyCmdType: 'tag',
  227. createEdgeModelFlag: false,
  228. infoVisible: true, // 左侧信息栏显隐
  229. propsInfoVisible: false, // 右侧树形图谱
  230. selectObj: {
  231. type: '',
  232. value: {}
  233. },
  234. tagMap: {},
  235. edgeMap: {},
  236. ontologyName: "",
  237. showFlag: false,
  238. keyword: "",
  239. searchTag: "",
  240. ontologyId: "",
  241. comment: "",
  242. qo: {
  243. pageNo: "",
  244. name: ""
  245. }
  246. }
  247. },
  248. created() {
  249. vm = this;
  250. },
  251. mounted() {
  252. vm.qo = {
  253. pageNo: this.$route.query.pageNo,
  254. name: this.$route.query.name
  255. };
  256. vm.ontologyId = this.$route.query.ontologyId;
  257. vm.space = this.$route.query.space;
  258. vm.comment = this.$route.query.comment;
  259. vm.handleSpaceChange();
  260. // vm.querySpaceList();
  261. // vm.initGraph();
  262. },
  263. methods: {
  264. querySpaceList() { // 查询图空间列表,供快速选择切换
  265. request({
  266. url: '/nebula_operate/showspace',
  267. method: 'get',
  268. data: {}
  269. }).then(res => {
  270. vm.spaceList = res.data;
  271. if (res.data.length > 0) {
  272. vm.space = res.data[0].name;
  273. vm.handleSpaceChange();
  274. } else {
  275. vm.space = "";
  276. vm.tagMap = {};
  277. vm.edgeMap = {};
  278. vm.tagList = [];
  279. vm.edgeList = [];
  280. vm.ontologyName = "";
  281. vm.destroyGraph();
  282. }
  283. });
  284. },
  285. queryOntologyDetail() {
  286. var id = vm.ontologyId;
  287. // vm.spaceList.forEach(item => {
  288. // if(item.name == vm.space){
  289. // id = item.OntologyId;
  290. // }
  291. // })
  292. if (id) {
  293. request({
  294. url: `/ontology/getone/` + id,
  295. method: 'post',
  296. data: {id: id}
  297. }).then(res => {
  298. vm.ontologyName = res.data.name;
  299. })
  300. }
  301. },
  302. handleSpaceChange() {
  303. vm.initGraph();
  304. vm.queryStatistics();
  305. vm.queryOntologyDetail();
  306. },
  307. queryStatistics() {
  308. request({
  309. url: `/nebula_operate/censusgraphbyspace/${vm.space}`,
  310. method: 'get',
  311. data: {}
  312. }).then(res => {
  313. let row = res.data;
  314. let tagMap = {};
  315. let edgeMap = {};
  316. row.forEach(item => {
  317. if (item.type === "Space") {
  318. if (item.name === "vertices") {
  319. tagMap['All'] = item.count;
  320. } else if (item.name === "edges") {
  321. edgeMap['All'] = item.count;
  322. }
  323. } else {
  324. if (item.type === "Tag") {
  325. tagMap[item.name] = item.count;
  326. } else if (item.type === "Edge") {
  327. edgeMap[item.name] = item.count;
  328. }
  329. }
  330. });
  331. vm.tagMap = tagMap;
  332. vm.edgeMap = edgeMap;
  333. vm.queryTagList();
  334. vm.queryEdgeList();
  335. });
  336. },
  337. queryTagList() { // 查询tag列表
  338. vm.propsInfoVisible = false;
  339. request({
  340. url: `/nebula_operate/showtag/${vm.space}`,
  341. method: 'get',
  342. data: {}
  343. }).then(res => {
  344. // res.data.unshift({
  345. // Name: `All`,
  346. // comment: '实体数'
  347. // });
  348. res.data.forEach(row => {
  349. row.count = vm.tagMap[row.Name] ? vm.tagMap[row.Name] : 0;
  350. });
  351. vm.tagList = res.data;
  352. });
  353. },
  354. queryEdgeList() {
  355. vm.propsInfoVisible = false;
  356. request({
  357. url: `/nebula_operate/showedge/${vm.space}`,
  358. method: 'get',
  359. data: {}
  360. }).then(res => {
  361. // res.data.unshift({
  362. // Name: `All`,
  363. // comment: '三元组数'
  364. // });
  365. res.data.forEach(row => {
  366. row.count = vm.edgeMap[row.Name] ? vm.edgeMap[row.Name] : 0;
  367. });
  368. vm.edgeList = res.data;
  369. });
  370. },
  371. modifyTag(row) {
  372. vm.tabModifyCmdType = 'tag';
  373. if (row.Name !== "All") {
  374. vm.propsInfoVisible = true;
  375. vm.$nextTick(() => {
  376. vm.$refs['tagModify'].updateValue(vm.space, row.Name);
  377. });
  378. } else {
  379. vm.propsInfoVisible = false;
  380. }
  381. // 更新图谱
  382. if (row.Name.length > 0) {
  383. vm.activeSelectType = row.Name;
  384. vm.querySpaceGraphData(row.Name, 'tag');
  385. }
  386. },
  387. modifyEdge(row) {
  388. vm.tabModifyCmdType = 'edge';
  389. if (row.Name !== "All") {
  390. vm.propsInfoVisible = true;
  391. vm.$nextTick(() => {
  392. vm.$refs['edgeTypeModify'].updateValue(vm.space, row.Name);
  393. });
  394. } else {
  395. vm.propsInfoVisible = false;
  396. }
  397. // 更新图谱
  398. if (row.Name.length > 0) {
  399. vm.activeSelectType = row.Name;
  400. vm.querySpaceGraphData(row.Name, 'edge');
  401. }
  402. },
  403. queryDetail(type, obj) { // type : node/edge
  404. vm.tabModifyCmdType = type + 'Detail';
  405. vm.propsInfoVisible = true;
  406. console.log(type);
  407. vm.$nextTick(() => {
  408. vm.$refs[type + 'Detail'].queryDetail(vm.space, obj);
  409. });
  410. },
  411. showAddGraphSpaceDialog() {
  412. vm.$refs['graphSpaceAdd'].openDialog();
  413. },
  414. removeGraphSpace() {
  415. this.$confirm('此操作将永久删除, 是否继续?', '提示', {
  416. confirmButtonText: '确定',
  417. cancelButtonText: '取消',
  418. type: 'warning'
  419. }).then(() => {
  420. request({
  421. url: `/nebula_operate/dropspace/${vm.space}`,
  422. method: 'get',
  423. data: {}
  424. }).then(res => {
  425. vm.$message.success("删除成功");
  426. vm.querySpaceList();
  427. });
  428. });
  429. },
  430. querySpaceGraphData(typeName, type) { // 查询图空间内节点关系
  431. if (typeName == undefined) {
  432. vm.activeSelectType = "All";
  433. typeName = "All";
  434. }
  435. // vm.propsInfoVisible = false;
  436. vm.graphLoading = true;
  437. // 根据查询类型区分接口,分三种,固定edgeType,固定tag的和查询全部类型的
  438. if (typeName === 'All') {
  439. request({
  440. url: `/nebula_operate/findpathinspace/${vm.space}`,
  441. method: 'post',
  442. data: {}
  443. }).then(res => {
  444. vm.resolveGraphData(res);
  445. });
  446. } else {
  447. if (type === "tag") {
  448. request({
  449. url: `/nebula_operate/findnodes/${vm.space}`,
  450. method: 'post',
  451. data: {
  452. tag: typeName
  453. }
  454. }).then(res => {
  455. vm.resolveGraphData({data: {nodes: res.data, relations: []}});
  456. });
  457. } else if (type === "edge") {
  458. request({
  459. url: `/nebula_operate/findrelations/${vm.space}`,
  460. method: 'post',
  461. data: {
  462. edge: typeName
  463. }
  464. }).then(res => {
  465. vm.resolveGraphData(res);
  466. });
  467. }
  468. }
  469. },
  470. resolveGraphData(res) { //处理图谱数据,格式化
  471. if (res.data && res.data.nodes) {
  472. res.data.nodes.forEach(node => {
  473. Reflect.ownKeys(nodeProps).forEach(key => {
  474. node[key] = _.get(node, nodeProps[key], '');
  475. });
  476. if (node.properties) {
  477. // node.label = node.name = getFirstStringProperty(node.properties);
  478. node.label = node.name = node.vid;
  479. }
  480. if(node.labels === 'tag_1412011336') {
  481. node.style = {
  482. fill: '#8fe8e8'
  483. }
  484. }
  485. });
  486. }
  487. if (res.data && res.data.relations) {
  488. res.data.relations.forEach(edge => {
  489. Reflect.ownKeys(edgeProps).forEach(key => {
  490. edge[key] = _.get(edge, edgeProps[key], '');
  491. });
  492. if (edge.properties) {
  493. edge.label = edge.name = getFirstStringProperty(edge.properties);
  494. }
  495. // 是否指向自己
  496. edge.type = edge.srcId === edge.dstId ? 'loop' : '';
  497. });
  498. }
  499. graphData = {
  500. nodes: res.data && res.data.nodes ? res.data.nodes : [],
  501. edges: res.data && res.data.relations ? res.data.relations : []
  502. };
  503. // 读取数据
  504. graph.data(graphData);
  505. // 渲染图
  506. graph.render();
  507. vm.graphLoading = false;
  508. },
  509. initGraph() {
  510. this.destroyGraph();
  511. let mountNodeDom = document.getElementById("mountNode");
  512. let _width = mountNodeDom.clientWidth, _height = mountNodeDom.clientHeight;
  513. // 创建 G6 图实例
  514. if (graph == undefined || graph == null) {
  515. graph = new G6.Graph(config({width: _width, height: _height, id: 'mountNode'}));
  516. graph.on('aftercreateedge', (e) => {
  517. vm.showAddEdgeDialog(e.edge._cfg.model);
  518. });
  519. graph.on('canvas:click', (e) => {
  520. vm.selectObj = {
  521. type: '',
  522. value: {}
  523. };
  524. vm.clearStates();
  525. });
  526. graph.on('edge:click', function (e) { // 点击边事件
  527. let edge = e.item._cfg.model;
  528. if (!edge.name) {
  529. return;
  530. }
  531. vm.queryDetail('edge', {
  532. edgeType: edge.edgeName,
  533. srcVid: edge.srcId,
  534. dstVid: edge.dstId
  535. });
  536. graph.getEdges().forEach((edge) => {
  537. graph.clearItemStates(edge);
  538. });
  539. graph.getNodes().forEach((node) => {
  540. graph.clearItemStates(node);
  541. });
  542. graph.setItemState(e.item, 'yourStateName', true);
  543. });
  544. graph.on('node:click', (e) => {
  545. if (!event.shiftKey && !vm.createEdgeModelFlag) { // 按住shift属于新增关系,不需要触发点击节点事件
  546. let node = e.item._cfg.model;
  547. vm.queryDetail('node', {
  548. tag: node.labels,
  549. vid: node.vid,
  550. });
  551. vm.clearStates();
  552. graph.setItemState(e.item, 'yourStateName', true);
  553. }
  554. });
  555. }
  556. vm.querySpaceGraphData('All');
  557. },
  558. destroyGraph() {
  559. try {
  560. if (graph != undefined && graph != null) {
  561. graph.clear();
  562. graph = null;
  563. document.getElementById("mountNode").innerHTML = "";
  564. }
  565. } catch (err) {
  566. console.log(err);
  567. }
  568. },
  569. clearStates() { // 清空选中状态
  570. graph.getNodes().forEach((node) => {
  571. graph.clearItemStates(node);
  572. });
  573. graph.getEdges().forEach((edge) => {
  574. graph.clearItemStates(edge);
  575. });
  576. },
  577. changeCreateEdgeModel() { // 切换模式,默认和添加关系模式之间切换
  578. vm.createEdgeModelFlag = !vm.createEdgeModelFlag;
  579. if (vm.createEdgeModelFlag) {
  580. graph.setMode("addEdge");
  581. } else {
  582. graph.setMode("default");
  583. }
  584. },
  585. showAddEdgeDialog(edge) {
  586. vm.tabModifyCmdType = 'edgeDetail';
  587. vm.propsInfoVisible = true;
  588. vm.$nextTick(() => {
  589. vm.$refs['edgeDetail'].createItem(vm.space, edge.source, edge.target);
  590. });
  591. },
  592. showAddNodeDialog() {
  593. vm.tabModifyCmdType = 'nodeDetail';
  594. vm.propsInfoVisible = true;
  595. vm.$nextTick(() => {
  596. vm.$refs['nodeDetail'].createItem(vm.space);
  597. });
  598. },
  599. showAddEdgeDialog() {
  600. vm.tabModifyCmdType = 'edgeAdd';
  601. vm.propsInfoVisible = true;
  602. vm.$nextTick(() => {
  603. vm.$refs['edgeAdd'].createItem(vm.space);
  604. });
  605. },
  606. //搜索
  607. searchBtn() {
  608. if (!vm.searchTag) {
  609. vm.$message.warning("请选择概念");
  610. return false;
  611. }
  612. if (!vm.keyword) {
  613. vm.$message.warning("请输入关键字");
  614. return false;
  615. }
  616. vm.graphLoading = true;
  617. request({
  618. url: `/nebula_operate/findnodebykeyword/${vm.space}/${vm.searchTag}`,
  619. method: 'post',
  620. data: {keyword: vm.keyword}
  621. }).then(res => {
  622. if (res.data) {
  623. res.data.forEach(node => {
  624. Reflect.ownKeys(nodeProps).forEach(key => {
  625. node[key] = _.get(node, nodeProps[key], '');
  626. });
  627. if (node.properties) {
  628. node.label = node.name = getFirstStringProperty(node.properties);
  629. }
  630. });
  631. }
  632. graphData = {
  633. nodes: res.data ? res.data : [],
  634. edges: []
  635. };
  636. // 读取数据
  637. graph.data(graphData);
  638. // 渲染图
  639. graph.render();
  640. vm.graphLoading = false;
  641. });
  642. },
  643. findOnePathBySrcidAndDctid(srcId, dstId) {
  644. request({
  645. url: `/nebula_operate/findonepathbysrcidanddctid/${vm.space}/${srcId}/${dstId}`,
  646. method: 'get',
  647. data: {}
  648. }).then(res => {
  649. vm.resolveGraphData(res);
  650. });
  651. },
  652. findNodeById(tag, vid) {
  653. request({
  654. url: `/nebula_operate/findnodebyid/${vm.space}/${tag}/${vid}`,
  655. method: 'get',
  656. data: {}
  657. }).then(res => {
  658. if (res.data) {
  659. Reflect.ownKeys(nodeProps).forEach(key => {
  660. res.data[key] = _.get(res.data, nodeProps[key], '');
  661. });
  662. if (res.data.properties) {
  663. res.data.label = res.data.name = getFirstStringProperty(res.data.properties);
  664. }
  665. }
  666. graphData = {
  667. nodes: res.data ? [res.data] : [],
  668. edges: []
  669. };
  670. // 读取数据
  671. graph.data(graphData);
  672. // 渲染图
  673. graph.render();
  674. vm.graphLoading = false;
  675. });
  676. },
  677. backGraph() { // 返回
  678. vm.$router.push({path: "/graphSpaceManage", query: {pageNo: vm.qo.pageNo, name: vm.qo.name}})
  679. },
  680. }
  681. }
  682. function refreshDragedNodePosition(e) {
  683. const model = e.item.get('model');
  684. model.fx = e.x;
  685. model.fy = e.y;
  686. }
  687. </script>
  688. <style>
  689. .el-tabs-item {
  690. height: calc(100% - 200px);
  691. }
  692. .el-tabs-item .el-tabs__content {
  693. height: calc(100% - 50px);
  694. }
  695. .el-tabs-item .el-tabs__content .el-tab-pane {
  696. height: 100%;
  697. }
  698. .tab-table-item {
  699. height: 100%;
  700. overflow-y: auto;
  701. }
  702. .node-box {
  703. height: 100%;
  704. }
  705. .info-item-content .el-select {
  706. width: 100%;
  707. }
  708. .info-item {
  709. margin-bottom: 45px;
  710. }
  711. .info-item-title {
  712. border-bottom: 1px solid #ccc;
  713. padding-bottom: 15px;
  714. margin-bottom: 15px;
  715. }
  716. .info-item-title label {
  717. font-size: 18px;
  718. font-weight: bold;
  719. color: #2f2f2f;
  720. }
  721. .graph-name-box {
  722. position: absolute;
  723. top: 25px;
  724. left: 65px;
  725. width: 250px;
  726. font-size: 18px;
  727. font-weight: bold;
  728. }
  729. .form-title {
  730. font-size: 16px;
  731. margin: 15px 0;
  732. }
  733. .add-attribute-box {
  734. margin-top: 10px;
  735. color: #409EFF;
  736. cursor: pointer;
  737. text-align: right;
  738. }
  739. .graph-info-box, .tree-graph-info {
  740. position: absolute;
  741. top: 0;
  742. overflow: hidden;
  743. bottom: 0;
  744. background-color: #fff;
  745. padding: 15px;
  746. z-index: 999;
  747. border-right: 1px solid rgba(52, 100, 224, 0.15);
  748. box-shadow: 0px 2px 21px 0px rgba(52, 100, 224, 0.15);
  749. }
  750. .graph-info-box {
  751. left: 0;
  752. width: 230px;
  753. }
  754. .tree-graph-info {
  755. right: 0;
  756. width: 500px;
  757. }
  758. .graph-info-child {
  759. width: 100%;
  760. height: 100%;
  761. position: relative;
  762. }
  763. .graph-info-child .el-table__header-wrapper {
  764. display: none;
  765. }
  766. .el-icon-d-arrow-left, .el-icon-d-arrow-right, .el-icon-d-arrow-right2 {
  767. position: absolute;
  768. top: 50%;
  769. margin-top: -10px;
  770. font-size: 20px;
  771. cursor: pointer;
  772. z-index: 1000;
  773. }
  774. .el-icon-d-arrow-left {
  775. right: 0px;
  776. }
  777. .el-icon-d-arrow-right {
  778. left: 0px;
  779. }
  780. .info-line-item {
  781. margin-bottom: 35px;
  782. }
  783. .info-title {
  784. font-size: 16px;
  785. font-weight: 600;
  786. margin-bottom: 12px;
  787. }
  788. .info-content > div {
  789. margin-bottom: 5px;
  790. line-height: 20px;
  791. }
  792. ol, ul {
  793. list-style: none;
  794. margin: 0;
  795. padding: 0;
  796. }
  797. .menu {
  798. /*这个样式不写,右键弹框会一直显示在画布的左下角*/
  799. position: absolute;
  800. background: white;
  801. border-radius: 5px;
  802. border: 1px solid #f2f4f7;
  803. left: -99999px;
  804. top: -999999px;
  805. }
  806. .menu div {
  807. list-style: none;
  808. padding: 5px 10px;
  809. color: black;
  810. border-bottom: 1px dashed #ffffff;
  811. font-size: 14px;
  812. cursor: pointer;
  813. }
  814. .menu div:hover {
  815. color: #659bc5;
  816. background: #f3f6fa;
  817. }
  818. .menu div:last-child {
  819. border-bottom: none;
  820. }
  821. ul#menuBox li {
  822. cursor: pointer;
  823. margin: 5px 0;
  824. }
  825. ul#menuBox li:hover {
  826. color: #1c63e0;
  827. }
  828. .tree-container {
  829. position: relative;
  830. padding-left: 300px;
  831. }
  832. .tips-box {
  833. position: absolute;
  834. top: 25px;
  835. left: 50%;
  836. width: 300px;
  837. margin-left: -150px;
  838. font-size: 16px;
  839. }
  840. div.icon-box {
  841. position: absolute;
  842. top: 25px;
  843. right: 75px;
  844. }
  845. div.icon {
  846. display: inline-block;
  847. cursor: pointer;
  848. padding: 15px;
  849. background-color: #fff;
  850. border-radius: 40px;
  851. margin-left: 15px;
  852. }
  853. div.icon i {
  854. width: 25px;
  855. height: 25px;
  856. margin: auto;
  857. display: inline-block;
  858. }
  859. div.icon-add i {
  860. background: url("../../assets/image/icon/bg-add.png") no-repeat center/contain;
  861. }
  862. div.icon-remove i {
  863. background: url("../../assets/image/icon/changyonggoupiaorenshanchu.png") no-repeat center/contain;
  864. }
  865. div.icon-save i {
  866. background: url("../../assets/image/icon/fanhui.png") no-repeat center/contain;
  867. }
  868. div.icon-arrow i {
  869. background: url("../../assets/image/icon/arrowTop-fill.png") no-repeat center/contain;
  870. }
  871. div.icon-tree i {
  872. background: url("../../assets/image/icon/shuxingicon.png") no-repeat center/contain;
  873. }
  874. div.icon-search i {
  875. background: url("../../assets/image/icon/search.png") no-repeat center/contain;
  876. }
  877. div.icon-arrow.active i {
  878. background: url("../../assets/image/icon/arrowTop-fill-active.png") no-repeat center/contain;
  879. }
  880. div.icon-arrow.active {
  881. box-shadow: 0px 0px 6px #979797;
  882. }
  883. </style>