简介:vue项目中添加复杂人物可视化关系,利用图表展示(以该节点为核心节点,拓展关系,群与群之间的同成员交叉关系等)
基于浏览器的动态可视化库 vis.js GitHub案例:可视网络-networkv jiis.js
Installation
```
npm install --save vue2vis
```
or
```
yarn add vue2vis
```
Usage
GraphVis2.vue
<template>
<div
class="relationship-wrap-chart"
@click.stop="hideContextMenu"
id="relationship"
>
<!-- loading -->
<!-- <vi-spin tip="Loading..." v-show="relationChart.isLoadingVisible" class="load-style">
</vi-spin> -->
<network
id="myNetwork"
class="graphdiv"
:nodes="relationChart.relationshipNodes"
:edges="relationChart.relationshipEdges"
:options="options"
@select-node="selectNode"
@oncontext="showMenu"
@after-drawing="afterDrawing"
v-contextmenu:menu
ref="network"
></network>
<!-- <control-btn> -->
<!-- <GraphControl v-if="!relationChart.isMinimapShow"></GraphControl> -->
<!-- </control-btn> -->
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { State, Action } from "vuex-class";
import { Network } from 'vue2vis';
import Image from '@/assets/default-touxiang.png' //默认图片
@Component({
components: {
Network
}
})
export default class PersonRelationShipChart extends Vue {
@State((state) => state.relationChart) private relationChart: any;
// @Action("relationChart/getPersonRelactionShip")
// private getPersonRelactionShip: any; // 编辑专项获取该专项的信息
// @Action("relationChart/expandNode")
private expandNode: any; // 编辑专项获取该专项的信息
private contextShow: boolean = false; //是否展示右击菜单
private remarkPopVisible: boolean = false; //展示一键扩展内容
private expandPopVisible: boolean = false; //展示一键扩展内容
private options: any = {};
private outMenu: boolean = false;
// 监听切换动态和静态布局
@Watch('relationChart.isFixed')
onFixedChange(val: boolean) {
this.options.physics.enabled = !val;
// this.options = this.$set(this.options, 'physics.enabled', !val)
}
// 监听选择画布模式
@Watch('relationChart.isBoxSelect')
onIsBoxSelectChange(val: boolean) {
this.options.interaction.dragView = !val;
}
@Watch('relationChart.expandOptions')
private changeCheckOptions(val: any) {
const self: any = this;
self.relationChart.checkOptions = [];
val.forEach((item: any) => {
self.relationChart.checkOptions.push(item.name);
});
}
// 人物关系图片
// 获取对应的关系图片
private defaultImage(item: any) {
// 微信
if (item.sourceType == 1) {
if (item.accountType == 1) {
// 微信群
item.image = weixin_qun;;
} else {
// 微信人
item.image = weixin_member;
}
} else {
// qq
if (item.accountType == 1) {
// QQ群
item.image = qq_qun;
} else {
// QQ人
item.image = qq_member;
}
}
}
private mounted() {
const self: any = this;
debugger
this.relationChart.chartOptions.nodes.brokenImage = Image; //定义默认图片
//监听键盘按键事件
self.$nextTick(() => {
// const element = document.getElementById('relationship') as HTMLElement;
document.addEventListener('keyup', function (e) {
// Esc键关闭全屏图标
if (e.keyCode == 27 && self.relationChart.isFullScren) {
self.relationChart.isFullScren = false
}
})
})
this.relationChart.checkList = [];
const canvas = document.getElementsByTagName('canvas')[0];
// const context = canvas.getContext('2d')
this.options = this.initOption
}
private initOption() {
const chartOptions = {
height: '100%',
autoResize: true,
physics: {
enabled: true,
forceAtlas2Based: {
// gravitationalConstant: -200,
centralGravity: 0.065,
springLength: 150,
springConstant: 1.9,
avoidOverlap: 0.5
},
maxVelocity: 115,
minVelocity: 3.03,
solver: 'forceAtlas2Based',
timestep: 0.16,
stabilization: {
iterations: 2000,
onlyDynamicEdges: false,
fit: true
}
},
layout: {
improvedLayout: true
},
interaction: {
dragView: true,
navigationButtons: false,
multiselect: true,
selectConnectedEdges: true
},
nodes: {
shape: 'circularImage',
brokenImage: '',
shapeProperties: {
useImageSize: true,
interpolation: false
},
chosen: true,
color: {
border: '#2B7CE9',
highlight: {
border: '#e89d2c'
},
background: 'transparent'
},
size: 15,
font: {
// size
background: 'transparent'
},
shadow: false
// label: ''
},
edges: {
chosen: true,
width: 1.5,
arrows: {
to: {
enabled: false,
scaleFactor: 0.5
}
},
shadow: false,
selectionWidth: (width: any) => width * 3,
font: {
// size
}
}
};
return chartOptions;
}
// 隐藏模态框
private hideContextMenu() {
this.contextShow = false;
this.expandPopVisible = false;
this.remarkPopVisible = false;
}
// 点击节点事件
private selectNode({ nodes: ids }) {
const self: any = this;
// ----------------------
// const nodeId:any = self.$refs.network.getNodeAt(params.pointer.DOM)
// 添加节点
// a.concat(b)
// this.relationChart.relationshipNodes = this.relationChart.relationshipNodes.concat(this.relationChart.addrelationshipNodes)
// this.relationChart.relationshipEdges = this.relationChart.relationshipEdges.concat(this.relationChart.addrelationshipEdges)
// const { enabled } = this.options.physics
// this.options = immutable.set(this.options, 'physics.enabled', enabled)
// const sn = _.intersectionWith(this.nodes, ids, (x, y) => x.id === y)
// this.changeSelectedNodes(sn)
}
// 右击
private showMenu(params: any) {
const self: any = this;
self.relationChart.selectNodeId = self.$refs.network.getNodeAt(params.pointer.DOM);
// debugger;
// 根据选中的是群账号还是个人/群主进行区分右击菜单
if (self.relationChart.selectNodeId) {
self.contextShow = true;
self.relationChart.relationshipNodes.forEach((item: any) => {
if (item.id == self.relationChart.selectNodeId) {
self.relationChart.selectNodeInfo = {
innerId: item.id,
accountType: item.accountType,
sourceType: item.sourceType
}
}
})
self.relationChart.selectNodeInfo;
// 根据accountType判断二级菜单拓展的人还是群
if (self.relationChart.selectNodeInfo.accountType == 1) {
self.relationChart.expandOptions = self.relationChart.expandAccount;
} else {
self.relationChart.expandOptions = self.relationChart.expandGroupMaster;
}
} else {
self.contextShow = false;
}
}
private afterDrawing(params: any) {
const self: any = this;
// 增加标签
if (self.isShowSign) {
self.clickXY = []
self.addSign(params)
const canvas = document.getElementsByTagName('canvas')[0]
// 移除标签的点击事件
canvas.removeEventListener('click', self.addClickEvent, false)
// 给标签增加点击事件
canvas.addEventListener('click', self.addClickEvent, false)
}
}
}
</script>
<style lang="less" scope>
.option-check {
.ant-checkbox-group-item {
display: block !important;
}
}
.primary-btn {
margin: 10px 10px 0 10px;
background-color: #4770d1;
width: 40px;
height: 25px;
text-align: center;
border-color: #4770d1;
padding: 0;
}
.default-btn {
width: 40px;
height: 25px;
text-align: center;
border-color: #a9a9a9;
padding: 0;
}
.relationship-wrap-chart {
width: 100%;
height: 100%;
position: relative;
margin-top: 10px;
background: #fff;
.load-style {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}
.graphdiv {
height: 100%;
}
}
.right-menu {
padding: 10px !important;
.remark-li {
cursor: pointer;
height: 26px;
line-height: 26px;
}
}
</style>
1、vue+typescript中,.vue文件import报错
在index.ts中引入.vue文件,会提示打不到module,但是编译可能成功,运行也不报错
解决:
只要在src根目录下,新建一个sfc.d.ts文件,里面内容如下: