社团紧密关系-点边
This commit is contained in:
parent
960130fba1
commit
2a15f5a5f9
|
|
@ -17,6 +17,7 @@ import * as echarts from "echarts"
|
||||||
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||||
|
|
||||||
let chart = null
|
let chart = null
|
||||||
|
let linkList = null
|
||||||
const emit = defineEmits(["click:node", "click:edge"])
|
const emit = defineEmits(["click:node", "click:edge"])
|
||||||
const statisticsList = inject("statisticsList");
|
const statisticsList = inject("statisticsList");
|
||||||
const communityNodeList = inject("communityNodeList");
|
const communityNodeList = inject("communityNodeList");
|
||||||
|
|
@ -49,16 +50,16 @@ const initChart = async () => {
|
||||||
target: communityNei.id,
|
target: communityNei.id,
|
||||||
edge: communityNei.isHidden ? 1 : 0, //该边存在互动隐关系则权值为1,否则为0
|
edge: communityNei.isHidden ? 1 : 0, //该边存在互动隐关系则权值为1,否则为0
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: communityNei.isHidden ? 4 : 1, // 无互动=细线,有互动=加粗
|
width: communityNei.isHidden ? 7 : 1, // 无互动=细线,有互动=加粗
|
||||||
color: communityNei.isHidden ? "#FF5E00" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
color: communityNei.isHidden ? "#FF5E00" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
||||||
type: communityNei.isHidden ? "dashed" : "solid", // 无互动=实线,有互动=虚线
|
type: communityNei.isHidden ? "dashed" : "solid", // 无互动=实线,有互动=虚线
|
||||||
dashArray: [2, 1] // 2像素实线,1像素空白
|
opacity: communityNei.isHidden ? 1 : 0.5,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
edgeSet.add(key)
|
edgeSet.add(key)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
linkList = links
|
||||||
const data = { nodes, links }
|
const data = { nodes, links }
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
|
|
@ -241,7 +242,36 @@ const handleClickNode = () => {
|
||||||
} else if (params.dataType == "edge") {
|
} else if (params.dataType == "edge") {
|
||||||
const { data } = params
|
const { data } = params
|
||||||
if (data.edge) {
|
if (data.edge) {
|
||||||
emit("click:edge", data)
|
const clickEdgeTarget = data.target
|
||||||
|
const clickEdgeSource = data.source
|
||||||
|
console.log("linkList", linkList)
|
||||||
|
// 找所有的虚线边
|
||||||
|
const dashedEdgeList = linkList.filter((item) => {
|
||||||
|
return item.lineStyle.type === "dashed"
|
||||||
|
})
|
||||||
|
console.log("dashedEdgeList", dashedEdgeList)
|
||||||
|
// 从所有的虚线边中找到连接了clickEdgeTarget或者clickEdgeSource的边
|
||||||
|
const connectEdgeList = dashedEdgeList.filter((item) => {
|
||||||
|
return item.source === clickEdgeSource
|
||||||
|
|| item.target === clickEdgeTarget
|
||||||
|
|| item.source === clickEdgeTarget
|
||||||
|
|| item.target === clickEdgeSource
|
||||||
|
})
|
||||||
|
console.log("connectEdgeList", connectEdgeList)
|
||||||
|
let groupIdList = []
|
||||||
|
// 遍历边的source和target,找到所有的groupId
|
||||||
|
connectEdgeList.forEach((item) => {
|
||||||
|
if(!groupIdList.includes(item.source)) {
|
||||||
|
groupIdList.push(item.source)
|
||||||
|
}
|
||||||
|
if(!groupIdList.includes(item.target)) {
|
||||||
|
groupIdList.push(item.target)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 只取前三个值
|
||||||
|
groupIdList = groupIdList.slice(0, 3)
|
||||||
|
console.log("打印点击边时,与目标节点和源节点相连的其他节点id",groupIdList);
|
||||||
|
emit("click:edge", data, groupIdList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,21 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<div class="active-sign" :style="{ left: `${currentPosition}px` }">
|
<el-tooltip
|
||||||
<div class="active-needle"></div>
|
:content="TansTimestamp(timeList[timeList.length - 1], 'YYYY.MM.DD HH:mm:ss')"
|
||||||
|
placement="bottom"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div class="active-needle" :style="{ left: `${lastPosition}px` }"></div>
|
||||||
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
:content="TansTimestamp(currentTime, 'YYYY.MM.DD HH:mm:ss')"
|
:content="TansTimestamp(currentTime, 'YYYY.MM.DD HH:mm:ss')"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
effect="light"
|
effect="light"
|
||||||
>
|
>
|
||||||
<div class="timeLine-point" @pointerdown.stop="handlePointPointerDown"></div>
|
<div class="timeLine-point" :style="{ left: `${currentPosition}px` }" @pointerdown.stop="handlePointPointerDown"></div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="time">{{ TansTimestamp(endTime, "YYYY.MM.DD HH:mm:ss") }}</div>
|
<div class="time">{{ TansTimestamp(endTime, "YYYY.MM.DD HH:mm:ss") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="current-time-display">
|
<div class="current-time-display">
|
||||||
|
|
@ -61,6 +65,21 @@ const socialGroupsStore = useSocialGroupsStore()
|
||||||
const { communityDetailNodeList } = storeToRefs(socialGroupsStore)
|
const { communityDetailNodeList } = storeToRefs(socialGroupsStore)
|
||||||
const { timeList } = storeToRefs(socialGroupsStore)
|
const { timeList } = storeToRefs(socialGroupsStore)
|
||||||
const { curHighlightUserIdList } = storeToRefs(socialGroupsStore)
|
const { curHighlightUserIdList } = storeToRefs(socialGroupsStore)
|
||||||
|
// 添加对curSelecedGroupIds的监听,用于检测列表项切换
|
||||||
|
const { curSelecedGroupIds } = storeToRefs(socialGroupsStore)
|
||||||
|
|
||||||
|
|
||||||
|
// 添加对curSelecedGroupIds的watch,确保切换列表项时重置时间轴
|
||||||
|
watch(curSelecedGroupIds, (newIds) => {
|
||||||
|
if (newIds && newIds.length > 0) {
|
||||||
|
// 重置时间轴位置到起点
|
||||||
|
currentPosition.value = 0;
|
||||||
|
currentTime.value = new Date("2024-05-16 16:56:04");
|
||||||
|
// 重新开始自动播放
|
||||||
|
pause(); // 先停止可能正在运行的计时器
|
||||||
|
play(); // 重新开始播放
|
||||||
|
}
|
||||||
|
}, { deep: true })
|
||||||
// 用于监听是从 列表 点进来的还是从 边 点进来的
|
// 用于监听是从 列表 点进来的还是从 边 点进来的
|
||||||
const { clickEvent } = storeToRefs(socialGroupsStore)
|
const { clickEvent } = storeToRefs(socialGroupsStore)
|
||||||
|
|
||||||
|
|
@ -68,6 +87,7 @@ const chartsData = ref({})
|
||||||
|
|
||||||
const emit = defineEmits(["click:goback"])
|
const emit = defineEmits(["click:goback"])
|
||||||
const handleGoback = () => {
|
const handleGoback = () => {
|
||||||
|
pause()
|
||||||
emit("click:goback", "CommunityNode")
|
emit("click:goback", "CommunityNode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,6 +120,48 @@ const startTime = ref(new Date("2024-05-16 16:56:04"))
|
||||||
const endTime = ref(new Date("2024-05-23 10:16:56"))
|
const endTime = ref(new Date("2024-05-23 10:16:56"))
|
||||||
const currentTime = ref(new Date("2024-05-16 16:56:04")) // 当前选中的时间
|
const currentTime = ref(new Date("2024-05-16 16:56:04")) // 当前选中的时间
|
||||||
const currentPosition = ref(0) // 初始位置(轴长度的一半)
|
const currentPosition = ref(0) // 初始位置(轴长度的一半)
|
||||||
|
const lastPosition = ref(0) // 时间列表最后的时间点的位置
|
||||||
|
const isPlaying = ref(false) // 是否自动播放
|
||||||
|
let playTimer = null
|
||||||
|
|
||||||
|
// 自动播放控制
|
||||||
|
const play = () => {
|
||||||
|
if (isPlaying.value) return
|
||||||
|
isPlaying.value = true
|
||||||
|
playTimer = setInterval(() => {
|
||||||
|
// 步进像素
|
||||||
|
const step = 4 // 每次移动4px,可根据需要调整速度
|
||||||
|
if (currentPosition.value >= axisWidth) {
|
||||||
|
pause()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentPosition.value = Math.min(axisWidth, currentPosition.value + step)
|
||||||
|
currentTime.value = getTimeFromPosition(currentPosition.value)
|
||||||
|
sendTimeChangeRequest()
|
||||||
|
}, 300) // 每300ms移动一次
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送请求逻辑封装
|
||||||
|
const sendTimeChangeRequest = () => {
|
||||||
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
if (socialGroupsStore.curRelationId == "") {
|
||||||
|
socialGroupsStore.initGraphCommunityDetailNode(socialGroupsStore.curSelecedGroupIds, currentTimes)
|
||||||
|
} else {
|
||||||
|
socialGroupsStore.initGraphCommunityDetailNode(
|
||||||
|
socialGroupsStore.curSelecedGroupIds,
|
||||||
|
currentTimes,
|
||||||
|
socialGroupsStore.curRelationId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pause = () => {
|
||||||
|
isPlaying.value = false
|
||||||
|
if (playTimer) {
|
||||||
|
clearInterval(playTimer)
|
||||||
|
playTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const axisRef = ref(null)
|
const axisRef = ref(null)
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
|
|
@ -132,18 +194,16 @@ watch(timeList, (newList) => {
|
||||||
// watch来监听timeList变化并设置初始值
|
// watch来监听timeList变化并设置初始值
|
||||||
watch(timePointsWithPositions, (newTimePoints) => {
|
watch(timePointsWithPositions, (newTimePoints) => {
|
||||||
if (newTimePoints && newTimePoints.length > 0) {
|
if (newTimePoints && newTimePoints.length > 0) {
|
||||||
const lastTimePoint = newTimePoints[newTimePoints.length - 1]
|
// 始终将currentPosition设置为0(时间轴起点)
|
||||||
currentTime.value = lastTimePoint.time
|
currentPosition.value = 0;
|
||||||
currentPosition.value = lastTimePoint.position
|
currentTime.value = "2024-05-16 16:56:04"
|
||||||
|
lastPosition.value = newTimePoints[newTimePoints.length - 1].position
|
||||||
// 触发图更新
|
// 触发图更新
|
||||||
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
socialGroupsStore.initGraphCommunityDetailNode(socialGroupsStore.curSelecedGroupIds, currentTimes)
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
// 判断当前时间点是否激活
|
|
||||||
/* const isTimeSignActive = (timeStr) => {
|
|
||||||
return new Date(timeStr).getTime() === currentTime.value.getTime()
|
|
||||||
} */
|
|
||||||
|
|
||||||
// 添加时间点点击事件处理函数
|
// 添加时间点点击事件处理函数
|
||||||
const handleTimePointClick = (timeStr) => {
|
const handleTimePointClick = (timeStr) => {
|
||||||
|
|
@ -167,6 +227,7 @@ const getTimeFromPosition = (position) => {
|
||||||
// 指针按下事件
|
// 指针按下事件
|
||||||
const handlePointerDown = (e) => {
|
const handlePointerDown = (e) => {
|
||||||
if (e.target.classList.contains("timeLine-point")) return
|
if (e.target.classList.contains("timeLine-point")) return
|
||||||
|
pause() // 拖动或点击时暂停自动播放
|
||||||
const rect = axisRef.value.getBoundingClientRect()
|
const rect = axisRef.value.getBoundingClientRect()
|
||||||
const position = Math.max(0, Math.min(axisWidth, e.clientX - rect.left))
|
const position = Math.max(0, Math.min(axisWidth, e.clientX - rect.left))
|
||||||
|
|
||||||
|
|
@ -247,6 +308,10 @@ const initChart = async () => {
|
||||||
else if (interactionTime > 30) return 10
|
else if (interactionTime > 30) return 10
|
||||||
else return 1
|
else return 1
|
||||||
}
|
}
|
||||||
|
// 添加边唯一标识集合,用于检测重复边
|
||||||
|
const edgeSet = new Set()
|
||||||
|
// 使用Map存储边,方便后续查找和更新
|
||||||
|
const edgeMap = new Map()
|
||||||
if (!Object.keys(socialGroupsStore.communityDetailNodeList).length) return
|
if (!Object.keys(socialGroupsStore.communityDetailNodeList).length) return
|
||||||
// 先获取到所有节点
|
// 先获取到所有节点
|
||||||
socialGroupsStore.communityAllNodeList.forEach((item) => {
|
socialGroupsStore.communityAllNodeList.forEach((item) => {
|
||||||
|
|
@ -260,17 +325,40 @@ const initChart = async () => {
|
||||||
})
|
})
|
||||||
Object.entries(socialGroupsStore.communityDetailNodeList).forEach(([parentId, children]) => {
|
Object.entries(socialGroupsStore.communityDetailNodeList).forEach(([parentId, children]) => {
|
||||||
children.forEach((child) => {
|
children.forEach((child) => {
|
||||||
links.push({
|
// 生成边的唯一标识符,与方向无关
|
||||||
|
const edgeKey = [parentId, child.id].sort().join('-')
|
||||||
|
// 检查边是否已存在
|
||||||
|
if (!edgeSet.has(edgeKey)) {
|
||||||
|
edgeSet.add(edgeKey)
|
||||||
|
const newEdge = {
|
||||||
source: parentId,
|
source: parentId,
|
||||||
target: child.id,
|
target: child.id,
|
||||||
edge: child.isHidden ? 1 : 0,
|
edge: child.isHidden ? 1 : 0,
|
||||||
interactionTimes: child.interactionTime,
|
interactionTimes: child.interactionTime,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: child.isHidden ? 4 : edgeWidth(child.interactionTime),
|
width: child.isHidden ? 7 : edgeWidth(child.interactionTime),
|
||||||
color: child.isHidden ? "#FF5E00" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
color: child.isHidden ? "#FF5E00" : "#37ACD7", // 无互动=橙色,有互动=蓝色
|
||||||
type: child.isHidden ? "dashed" : "solid" // 无互动=实线,有互动=虚线
|
type: child.isHidden ? "dashed" : "solid", // 无互动=虚线,有互动=实线
|
||||||
|
opacity: child.isHidden ? 1 : 0.5,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
links.push(newEdge)
|
||||||
|
edgeMap.set(edgeKey, newEdge)
|
||||||
|
} else {
|
||||||
|
// 边已存在,检查isHidden状态是否变化
|
||||||
|
const existingEdge = edgeMap.get(edgeKey)
|
||||||
|
// 只在isHidden从false变为true时更新样式
|
||||||
|
if (!existingEdge.edge && child.isHidden) {
|
||||||
|
existingEdge.edge = 1
|
||||||
|
existingEdge.lineStyle = {
|
||||||
|
width: 7,
|
||||||
|
color: "#FF5E00",
|
||||||
|
type: "dashed",
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
@ -443,7 +531,7 @@ const initChart = async () => {
|
||||||
categories: categories,
|
categories: categories,
|
||||||
force: {
|
force: {
|
||||||
edgeLength: 2500,
|
edgeLength: 2500,
|
||||||
repulsion: 4000,
|
repulsion: 20000,
|
||||||
gravity: 0.1,
|
gravity: 0.1,
|
||||||
friction: 0.02,
|
friction: 0.02,
|
||||||
coolingFactor: 0.1
|
coolingFactor: 0.1
|
||||||
|
|
@ -499,8 +587,6 @@ const highLightUserNodes = (newHiglightUserIdList) => {
|
||||||
//等待所有节点添加完毕后再查找
|
//等待所有节点添加完毕后再查找
|
||||||
newHiglightUserIdList.forEach((id) => {
|
newHiglightUserIdList.forEach((id) => {
|
||||||
const index = chartsData.value.nodes.findIndex((node) => node.id === id)
|
const index = chartsData.value.nodes.findIndex((node) => node.id === id)
|
||||||
console.log(index);
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
chart.dispatchAction({
|
chart.dispatchAction({
|
||||||
type: "highlight",
|
type: "highlight",
|
||||||
|
|
@ -520,6 +606,7 @@ onMounted(() => {
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
})
|
||||||
highLightUserNodes()
|
highLightUserNodes()
|
||||||
|
play()
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -642,11 +729,8 @@ onMounted(() => {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.active-sign {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
.active-needle {
|
.active-needle {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
|
|
@ -657,6 +741,7 @@ onMounted(() => {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
.timeLine-point {
|
.timeLine-point {
|
||||||
|
z-index: 2;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
@ -687,8 +772,6 @@ onMounted(() => {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.current-time-display {
|
.current-time-display {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,17 @@ const handleClickNode = async (nodeInfo) => {
|
||||||
socialGroupsStore.curRelationId = ""
|
socialGroupsStore.curRelationId = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickEdge = async (edgeInfo) => {
|
const handleClickEdge = async (edgeInfo, groupIdList) => {
|
||||||
console.log("点击边");
|
console.log("点击边");
|
||||||
socialGroupsStore.curComponent = "detailNode"
|
socialGroupsStore.curComponent = "detailNode"
|
||||||
socialGroupsStore.clickEvent = "edge"
|
socialGroupsStore.clickEvent = "edge"
|
||||||
socialGroupsStore.curRelationId = ""
|
socialGroupsStore.curRelationId = ""
|
||||||
// 先设置socialGroupsStore.clickEdgeTimeList
|
// 先设置socialGroupsStore.clickEdgeTimeList
|
||||||
await socialGroupsStore.getClickEdgeTimeList([edgeInfo.source, edgeInfo.target])
|
// await socialGroupsStore.getClickEdgeTimeList([edgeInfo.source, edgeInfo.target])
|
||||||
|
await socialGroupsStore.getClickEdgeTimeList(groupIdList)
|
||||||
const lastTime = socialGroupsStore.timeList[socialGroupsStore.timeList.length - 1]
|
const lastTime = socialGroupsStore.timeList[socialGroupsStore.timeList.length - 1]
|
||||||
await socialGroupsStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target], lastTime)
|
// await socialGroupsStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target], lastTime)
|
||||||
|
await socialGroupsStore.initGraphCommunityDetailNode(groupIdList, lastTime)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
>
|
>
|
||||||
<div class="group-type" v-if="group.list.length > 2 && group?.interactivity != ' '">
|
<div class="group-type" v-if="group.list.length > 2 && group?.interactivity != ' '">
|
||||||
<img
|
<img
|
||||||
src="@/assets/images/linkPrediction/title/group-item-title.png"
|
src="@/assets/images/linkPrediction/icon/top-icon.png"
|
||||||
class="group-type-back"
|
class="group-type-back"
|
||||||
/>
|
/>
|
||||||
<div class="group-type-content">TOP{{ index+1 }}</div>
|
<div class="group-type-content">TOP{{ index+1 }}</div>
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,7 @@ const handleSelectedUserGroup = (group) => {
|
||||||
console.log("点击列表group:",group)
|
console.log("点击列表group:",group)
|
||||||
const groupIds = group.list.map((item)=>item.groupId)
|
const groupIds = group.list.map((item)=>item.groupId)
|
||||||
socialGroupsStore.curRelationId = group.relationId //保存当前点击的relationid,为了区分到底是从哪点进二级界面的
|
socialGroupsStore.curRelationId = group.relationId //保存当前点击的relationid,为了区分到底是从哪点进二级界面的
|
||||||
const length = group.timeList.length
|
socialGroupsStore.initGraphCommunityDetailNode(groupIds, "2024-05-16 16:56:04", group.relationId)
|
||||||
const lastTime = group.timeList[length - 1]
|
|
||||||
socialGroupsStore.initGraphCommunityDetailNode(groupIds, lastTime, group.relationId)
|
|
||||||
socialGroupsStore.setTimeList(group.timeList)
|
socialGroupsStore.setTimeList(group.timeList)
|
||||||
socialGroupsStore.getSocialGroupPostListByRelationId(group.relationId)
|
socialGroupsStore.getSocialGroupPostListByRelationId(group.relationId)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user