形成隐关系的时间轴定点完成
This commit is contained in:
parent
450852ebdf
commit
108fe6a71d
BIN
src/assets/images/linkPrediction/icon/triangle-piont.png
Normal file
BIN
src/assets/images/linkPrediction/icon/triangle-piont.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 303 B |
|
|
@ -14,7 +14,7 @@ export function getInteractionCommunityNodes() {
|
||||||
export function getInteractionCommunityDetailNodes(ids, relationId, time = "2024-05-16 16:56:04") {
|
export function getInteractionCommunityDetailNodes(ids, relationId, time = "2024-05-16 16:56:04") {
|
||||||
if (relationId != -1) {
|
if (relationId != -1) {
|
||||||
return http.get(
|
return http.get(
|
||||||
`/linkPrediction/interaction/community_detail?groupIds=${ids}&relationId=${relationId}&dateTime=${time}`
|
`/linkPrediction/interaction/community_detail?groupIds=${ids}&dateTime=${time}&relationId=${relationId}`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return http.get(`/linkPrediction/interaction/community_detail?groupIds=${ids}&dateTime=${time}`)
|
return http.get(`/linkPrediction/interaction/community_detail?groupIds=${ids}&dateTime=${time}`)
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ import {
|
||||||
getSocialCommunityList,
|
getSocialCommunityList,
|
||||||
getSocialCommunityStatistics,
|
getSocialCommunityStatistics,
|
||||||
getSocialCommunityDetailNodes,
|
getSocialCommunityDetailNodes,
|
||||||
getSocialPostListByRelationId,
|
getSocialPostListByRelationId
|
||||||
getInteractionCommunityDetailFromUserGroup
|
|
||||||
} from "@/service/api/linkPrediction"
|
} from "@/service/api/linkPrediction"
|
||||||
|
|
||||||
import defaultAvatar from "@/assets/images/avatar/default.png"
|
import defaultAvatar from "@/assets/images/avatar/default.png"
|
||||||
|
|
@ -33,6 +32,7 @@ export const useCharacterInteractionStore = defineStore("characterInteraction",
|
||||||
communityDetailNodeList: [], //节点用户列表
|
communityDetailNodeList: [], //节点用户列表
|
||||||
timeList: [],
|
timeList: [],
|
||||||
predictionUserIds: [], //包含从用户组选择的用户id或者是点击某个社团或者连边后,所需要高亮的所有用户的id
|
predictionUserIds: [], //包含从用户组选择的用户id或者是点击某个社团或者连边后,所需要高亮的所有用户的id
|
||||||
|
curRelationId: "",
|
||||||
anlysisList: [
|
anlysisList: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
@ -175,13 +175,24 @@ export const useCharacterInteractionStore = defineStore("characterInteraction",
|
||||||
if (res.code != 200) return
|
if (res.code != 200) return
|
||||||
this.communityNodeList = res.data
|
this.communityNodeList = res.data
|
||||||
},
|
},
|
||||||
async initGraphCommunityDetailNode(ids, relationId = -1, time = "2024-05-16 16:56:04") {
|
async initGraphCommunityDetailNode(ids, time = "2024-05-16 16:56:04", relationId = -1) {
|
||||||
this.curSelecedGroupIds = ids
|
this.curSelecedGroupIds = ids
|
||||||
const res = await getInteractionCommunityDetailNodes(ids, relationId, time)
|
const res = await getInteractionCommunityDetailNodes(ids, relationId, time)
|
||||||
if (res.code != 200) return
|
if (res.code != 200) return
|
||||||
|
const customStatisticsObj = Object.assign({}, res.data.communityStatistics)
|
||||||
|
//计算两个用户是否同属与同一个社团
|
||||||
|
|
||||||
|
if (
|
||||||
|
customStatisticsObj.groupCount == null &&
|
||||||
|
customStatisticsObj.hiddenInteractionCount == null
|
||||||
|
) {
|
||||||
|
customStatisticsObj.hiddenInteractionCount = 1
|
||||||
|
customStatisticsObj.groupCount = ids[0] === ids[1] ? 1 : 2
|
||||||
|
}
|
||||||
|
|
||||||
this.statisticsDetailList = this.statisticsDetailList.map((item) => ({
|
this.statisticsDetailList = this.statisticsDetailList.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
count: res.data.communityStatistics[item.key]
|
count: customStatisticsObj[item.key]
|
||||||
}))
|
}))
|
||||||
this.communityDetailNodeRelation = res.data.userRelation
|
this.communityDetailNodeRelation = res.data.userRelation
|
||||||
this.communityDetailNodeList = res.data.userList
|
this.communityDetailNodeList = res.data.userList
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,12 @@ const handleSelectedUserGroup = (group) => {
|
||||||
interactionStore.curComponent = "detailNode"
|
interactionStore.curComponent = "detailNode"
|
||||||
interactionStore.timeList = group.timeList //保存从用户列表选择的用户组,为了显示这两个用户交互的时间切片
|
interactionStore.timeList = group.timeList //保存从用户列表选择的用户组,为了显示这两个用户交互的时间切片
|
||||||
interactionStore.userIds = group.list.map((user) => user.userId) //保存选中的用户组的所有用户id,为了高亮二级关系图的用户
|
interactionStore.userIds = group.list.map((user) => user.userId) //保存选中的用户组的所有用户id,为了高亮二级关系图的用户
|
||||||
|
interactionStore.curRelationId = group.relationId //保存当前点击的relationid,为了区分到底是从哪点进二级界面的
|
||||||
interactionStore.initInteractionPostList(group.relationId)
|
interactionStore.initInteractionPostList(group.relationId)
|
||||||
interactionStore.initGraphCommunityDetailNode(
|
interactionStore.initGraphCommunityDetailNode(
|
||||||
group.list.map((item) => item.groupId),
|
group.list.map((item) => item.groupId),
|
||||||
group.relationId,
|
"2024-05-16 16:56:04",
|
||||||
"2024-05-16 16:56:04"
|
group.relationId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,36 @@
|
||||||
<div class="time-axis">
|
<div class="time-axis">
|
||||||
<div class="time">{{ TansTimestamp(startTime, "YYYY.MM.DD HH:mm:ss") }}</div>
|
<div class="time">{{ TansTimestamp(startTime, "YYYY.MM.DD HH:mm:ss") }}</div>
|
||||||
<div class="axis" ref="axisRef" @pointerdown="handlePointerDown">
|
<div class="axis" ref="axisRef" @pointerdown="handlePointerDown">
|
||||||
<div
|
<el-tooltip
|
||||||
class="time-section"
|
|
||||||
v-for="time in timeList"
|
v-for="time in timeList"
|
||||||
:key="time"
|
:key="time"
|
||||||
:style="{ left: getTimeSectionLeft(time) + 5 + 'px' }"
|
:content="TansTimestamp(time, 'YYYY.MM.DD HH:mm:ss')"
|
||||||
></div>
|
placement="bottom"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div class="time-section" :style="{ left: getTimeSectionLeft(time) + 5 + 'px' }"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
<div class="progress-bar" :style="trackStyle"></div>
|
<div class="progress-bar" :style="trackStyle"></div>
|
||||||
<div class="active-sign" :style="{ left: `${currentPosition}px` }">
|
<div class="active-sign">
|
||||||
<div class="active-needle"></div>
|
<el-tooltip
|
||||||
|
:content="TansTimestamp(timeList[timeList.length - 1], 'YYYY.MM.DD HH:mm:ss')"
|
||||||
|
placement="bottom"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div class="active-needle" :style="showHidden"></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"
|
||||||
|
@pointerdown.stop="handlePointPointerDown"
|
||||||
|
:style="{ left: `${currentPosition}px` }"
|
||||||
|
></div>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -100,6 +115,16 @@ const getTimeSectionLeft = computed(() => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 让 active-needle 标定在 timeList 最后一个时间点
|
||||||
|
const showHidden = computed(() => {
|
||||||
|
if (!timeList.value || timeList.value.length === 0) return {}
|
||||||
|
// 取最后一个时间点
|
||||||
|
const lastTime = timeList.value[timeList.value.length - 1]
|
||||||
|
// 计算 left 位置
|
||||||
|
const left = getTimeSectionLeft.value(lastTime) + 5 // +5px 保持和 time-section 对齐
|
||||||
|
return { left: `${left}px` }
|
||||||
|
})
|
||||||
|
|
||||||
// 根据位置计算时间
|
// 根据位置计算时间
|
||||||
const getTimeFromPosition = (position) => {
|
const getTimeFromPosition = (position) => {
|
||||||
const ratio = Math.max(0, Math.min(1, position / axisWidth))
|
const ratio = Math.max(0, Math.min(1, position / axisWidth))
|
||||||
|
|
@ -119,7 +144,15 @@ const handlePointerDown = (e) => {
|
||||||
|
|
||||||
// 点击后输出当前时间
|
// 点击后输出当前时间
|
||||||
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
interactionStore.initGraphCommunityDetailNode(interactionStore.curSelecedGroupIds, currentTimes)
|
if (interactionStore.curRelationId == "") {
|
||||||
|
interactionStore.initGraphCommunityDetailNode(interactionStore.curSelecedGroupIds, currentTimes)
|
||||||
|
} else {
|
||||||
|
interactionStore.initGraphCommunityDetailNode(
|
||||||
|
interactionStore.curSelecedGroupIds,
|
||||||
|
currentTimes,
|
||||||
|
interactionStore.curRelationId
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 时间点指针按下事件
|
// 时间点指针按下事件
|
||||||
|
|
@ -144,7 +177,18 @@ const handlePointPointerDown = (e) => {
|
||||||
isDragging.value = false
|
isDragging.value = false
|
||||||
// 拖动结束时输出当前时间
|
// 拖动结束时输出当前时间
|
||||||
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
interactionStore.initGraphCommunityDetailNode(interactionStore.curSelecedGroupIds, currentTimes)
|
if (interactionStore.curRelationId == "") {
|
||||||
|
interactionStore.initGraphCommunityDetailNode(
|
||||||
|
interactionStore.curSelecedGroupIds,
|
||||||
|
currentTimes
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
interactionStore.initGraphCommunityDetailNode(
|
||||||
|
interactionStore.curSelecedGroupIds,
|
||||||
|
currentTimes,
|
||||||
|
interactionStore.curRelationId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
document.removeEventListener("pointermove", handlePointerMove)
|
document.removeEventListener("pointermove", handlePointerMove)
|
||||||
document.removeEventListener("pointerup", handlePointerUp)
|
document.removeEventListener("pointerup", handlePointerUp)
|
||||||
|
|
@ -518,7 +562,7 @@ onMounted(() => {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
background-image: url("@/assets/images/point.png");
|
background-image: url("@/assets/images/point.png");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
bottom: 1px;
|
bottom: -8px;
|
||||||
left: -11px;
|
left: -11px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,13 @@ const props = defineProps({
|
||||||
//点击社团节点
|
//点击社团节点
|
||||||
const handleClickNode = async (nodeInfo) => {
|
const handleClickNode = async (nodeInfo) => {
|
||||||
interactionStore.curComponent = "detailNode"
|
interactionStore.curComponent = "detailNode"
|
||||||
|
interactionStore.curRelationId = ""
|
||||||
interactionStore.initGraphCommunityDetailNode([nodeInfo.id])
|
interactionStore.initGraphCommunityDetailNode([nodeInfo.id])
|
||||||
}
|
}
|
||||||
//点击社团边
|
//点击社团边
|
||||||
const handleClickEdge = (edgeInfo) => {
|
const handleClickEdge = (edgeInfo) => {
|
||||||
interactionStore.curComponent = "detailNode"
|
interactionStore.curComponent = "detailNode"
|
||||||
|
interactionStore.curRelationId = ""
|
||||||
interactionStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target])
|
interactionStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="group-type-content">TOP{{ group.rank }}</div>
|
<div class="group-type-content">TOP{{ group.rank }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-list-item" v-for="child in group.list" :key="child.id">
|
<div class="user-list-item" v-for="child in group.list" :key="child.id">
|
||||||
<img :src="getAvatarUrl(child.avatarData)" alt="" class="avatar" />
|
<img :src="getAvatarUrl(child.avatarData) ?? defaultAvatar" class="avatar" />
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<div class="username">{{ child.userName }}</div>
|
<div class="username">{{ child.userName }}</div>
|
||||||
<div class="userState">
|
<div class="userState">
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits, ref } from "vue"
|
import { defineProps, defineEmits, ref } from "vue"
|
||||||
|
import defaultAvatar from "@/assets/images/avatar/default.png"
|
||||||
import { getAvatarUrl } from "@/utils/transform"
|
import { getAvatarUrl } from "@/utils/transform"
|
||||||
const curUserGroupIndex = ref(0)
|
const curUserGroupIndex = ref(0)
|
||||||
const emit = defineEmits(["click:selectedGroup"])
|
const emit = defineEmits(["click:selectedGroup"])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user