使用Vue-neo4j實(shí)現(xiàn)繪制三國(guó)人物圖譜關(guān)系
前言
發(fā)現(xiàn)一個(gè)很有用的Vue插件:https://www.npmjs.com/package/vue-neo4j
這個(gè)能在前端直連neo4j服務(wù)器啦,下圖就是測(cè)試效果繪制三國(guó)人物圖譜關(guān)系

版本說(shuō)明
- vue
3.0版本 - vue-neo4j
0.4.8版本 - neo4j服務(wù)端版本
4.3.6版本,可以直接去官網(wǎng)下載。 - echarts
5.22版本
開(kāi)發(fā)說(shuō)明
一、windows服務(wù)器部署neo4j
1、部署jdk11, 因?yàn)閚eo4j 4.3.6使用的版本是jdk11以上,否則啟動(dòng)不了哦
2、下載neo4j,解壓
3、到 .\conf目錄修改 neo4j.conf文件修改default_listen_address為0.0.0.0,因?yàn)椴桓牡脑?,?huì)導(dǎo)致啟動(dòng)后只能本機(jī)訪問(wèn)

4、cmd到.\bin目錄執(zhí)行 noe4j console. 這樣就完成啟動(dòng)了

5、瀏覽器打開(kāi)網(wǎng)址 http://127.0.0.1:7474
默認(rèn)密碼為neo4j/neo4j 首次進(jìn)入會(huì)提示修改密碼
二、vue3.0 版本前端工程說(shuō)明
1、引入依賴 yarn add vue-neo4j
2、由于vue-neo4j 0.4.8 該版本只適配vue2.0版本,所以要改下源碼,把 Vue.property.$neo4j 改為 Vue.config.globalProperties.$neo4j

3、main.js引入
import VueNeo4j from 'vue-neo4j'; let app = createApp(App); app.use(VueNeo4j );

4、邏輯編寫
<template>
<div class="neo4jMain">
<div class="addContent">
<el-form :inline="true" :model="formInline" :rules="rules" ref="neo4jform">
<el-form-item label="開(kāi)始節(jié)點(diǎn)名稱" prop="startNode">
<el-input v-model="formInline.startNode" placeholder="請(qǐng)輸入開(kāi)始節(jié)點(diǎn)名稱"></el-input>
</el-form-item>
<el-form-item label="關(guān)系名稱" prop="relationName">
<el-input v-model="formInline.relationName" placeholder="請(qǐng)輸入關(guān)系名稱"></el-input>
</el-form-item>
<el-form-item label="結(jié)束節(jié)點(diǎn)名稱" prop="endNode">
<el-input v-model="formInline.endNode" placeholder="請(qǐng)輸入結(jié)束節(jié)點(diǎn)名稱"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">提交</el-button>
<!-- <el-button type="primary" @click="onDelete">清空表</el-button>-->
</el-form-item>
</el-form>
</div>
<div class="echarts" ref="echarts">
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
name: "Main.vue",
mounted() {
this.query();
},
data() {
return {
formInline: {
startNode: '',
endNode: '',
relationName: ''
},
rules: {
startNode: [{required: true, trigger: 'blur'}],
endNode: [{required: true, trigger: 'blur'}],
relationName: [{required: true, trigger: 'blur'}]
},
protocol: 'bolt',
host: '127.0.0.1',
port: 7687,
username: 'neo4j',
password: '123456',
echartsData: [],
nodesRelation: []
}
},
methods: {
onDelete() {
this.connect();
const session = this.$neo4j.getSession();
let cypher = `match p=(n:Person)-[]->(m:Person) delete p `;
session.run(cypher);
cypher = `MATCH (n:Person) delete n`;
session.run(cypher).then(() => {
session.close()
});
this.query();
},
initEcharts() {
// 基于準(zhǔn)備好的dom,初始化echarts實(shí)例
let myChart = echarts.init(this.$refs.echarts)
// 繪制圖表
myChart.setOption({
title: {
text: 'Neo4j 圖譜關(guān)系'
},
tooltip: {},
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'force',
force: {
edgeLength: 40,
repulsion: 50,
gravity: 0.1
},
symbolSize: 50,
roam: true,
label: {
show: true
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
edgeLabel: {
fontSize: 20
},
data: this.echartsData,
// links: [],
links: this.nodesRelation,
lineStyle: {
opacity: 0.9,
width: 2,
curveness: 0
}
}
]
});
},
query() {
this.connect();
const session = this.$neo4j.getSession();
let cypher = `match p=(n:Person)-[]->(m:Person) return p limit 1000`;
session.run(cypher).then(res => {
let records = res.records;
let nodes = new Set();
this.nodesRelation = [];
for (let i = 0; i < records.length; i++) {
nodes.add(records[i]._fields[0].segments[0].start.properties.name);
nodes.add(records[i]._fields[0].segments[0].end.properties.name);
this.nodesRelation.push({
source: records[i]._fields[0].segments[0].start.properties.name,
target: records[i]._fields[0].segments[0].end.properties.name,
lineStyle: {
curveness: 0
},
label: {
show: true,
formatter: function() {
return records[i]._fields[0].segments[0].relationship.type
}
}
});
}
let curveness = [0, 0.4, -0.4, 0.3, -0.3, 0.2, -0.2, 0.1, -0.1];
for (let j = 0; j < this.nodesRelation.length; j++) {
let repeatNumber = 0;
for (let s = j+1; s < this.nodesRelation.length; s++) {
let r1 = this.nodesRelation[j];
let r2 = this.nodesRelation[s];
if (r1.source === r2.source &&
r1.target === r2.target) {
repeatNumber = repeatNumber + 1;
}
else if (r1.target === r2.source &&
r1.source === r2.target) {
repeatNumber = repeatNumber + 1;
}
}
this.nodesRelation[j].repeatNumber = repeatNumber;
}
for (let j = 0; j < this.nodesRelation.length; j++) {
console.log(this.nodesRelation[j].repeatNumber);
this.nodesRelation[j].lineStyle.curveness = curveness[this.nodesRelation[j].repeatNumber];
}
this.echartsData = [];
nodes.forEach((e) => {
let index = Math.ceil(Math.random()*10);
let color = () => {
if (index%4===0) {
return '#228B22'
} else if (index%4===1) {
return '#FFFF00'
} else if (index%4===2) {
return '#20B2AA'
} else if (index%4===3) {
return '#FFB6C1'
}
return '#87CEFA';
}
this.echartsData.push({
name: e,
x: Math.random() * 100,
y: Math.random() * 100,
itemStyle: {
color: color()
}
});
})
this.initEcharts();
}).then(() => {
session.close()
});
},
onSubmit() {
this.$refs.neo4jform.validate((valid) => {
if (valid) {
this.connect();
const session = this.$neo4j.getSession();
let cypher = `Merge (n:Person{name: '${this.formInline.startNode}'})
Merge (m:Person{name: '${this.formInline.endNode}'}) Merge (n)-[r:${this.formInline.relationName}]->(m)`;
session.run(cypher).then(() => {
this.formInline = {
startNode: '',
endNode: '',
relationName: ''
};
this.query();
}).then(() => {
session.close()
});
}
})
},
connect() {
return this.$neo4j.connect(this.protocol, this.host, this.port, this.username, this.password);
},
driver() {
// Get a driver instance
return this.$neo4j.getDriver()
},
testQuery() {
// Get a session from the driver
const session = this.$neo4j.getSession()
// Or you can just call this.$neo4j.run(cypher, params)
session.run('MATCH (n) RETURN n')
.then(res => {
console.log(res)
// console.log(res.records[0].get('count'))
})
.then(() => {
session.close()
})
}
}
}
</script>
<style scoped lang="less">
.neo4jMain {
height: 100%;
display: flex;
flex-direction: column;
.addContent {
padding: 15px;
box-shadow: -10px 0 10px #D3D3D3, /*左邊陰影*/ 10px 0 10px #D3D3D3, /*右邊陰影*/
0 -10px 10px #D3D3D3, /*頂部陰影*/ 0 10px 10px #D3D3D3;
}
.echarts {
background-color: #333;
flex: 1;
flex-grow: 1;
}
}
</style>
心得:
- 優(yōu)點(diǎn)是前端可以直接操作數(shù)據(jù)庫(kù),弊端是數(shù)據(jù)庫(kù)配置暴露了。
- 建議還是通過(guò)后端連接數(shù)據(jù)庫(kù)操作數(shù)據(jù)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue3利用keepAlive緩存頁(yè)面實(shí)例詳解
<keep-alive> 是一個(gè)抽象組件,它自身不會(huì)渲染一個(gè)DOM元素,也不會(huì)出現(xiàn)在組件的父組件鏈中,下面這篇文章主要給大家介紹了關(guān)于vue3利用keepAlive緩存頁(yè)面的相關(guān)資料,需要的朋友可以參考下2022-11-11
vue-cli項(xiàng)目無(wú)法用本機(jī)IP訪問(wèn)的解決方法
今天小編就為大家分享一篇vue-cli項(xiàng)目無(wú)法用本機(jī)IP訪問(wèn)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Vue3使用LogicFlow更新節(jié)點(diǎn)名稱的方法
本文介紹了在Vue3中更新LogicFlow節(jié)點(diǎn)名稱的多種方法,包括使用updateText和setProperties方法,以及事件監(jiān)聽(tīng)與交互方式,還討論了自定義節(jié)點(diǎn)名稱編輯、批量更新與高級(jí)功能,并強(qiáng)調(diào)了注意事項(xiàng)與最佳實(shí)踐,需要的朋友可以參考下2026-01-01
前端Vue全屏screenfull通用解決方案及原理詳細(xì)分析
這篇文章主要給大家介紹了關(guān)于前端Vue全屏screenfull通用解決方案及原理的相關(guān)資料,使用screenfull這一第三方庫(kù)可以實(shí)現(xiàn)更穩(wěn)定的全屏功能,需要的朋友可以參考下2024-10-10
前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法
這篇文章主要介紹了前端vue-cli項(xiàng)目中使用img圖片和background背景圖的幾種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Vue實(shí)現(xiàn)獲取剪切板內(nèi)容的兩種方法
這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)獲取剪切板內(nèi)容的兩種方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以了解下2025-02-02
vue實(shí)現(xiàn)商城上貨組件簡(jiǎn)易版
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)商城上貨組件簡(jiǎn)易版,50行js代碼實(shí)現(xiàn)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11

