Merge branch 'master' of http://172.16.20.1:3000/duanhao/SocialNetworks_duan
This commit is contained in:
commit
46d5edfa63
BIN
src/assets/images/linkPrediction/icon/hidden-icon.png
Normal file
BIN
src/assets/images/linkPrediction/icon/hidden-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 151 B |
BIN
src/assets/images/linkPrediction/icon/interaction-icon.png
Normal file
BIN
src/assets/images/linkPrediction/icon/interaction-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
BIN
src/assets/images/linkPrediction/icon/interaction-icon2.png
Normal file
BIN
src/assets/images/linkPrediction/icon/interaction-icon2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 191 B |
|
|
@ -30,8 +30,12 @@ export function getGroupUserListFromSocial() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//人物互动隐关系预测的贴文列表
|
//人物互动隐关系预测的贴文列表
|
||||||
export function getInteractionPostList(outoIncrement) {
|
// export function getInteractionPostList(outoIncrement) {
|
||||||
return http.get(`/linkPrediction/interaction/post_list?page=${outoIncrement}`)
|
// return http.get(`/linkPrediction/interaction/post_list?page=${outoIncrement}`)
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function getInteractionPostList(userGroupId) {
|
||||||
|
return http.get(`/linkPrediction/user_posts_list?relationId=${userGroupId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//社交紧密团体识别的贴文列表
|
//社交紧密团体识别的贴文列表
|
||||||
|
|
|
||||||
|
|
@ -164,14 +164,10 @@ export const useCharacterInteractionStore = defineStore("characterInteraction",
|
||||||
initGroupCorrelationForChart() {
|
initGroupCorrelationForChart() {
|
||||||
this.userChartList = this.userList
|
this.userChartList = this.userList
|
||||||
},
|
},
|
||||||
async initInteractionPostList(autoIncrement) {
|
async initInteractionPostList(userGroupId) {
|
||||||
const res = await getInteractionPostList(autoIncrement)
|
const res = await getInteractionPostList(userGroupId)
|
||||||
if (res.code != 200) return
|
if (res.code != 200) return
|
||||||
if (this.posts.length != 0) {
|
this.posts = res.data
|
||||||
this.posts.push(...res.data)
|
|
||||||
} else {
|
|
||||||
this.posts = res.data
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async initGraphCommunityNode() {
|
async initGraphCommunityNode() {
|
||||||
const res = await getInteractionCommunityNodes()
|
const res = await getInteractionCommunityNodes()
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,7 @@
|
||||||
<Graph :title="graphTitleImg"></Graph>
|
<Graph :title="graphTitleImg"></Graph>
|
||||||
</div>
|
</div>
|
||||||
<div class="postList">
|
<div class="postList">
|
||||||
<PostList
|
<PostList :posts="interactionStore.posts"></PostList>
|
||||||
:posts="interactionStore.posts"
|
|
||||||
@click:openDialog="handleOpenPostDialog"
|
|
||||||
@scroll:touchButtom="handleTouchButtom"
|
|
||||||
></PostList>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-container">
|
<div class="right-container">
|
||||||
|
|
@ -36,34 +32,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog v-model="postDialog" width="640" align-center class="custom-dialog">
|
|
||||||
<img src="@/assets/images/head/post-dialog-title.png" alt="" class="postTitleImage" />
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="post-content">{{ currentPostPost.content }}</div>
|
|
||||||
<div class="heat">
|
|
||||||
<div class="item-heat-detail">
|
|
||||||
<div class="item-heat-like">
|
|
||||||
<Icon icon="ei:like" width="25" height="25" /> {{ currentPostPost.like }}
|
|
||||||
</div>
|
|
||||||
<div class="item-heat-comment">
|
|
||||||
<Icon icon="la:comment-dots" width="25" height="25" /> {{ currentPostPost.comment }}
|
|
||||||
</div>
|
|
||||||
<div class="item-heat-transmit">
|
|
||||||
<Icon icon="mdi:share-outline" width="25" height="25" /> {{
|
|
||||||
currentPostPost.transmit
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from "vue"
|
import { onMounted } from "vue"
|
||||||
import { Icon } from "@iconify/vue"
|
|
||||||
import { provide } from "vue"
|
import { provide } from "vue"
|
||||||
import { useCharacterInteractionStore } from "@/store/llinkPrediction/index"
|
import { useCharacterInteractionStore } from "@/store/llinkPrediction/index"
|
||||||
import UserPanel from "../components/userPanel.vue"
|
import UserPanel from "../components/userPanel.vue"
|
||||||
|
|
@ -76,31 +49,16 @@ import graphTitleImg from "@/assets/images/linkPrediction/title/graph1-title.png
|
||||||
import analysisTitleImg from "@/assets/images/linkPrediction/title/analysis-title.png"
|
import analysisTitleImg from "@/assets/images/linkPrediction/title/analysis-title.png"
|
||||||
const interactionStore = useCharacterInteractionStore()
|
const interactionStore = useCharacterInteractionStore()
|
||||||
|
|
||||||
//控制弹窗
|
//选择某个用户组,更新贴文列表
|
||||||
const postDialog = ref(false)
|
|
||||||
|
|
||||||
//当前选中的贴文数据
|
|
||||||
const currentPostPost = ref(null)
|
|
||||||
|
|
||||||
const handleSelectedUserGroup = (group) => {
|
const handleSelectedUserGroup = (group) => {
|
||||||
interactionStore.curComponent = "detailNode"
|
interactionStore.initInteractionPostList(group.relationId)
|
||||||
console.log(group)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOpenPostDialog = (post) => {
|
|
||||||
postDialog.value = true
|
|
||||||
currentPostPost.value = post
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleTouchButtom = (outoIncrement) => {
|
|
||||||
interactionStore.initInteractionPostList(outoIncrement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
interactionStore.initGroupList()
|
interactionStore.initGroupList() //初始化用户组列表
|
||||||
interactionStore.initInteractionPostList(1)
|
interactionStore.initGraphCommunityNode() //初始化所有社团节点
|
||||||
interactionStore.initGraphCommunityNode()
|
interactionStore.initGraphStatistics() //初始化所有社团状态统计
|
||||||
interactionStore.initGraphStatistics()
|
interactionStore.initInteractionPostList("106888") //初始贴文列表
|
||||||
})
|
})
|
||||||
provide("communityNodeList", interactionStore.communityNodeList) // 提供数据
|
provide("communityNodeList", interactionStore.communityNodeList) // 提供数据
|
||||||
provide("statisticsList", interactionStore.statisticsList)
|
provide("statisticsList", interactionStore.statisticsList)
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,13 @@ const initChart = async () => {
|
||||||
const data = { nodes, links }
|
const data = { nodes, links }
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{ name: "普通社团", category: 0 },
|
{ name: "普通社团", category: 0, icon: "circle" },
|
||||||
{ name: "含预测节点社团", category: 1 },
|
{ name: "含预测节点社团", category: 1, icon: "circle" },
|
||||||
{ name: "互动隐关系", category: 2 }
|
{
|
||||||
|
name: "互动隐关系",
|
||||||
|
category: 2,
|
||||||
|
icon: `image://${new URL("@/assets/images/linkPrediction/icon/hidden-icon.png", import.meta.url)}`
|
||||||
|
}
|
||||||
]
|
]
|
||||||
const option = {
|
const option = {
|
||||||
//图例配置
|
//图例配置
|
||||||
|
|
@ -78,18 +82,13 @@ const initChart = async () => {
|
||||||
{ offset: 0.5, color: "#38546b" },
|
{ offset: 0.5, color: "#38546b" },
|
||||||
{ offset: 0, color: "#5fb3b3" }
|
{ offset: 0, color: "#5fb3b3" }
|
||||||
])
|
])
|
||||||
: c.category === 1
|
: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||||
? new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
{ offset: 0, color: "#9eec9c" }, // 绿色渐变
|
||||||
{ offset: 0, color: "#9eec9c" }, // 绿色渐变
|
{ offset: 0.37, color: "#aef295" },
|
||||||
{ offset: 0.37, color: "#aef295" },
|
{ offset: 1, color: "#c2f989" }
|
||||||
{ offset: 1, color: "#c2f989" }
|
])
|
||||||
])
|
},
|
||||||
: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
icon: c.icon
|
||||||
{ offset: 0, color: "#ff9a9e" }, // 红色渐变(用于 category === 2)
|
|
||||||
{ offset: 0.5, color: "#fad0c4" },
|
|
||||||
{ offset: 1, color: "#fbc2eb" }
|
|
||||||
])
|
|
||||||
}
|
|
||||||
})),
|
})),
|
||||||
right: 15,
|
right: 15,
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,13 @@
|
||||||
<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" :style="{ left: `${currentPosition}px` }">
|
||||||
<div class="active-needle"></div>
|
<div class="active-needle"></div>
|
||||||
<div class="timeLine-point" @pointerdown.stop="handlePointPointerDown"></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>
|
</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>
|
||||||
|
|
@ -110,8 +116,8 @@ const handlePointPointerDown = (e) => {
|
||||||
const handlePointerUp = () => {
|
const handlePointerUp = () => {
|
||||||
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")
|
||||||
console.log("拖动结束,当前时间:", currentTimes)
|
interactionStore.initGraphCommunityDetailNode(interactionStore.curSelecedGroupIds, currentTimes)
|
||||||
|
|
||||||
document.removeEventListener("pointermove", handlePointerMove)
|
document.removeEventListener("pointermove", handlePointerMove)
|
||||||
document.removeEventListener("pointerup", handlePointerUp)
|
document.removeEventListener("pointerup", handlePointerUp)
|
||||||
|
|
@ -136,11 +142,19 @@ onUnmounted(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
let chart = null
|
let chart = null
|
||||||
|
|
||||||
const initChart = async () => {
|
const initChart = async () => {
|
||||||
chart = echarts.init(document.getElementById("container"))
|
chart = echarts.init(document.getElementById("container"))
|
||||||
|
|
||||||
const links = []
|
const links = []
|
||||||
const nodes = []
|
const nodes = []
|
||||||
|
const edgeWidth = (interactionTime) => {
|
||||||
|
if (interactionTime > 3) return 4
|
||||||
|
else if (interactionTime > 10) return 6
|
||||||
|
else if (interactionTime > 20) return 8
|
||||||
|
else if (interactionTime > 30) return 10
|
||||||
|
else return 1
|
||||||
|
}
|
||||||
if (!Object.keys(interactionStore.communityDetailNodeList).length) return
|
if (!Object.keys(interactionStore.communityDetailNodeList).length) return
|
||||||
Object.entries(interactionStore.communityDetailNodeList).forEach(([parentId, children]) => {
|
Object.entries(interactionStore.communityDetailNodeList).forEach(([parentId, children]) => {
|
||||||
nodes.push({
|
nodes.push({
|
||||||
|
|
@ -155,17 +169,29 @@ const initChart = async () => {
|
||||||
source: `parent_${parentId}`,
|
source: `parent_${parentId}`,
|
||||||
target: child.id,
|
target: child.id,
|
||||||
edge: child.isHidden ? 1 : 0,
|
edge: child.isHidden ? 1 : 0,
|
||||||
interactionTimes: child.interactionTime
|
interactionTimes: child.interactionTime,
|
||||||
|
lineStyle: {
|
||||||
|
width: child.isHidden ? 4 : edgeWidth(child.interactionTime),
|
||||||
|
color: child.isHidden ? "#f8bf38" : "#37ACD7", // 无互动=灰色,有互动=黄色
|
||||||
|
type: child.isHidden ? "dashed" : "solid" // 无互动=实线,有互动=虚线
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const data = { links, nodes }
|
const data = { links, nodes }
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{ name: "事件活跃者", category: 0 },
|
{ name: "事件活跃者", category: 0, icon: "circle" },
|
||||||
{ name: "信息发布者", category: 1 },
|
{
|
||||||
{ name: "互动关系", category: 2 },
|
name: "互动关系",
|
||||||
{ name: "互动隐关系", category: 3 }
|
category: 1,
|
||||||
|
icon: `image://${new URL("@/assets/images/linkPrediction/icon/interaction-icon2.png", import.meta.url)}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "互动隐关系",
|
||||||
|
category: 2,
|
||||||
|
icon: `image://${new URL("@/assets/images/linkPrediction/icon/hidden-icon.png", import.meta.url)}`
|
||||||
|
}
|
||||||
]
|
]
|
||||||
const option = {
|
const option = {
|
||||||
//图例配置
|
//图例配置
|
||||||
|
|
@ -192,11 +218,12 @@ const initChart = async () => {
|
||||||
{ offset: 0.5, color: "#fad0c4" },
|
{ offset: 0.5, color: "#fad0c4" },
|
||||||
{ offset: 1, color: "#fbc2eb" }
|
{ offset: 1, color: "#fbc2eb" }
|
||||||
])
|
])
|
||||||
}
|
},
|
||||||
|
icon: c.icon
|
||||||
})),
|
})),
|
||||||
right: 21,
|
right: 21,
|
||||||
|
symbolKeepAspect: false,
|
||||||
bottom: 70,
|
bottom: 70,
|
||||||
icon: "circle",
|
|
||||||
orient: "vertical",
|
orient: "vertical",
|
||||||
itemWidth: 16,
|
itemWidth: 16,
|
||||||
itemHeight: 16,
|
itemHeight: 16,
|
||||||
|
|
@ -248,7 +275,7 @@ const initChart = async () => {
|
||||||
show: false,
|
show: false,
|
||||||
position: "middle",
|
position: "middle",
|
||||||
formatter: function (params) {
|
formatter: function (params) {
|
||||||
return params.data.edge
|
return params.data.interactionTimes
|
||||||
},
|
},
|
||||||
fontSize: 14
|
fontSize: 14
|
||||||
},
|
},
|
||||||
|
|
@ -270,7 +297,7 @@ const initChart = async () => {
|
||||||
animation: false,
|
animation: false,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
roam: true,
|
roam: true,
|
||||||
zoom: 0.3,
|
zoom: 0.15,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
force: {
|
force: {
|
||||||
edgeLength: 2500,
|
edgeLength: 2500,
|
||||||
|
|
@ -321,6 +348,12 @@ const initChart = async () => {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart()
|
initChart()
|
||||||
|
chart.on("legendselectchanged", function (params) {
|
||||||
|
// 解决变形问题
|
||||||
|
setTimeout(() => {
|
||||||
|
chart.resize()
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -432,7 +465,7 @@ onMounted(() => {
|
||||||
background-image: url("@/assets/images/point.png");
|
background-image: url("@/assets/images/point.png");
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
bottom: 1px;
|
bottom: 1px;
|
||||||
left: -6px;
|
left: -11px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
.timeLine-point {
|
.timeLine-point {
|
||||||
|
|
@ -446,6 +479,7 @@ onMounted(() => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
|
left: -5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
will-change: left;
|
will-change: left;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
alt=""
|
alt=""
|
||||||
style="margin-top: -17px; margin-left: -11px"
|
style="margin-top: -17px; margin-left: -11px"
|
||||||
/>
|
/>
|
||||||
<div class="post-list-wrapper" ref="listRef" @scroll="handleScroll">
|
<div class="post-list-wrapper" ref="listRef">
|
||||||
<div class="scrolling-content">
|
<div class="scrolling-content">
|
||||||
<div
|
<div
|
||||||
class="post-item"
|
class="post-item"
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
@click="handleLeaderPost(post)"
|
@click="handleLeaderPost(post)"
|
||||||
>
|
>
|
||||||
<img src="@/assets/images/linkPrediction/icon/post-prefix.png" alt="" class="prefix" />
|
<img src="@/assets/images/linkPrediction/icon/post-prefix.png" alt="" class="prefix" />
|
||||||
<div class="timestamp">{{ TansTimestamp(post.time) }}</div>
|
<div class="timestamp">{{ TansTimestamp(post.interactionTime) }}</div>
|
||||||
<div class="behavior">
|
<div class="behavior">
|
||||||
【{{ post.userName }}】{{ post.behavior }} 【{{ post.neighborName }}】的贴文
|
【{{ post.userName }}】{{ post.behavior }} 【{{ post.neighborName }}】的贴文
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -26,36 +26,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineProps, defineEmits, onMounted, onBeforeUnmount } from "vue";
|
import { ref, defineProps, defineEmits, onMounted } from "vue"
|
||||||
import { TansTimestamp } from "@/utils/transform";
|
import { TansTimestamp } from "@/utils/transform"
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
posts: {
|
posts: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
const listRef = ref(null)
|
||||||
|
|
||||||
const emit = defineEmits(["click:openDialog", "scroll:touchButtom"]);
|
onMounted(() => {})
|
||||||
const page = ref(0);
|
|
||||||
const listRef = ref(null);
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
|
||||||
const el = listRef.value;
|
|
||||||
if (!el) return;
|
|
||||||
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 1) {
|
|
||||||
emit("scroll:touchButtom", page.value++);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLeaderPost = (item) => {
|
|
||||||
emit("click:openDialog", item);
|
|
||||||
console.log(item);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
handleScroll();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user