社团紧密关系-点边

This commit is contained in:
duanhao 2025-07-31 17:06:11 +08:00
parent 960130fba1
commit 2a15f5a5f9
5 changed files with 194 additions and 81 deletions

View File

@ -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, //10 edge: communityNei.isHidden ? 1 : 0, //10
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)
// 线clickEdgeTargetclickEdgeSource
const connectEdgeList = dashedEdgeList.filter((item) => {
return item.source === clickEdgeSource
|| item.target === clickEdgeTarget
|| item.source === clickEdgeTarget
|| item.target === clickEdgeSource
})
console.log("connectEdgeList", connectEdgeList)
let groupIdList = []
// sourcetargetgroupId
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)
} }
} }
}) })

View File

@ -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)
// curSelecedGroupIdswatch
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) => {
// watchtimeList // watchtimeList
watch(timePointsWithPositions, (newTimePoints) => { watch(timePointsWithPositions, (newTimePoints) => {
if (newTimePoints && newTimePoints.length > 0) { if (newTimePoints && newTimePoints.length > 0) {
const lastTimePoint = newTimePoints[newTimePoints.length - 1] // currentPosition0
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)
// isHiddenfalsetrue
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;

View File

@ -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)
} }

View File

@ -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>

View File

@ -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)
}; };