vue使用GraphVis開發無限拓展的關系圖譜的實現
1、去GraphVis官網下載對應的js,新版和舊版的js有所不同,看自己需求引入舊版還是新版(GraphVis官方網址:http://www.graphvis.cn/)
- visgraph.min.js (基本配置js)
- visgraph-layout.min.js(配置佈局js)
2、在需要的vue文件引入js文件
import VisGraph from '@/assets/js/GraphVis/old/visgraph.min.js' // 自己對應的js文件位置 import LayoutFactory from '@/assets/js/GraphVis/old/visgraph-layout.min.js' // 自己對應的js文件位置 export default { components: { VisGraph, LayoutFactory } }
3、加載畫佈和配置
配置(自己根據需求修改配置):
config: { // 節點配置 node: { label: { // 標簽配置 show: true, // 是否顯示 color: '250,250,250', // 字體顏色 font: 'normal 14px Microsoft YaHei', // 字體大小及類型 textPosition: 'Middle_Center', // 字體位置 wrapText: true // 節點包裹文字(該屬性為true時隻對於字體位置為Middle_Center時有效) }, shape: 'circle', // 節點形狀 circle,rect,square,ellipse,triangle,star,polygon,text // width: 60, // 節點寬度(隻對於shape為rect時有效) // height: 60, // 節點高度(隻對於shape為rect時有效) color: '62,160,250', // 節點顏色 borderColor: '62,160,250', // 節點邊框顏色 borderWidth: 0, // 節點邊框寬度 borderRadius: 0, // 節點圓角 lineDash: [0], // 節點邊框線條類型 [0] 表示實線 [5,8] 表示虛線 borderWidth > 0有效 alpha: 1, // 節點透明度 size: 60, // 節點大小 selected: { // 節點選中後樣式 borderColor: '136,198,255', // 選中時邊框顏色 borderAlpha: 1, // 選中時的邊框透明 borderWidth: 3, // 選中是的邊框寬度 showShadow: true, // 是否展示陰影 shadowColor: '136,198,255' // 選中是的陰影顏色 } }, // 線條配置 link: { label: { // 標簽配置 show: true, // 是否顯示 color: '100,100,200', // 標簽顏色 font: 'normal 10px Arial' // 標簽文字大小及類型 }, lineType: 'direct', // 線條類型direct,curver,vlink,hlink,bezier,vbezier,hbezier colorType: 'defined', // 連線顏色類型 source:繼承source顏色,target:繼承target顏色 both:用雙邊顏色,defined:自定義 color: '200,200,200', // 線條顏色 alpha: 1, // 連線透明度 lineWidth: 1, // 連線寬度 lineDash: [0], // 虛線間隔樣式如:[5,8] showArrow: true, // 顯示箭頭 selected: { // 選中時的樣式設置 color: '20,250,50', // 選中時的顏色 alpha: 1, // 選中時的透明度 lineWidth: 4, // 選中線條寬度 showShadow: true, // 顯示陰影 shadowColor: '50,250,50' // 陰影顏色 } }, highLightNeiber: true, // 相鄰節點高度標志 wheelZoom: 0.8 // 滾輪縮放開關,不使用時不設置[0,1] }
加載畫佈:
this.visgraph = new VisGraph( document.getElementById(this.canvasId), this.canvasConfig ) this.visgraph.clearAll() this.visgraph.drawData(this.graphData)
4、拓展功能:
無限拓展子節點,雙擊節點觸發(ondblClick):
this.visgraph.restoreHightLight() // 取消高亮 const allNodes = this.visgraph.getVisibleData() this.currentNode.push(node.id) allNodes.nodes.forEach(item => { if (this.currentNode.indexOf(item.id) === (-1)) { this.visgraph.deleteNode(item) } }) const findNodeNum = Math.round(Math.random() * 50) const increamData = this.buildIncreamData(node, findNodeNum) this.visgraph.activeAddNodeLinks(increamData.nodes, increamData.links) this.visgraph.translateToCenter()
完整代碼(relation.vue):
<!-- * @Author: CarlYang * @Date: 2021-07-23 15:31:51 * @LastEditTime: 2021-07-30 09:46:05 * @LastEditors: Please set LastEditors * @Description: 關系圖譜 * @FilePath: \vue-g6\src\views\GraphVis\company.vue --> <template> <div id="container"> <!-- ============================================= 畫佈視圖 ============================================= --> <div id="graph-panel" ref="graphpanel" @contextmenu="globalClickedDispatch" ></div> <!-- ============================================= 左側工具欄 ============================================= --> <div class="left-toolbar"> <ul> <li @click="setZoomOut" title="放大"> <i class="iconfont icon-zoomin"></i> </li> <li @click="setZoomIn" title="縮小"> <i class="iconfont icon-zoomout"></i> </li> <li @click="saveImage" title="保存圖片"> <i class="iconfont icon-baocun-"></i> </li> <li @click="exportJson" title="導出JSON"> <i class="iconfont icon-json"></i> </li> <li @click="showOverView" title="縮略圖"> <i class="iconfont icon-suolvetu" style="font-size: 14px"></i> </li> <li @click="clockwiseRotate" title="順時針旋轉"> <i class="iconfont icon-shunshizhenfangxiangclockwise4" style="font-size: 14px"></i> </li> <li @click="counterclockwiseRotate" title="逆時針旋轉"> <i class="iconfont icon-nishizhencounterclockwise3" style="font-size: 14px"></i> </li> <li @click="setMouseModel('normal')" title="正常模式"> <i class="iconfont icon-pointer-up"></i> </li> <li @click="setMouseModel('drag')" title="拖拽模式"> <i class="iconfont icon-line-dragmovetuozhuai-01"></i> </li> <li @click="setMouseModel('select')" title="框選模式"> <i class="iconfont icon-kuangxuan1"></i> </li> <li @click="fullScreen" title="全屏顯示"> <i class="iconfont icon-quanping" style="font-size: 20px"></i> </li> </ul> </div> <!-- ============================================= 右鍵菜單 ============================================= --> <div id="nodeMenuDialog" class="nodeMenuDialog"> <ul> <li @click="clickNodeInfo">節點信息</li> <li @click="settingNode">配置節點</li> <li @click="selectRelation">選中關聯</li> <li @click="deleteNode">刪除節點</li> <li @click="contractNode">收起節點</li> <li @click="expandedNode">展開節點</li> </ul> </div> <!-- ============================================= 節點信息彈框 ============================================= --> <el-drawer title="節點信息" :visible.sync="nodeInfoDrawer" direction="rtl" :modal="false" size="20%" > <div class="nodeInfo"> <el-form class="nodeInfoForm" ref="nodeInfoForm" :model="nodeInfoForm" label-width="80px"> <el-form-item label="節點名稱"> <el-input v-model="nodeInfoForm.label"></el-input> </el-form-item> <el-form-item label="節點ID"> <el-input v-model="nodeInfoForm.id"></el-input> </el-form-item> </el-form> <el-tabs v-model="nodeInfoActiveName" :stretch="true" class="nodeInfoTabs"> <el-tab-pane label="關聯關系" name="first"> <div class="nodeInfoRelation"> <el-collapse v-model="nodeInfoRelationActive"> <el-collapse-item title="目標節點" name="1"> <template slot="title"> <el-badge :value="nodeInfoSourceList.length">目標節點</el-badge> </template> <table border="0" cellspacing="0" cellpadding="0" class="nodeInfo-table" v-if="nodeInfoSourceList.length > 0" > <thead> <tr> <th>實體對象</th> <th>關系類型</th> </tr> </thead> <tbody> <tr v-for="(item, index) in nodeInfoSourceList" :key="index"> <td :style="{ color: item.color }" style="cursor: pointer;" @click="moveCenterThisNode(item.id)" >{{ item.label }}</td> <td>{{ item.relationType }}</td> </tr> </tbody> </table> <p v-else>無數據</p> </el-collapse-item> <el-collapse-item title="來源節點" name="2"> <template slot="title"> <el-badge :value="nodeInfoTargetList.length">來源節點</el-badge> </template> <table border="0" cellspacing="0" cellpadding="0" class="nodeInfo-table" v-if="nodeInfoTargetList.length > 0" > <thead> <tr> <th>實體對象</th> <th>關系類型</th> </tr> </thead> <tbody> <tr v-for="(item, index) in nodeInfoTargetList" :key="index"> <td :style="{ color: item.color }" style="cursor: pointer;" @click="moveCenterThisNode(item.id)" >{{ item.label }}</td> <td>{{ item.relationType }}</td> </tr> </tbody> </table> <p v-else>無數據</p> </el-collapse-item> </el-collapse> </div> </el-tab-pane> <el-tab-pane label="屬性" name="second"> <div class="nodeInfoAttribute"> <el-table :data="nodeInfoAttributeList" border style="width: 100%"> <el-table-column prop="name" label="屬性名"></el-table-column> <el-table-column prop="value" label="屬性值"></el-table-column> </el-table> </div> </el-tab-pane> </el-tabs> </div> </el-drawer> <!-- ============================================= 節點配置 ============================================= --> <el-drawer title="節點配置" :visible.sync="nodeConfigDrawer" direction="rtl" :modal="false" size="20%" > <div class="nodeConfig"> <el-form ref="form" :model="nodeConfigForm" label-width="80px" label-position="left"> <el-form-item label="名稱"> <el-input v-model="nodeConfigForm.label" placeholder="請輸入節點名稱"></el-input> </el-form-item> <el-form-item label="類型"> <el-select v-model="nodeConfigForm.shape" placeholder="請選擇節點類型"> <el-option v-for="(item, index) in nodeTypeList" :key="'node' + index" :label="item.label" :value="item.value" ></el-option> </el-select> </el-form-item> <el-form-item label="顏色"> <div class="form-color"> <el-input v-model="nodeConfigForm.fillColor" class="form-input" placeholder="請選擇顏色" readonly ></el-input> <el-color-picker v-model="nodeConfigForm.hexColor" class="form-color-select" @change="colorChange(nodeConfigForm.hexColor, 'fillColor')" ></el-color-picker> </div> </el-form-item> <el-form-item label="大小"> <el-input v-model="nodeConfigForm.size" placeholder="請輸入節點大小" type="number"></el-input> </el-form-item> <el-form-item label="邊框寬度"> <el-input v-model="nodeConfigForm.borderWidth" placeholder="請輸入邊框寬度" type="number"></el-input> </el-form-item> <el-form-item label="邊框虛線"> <el-select v-model="nodeConfigForm.borderDash" placeholder="請選擇邊框虛線"> <el-option label="否" :value="false"></el-option> <el-option label="是" :value="true"></el-option> </el-select> </el-form-item> <el-form-item label="邊框顏色"> <div class="form-color"> <el-input v-model="nodeConfigForm.borderColor" class="form-input" placeholder="請選擇邊框顏色" readonly ></el-input> <el-color-picker v-model="nodeConfigForm.borderHexColor" class="form-color-select" @change="colorChange(nodeConfigForm.borderHexColor, 'borderColor')" ></el-color-picker> </div> </el-form-item> <el-form-item label="字體位置"> <el-select v-model="nodeConfigForm.textPosition" placeholder="請選擇字體位置"> <el-option v-for="(item, index) in textPositionList" :key="index" :label="item.label" :value="item.value" ></el-option> </el-select> </el-form-item> <el-form-item label="字體樣式"> <el-input v-model="nodeConfigForm.font" placeholder="請輸入字體樣式"></el-input> </el-form-item> <el-form-item label="字體顏色"> <div class="form-color"> <el-input v-model="nodeConfigForm.fontColor" class="form-input" placeholder="請選擇字體顏色" readonly ></el-input> <el-color-picker v-model="nodeConfigForm.fontHexColor" class="form-color-select" @change="colorChange(nodeConfigForm.fontHexColor, 'fontColor')" ></el-color-picker> </div> </el-form-item> <el-form-item label="字體背景"> <div class="form-color"> <el-input v-model="nodeConfigForm.fontBgColor" class="form-input" placeholder="請選擇字體背景" readonly ></el-input> <el-color-picker v-model="nodeConfigForm.fontBgHexColor" class="form-color-select" @change="colorChange(nodeConfigForm.fontBgHexColor, 'fontBgColor')" ></el-color-picker> </div> </el-form-item> <el-form-item label="字體偏移"> <el-input v-model="nodeConfigForm.textOffset" placeholder="請輸入字體偏移" type="number" max="100" min="-100" ></el-input> </el-form-item> </el-form> <div class="save-setting"> <el-button type="primary" @click="saveSetting">保存配置</el-button> </div> </div> </el-drawer> </div> </template> <script> import VisGraph from '@/assets/js/GraphVis/old/visgraph.min.js' import LayoutFactory from '@/assets/js/GraphVis/old/visgraph-layout.min.js' import screenfull from 'screenfull' import { company } from '@/assets/js/company.js' export default { name: 'DesignGraph', components: { VisGraph, LayoutFactory }, data() { return { // 畫佈實例 visgraph: null, // 中心節點 centerNode: null, // 已選中節點 currentNode: [], // 選中節點信息 checkedNodeInfo: {}, // 圖譜配置 config: { node: { label: { show: true, color: '250,250,250', font: 'normal 14px Microsoft YaHei', textPosition: 'Middle_Center', borderWidth: 0, wrapText: true }, shape: 'circle', width: 60, height: 60, color: '62,160,250', borderColor: '62,160,250', borderWidth: 0, borderRadius: 0, lineDash: [0], alpha: 1, selected: { borderColor: '136,198,255', borderAlpha: 1, borderWidth: 3, showShadow: true, shadowColor: '136,198,255' }, onClick: (event, node) => { // this.visgraph.highLightNeiberNodes(node, 1) // 高亮 }, ondblClick: (event, node) => { this.visgraph.restoreHightLight() // 取消高亮 const allNodes = this.visgraph.getVisibleData() this.currentNode.push(node.id) allNodes.nodes.forEach(item => { if (this.currentNode.indexOf(item.id) === (-1)) { this.visgraph.deleteNode(item) } }) const findNodeNum = Math.round(Math.random() * 50) const increamData = this.buildIncreamData(node, findNodeNum) this.visgraph.activeAddNodeLinks(increamData.nodes, increamData.links) this.visgraph.translateToCenter() }, onMouseOver: (event, node) => { }, onMouseOut: (event, node) => { } }, link: { label: { show: true, color: '100,100,200', font: 'normal 10px Arial' }, lineType: 'direct', colorType: 'defined', color: '200,200,200', alpha: 1, lineWidth: 1, lineDash: [0], showArrow: true, selected: { color: '20,250,50', alpha: 1, lineWidth: 4, showShadow: true, shadowColor: '50,250,50' } }, highLightNeiber: true, wheelZoom: 0.8, noElementClick: (event, _graphvis) => { // 點擊畫佈其他位置,彈框隱藏 this.nodeMenuDialogClose() } }, // 節點信息彈框 nodeInfoDrawer: false, // 節點信息表單 nodeInfoForm: { label: '', id: '' }, // 節點信息彈框tab選項名稱 nodeInfoActiveName: 'first', // 關聯關系 nodeInfoRelationActive: ['1', '2'], // 目標節點列表 nodeInfoTargetList: [], // 來源節點列表 nodeInfoSourceList: [], // 節點屬性列表 nodeInfoAttributeList: [], // 節點配置彈框 nodeConfigDrawer: false, // 節點配置表單 nodeConfigForm: { label: '', shape: '', fillColor: '', hexColor: '', size: '', borderWidth: '', borderDash: '', borderColor: '', borderHexColor: '', textPosition: '', font: '', fontColor: '', fontHexColor: '', fontBgColor: '', fontBgHexColor: '', textOffset: '' }, // 節點類型列表 nodeTypeList: [ { value: 'circle', label: '圓形' }, { value: 'rect', label: '矩形' }, { value: 'ellipse', label: '橢圓形' }, { value: 'star', label: '五角形' }, { value: 'triangle', label: '三角形' }, { value: 'polygon', label: '六邊形' } ], // 字體位置列表 textPositionList: [ { value: 'Middle_Center', label: '居中' }, { value: 'Bottom_Center', label: '底部' }, { value: 'top_Center', label: '頂部' }, { value: 'Middle_Left', label: '左方' }, { value: 'Middle_right', label: '右方' }, ] } }, mounted() { this.handleData(company) window.onresize = () => { if (this.visgraph) { this.visgraph.moveCenter() } } }, methods: { /** * 處理數據 * @date 2021-07-23 * @param {Object} data */ handleData(data) { const obj = { nodes: [], links: [] } const nodes = data.nodes nodes.forEach(item => { if (item.label === '總公司') { const nodeObj = { id: item.id, label: item.label, properties: item, color: '38,186,191', selectedBorderColor: '131,218,228', shadowColor: '131,218,228' } nodeObj.size = 80 nodeObj.x = 250 nodeObj.y = 250 this.centerNode = nodeObj this.currentNode.push(item.id) } else { const nodeObj = { id: item.id, label: item.label, properties: item, size: 60 } switch (item.type) { case 1: nodeObj.color = '242,105,97' nodeObj.selectedBorderColor = '249,179,157' nodeObj.shadowColor = '249,179,157' break } obj.nodes.push(nodeObj) } }) const links = data.edges links.forEach(item => { const linkObj = { id: item.id, target: item.to, source: item.from, label: item.label, properties: item // strokeColor: this.getRandomColor() } switch (item.type) { case 11: linkObj.color = '40,194,199' linkObj.selectedColor = '40,194,199' linkObj.shadowColor = '40,194,199' break case 12: linkObj.color = '250,108,100' linkObj.selectedColor = '250,108,100' linkObj.shadowColor = '250,108,100' break case 13: linkObj.color = '0,132,255' linkObj.selectedColor = '0,132,255' linkObj.shadowColor = '0,132,255' break case 15: linkObj.color = '250,108,100' linkObj.selectedColor = '250,108,100' linkObj.shadowColor = '250,108,100' break } obj.links.push(linkObj) }) this.buildData(obj) }, /** * 搭建實例 * @date 2021-07-23 * @param {Object} gxData */ buildData(gxData) { this.visgraph = new VisGraph(document.getElementById('graph-panel'), this.config) const nodeCount = gxData.nodes.length const xyArr = this.getXY(this.centerNode, nodeCount, nodeCount * 20) gxData.nodes.forEach((n, i) => { n.x = xyArr[i].x n.y = xyArr[i].y n.size = 60 }) gxData.nodes.push(this.centerNode) this.visgraph.drawData(gxData) this.visgraph.setZoom() }, /** * 遍佈選中節點周圍的點坐標 * @date 2021-07-23 * @param {中心節點} centerNode * @param {節點數量} nodeCount * @param {距離中心點距離} raduis * @returns {Array} */ getXY(centerNode, nodeCount, raduis) { const aop = 360.0 / nodeCount const arr = [] for (let i = 0; i < nodeCount; i++) { let r1 = raduis if (nodeCount > 10) { r1 = (i % 2 === 0 ? raduis + 35 : raduis - 35) } const ao = i * aop const o1 = {} o1.x = centerNode.x + r1 * Math.cos(ao * Math.PI / 180) o1.y = centerNode.y + r1 * Math.sin(ao * Math.PI / 180) arr[i] = o1 } return arr }, /** * 隨機產生節點 * @date 2021-07-23 * @param {當前選中節點} centerNode * @param {節點數量} nodeNum * @returns {Object} */ buildIncreamData(centerNode, nodeNum) { const gxData = { nodes: [], links: [] } const count = nodeNum const nodeIdPrefix = 'node_' + Math.round(Math.random() * 1000) + '_' for (let i = 0; i < count; i++) { gxData.nodes.push({ id: nodeIdPrefix + i, label: '子節點+' + i, size: 60 // color: this.getRandomColor() }) gxData.links.push({ source: centerNode.id, target: nodeIdPrefix + i, label: '關系' + i }) } return gxData }, /** * 畫佈右鍵事件 * @date 2021-07-26 * @param {Object} event */ globalClickedDispatch(event) { if (event.button === 2) { if (this.visgraph.currentNode) { this.nodeMenuDialogOpen(event, this.visgraph.currentNode) } } }, /** * 右鍵節點菜單顯示 * @date 2021-07-26 * @param {Object} event * @param {Object} node */ nodeMenuDialogOpen(event, node) { let nodeMenuDialog = document.getElementById("nodeMenuDialog"); nodeMenuDialog.style.left = event.clientX + "px"; nodeMenuDialog.style.top = (event.clientY - 76) + "px"; nodeMenuDialog.style.display = "block"; this.checkedNodeInfo = node; event.stopPropagation(); }, /** * 關閉節點菜單 * @date 2021-07-26 */ nodeMenuDialogClose() { let nodeMenuDialog = document.getElementById("nodeMenuDialog"); nodeMenuDialog.style.display = "none"; }, /** * 點擊節點信息 * @date 2021-07-26 */ clickNodeInfo() { this.nodeInfoDrawer = true // 賦值表單 this.nodeInfoForm = this.checkedNodeInfo // 關聯節點 // 出節點 const k = this.checkedNodeInfo const g = (k.outLinks || []).map((link) => { return { id: link.target.id, label: link.target.label, type: link.target.type, color: 'rgb(' + link.target.fillColor + ')', relationType: link.type || link.label || '--' } }) // 入節點 const h = (k.inLinks || []).map((link) => { return { id: link.source.id, label: link.source.label, type: link.source.type, color: 'rgb(' + link.source.fillColor + ')', relationType: link.type || link.label || '--' } }) this.nodeInfoTargetList = h this.nodeInfoSourceList = g // 屬性賦值 const list = [] const nameList = ['id', 'label', 'type', 'cluster', 'fillColor', 'shape', 'size', 'font', 'fontColor', 'x', 'y'] nameList.forEach(item => { const obj = { name: item, value: this.checkedNodeInfo[item] } list.push(obj) }) this.nodeInfoAttributeList = list this.nodeMenuDialogClose() }, /** * 選中關聯操作 * @date 2021-07-26 */ selectRelation() { this.visgraph.rightMenuOprate('selRelate') }, /** * 刪除指定節點 * @date 2021-07-26 * @returns {any} */ deleteNode() { this.visgraph.deleteNode(this.visgraph.currentNode) this.nodeMenuDialogClose() }, /** * 收起指定節點 * @date 2021-07-26 * @returns {any} */ contractNode() { if (this.visgraph.currentNode.outLinks.length > 0) { this.visgraph.contract(this.visgraph.currentNode) this.nodeMenuDialogClose() } else { this.$message.warning('當前節點無子節點,無法收起') } }, /** * 展開指定節點 * @date 2021-07-26 * @returns {any} */ expandedNode() { if (this.visgraph.currentNode.outLinks.length > 0) { this.visgraph.expanded(this.visgraph.currentNode) this.nodeMenuDialogClose() } else { this.$message.warning('當前節點無子節點,無法展開') } }, /** * 以指定節點為中心移動 * @date 2021-07-26 * @param {String} id */ moveCenterThisNode(id) { const node = this.visgraph.findNodeById(id) this.visgraph.moveNodeToCenter(node) }, /** * 節點配置 * @date 2021-07-30 * @returns {any} */ settingNode () { this.nodeMenuDialogClose() const { id, label, shape, fillColor, size, borderWidth, lineDash, borderColor, textPosition, font, fontColor, labelBackGround, textOffsetX } = this.visgraph.currentNode this.nodeConfigForm.id = id this.nodeConfigForm.label = label this.nodeConfigForm.shape = shape this.nodeConfigForm.fillColor = 'rgb(' + fillColor + ')' this.nodeConfigForm.hexColor = this.rgbToHex('rgb(' + fillColor + ')') this.nodeConfigForm.size = size this.nodeConfigForm.borderWidth = borderWidth this.nodeConfigForm.borderDash = lineDash.length === 2 this.nodeConfigForm.borderColor = 'rgb(' + borderColor + ')' this.nodeConfigForm.borderHexColor = this.rgbToHex('rgb(' + borderColor + ')') this.nodeConfigForm.textPosition = textPosition this.nodeConfigForm.font = font this.nodeConfigForm.fontColor = 'rgb(' + fontColor + ')' this.nodeConfigForm.fontHexColor = this.rgbToHex('rgb(' + fontColor + ')') this.nodeConfigForm.fontBgColor = labelBackGround ? 'rgb(' + labelBackGround + ')' : '' this.nodeConfigForm.fontBgHexColor = labelBackGround ? this.rgbToHex('rgb(' + labelBackGround + ')') : '' this.nodeConfigForm.textOffset = textOffsetX this.nodeConfigDrawer = true }, /** * 保存節點配置 * @date 2021-07-30 * @returns {any} */ saveSetting () { const { id, label, shape, fillColor, hexColor, size, borderWidth, borderDash, borderColor, borderHexColor, textPosition, font, fontColor, fontHexColor, fontBgColor, fontBgHexColor, textOffset } = this.nodeConfigForm const b = this.visgraph.findNodeByAttr('id', id) if (b) { b.label = label b.size = size b.shape = shape b.fillColor = this.getColorRgb(fillColor) b.textPosition = textPosition b.fontColor = this.getColorRgb(fontColor) b.labelBackGround = this.getColorRgb(fontBgColor) b.font = font; b.textOffsetY = Number(textOffset) || 2 b.borderWidth = Number(borderWidth) || 2 b.borderColor = this.getColorRgb(borderColor) b.lineDash = (borderDash ? [8, 5] : [0]) this.visgraph.refresh() this.$message({ message: '修改配置成功!', type: 'success', duration: 2000 }) this.nodeConfigDrawer = false } else { this.$message({ message: '無法找到選中節點!', type: 'error', duration: 2000 }) } }, /** * 隨機獲取顏色 * @date 2021-07-20 * @returns {String} 樣式 */ getRandomColor() { const r = Math.floor(Math.random() * 256) const g = Math.floor(Math.random() * 256) const b = Math.floor(Math.random() * 256) return 'rgb(' + r + ',' + g + ',' + b + ')' }, /** * 顏色選擇框變化賦值 * @date 2021-07-26 * @param {String} val * @param {String} kay */ colorChange(val, key) { this.nodeConfigForm[key] = this.hexToRgba(val) }, /** * 16進制色值轉rgb * @date 2021-07-26 * @param {String} hex * @returns {String} */ hexToRgba(hex) { return "rgb(" + parseInt("0x" + hex.slice(1, 3)) + "," + parseInt("0x" + hex.slice(3, 5)) + "," + parseInt("0x" + hex.slice(5, 7)) + ")" }, /** * rgb色值轉16進制 * @date 2021-07-26 * @param {String} color * @returns {String} */ rgbToHex(color) { const rgb = color.split(','); const r = parseInt(rgb[0].split('(')[1]); const g = parseInt(rgb[1]); const b = parseInt(rgb[2].split(')')[0]); const hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); return hex; }, /** * 去掉rgb * @date 2021-07-30 * @param {String} a * @returns {String} */ getColorRgb (a) { if (a && a.length > 0) { a = a.replace('rgb(', '').replace(')', '') } else { a = null } return a }, /** * 保存圖片 * @date 2021-07-23 */ saveImage() { this.visgraph.saveImage() }, /** * 導出json * @date 2021-07-30 */ exportJson () { this.visgraph.exportJsonFile() }, /** * 打開縮略圖 * @date 2021-07-23 */ showOverView() { console.log(this.showOverViewFlag) this.showOverViewFlag = !this.showOverViewFlag this.visgraph.showOverView(this.showOverView) }, /** * 縮小操作 * @date 2021-07-23 */ setZoomIn() { this.visgraph.setZoom('zoomIn') }, /** * 放大操作 * @date 2021-07-23 */ setZoomOut() { this.visgraph.setZoom('zoomOut') }, /** * 順時針旋轉 * @date 2021-07-23 */ clockwiseRotate() { this.visgraph.rotateGraph(-10) }, /** * 逆時針旋轉 * @date 2021-07-23 */ counterclockwiseRotate() { this.visgraph.rotateGraph(10) }, /** * 設置鼠標模式 * @date 2021-07-23 * @param {String} type drag:拖動模式 select:框選模式 normal:正常模式 */ setMouseModel(type) { this.visgraph.setMouseModel(type) }, /** * 全屏顯示 * @date 2021-07-23 */ fullScreen() { screenfull.request(this.$refs.graphpanel) } } } </script> <style lang="scss" scoped> #container { width: 100%; position: relative; #graph-panel { width:100%; height:100%; background:#9dadc1; position: absolute; z-index: 1; } /* 測試菜單欄 */ .left-toolbar { position: absolute; top: 0; left: 0; z-index: 1000; width: 45px; height: 100%; background-color: #fafafa; border-right: 1px solid #e5e2e2; ul { li { text-align: center; height: 35px; color: #066fba; line-height: 35px; cursor: pointer; padding: 0; i { font-size: 18px; } &:hover { background-color: #6ea36d; color: #fff; } } } } /* 右鍵彈框樣式 */ .nodeMenuDialog { display: none; position: absolute; min-width: 100px; padding: 2px 3px; margin: 0; border: 1px solid #e3e6eb; background: #f9f9f9; color: #333; z-index: 100; border-radius: 5px; box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2); transform: translate(0, 15px) scale(0.95); transition: transform 0.1s ease-out, opacity 0.1s ease-out; overflow: hidden; cursor: pointer; li { display: block; position: relative; margin: 0; padding: 0 10px; border-radius: 5px; white-space: nowrap; line-height: 30px; text-align: center; &:hover { background-color: #c3e5fd; } } } /* 節點信息彈框 */ .nodeInfo { .nodeInfoForm { padding: 20px 20px 0 20px; border: solid 1px #dcdfe6; border-left: none; border-right: none; margin: 20px 0; } .nodeInfoRelation { padding: 0 20px; .nodeInfo-table { width: 100%; overflow-y: scroll; th { width: 50%; border: 1px solid #ebeef5; padding: 9px 0 9px 9px; text-align: left; &:first-child { border-right: none; } } td { width: 50%; border: 1px solid #ebeef5; border-top: none; padding: 9px 0 9px 9px; &:first-child { border-right: none; } } } /deep/ .el-badge__content.is-fixed { top: 24px; right: -7px; } p { text-align: center; padding: 20px 0; } } .nodeInfoAttribute { padding: 0 20px; } } /* 節點配置彈框 */ .nodeConfig { padding: 20px 20px 0 20px; border: solid 1px #dcdfe6; border-left: none; border-right: none; margin: 20px 0; .form-color { display: flex; justify-content: space-between; .form-input { width: calc(100% - 50px); } } .save-setting { width: 100%; margin-bottom: 20px; .el-button { width: 100%; } } } } </style>
註:引入兩個js的文件eslint會報錯,可以把這個文件忽略,不使用eslint的可以忽略。同時該項目還基於element-ui開發,引入screenfull全屏插件,還有阿裡圖標庫圖標,自己按需引入。
Demo演示:
到此這篇關於vue使用GraphVis開發無限拓展的關系圖譜的實現的文章就介紹到這瞭,更多相關vue GraphVis關系圖譜內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- vue element實現表格增加刪除修改數據
- el-form resetFields無效和validate無效的可能原因及解決方法
- Vue.$set 失效的坑 問題發現及解決方案
- 總結Vue Element UI使用中遇到的問題
- Vue3+Element+Ts實現表單的基礎搜索重置等功能