社团紧密关系-点边

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"
let chart = null
let linkList = null
const emit = defineEmits(["click:node", "click:edge"])
const statisticsList = inject("statisticsList");
const communityNodeList = inject("communityNodeList");
@ -49,16 +50,16 @@ const initChart = async () => {
target: communityNei.id,
edge: communityNei.isHidden ? 1 : 0, //10
lineStyle: {
width: communityNei.isHidden ? 4 : 1, // =线=
width: communityNei.isHidden ? 7 : 1, // =线=
color: communityNei.isHidden ? "#FF5E00" : "#37ACD7", // ==
type: communityNei.isHidden ? "dashed" : "solid", // =线=线
dashArray: [2, 1] // 2线1
opacity: communityNei.isHidden ? 1 : 0.5,
}
})
edgeSet.add(key)
})
})
linkList = links
const data = { nodes, links }
const categories = [
@ -241,7 +242,36 @@ const handleClickNode = () => {
} else if (params.dataType == "edge") {
const { data } = params
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,16 +31,20 @@
>
</div>
</el-tooltip>
<div class="active-sign" :style="{ left: `${currentPosition}px` }">
<div class="active-needle"></div>
<el-tooltip
:content="TansTimestamp(currentTime, 'YYYY.MM.DD HH:mm:ss')"
placement="bottom"
effect="light"
>
<div class="timeLine-point" @pointerdown.stop="handlePointPointerDown"></div>
</el-tooltip>
</div>
<el-tooltip
: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
:content="TansTimestamp(currentTime, 'YYYY.MM.DD HH:mm:ss')"
placement="bottom"
effect="light"
>
<div class="timeLine-point" :style="{ left: `${currentPosition}px` }" @pointerdown.stop="handlePointPointerDown"></div>
</el-tooltip>
</div>
<div class="time">{{ TansTimestamp(endTime, "YYYY.MM.DD HH:mm:ss") }}</div>
</div>
@ -61,6 +65,21 @@ const socialGroupsStore = useSocialGroupsStore()
const { communityDetailNodeList } = storeToRefs(socialGroupsStore)
const { timeList } = 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)
@ -68,6 +87,7 @@ const chartsData = ref({})
const emit = defineEmits(["click:goback"])
const handleGoback = () => {
pause()
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 currentTime = ref(new Date("2024-05-16 16:56:04")) //
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 isDragging = ref(false)
@ -132,18 +194,16 @@ watch(timeList, (newList) => {
// watchtimeList
watch(timePointsWithPositions, (newTimePoints) => {
if (newTimePoints && newTimePoints.length > 0) {
const lastTimePoint = newTimePoints[newTimePoints.length - 1]
currentTime.value = lastTimePoint.time
currentPosition.value = lastTimePoint.position
// currentPosition0
currentPosition.value = 0;
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")
socialGroupsStore.initGraphCommunityDetailNode(socialGroupsStore.curSelecedGroupIds, currentTimes)
}
}, { immediate: true })
//
/* const isTimeSignActive = (timeStr) => {
return new Date(timeStr).getTime() === currentTime.value.getTime()
} */
//
const handleTimePointClick = (timeStr) => {
@ -167,6 +227,7 @@ const getTimeFromPosition = (position) => {
//
const handlePointerDown = (e) => {
if (e.target.classList.contains("timeLine-point")) return
pause() //
const rect = axisRef.value.getBoundingClientRect()
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 return 1
}
//
const edgeSet = new Set()
// 使Map便
const edgeMap = new Map()
if (!Object.keys(socialGroupsStore.communityDetailNodeList).length) return
//
socialGroupsStore.communityAllNodeList.forEach((item) => {
@ -260,17 +325,40 @@ const initChart = async () => {
})
Object.entries(socialGroupsStore.communityDetailNodeList).forEach(([parentId, children]) => {
children.forEach((child) => {
links.push({
source: parentId,
target: child.id,
edge: child.isHidden ? 1 : 0,
interactionTimes: child.interactionTime,
lineStyle: {
width: child.isHidden ? 4 : edgeWidth(child.interactionTime),
color: child.isHidden ? "#FF5E00" : "#37ACD7", // ==
type: child.isHidden ? "dashed" : "solid" // =线=线
//
const edgeKey = [parentId, child.id].sort().join('-')
//
if (!edgeSet.has(edgeKey)) {
edgeSet.add(edgeKey)
const newEdge = {
source: parentId,
target: child.id,
edge: child.isHidden ? 1 : 0,
interactionTimes: child.interactionTime,
lineStyle: {
width: child.isHidden ? 7 : edgeWidth(child.interactionTime),
color: child.isHidden ? "#FF5E00" : "#37ACD7", // ==
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,
force: {
edgeLength: 2500,
repulsion: 4000,
repulsion: 20000,
gravity: 0.1,
friction: 0.02,
coolingFactor: 0.1
@ -499,8 +587,6 @@ const highLightUserNodes = (newHiglightUserIdList) => {
//
newHiglightUserIdList.forEach((id) => {
const index = chartsData.value.nodes.findIndex((node) => node.id === id)
console.log(index);
if (index != -1) {
chart.dispatchAction({
type: "highlight",
@ -520,6 +606,7 @@ onMounted(() => {
}, 0)
})
highLightUserNodes()
play()
})
</script>
@ -642,52 +729,48 @@ onMounted(() => {
height: 30px;
background: transparent;
}
}
.active-sign {
position: relative;
.active-needle {
width: 30px;
height: 34px;
background-image: url("@/assets/images/point.png");
background-size: cover;
bottom: 1px;
left: -11px;
position: absolute;
}
.timeLine-point {
z-index: 2;
.active-needle {
width: 30px;
height: 34px;
background-image: url("@/assets/images/point.png");
background-size: cover;
bottom: 1px;
left: -11px;
position: absolute;
width: 18px;
height: 18px;
background-color: transparent;
border-radius: 50%;
border: 1.6px solid #ffe5a4;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: -6px;
left: -5px;
cursor: pointer;
user-select: none;
will-change: left;
transform: translate3d(0, 0, 0); //
&:hover {
transform: translate3d(0, 0, 0) scale(1.1);
}
.timeLine-point {
width: 18px;
height: 18px;
background-color: transparent;
&:active {
transform: translate3d(0, 0, 0) scale(0.95);
}
&::after {
content: "";
width: 10px;
height: 10px;
background-color: #f9bd25;
border-radius: 50%;
border: 1.6px solid #ffe5a4;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: -6px;
left: -5px;
cursor: pointer;
user-select: none;
will-change: left;
transform: translate3d(0, 0, 0); //
&:hover {
transform: translate3d(0, 0, 0) scale(1.1);
}
&:active {
transform: translate3d(0, 0, 0) scale(0.95);
}
&::after {
content: "";
width: 10px;
height: 10px;
background-color: #f9bd25;
border-radius: 50%;
position: absolute;
}
}
}
}
.current-time-display {

View File

@ -34,15 +34,17 @@ const handleClickNode = async (nodeInfo) => {
socialGroupsStore.curRelationId = ""
}
const handleClickEdge = async (edgeInfo) => {
const handleClickEdge = async (edgeInfo, groupIdList) => {
console.log("点击边");
socialGroupsStore.curComponent = "detailNode"
socialGroupsStore.clickEvent = "edge"
socialGroupsStore.curRelationId = ""
// 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]
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 != ' '">
<img
src="@/assets/images/linkPrediction/title/group-item-title.png"
src="@/assets/images/linkPrediction/icon/top-icon.png"
class="group-type-back"
/>
<div class="group-type-content">TOP{{ index+1 }}</div>

View File

@ -88,9 +88,7 @@ const handleSelectedUserGroup = (group) => {
console.log("点击列表group",group)
const groupIds = group.list.map((item)=>item.groupId)
socialGroupsStore.curRelationId = group.relationId //relationid
const length = group.timeList.length
const lastTime = group.timeList[length - 1]
socialGroupsStore.initGraphCommunityDetailNode(groupIds, lastTime, group.relationId)
socialGroupsStore.initGraphCommunityDetailNode(groupIds, "2024-05-16 16:56:04", group.relationId)
socialGroupsStore.setTimeList(group.timeList)
socialGroupsStore.getSocialGroupPostListByRelationId(group.relationId)
};