Merge branch 'master' of http://172.16.20.1:3000/duanhao/SocialNetworks_duan
This commit is contained in:
commit
450852ebdf
|
|
@ -207,6 +207,10 @@ export const useSocialGroupsStore = defineStore("socialGroups", {
|
||||||
curSelecedGroupIds: [],
|
curSelecedGroupIds: [],
|
||||||
communityDetailNodeList: [],
|
communityDetailNodeList: [],
|
||||||
timeList: [],
|
timeList: [],
|
||||||
|
// 当前需要高亮的用户id
|
||||||
|
curHighlightUserIdList: [],
|
||||||
|
// 记录点击边的时序列表
|
||||||
|
clickEdgeTimeList: [],
|
||||||
statisticsList: [
|
statisticsList: [
|
||||||
{ id: 1, icon: nodePrefix, name: "节点数", key: "nodesCount" },
|
{ id: 1, icon: nodePrefix, name: "节点数", key: "nodesCount" },
|
||||||
{ id: 2, icon: communityPrefix, name: "社团数", key: "groupCount" },
|
{ id: 2, icon: communityPrefix, name: "社团数", key: "groupCount" },
|
||||||
|
|
@ -442,6 +446,19 @@ export const useSocialGroupsStore = defineStore("socialGroups", {
|
||||||
}))
|
}))
|
||||||
this.timeList = res.data.timeList || []
|
this.timeList = res.data.timeList || []
|
||||||
this.communityDetailNodeList = res.data.userRelation
|
this.communityDetailNodeList = res.data.userRelation
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改timeList
|
||||||
|
setTimeList(timeList) {
|
||||||
|
this.timeList = timeList
|
||||||
|
},
|
||||||
|
// 点击边,先获取需要的timeList
|
||||||
|
async getClickEdgeTimeList(ids, time = "2024-05-16 16:56:04") {
|
||||||
|
const res = await getSocialCommunityDetailNodes(ids, time)
|
||||||
|
if (res.code != 200) return
|
||||||
|
console.log("3.获取边中的timelist并设置成它")
|
||||||
|
this.curHighlightUserIdList = res.data.predictNodes
|
||||||
|
this.setTimeList(res.data.timeList)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
persist: true // 开启持久化
|
persist: true // 开启持久化
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,20 @@
|
||||||
<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 class="progress-bar" :style="trackStyle"></div>
|
<div class="progress-bar" :style="trackStyle"></div>
|
||||||
|
<el-tooltip
|
||||||
|
v-for="(time, index) in timePointsWithPositions"
|
||||||
|
:key="index"
|
||||||
|
:content="TansTimestamp(time.time, 'YYYY.MM.DD HH:mm:ss')"
|
||||||
|
placement="bottom"
|
||||||
|
effect="light"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="time-sign"
|
||||||
|
:class="{ active: isTimeSignActive(time.timeStr) }"
|
||||||
|
:style="{ left: `${time.position}px` }"
|
||||||
|
@click="handleTimePointClick(time.timeStr)"
|
||||||
|
></div>
|
||||||
|
</el-tooltip>
|
||||||
<div class="active-sign" :style="{ left: `${currentPosition}px` }">
|
<div class="active-sign" :style="{ left: `${currentPosition}px` }">
|
||||||
<div class="active-needle"></div>
|
<div class="active-needle"></div>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
|
|
@ -37,7 +51,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineEmits, onMounted, ref, onUnmounted, computed, watch } from "vue"
|
import { defineEmits, onMounted, ref, onUnmounted, computed, watch, nextTick } from "vue"
|
||||||
import { TansTimestamp } from "@/utils/transform"
|
import { TansTimestamp } from "@/utils/transform"
|
||||||
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
import nodeHoverImg from "@/assets/images/nodeHover.png"
|
||||||
import * as echarts from "echarts"
|
import * as echarts from "echarts"
|
||||||
|
|
@ -45,6 +59,10 @@ import { storeToRefs } from "pinia"
|
||||||
import { useSocialGroupsStore } from "@/store/llinkPrediction/index"
|
import { useSocialGroupsStore } from "@/store/llinkPrediction/index"
|
||||||
const socialGroupsStore = useSocialGroupsStore()
|
const socialGroupsStore = useSocialGroupsStore()
|
||||||
const { communityDetailNodeList } = storeToRefs(socialGroupsStore)
|
const { communityDetailNodeList } = storeToRefs(socialGroupsStore)
|
||||||
|
const { timeList } = storeToRefs(socialGroupsStore)
|
||||||
|
const { curHighlightUserIdList } = storeToRefs(socialGroupsStore)
|
||||||
|
const chartsData = ref({})
|
||||||
|
|
||||||
const emit = defineEmits(["click:goback"])
|
const emit = defineEmits(["click:goback"])
|
||||||
const handleGoback = () => {
|
const handleGoback = () => {
|
||||||
emit("click:goback", "CommunityNode")
|
emit("click:goback", "CommunityNode")
|
||||||
|
|
@ -58,12 +76,25 @@ watch(
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
// 监听需要高亮的用户id
|
||||||
|
watch(
|
||||||
|
curHighlightUserIdList,
|
||||||
|
(newHiglightUserIdList) => {
|
||||||
|
if(newHiglightUserIdList.length!=0) {
|
||||||
|
nextTick(()=> {
|
||||||
|
console.log(newHiglightUserIdList)
|
||||||
|
highLightUserNodes(newHiglightUserIdList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 时间轴相关数据
|
// 时间轴相关数据
|
||||||
const startTime = ref(new Date("2024-05-16 16:56:04"))
|
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 axisRef = ref(null)
|
const axisRef = ref(null)
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false)
|
||||||
|
|
||||||
|
|
@ -73,6 +104,53 @@ const startTimeMs = startTime.value.getTime()
|
||||||
const endTimeMs = endTime.value.getTime()
|
const endTimeMs = endTime.value.getTime()
|
||||||
const totalDuration = endTimeMs - startTimeMs
|
const totalDuration = endTimeMs - startTimeMs
|
||||||
|
|
||||||
|
// 计算每个时间点的位置
|
||||||
|
const timePointsWithPositions = computed(() => {
|
||||||
|
// 确保 timeList 是数组
|
||||||
|
const list = Array.isArray(timeList.value) ? timeList.value : []
|
||||||
|
if (list.length === 0) return []
|
||||||
|
|
||||||
|
return list.map(timeStr => {
|
||||||
|
const time = new Date(timeStr)
|
||||||
|
const timeMs = time.getTime()
|
||||||
|
const ratio = Math.max(0, Math.min(1, (timeMs - startTimeMs) / totalDuration))
|
||||||
|
const position = ratio * axisWidth
|
||||||
|
return { time, position, timeStr }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(timeList, (newList) => {
|
||||||
|
console.log("🔥 timeList 被更新了:", newList)
|
||||||
|
})
|
||||||
|
|
||||||
|
// watch来监听timeList变化并设置初始值
|
||||||
|
watch(timePointsWithPositions, (newTimePoints) => {
|
||||||
|
if (newTimePoints && newTimePoints.length > 0) {
|
||||||
|
const lastTimePoint = newTimePoints[newTimePoints.length - 1]
|
||||||
|
currentTime.value = lastTimePoint.time
|
||||||
|
currentPosition.value = lastTimePoint.position
|
||||||
|
// 触发图更新
|
||||||
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 判断当前时间点是否激活
|
||||||
|
const isTimeSignActive = (timeStr) => {
|
||||||
|
return new Date(timeStr).getTime() === currentTime.value.getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加时间点点击事件处理函数
|
||||||
|
const handleTimePointClick = (timeStr) => {
|
||||||
|
const time = new Date(timeStr)
|
||||||
|
currentTime.value = time
|
||||||
|
const ratio = (time.getTime() - startTimeMs) / totalDuration
|
||||||
|
currentPosition.value = ratio * axisWidth
|
||||||
|
|
||||||
|
// 触发图更新
|
||||||
|
const currentTimes = TansTimestamp(currentTime.value, "YYYY-MM-DD HH:mm:ss")
|
||||||
|
socialGroupsStore.initGraphCommunityDetailNode(socialGroupsStore.curSelecedGroupIds, currentTimes)
|
||||||
|
}
|
||||||
|
|
||||||
// 根据位置计算时间
|
// 根据位置计算时间
|
||||||
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))
|
||||||
|
|
@ -172,12 +250,55 @@ const initChart = async () => {
|
||||||
interactionTimes: child.interactionTime,
|
interactionTimes: child.interactionTime,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: child.isHidden ? 4 : edgeWidth(child.interactionTime),
|
width: child.isHidden ? 4 : edgeWidth(child.interactionTime),
|
||||||
color: child.isHidden ? "#FF5E00" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
color: child.isHidden ? "#37ACD7" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
||||||
type: child.isHidden ? "dashed" : "solid" // 无互动=实线,有互动=虚线
|
type: child.isHidden ? "dashed" : "solid" // 无互动=实线,有互动=虚线
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if(curHighlightUserIdList.value && curHighlightUserIdList.value.length >= 2) {
|
||||||
|
// 获取timeList的最后一个时间
|
||||||
|
const lastTimeStr = Array.isArray(timeList.value) && timeList.value.length > 0
|
||||||
|
? timeList.value[timeList.value.length - 1]
|
||||||
|
: null;
|
||||||
|
if (lastTimeStr) {
|
||||||
|
const lastTime = new Date(lastTimeStr).getTime();
|
||||||
|
const currentTimeMs = currentTime.value.getTime();
|
||||||
|
|
||||||
|
// 检查当前时间是否大于等于最后一个时间点
|
||||||
|
if (currentTimeMs >= lastTime) {
|
||||||
|
// 在每两个高亮节点之间添加连边
|
||||||
|
const highlightNodes = curHighlightUserIdList.value;
|
||||||
|
for (let i = 0; i < highlightNodes.length; i++) {
|
||||||
|
for (let j = i + 1; j < highlightNodes.length; j++) {
|
||||||
|
const sourceId = `parent_${highlightNodes[i]}`;
|
||||||
|
const targetId = `parent_${highlightNodes[j]}`;
|
||||||
|
|
||||||
|
// 检查这两个节点是否存在于nodes中
|
||||||
|
const sourceExists = nodes.some(n => n.id === sourceId);
|
||||||
|
const targetExists = nodes.some(n => n.id === targetId);
|
||||||
|
|
||||||
|
if (sourceExists && targetExists) {
|
||||||
|
links.push({
|
||||||
|
source: sourceId,
|
||||||
|
target: targetId,
|
||||||
|
edge: 1,
|
||||||
|
interactionTimes: 0,
|
||||||
|
lineStyle: {
|
||||||
|
width: 4,
|
||||||
|
color: "#FF5E00",
|
||||||
|
type: "dashed"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chartsData.value = { links, nodes }
|
||||||
const data = { links, nodes }
|
const data = { links, nodes }
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
|
|
@ -330,7 +451,7 @@ const initChart = async () => {
|
||||||
shadowBlur: 20,
|
shadowBlur: 20,
|
||||||
shadowColor: "#c4a651",
|
shadowColor: "#c4a651",
|
||||||
borderColor: "#fcd267",
|
borderColor: "#fcd267",
|
||||||
borderWidth: 1,
|
borderWidth: 5,
|
||||||
borderType: "solid"
|
borderType: "solid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,6 +467,27 @@ const initChart = async () => {
|
||||||
chart.setOption(option)
|
chart.setOption(option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const highLightUserNodes = (newHiglightUserIdList) => {
|
||||||
|
if (!newHiglightUserIdList) return
|
||||||
|
chart.dispatchAction({
|
||||||
|
type: "downplay",
|
||||||
|
seriesIndex: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
//等待所有节点添加完毕后再查找
|
||||||
|
newHiglightUserIdList.forEach((id) => {
|
||||||
|
const index = chartsData.value.nodes.findIndex((node) => node.id === `parent_${id}`)
|
||||||
|
if (index != -1) {
|
||||||
|
chart.dispatchAction({
|
||||||
|
type: "highlight",
|
||||||
|
dataIndex: index
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart()
|
initChart()
|
||||||
chart.on("legendselectchanged", function (params) {
|
chart.on("legendselectchanged", function (params) {
|
||||||
|
|
@ -354,6 +496,8 @@ onMounted(() => {
|
||||||
chart.resize()
|
chart.resize()
|
||||||
}, 0)
|
}, 0)
|
||||||
})
|
})
|
||||||
|
highLightUserNodes()
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -456,6 +600,30 @@ onMounted(() => {
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
.time-sign{
|
||||||
|
width: 4px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: linear-gradient(180deg, #FEE39E 0%, #F9BD25 100%);
|
||||||
|
z-index: 1;
|
||||||
|
// 新增:添加绝对定位
|
||||||
|
position: absolute;
|
||||||
|
// 调整垂直位置使其居中于时间轴
|
||||||
|
top: -6px;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: -8px;
|
||||||
|
width: 20px;
|
||||||
|
height: 30px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
.active-sign {
|
.active-sign {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
@ -499,6 +667,7 @@ onMounted(() => {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.current-time-display {
|
.current-time-display {
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,13 @@ const handleClickNode = async (nodeInfo) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickEdge = async (edgeInfo) => {
|
const handleClickEdge = async (edgeInfo) => {
|
||||||
|
console.log("点击边");
|
||||||
socialGroupsStore.curComponent = "detailNode"
|
socialGroupsStore.curComponent = "detailNode"
|
||||||
socialGroupsStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target])
|
// 先设置socialGroupsStore.clickEdgeTimeList
|
||||||
|
console.log("1.初始化获取边中的详情节点-initGraphCommunityDetailNode");
|
||||||
|
await socialGroupsStore.initGraphCommunityDetailNode([edgeInfo.source, edgeInfo.target])
|
||||||
|
console.log("2.获取边中的timelist并设置");
|
||||||
|
await socialGroupsStore.getClickEdgeTimeList([edgeInfo.source, edgeInfo.target])
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickGoBack = (currentComponentName) => {
|
const handleClickGoBack = (currentComponentName) => {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@
|
||||||
<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 defaultAvatar from "@/assets/images/avatar/default.png";
|
||||||
|
import { useSocialGroupsStore } from "@/store/llinkPrediction/index";
|
||||||
|
const socialGroupsStore = useSocialGroupsStore()
|
||||||
const curUserGroupIndex = ref(0);
|
const curUserGroupIndex = ref(0);
|
||||||
const emit = defineEmits(["click:selectedGroup"]);
|
const emit = defineEmits(["click:selectedGroup"]);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -59,8 +61,8 @@ const props = defineProps({
|
||||||
|
|
||||||
const handleUserItem = (index, group = {}) => {
|
const handleUserItem = (index, group = {}) => {
|
||||||
curUserGroupIndex.value = index;
|
curUserGroupIndex.value = index;
|
||||||
// console.log("点击用户列表中的item",index);
|
// 设置需要高亮的用户id
|
||||||
console.log("点击用户列表中的item",group);
|
socialGroupsStore.curHighlightUserIdList = group.list.map((item)=>item.userId)
|
||||||
emit("click:selectedGroup", group);
|
emit("click:selectedGroup", group);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -83,12 +83,12 @@ const currentPostPost = ref(null);
|
||||||
|
|
||||||
const handleSelectedUserGroup = (group) => {
|
const handleSelectedUserGroup = (group) => {
|
||||||
socialGroupsStore.curComponent = "detailkNode"
|
socialGroupsStore.curComponent = "detailkNode"
|
||||||
const groupIds = group?.list.map((item)=>item.groupId)
|
const groupIds = group.list.map((item)=>item.groupId)
|
||||||
socialGroupsStore.timeList = group?.timeList
|
const length = group.timeList.length
|
||||||
const length = socialGroupsStore.timeList.length
|
const lastTime = group.timeList[length - 1]
|
||||||
const lastTime = socialGroupsStore.timeList[length - 1]
|
|
||||||
socialGroupsStore.initGraphCommunityDetailNode(groupIds, lastTime)
|
socialGroupsStore.initGraphCommunityDetailNode(groupIds, lastTime)
|
||||||
socialGroupsStore.getSocialGroupPostListByRelationId(group?.relationId)
|
socialGroupsStore.setTimeList(group.timeList)
|
||||||
|
socialGroupsStore.getSocialGroupPostListByRelationId(group.relationId)
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user