新框架pange1意见领袖实现
Signed-off-by: changyunju <2743319061@qq.com>
This commit is contained in:
		
							parent
							
								
									3c23a084a3
								
							
						
					
					
						commit
						60031902a2
					
				| 
						 | 
				
			
			@ -2,27 +2,36 @@
 | 
			
		|||
	<div>
 | 
			
		||||
		<!-- 1. 顶部介绍图片 -->
 | 
			
		||||
		<div>
 | 
			
		||||
			<img src="../assets/images/instruction.png" alt="" class="intruduction">
 | 
			
		||||
			<img src="../assets/images/instruction.png" alt="系统介绍" class="intruduction">
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<!-- 2. 第一行布局容器 -->
 | 
			
		||||
		<div class="leader-containner1">
 | 
			
		||||
			<OpinionLeaders :button-list="buttonList" />
 | 
			
		||||
			<OpinionLeaders :leaders-data="leadersData" :button-list="buttonList" />
 | 
			
		||||
			<PelosiGraph />
 | 
			
		||||
			<LeaderAnalysis />
 | 
			
		||||
			<LeaderAnalysis :chart-data="analysisChartData" />
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<!-- 3. 第二行布局容器 -->
 | 
			
		||||
		<div class="leader-containner2">
 | 
			
		||||
			<EventHeatmap />
 | 
			
		||||
			<EventHeatmap @show-details="openDetailsModal" />
 | 
			
		||||
			<PostDynamics />
 | 
			
		||||
			<WordCloud />
 | 
			
		||||
			<WordCloud :word-data="wordCloudData" />
 | 
			
		||||
		</div>
 | 
			
		||||
		
 | 
			
		||||
		<!-- 4. 详情弹窗 (由主文件控制) -->
 | 
			
		||||
		<div v-if="showDetailsModal" class="modal-overlay" @click="closeDetailsModal">
 | 
			
		||||
			<div class="modal-content" @click.stop>
 | 
			
		||||
				<button class="close-btn" @click="closeDetailsModal">×</button>
 | 
			
		||||
				<div ref="detailsChart" class="details-chart-container"></div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { ref, onUnmounted, nextTick } from 'vue';
 | 
			
		||||
import * as echarts from 'echarts';
 | 
			
		||||
 | 
			
		||||
// 引入所有子组件
 | 
			
		||||
import OpinionLeaders from './KeyNodeRecognition1/OpinionLeaders.vue';
 | 
			
		||||
| 
						 | 
				
			
			@ -32,32 +41,74 @@ import EventHeatmap from './KeyNodeRecognition1/EventHeatmap.vue';
 | 
			
		|||
import PostDynamics from './KeyNodeRecognition1/PostDynamics.vue';
 | 
			
		||||
import WordCloud from './KeyNodeRecognition1/WordCloud.vue';
 | 
			
		||||
 | 
			
		||||
// 将来所有的数据都将在这里定义,并通过 props 传递给子组件
 | 
			
		||||
const buttonList = ['全部', '新闻媒体', '自媒体', '政府官号'];
 | 
			
		||||
// ===================================================================
 | 
			
		||||
// 数据定义 (所有数据保留在主文件中,通过 props 传递)
 | 
			
		||||
// ===================================================================
 | 
			
		||||
 | 
			
		||||
// 1. 意见领袖数据
 | 
			
		||||
const buttonList = ['全部', '新闻媒体', '自媒体', '政府官号'];
 | 
			
		||||
const leadersData = ref([
 | 
			
		||||
    { name: 'Hu Xijin 胡锡进', followers: '53.8万', posts: '54', avatar: 'src/views/user/huxijin.png', category: '自媒体' },
 | 
			
		||||
    { name: 'bidishalolo', followers: '2387', posts: '8380', avatar: 'src/views/user/bidishalolo.png', category: '自媒体' },
 | 
			
		||||
    { name: 'Indo-Pacific News', followers: '11.5万', posts: '11.3万', avatar: 'src/views/user/indo.png', category: '新闻媒体' },
 | 
			
		||||
    { name: 'The Spectator Index', followers: '233.5万', posts: '56', avatar: 'src/views/user/spectator.png', category: '新闻媒体' },
 | 
			
		||||
    { name: 'Mick Wallace', followers: '24.8万', posts: '10259', avatar: 'src/views/user/mick.png', category: '自媒体' },
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 2. 领袖分析数据
 | 
			
		||||
const analysisChartData = ref([
 | 
			
		||||
    { title: '平均发帖数', unit: '数量', max: 10, series: [{ name: '领袖', value: 6.4 }, { name: '所有用户', value: 0.46 }] },
 | 
			
		||||
    { title: '帖子平均生存周期', unit: '天数', max: 10, series: [{ name: '领袖', value: 2.19 }, { name: '所有用户', value: 0.46 }] },
 | 
			
		||||
    { title: '平均粉丝数', unit: '天数', max: 10, series: [{ name: '领袖', value: 2.19 }, { name: '所有用户', value: 0.46 }] }
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// 3. 词云数据
 | 
			
		||||
const wordCloudData = ref([
 | 
			
		||||
    { text: '佩洛西', size: 'large', color: '#56a9de', top: '28%', left: '70%' },
 | 
			
		||||
    { text: '中国', size: 'large', color: '#56a9de', top: '70%', left: '22%' },
 | 
			
		||||
    { text: '中国人民解放军', size: 'medium', color: '#cdeeff', top: '15%', left: '40%' },
 | 
			
		||||
    { text: '中美关系', size: 'medium', color: '#cdeeff', top: '50%', left: '60%' },
 | 
			
		||||
    { text: '台海和平', size: 'medium', color: '#27c1a8', top: '80%', left: '65%' },
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
// ===================================================================
 | 
			
		||||
// 事件热度图的弹窗逻辑
 | 
			
		||||
// ===================================================================
 | 
			
		||||
const showDetailsModal = ref(false);
 | 
			
		||||
const detailsChart = ref(null);
 | 
			
		||||
let myDetailsChart = null;
 | 
			
		||||
 | 
			
		||||
const openDetailsModal = (chartConfig) => {
 | 
			
		||||
    showDetailsModal.value = true;
 | 
			
		||||
    nextTick(() => {
 | 
			
		||||
        if (detailsChart.value && !myDetailsChart) {
 | 
			
		||||
            myDetailsChart = echarts.init(detailsChart.value);
 | 
			
		||||
            myDetailsChart.setOption(chartConfig.option);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const closeDetailsModal = () => {
 | 
			
		||||
    showDetailsModal.value = false;
 | 
			
		||||
    if (myDetailsChart) {
 | 
			
		||||
        myDetailsChart.dispose();
 | 
			
		||||
        myDetailsChart = null;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
    if (myDetailsChart) myDetailsChart.dispose();
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
/* 主文件只保留最核心的布局样式 */
 | 
			
		||||
.intruduction {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	margin-top: 0px;
 | 
			
		||||
	border-radius: 2px;
 | 
			
		||||
}
 | 
			
		||||
.intruduction { width: 100%; margin-top: 0px; border-radius: 2px; }
 | 
			
		||||
.leader-containner1, .leader-containner2 { display: flex; flex-direction: row; gap: 10px; }
 | 
			
		||||
.leader-containner2 { margin-top: 10px; }
 | 
			
		||||
 | 
			
		||||
.leader-containner1 {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: row;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.leader-containner2 {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	flex-direction: row;
 | 
			
		||||
    margin-top: 10px; /* 增加上下两行的间距 */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 使用 gap 来简化组件间的间距处理 */
 | 
			
		||||
.leader-containner1, .leader-containner2 {
 | 
			
		||||
    gap: 10px;
 | 
			
		||||
}
 | 
			
		||||
/* 弹窗样式 */
 | 
			
		||||
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); display: flex; justify-content: center; align-items: center; z-index: 1000; }
 | 
			
		||||
.modal-content { position: relative; background-color: #0d1b38; padding: 20px; border-radius: 8px; border: 1px solid #3a95ff; box-shadow: 0 0 25px rgba(58, 149, 255, 0.5); width: 80vw; height: 75vh; display: flex; flex-direction: column; }
 | 
			
		||||
.close-btn { position: absolute; top: 10px; right: 15px; background: none; border: none; color: #a7c5d4; font-size: 24px; cursor: pointer; }
 | 
			
		||||
.details-chart-container { width: 100%; flex-grow: 1; }
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,80 +1,57 @@
 | 
			
		|||
<template>
 | 
			
		||||
    <div class="leader-show">
 | 
			
		||||
        <img src="../../assets/images/leader-show.png" alt="" style="margin-top: -6px;">
 | 
			
		||||
    <div class="panel-container leader-show">
 | 
			
		||||
        <img src="../../assets/images/leader-show.png" alt="意见领袖展示" class="panel-title-img">
 | 
			
		||||
        <div class="all-leader">
 | 
			
		||||
            <button v-for="(item, index) in props.buttonList" :key="index" :class="{ active: activeButton === index }"
 | 
			
		||||
                @click="changeData(index)">
 | 
			
		||||
            <button v-for="(item, index) in props.buttonList" :key="index" :class="{ active: activeButton === index }" @click="activeButton = index">
 | 
			
		||||
                {{ item }}
 | 
			
		||||
            </button>
 | 
			
		||||
            <div class="leader-data">
 | 
			
		||||
                <!-- 这里是根据按钮和时间点切换内容的逻辑 -->
 | 
			
		||||
                <div v-if="timePointDataIndex === 0">
 | 
			
		||||
                    <div v-if="activeButton === 0">全部种类的数据信息</div>
 | 
			
		||||
                    <div v-if="activeButton === 1"><el-avatar shape="square" :size="50" :src="squareUrl" /></div>
 | 
			
		||||
                    <div v-if="activeButton === 2">自媒体的数据信息</div>
 | 
			
		||||
                    <div v-if="activeButton === 3">中国海警</div>
 | 
			
		||||
                <div v-for="leader in filteredLeaders" :key="leader.name" class="leader-item">
 | 
			
		||||
                    <img :src="leader.avatar" alt="avatar" class="leader-avatar" />
 | 
			
		||||
                    <div class="leader-info">
 | 
			
		||||
                        <p class="leader-name">{{ leader.name }}</p>
 | 
			
		||||
                        <span class="leader-stat">粉丝数量:{{ leader.followers }}</span>
 | 
			
		||||
                        <span class="leader-stat">发帖总数:{{ leader.posts }}</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div v-if="timePointDataIndex === 1">时间点 2 的数据信息</div>
 | 
			
		||||
                <!-- ... 其他时间点 ... -->
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup>
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { ref, computed } from 'vue';
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
    leadersData: Array,
 | 
			
		||||
    buttonList: Array
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const activeButton = ref(0);
 | 
			
		||||
const timePointDataIndex = ref(0); // 这个状态也属于本组件
 | 
			
		||||
 | 
			
		||||
const changeData = (index) => {
 | 
			
		||||
    activeButton.value = index;
 | 
			
		||||
};
 | 
			
		||||
const filteredLeaders = computed(() => {
 | 
			
		||||
    if (!props.leadersData) return [];
 | 
			
		||||
    const currentCategory = props.buttonList[activeButton.value];
 | 
			
		||||
    if (currentCategory === '全部') return props.leadersData;
 | 
			
		||||
    return props.leadersData.filter(leader => leader.category === currentCategory);
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
/* 粘贴所有与 .leader-show 和 .all-leader 相关的样式 */
 | 
			
		||||
.leader-show {
 | 
			
		||||
	width: 352px;
 | 
			
		||||
	height: 540px;
 | 
			
		||||
	background-color: #04142166;
 | 
			
		||||
	border-radius: 2px;
 | 
			
		||||
	box-shadow: 0px 0px 18px 0px #0A2E55 inset;
 | 
			
		||||
	backdrop-filter: blur(4px);
 | 
			
		||||
}
 | 
			
		||||
.all-leader {
 | 
			
		||||
	width: 305px;
 | 
			
		||||
	height: 450px;
 | 
			
		||||
	margin-left: 24px;
 | 
			
		||||
	font-size: 0;
 | 
			
		||||
}
 | 
			
		||||
.all-leader button {
 | 
			
		||||
	height: 24px;
 | 
			
		||||
	font-family: OPPOSans;
 | 
			
		||||
	font-size: 14px;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	background-color: #04142166;
 | 
			
		||||
	color: #E1F4FF;
 | 
			
		||||
	border: 1px solid #1C588F;
 | 
			
		||||
	border-radius: 0;
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
.all-leader button:first-child {
 | 
			
		||||
	border-top-left-radius: 3px;
 | 
			
		||||
	border-bottom-left-radius: 3px;
 | 
			
		||||
}
 | 
			
		||||
.all-leader button:nth-child(4) {
 | 
			
		||||
	border-top-right-radius: 3px;
 | 
			
		||||
	border-bottom-right-radius: 3px;
 | 
			
		||||
}
 | 
			
		||||
.all-leader button.active {
 | 
			
		||||
	background-color: #236291;
 | 
			
		||||
}
 | 
			
		||||
.leader-data {
 | 
			
		||||
    color: white; /* 示例文字颜色 */
 | 
			
		||||
}
 | 
			
		||||
.panel-container { width: 352px; height: 540px; background-color: #04142166; box-shadow: 0 0 18px 0 #0A2E55 inset; backdrop-filter: blur(4px); border-radius: 2px; }
 | 
			
		||||
.panel-title-img { margin-top: -6px; }
 | 
			
		||||
.all-leader { width: 305px; height: 450px; margin: 0 auto; }
 | 
			
		||||
.all-leader button { height: 24px; font-size: 14px; cursor: pointer; background-color: #04142166; color: #E1F4FF; border: 1px solid #1C588F; display: inline-block; }
 | 
			
		||||
.all-leader button:first-child { border-top-left-radius: 3px; border-bottom-left-radius: 3px; }
 | 
			
		||||
.all-leader button:last-child { border-top-right-radius: 3px; border-bottom-right-radius: 3px; }
 | 
			
		||||
.all-leader button.active { background-color: #236291; }
 | 
			
		||||
.leader-data { height: calc(100% - 34px); margin-top: 10px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2a3e5e #0d1b38; }
 | 
			
		||||
.leader-data::-webkit-scrollbar { width: 5px; }
 | 
			
		||||
.leader-data::-webkit-scrollbar-track { background: #0d1b38; }
 | 
			
		||||
.leader-data::-webkit-scrollbar-thumb { background-color: #2a3e5e; border-radius: 10px; }
 | 
			
		||||
.leader-item { display: flex; align-items: flex-start; margin-bottom: 18px; color: #a7c5d4; }
 | 
			
		||||
.leader-avatar { width: 40px; height: 40px; border-radius: 4px; margin-right: 15px; flex-shrink: 0; }
 | 
			
		||||
.leader-info { display: flex; flex-direction: column; }
 | 
			
		||||
.leader-name { font-size: 15px; font-weight: 500; margin: 0 0 5px 0; color: #e0f7ff; }
 | 
			
		||||
.leader-stat { font-size: 13px; color: #a7c5d4; line-height: 1.5; }
 | 
			
		||||
</style>
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user