Merge branch 'master' of http://172.16.20.1:3000/duanhao/SocialNetworks_duan
This commit is contained in:
		
						commit
						f24cce9202
					
				
							
								
								
									
										116
									
								
								src/assets/package/layoutParamsDict.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/assets/package/layoutParamsDict.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
//布局算法的参数定义
 | 
			
		||||
export const layoutParamsDict ={
 | 
			
		||||
    'hierarchical':[ //层级布局
 | 
			
		||||
        {'name':'layerSpace','value': 150,'type':'number','label':'层间距'},
 | 
			
		||||
        {'name':'nodeSpace','value': 80,'type':'number','label':'点间距'},
 | 
			
		||||
        {'name':'sortType','value': 'selected','type':'array','label':'排序类型',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'指定点','value':'selected'},
 | 
			
		||||
                {'label':'度大小','value':'hubsize'},
 | 
			
		||||
                {'label':'连线方向','value':'directed'}
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        {'name':'direction','value': 'UD','type':'array','label':'方向',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'上下','value':'UD'},
 | 
			
		||||
                {'label':'下上','value':'DU'},
 | 
			
		||||
                {'label':'左右','value':'LR'},
 | 
			
		||||
                {'label':'右左','value':'RL'}
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    'grid':[ //网格布局
 | 
			
		||||
        {'name':'layerSpace','value': 120,'type':'number','label':'层间距'},
 | 
			
		||||
        {'name':'nodeSpace','value': 140,'type':'number','label':'点间距'}
 | 
			
		||||
    ],
 | 
			
		||||
    'concentric':[ //同心圆布局
 | 
			
		||||
        {'name':'maxNodeSize','value': 70,'type':'number','label':'节点大小'}
 | 
			
		||||
    ],
 | 
			
		||||
    'fastForce':[ //网络布局
 | 
			
		||||
        {'name':'linkDistance','value': 150,'type':'number','label':'连线长度'},
 | 
			
		||||
        {'name':'charge','value': -450,'type':'number','label':'吸引力'},
 | 
			
		||||
        {'name':'friction','value': 0.85,'type':'decimal','label':'排斥力'},
 | 
			
		||||
        {'name':'linkStrength','value': 0.1,'type':'decimal','label':'连线强度'},
 | 
			
		||||
        {'name':'gravity','value': 0.05,'type':'decimal','label':'中心引力'}
 | 
			
		||||
    ],
 | 
			
		||||
    'simulation':[ //力导向布局
 | 
			
		||||
        {'name':'strength','value': -800,'type':'number','label':'吸引力'},
 | 
			
		||||
        {'name':'linkDistance','value': 150,'type':'number','label':'连线长度'},
 | 
			
		||||
        {'name':'ajustCluster','value': false,'type':'array','label':'类型分组',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'是','value':true},
 | 
			
		||||
                {'label':'否','value':false}
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    'kawakai':[ //最优路径布局
 | 
			
		||||
        {'name':'sizeScale','value': 1.5,'type':'number','label':'缩放比例'}
 | 
			
		||||
    ],
 | 
			
		||||
    'circle':[ //环形布局
 | 
			
		||||
        {'name':'scale','value': 1.2,'type':'number','label':'缩放比例'},
 | 
			
		||||
        {'name':'ordering','value': 'degree','type':'array','label':'排序方式',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'度大小排序','value':'degree'},
 | 
			
		||||
                {'label':'拓扑排序','value':'topology'}
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    'arf':[
 | 
			
		||||
        {'name':'neighberForce','value': 5.0,'type':'number','label':'邻边引力'},
 | 
			
		||||
        {'name':'attraction','value': 0.05,'type':'number','label':'向心力'},
 | 
			
		||||
        {'name':'forceScale','value': 5.0,'type':'number','label':'缩放比例'}
 | 
			
		||||
    ],
 | 
			
		||||
    'tree':[ //树形布局
 | 
			
		||||
        {'name':'layerSpace','value': 150,'type':'number','label':'层间距'},
 | 
			
		||||
        {'name':'nodeSpace','value': 80,'type':'number','label':'点间距'},
 | 
			
		||||
        {'name':'direction','value': 'UD','type':'array','label':'方向',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'上下','value':'UD'},
 | 
			
		||||
                {'label':'下上','value':'DU'},
 | 
			
		||||
                {'label':'左右','value':'LR'},
 | 
			
		||||
                {'label':'右左','value':'RL'}
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    'avoidlap':[ //避免重叠
 | 
			
		||||
        {'name':'maxPadding','value': 5,'type':'number','label':'节点间距'}
 | 
			
		||||
    ],
 | 
			
		||||
    'dagre':[ //流程图布局
 | 
			
		||||
        {'name':'nodesep','value': 10,'type':'number','label':'点间距'},
 | 
			
		||||
        {'name':'ranksep','value': 100,'type':'number','label':'层间距'},
 | 
			
		||||
        {'name':'nodeSize','value': 50,'type':'number','label':'节点大小'},
 | 
			
		||||
        {'name':'rankdir','value': 'TB','type':'array','label':'排列方向',
 | 
			
		||||
            'types':[
 | 
			
		||||
                {'label':'上下','value':'TB'},
 | 
			
		||||
                {'label':'下上','value':'BT'},
 | 
			
		||||
                {'label':'左右','value':'LR'},
 | 
			
		||||
                {'label':'右左','value':'RL'}
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//获取默认的布局参数
 | 
			
		||||
export const getLayoutDefaultConfig = function(layoutType){
 | 
			
		||||
    let layoutConfig = {};
 | 
			
		||||
    layoutParamsDict[layoutType].forEach(param=>{
 | 
			
		||||
        layoutConfig[param.name] = param.value;
 | 
			
		||||
    });
 | 
			
		||||
    return layoutConfig;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//获取布局参数列表
 | 
			
		||||
export const getLayoutParams = function(layoutType){
 | 
			
		||||
    return layoutParamsDict[layoutType];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//布局算法列表
 | 
			
		||||
export const layoutTypeList = [
 | 
			
		||||
    {label:'快速弹性布局',value:'fastForce',icon:'el-icon-orange'},
 | 
			
		||||
    {label:'力导向布局',value:'simulation',icon:'el-icon-orange'},
 | 
			
		||||
    {label:'最优路径布局',value:'kawakai',icon:'el-icon-cpu'},
 | 
			
		||||
    //{label:'球面引力布局',value:'arf',icon:'el-icon-orange'},
 | 
			
		||||
    {label:'层级布局',value:'hierarchical',icon:'el-icon-share'},
 | 
			
		||||
    {label:'中心圆布局',value:'concentric',icon:'el-icon-bangzhu'},
 | 
			
		||||
    {label:'去除重叠',value:'avoidlap',icon:'el-icon-copy-document'}
 | 
			
		||||
];
 | 
			
		||||
| 
						 | 
				
			
			@ -211,7 +211,6 @@ export const useGroupDiscoveryStore = defineStore("groupDiscovery", {
 | 
			
		|||
      this.timeList = res.data
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //根据时间参数获取节点数据
 | 
			
		||||
    async initialGraphByUtcTime(utcTime = "") {
 | 
			
		||||
      const setColor = (groupId) => {
 | 
			
		||||
        const colorMap = {
 | 
			
		||||
| 
						 | 
				
			
			@ -237,16 +236,23 @@ export const useGroupDiscoveryStore = defineStore("groupDiscovery", {
 | 
			
		|||
          }
 | 
			
		||||
          return false
 | 
			
		||||
        })
 | 
			
		||||
        .map((node) => ({
 | 
			
		||||
          id: node.name,
 | 
			
		||||
          label: node.name,
 | 
			
		||||
          color: setColor(node.groupId)
 | 
			
		||||
        }))
 | 
			
		||||
        .map((node) => {
 | 
			
		||||
          return {
 | 
			
		||||
            id: node.name,
 | 
			
		||||
            label: node.name,
 | 
			
		||||
            color: setColor(node.groupId),
 | 
			
		||||
            cluster: parseInt(node.groupId)
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // 通过时间来获取帖文列表
 | 
			
		||||
    //根据时间参数获取贴文数据
 | 
			
		||||
    async initialPostByUtcTime(utcTime) {}
 | 
			
		||||
    async initialPostByUtcTime(utcTime) {
 | 
			
		||||
      const res = await getPostByUtcTime(utcTime)
 | 
			
		||||
      if (res.code != 200) return
 | 
			
		||||
      this.posts = res.data
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  persist: true // 开启持久化
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,36 +17,23 @@
 | 
			
		|||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import {
 | 
			
		||||
  defineProps,
 | 
			
		||||
  defineEmits,
 | 
			
		||||
  onMounted,
 | 
			
		||||
  onUnmounted,
 | 
			
		||||
  nextTick,
 | 
			
		||||
  ref,
 | 
			
		||||
  toRaw,
 | 
			
		||||
  watchEffect,
 | 
			
		||||
  watch
 | 
			
		||||
} from "vue"
 | 
			
		||||
import { defineProps, defineEmits, onUnmounted, ref, toRaw, watch } from "vue"
 | 
			
		||||
import { storeToRefs } from "pinia"
 | 
			
		||||
import { convertToUtcIsoString } from "@/utils/transform"
 | 
			
		||||
import { paintNodeFunction, paintLineFunction } from "@/utils/customePaint"
 | 
			
		||||
import { demoData } from "@/assets/customeGraph/data"
 | 
			
		||||
import TimeAxis from "@/components/timeAxis.vue"
 | 
			
		||||
import GraphVis from "@/assets/package/graphvis.esm.min.js"
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  store: {
 | 
			
		||||
    required: true
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
const emit = defineEmits(["click:pointerDownAndSlide"])
 | 
			
		||||
// 解构 store 中的 state
 | 
			
		||||
const { timeList, graph } = storeToRefs(props.store)
 | 
			
		||||
let graphVis = null
 | 
			
		||||
let forceSimulator = null
 | 
			
		||||
let currentSelectNode = ref(null)
 | 
			
		||||
let isGraphVisReady = false // 添加一个标志位
 | 
			
		||||
const defaultConfig = {
 | 
			
		||||
  node: {
 | 
			
		||||
    label: {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +52,7 @@ const defaultConfig = {
 | 
			
		|||
      borderWidth: 2,
 | 
			
		||||
      borderColor: "100,250,100",
 | 
			
		||||
      showShadow: true, // 是否展示阴影
 | 
			
		||||
      shadowBlur: 30, //阴影范围大小
 | 
			
		||||
      shadowBlur: 10, //阴影范围大小
 | 
			
		||||
      shadowColor: "50,250,30" // 选中是的阴影颜色
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +64,7 @@ const defaultConfig = {
 | 
			
		|||
      font: "normal 11px KaiTi", // 字体大小及类型
 | 
			
		||||
      background: "255,255,255" //文字背景色(设置后文字居中,一般与画布背景色一致)
 | 
			
		||||
    },
 | 
			
		||||
    lineType: "curver", //straight
 | 
			
		||||
    lineType: "straight", // curver
 | 
			
		||||
    showArrow: false,
 | 
			
		||||
    lineWidth: 2,
 | 
			
		||||
    colorType: "both",
 | 
			
		||||
| 
						 | 
				
			
			@ -177,46 +164,20 @@ const runForceLayout = () => {
 | 
			
		|||
// 添加更新图表的函数
 | 
			
		||||
const updateChart = (newGraphData) => {
 | 
			
		||||
  if (!graphVis) {
 | 
			
		||||
    initChart();
 | 
			
		||||
    return;
 | 
			
		||||
    initChart()
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  // 清除现有图表
 | 
			
		||||
  graphVis.clearAll();
 | 
			
		||||
  graphVis.clearAll()
 | 
			
		||||
 | 
			
		||||
  // 添加新数据
 | 
			
		||||
  graphVis.addGraph({ ...toRaw(newGraphData) });
 | 
			
		||||
  graphVis.addGraph({ ...toRaw(newGraphData) })
 | 
			
		||||
  graphVis.autoGroupLayout(toRaw(newGraphData).nodes)
 | 
			
		||||
 | 
			
		||||
  // 重新运行力导向布局
 | 
			
		||||
  runForceLayout();
 | 
			
		||||
  runForceLayout()
 | 
			
		||||
}
 | 
			
		||||
const initChart = () => {
 | 
			
		||||
  const getGroupCenters = (groupCount, width, height, radius = 200) => {
 | 
			
		||||
    // 三组分布在三角形顶点
 | 
			
		||||
    const angleStep = (2 * Math.PI) / groupCount
 | 
			
		||||
    const centerX = width / 2
 | 
			
		||||
    const centerY = height / 2
 | 
			
		||||
    return Array.from({ length: groupCount }).map((_, i) => ({
 | 
			
		||||
      x: centerX + Math.cos(i * angleStep) * radius,
 | 
			
		||||
      y: centerY + Math.sin(i * angleStep) * radius
 | 
			
		||||
    }))
 | 
			
		||||
  }
 | 
			
		||||
  const assignNodePositions = (nodes, groupCenters) => {
 | 
			
		||||
    const groupMap = {}
 | 
			
		||||
    nodes.forEach((node) => {
 | 
			
		||||
      const groupIdx = node.groupId || 0
 | 
			
		||||
      if (!groupMap[groupIdx]) groupMap[groupIdx] = []
 | 
			
		||||
      groupMap[groupIdx].push(node)
 | 
			
		||||
    })
 | 
			
		||||
    Object.entries(groupMap).forEach(([groupIdx, groupNodes]) => {
 | 
			
		||||
      const center = groupCenters[groupIdx]
 | 
			
		||||
      groupNodes.forEach((node, i) => {
 | 
			
		||||
        // 每组节点在各自中心附近随机分布
 | 
			
		||||
        node.x = center.x + Math.random() * 80 - 40
 | 
			
		||||
        node.y = center.y + Math.random() * 80 - 40
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const createGraph = () => {
 | 
			
		||||
    if (!graphVis) {
 | 
			
		||||
      graphVis = new GraphVis({
 | 
			
		||||
| 
						 | 
				
			
			@ -227,31 +188,27 @@ const initChart = () => {
 | 
			
		|||
    }
 | 
			
		||||
    graphVis.setDragHideLine(false) //拖拽时隐藏连线
 | 
			
		||||
    graphVis.setShowDetailScale(0.1) //展示细节的比例
 | 
			
		||||
    graphVis.setZoomRange(0.1, 5) //缩放区间
 | 
			
		||||
    graphVis.setZoomRange(0.05, 5) //缩放区间
 | 
			
		||||
    registCustomePaintFunc() //注册自定义绘图方法
 | 
			
		||||
    registEvents()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // const groupCount = 3 // 分组数量
 | 
			
		||||
  // const width = 900,
 | 
			
		||||
  //   height = 500 // 画布大小
 | 
			
		||||
  // const groupCenters = getGroupCenters(groupCount, width, height)
 | 
			
		||||
  // assignNodePositions(graph.value.nodes, groupCenters)
 | 
			
		||||
  createGraph()
 | 
			
		||||
  console.log(graph.value)
 | 
			
		||||
 | 
			
		||||
  graphVis.addGraph({ ...toRaw(graph.value) })
 | 
			
		||||
  runForceLayout()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let lastLength = 0 //记录上一次的长度
 | 
			
		||||
watch(
 | 
			
		||||
  graph,
 | 
			
		||||
  (newValue) => {
 | 
			
		||||
    if (newValue && Object.keys(newValue).length > 0) {
 | 
			
		||||
      updateChart(newValue);
 | 
			
		||||
    if (newValue && newValue.nodes.length > lastLength) {
 | 
			
		||||
      updateChart(newValue)
 | 
			
		||||
    }
 | 
			
		||||
    lastLength = newValue.nodes.length
 | 
			
		||||
  },
 | 
			
		||||
  { deep: true, immediate: true }
 | 
			
		||||
);
 | 
			
		||||
  { deep: true }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  if (graphVis) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user