Signed-off-by: changyunju <2743319061@qq.com>
This commit is contained in:
parent
9f39908deb
commit
288a17892c
|
|
@ -1,8 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="main-container">
|
<div class="opinion-leader-layout">
|
||||||
<!-- ========================================================== -->
|
<!-- 顶部描述 -->
|
||||||
<!-- 左侧区域: 意见领袖列表 (OpinionLeaders) -->
|
<header class="main-header">
|
||||||
<!-- ========================================================== -->
|
评估、高影响节点识别。特色在于评估节点重要性时有机结合了节点微观影响力与全局影响力,提升了意...
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="main-content">
|
||||||
|
<!-- ==================== 左侧面板: 意见领袖列表 ==================== -->
|
||||||
<div class="left-panel">
|
<div class="left-panel">
|
||||||
<h2 class="panel-title">意见领袖抽展示</h2>
|
<h2 class="panel-title">意见领袖抽展示</h2>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
|
|
@ -16,7 +20,6 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="leader-list">
|
<div class="leader-list">
|
||||||
<!-- v-for 循环的是根据 activeTab 筛选后的可见领袖 -->
|
|
||||||
<div v-for="leader in filteredVisibleLeaders" :key="leader.id" class="leader-item">
|
<div v-for="leader in filteredVisibleLeaders" :key="leader.id" class="leader-item">
|
||||||
<img :src="leader.avatar" :alt="leader.name" class="avatar">
|
<img :src="leader.avatar" :alt="leader.name" class="avatar">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
|
|
@ -33,26 +36,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ========================================================== -->
|
<!-- ==================== 右侧面板: 网络图与时间轴 ==================== -->
|
||||||
<!-- 右侧区域: 佩洛西图谱 (PelosiGraph) -->
|
|
||||||
<!-- ========================================================== -->
|
|
||||||
<div class="right-panel">
|
<div class="right-panel">
|
||||||
|
<!-- 这个组件现在被整合到右侧面板中 -->
|
||||||
<div class="key-node-recognition">
|
<div class="key-node-recognition">
|
||||||
<div class="background-svg-wrapper">
|
<div class="background-svg-wrapper">
|
||||||
|
<!-- 背景SVG与之前相同 -->
|
||||||
<svg width="100%" height="100%" viewBox="0 0 800 540" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="100%" height="100%" viewBox="0 0 800 540" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs><linearGradient id="paint0_linear_bg" x1="0" y1="167.1" x2="800" y2="167.1" gradientUnits="userSpaceOnUse"><stop stop-color="#063D71" stop-opacity="0.2" /><stop offset="1" stop-color="#081E38" stop-opacity="0.8" /></linearGradient><linearGradient id="paint1_linear_border" x1="400" y1="0" x2="400" y2="540" gradientUnits="userSpaceOnUse"><stop stop-color="#3AA1F8" /><stop offset="1" stop-color="#3AA1F8" stop-opacity="0.2" /></linearGradient></defs>
|
<defs><linearGradient id="paint0_linear_bg" x1="0" y1="167.1" x2="800" y2="167.1" gradientUnits="userSpaceOnUse"><stop stop-color="#063D71" stop-opacity="0.2" /><stop offset="1" stop-color="#081E38" stop-opacity="0.8" /></linearGradient><linearGradient id="paint1_linear_border" x1="400" y1="0" x2="400" y2="540" gradientUnits="userSpaceOnUse"><stop stop-color="#3AA1F8" /><stop offset="1" stop-color="#3AA1F8" stop-opacity="0.2" /></linearGradient></defs>
|
||||||
<path d="M798 0.5H2C1.17159 0.5 0.500003 1.17158 0.5 2V538C0.5 538.828 1.17159 539.5 2 539.5H798C798.828 539.5 799.5 538.828 799.5 538V2C799.5 1.17157 798.828 0.5 798 0.5Z" fill="url(#paint0_linear_bg)" fill-opacity="0.48" stroke="url(#paint1_linear_border)" />
|
<path d="M798 0.5H2C1.17159 0.5 0.500003 1.17158 0.5 2V538C0.5 538.828 1.17159 539.5 2 539.5H798C798.828 539.5 799.5 538.828 799.5 538V2C799.5 1.17157 798.828 0.5 798 0.5Z" fill="url(#paint0_linear_bg)" fill-opacity="0.48" stroke="url(#paint1_linear_border)" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<h1 class="graph-title">佩洛西系列事件</h1>
|
<h1 class="main-title">佩洛西系列事件</h1>
|
||||||
<!-- ECharts 图表容器 -->
|
<!-- 图表容器 -->
|
||||||
<div ref="chartContainer" class="chart-container"></div>
|
<div ref="chartContainer" class="chart-container"></div>
|
||||||
<!-- 时间轴 -->
|
<!-- 时间轴 -->
|
||||||
<div class="timeline-container">
|
<div class="timeline-container">
|
||||||
<span class="time-label">2022.07.31 00:00:00</span>
|
<span class="time-label">2022.07.31 00:00:00</span>
|
||||||
<div class="timeline-track">
|
<div class="timeline-track">
|
||||||
<div v-for="point in timePoints" :key="point.id" class="timeline-point-wrapper" @click="onTimePointClick(point.id)">
|
<div v-for="point in timePoints" :key="point.id" class="timeline-point-wrapper" @click="activeTimePoint = point.id">
|
||||||
<div class="timeline-point" :class="{ active: activeTimePoint === point.id }">
|
<div class="timeline-point" :class="{ active: activeTimePoint === point.id }">
|
||||||
<div v-if="activeTimePoint === point.id" class="active-pin"></div>
|
<div v-if="activeTimePoint === point.id" class="active-pin"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -64,10 +67,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, computed, watch } from 'vue';
|
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
|
||||||
import * as echarts from 'echarts/core';
|
import * as echarts from 'echarts/core';
|
||||||
import { GraphChart } from 'echarts/charts';
|
import { GraphChart } from 'echarts/charts';
|
||||||
import { TitleComponent, TooltipComponent } from 'echarts/components';
|
import { TitleComponent, TooltipComponent } from 'echarts/components';
|
||||||
|
|
@ -75,32 +79,36 @@ import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
|
||||||
echarts.use([TitleComponent, TooltipComponent, GraphChart, CanvasRenderer]);
|
echarts.use([TitleComponent, TooltipComponent, GraphChart, CanvasRenderer]);
|
||||||
|
|
||||||
// ===================================================================
|
// --- 状态管理 ---
|
||||||
// 核心状态与数据 (全部在此文件中管理)
|
const chartContainer = ref(null);
|
||||||
// ===================================================================
|
let myChart = null;
|
||||||
|
|
||||||
// 1. 核心状态:当前激活的时间点
|
|
||||||
const activeTimePoint = ref(1);
|
|
||||||
const timePoints = ref(Array.from({ length: 10 }, (_, i) => ({ id: i + 1 })));
|
|
||||||
|
|
||||||
// 2. 全量意见领袖数据,其顺序决定了出场顺序
|
|
||||||
const allLeaderData = ref([
|
|
||||||
{ id: '5', name: 'Hu Xijin', chineseName: '胡锡进', followers: '53.8万', posts: '54', avatar: 'https://i.imgur.com/Y3vH2oP.png', category: '自媒体' },
|
|
||||||
{ id: 'bidishalolo', name: 'bidishalolo', followers: '2387', posts: '8380', avatar: 'https://i.imgur.com/6a5A466.png', category: '自媒体' },
|
|
||||||
{ id: 'indo-pacific', name: 'Indo-Pacific News', followers: '11.5万', posts: '11.3万', avatar: 'https://i.imgur.com/PkooCvB.png', category: '新闻媒体' },
|
|
||||||
{ id: '1', name: 'The Spectator Index', followers: '233.5万', posts: '56', avatar: 'https://i.imgur.com/rS2aP3s.png', category: '新闻媒体' },
|
|
||||||
{ id: 'mickwallace', name: 'Mick Wallace', followers: '24.8万', posts: '10259', avatar: 'https://i.imgur.com/gKk9p3j.png', category: '自媒体' },
|
|
||||||
// 您可以按需继续添加更多领袖
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 3. 计算属性:根据时间点,动态生成当前可见的领袖列表
|
|
||||||
const visibleLeaders = computed(() => {
|
|
||||||
return allLeaderData.value.slice(0, activeTimePoint.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 4. 左侧列表状态与逻辑
|
|
||||||
const tabs = ref(['全部', '新闻媒体', '自媒体', '政府官号']);
|
const tabs = ref(['全部', '新闻媒体', '自媒体', '政府官号']);
|
||||||
const activeTab = ref('全部');
|
const activeTab = ref('全部');
|
||||||
|
|
||||||
|
const timePoints = ref(Array.from({ length: 10 }, (_, i) => ({ id: i + 1 })));
|
||||||
|
const activeTimePoint = ref(1); // 默认从第一个时间点开始
|
||||||
|
|
||||||
|
// --- 统一数据源 ---
|
||||||
|
|
||||||
|
// 1. 定义全量意见领袖数据,顺序即为出场顺序
|
||||||
|
const allLeaderData = [
|
||||||
|
{ id: '5', name: 'Hu Xijin', chineseName: '胡锡进', avatar: 'https://i.imgur.com/Y3vH2oP.png', followers: '53.8万', posts: '54', category: '自媒体' },
|
||||||
|
{ id: 'bidishalolo', name: 'bidishalolo', avatar: 'https://i.imgur.com/6a5A466.png', followers: '2387', posts: '8380', category: '自媒体' },
|
||||||
|
{ id: 'indo-pacific', name: 'Indo-Pacific News', avatar: 'https://i.imgur.com/PkooCvB.png', followers: '11.5万', posts: '11.3万', category: '新闻媒体' },
|
||||||
|
{ id: '1', name: 'The Spectator Index', avatar: 'https://i.imgur.com/rS2aP3s.png', followers: '233.5万', posts: '56', category: '新闻媒体' },
|
||||||
|
{ id: 'mickwallace', name: 'Mick Wallace', avatar: 'https://i.imgur.com/gKk9p3j.png', followers: '24.8万', posts: '10259', category: '自媒体' },
|
||||||
|
{ id: '0', name: 'China Coast Guard', chineseName: '中国海警', avatar: 'https://i.imgur.com/rN5V6fD.png', followers: '120万', posts: '88', category: '政府官号' },
|
||||||
|
{ id: '2', name: 'Nancy Pelosi', avatar: 'https://i.imgur.com/g0t6GqB.png', followers: '1780万', posts: '320', category: '政府官号' },
|
||||||
|
{ id: '6', name: 'Cat Avatar User', avatar: 'https://i.imgur.com/QhT8k5q.png', followers: '1.2万', posts: '950', category: '自媒体' },
|
||||||
|
{ id: '7', name: 'User C', avatar: 'https://i.imgur.com/7bO2A5a.png', followers: '8000', posts: '120', category: '自媒体' },
|
||||||
|
{ id: '8', name: 'Default User', avatar: 'https://i.imgur.com/tP2x2Jg.png', followers: '500', posts: '30', category: '自媒体' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 2. 响应式变量,用于存储当前可见的领袖
|
||||||
|
const visibleLeaders = ref([]);
|
||||||
|
|
||||||
|
// 3. 计算属性,根据左侧Tab筛选可见的领袖
|
||||||
const filteredVisibleLeaders = computed(() => {
|
const filteredVisibleLeaders = computed(() => {
|
||||||
if (activeTab.value === '全部') {
|
if (activeTab.value === '全部') {
|
||||||
return visibleLeaders.value;
|
return visibleLeaders.value;
|
||||||
|
|
@ -108,124 +116,289 @@ const filteredVisibleLeaders = computed(() => {
|
||||||
return visibleLeaders.value.filter(leader => leader.category === activeTab.value);
|
return visibleLeaders.value.filter(leader => leader.category === activeTab.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5. 右侧图谱状态与逻辑
|
|
||||||
const chartContainer = ref(null);
|
// 4. 定义图表的全量节点和连线
|
||||||
let myChart = null;
|
const allGraphNodes = [
|
||||||
// 全量图谱数据
|
...allLeaderData.map(leader => ({
|
||||||
const allGraphNodes = computed(() => [
|
id: leader.id,
|
||||||
...allLeaderData.value.map(leader => ({
|
name: leader.name,
|
||||||
id: leader.id, name: leader.name, symbol: `image://${leader.avatar}`,
|
symbol: `image://${leader.avatar}`,
|
||||||
symbolSize: 50, category: leader.category === '新闻媒体' ? 1 : 0
|
symbolSize: 50,
|
||||||
|
category: leader.category === '政府官号' ? 0 : 1,
|
||||||
|
label: { show: leader.chineseName, color: '#fff' }
|
||||||
})),
|
})),
|
||||||
]);
|
// 普通节点
|
||||||
const allGraphLinks = ref([ { source: 'bidishalolo', target: '5' }, { source: 'indo-pacific', target: '1' } ]);
|
...Array.from({ length: 30 }, (_, i) => ({ id: `n${i}`, name: `User ${i}`, symbolSize: 15, category: 2 })),
|
||||||
|
];
|
||||||
|
|
||||||
// ===================================================================
|
const allGraphLinks = [
|
||||||
// 核心交互逻辑与方法
|
// 领袖之间的连接
|
||||||
// ===================================================================
|
{ source: '5', target: '1' }, { source: '1', target: '2' }, { source: 'indo-pacific', target: '1' },
|
||||||
|
{ source: '0', target: '2' }, { source: 'mickwallace', target: '5' },
|
||||||
|
// 领袖与普通节点的连接
|
||||||
|
{ source: '5', target: 'n0' }, { source: '5', target: 'n1' },
|
||||||
|
{ source: 'bidishalolo', target: 'n2' },
|
||||||
|
{ source: 'indo-pacific', target: 'n3' }, { source: 'indo-pacific', target: 'n4' },
|
||||||
|
{ source: '1', target: 'n5' }, { source: '1', target: 'n6' },
|
||||||
|
{ source: 'mickwallace', target: 'n7' },
|
||||||
|
{ source: '0', target: 'n8' }, { source: '0', target: 'n9' }, { source: '0', target: 'n10' },
|
||||||
|
{ source: '2', target: 'n11' },
|
||||||
|
{ source: '6', target: 'n12' }, { source: '7', target: 'n13' }, { source: '8', target: 'n14' },
|
||||||
|
];
|
||||||
|
|
||||||
// A. 当时间轴被点击时,更新核心状态
|
// 5. ECharts 基础配置
|
||||||
const onTimePointClick = (pointId) => {
|
const chartOptions = {
|
||||||
activeTimePoint.value = pointId;
|
tooltip: {},
|
||||||
|
animationDurationUpdate: 1000,
|
||||||
|
animationEasingUpdate: 'quinticInOut',
|
||||||
|
series: [{
|
||||||
|
type: 'graph', layout: 'force', roam: true, draggable: true,
|
||||||
|
categories: [
|
||||||
|
{ name: '政府官号', itemStyle: { color: '#E06300', shadowBlur: 20, shadowColor: '#E06300', borderColor: '#FF8A2B', borderWidth: 2 } },
|
||||||
|
{ name: '新闻媒体/自媒体', itemStyle: { color: '#0A53B5', shadowBlur: 15, shadowColor: '#3AA1F8', borderColor: '#3AA1F8', borderWidth: 2 } },
|
||||||
|
{ name: '普通用户', itemStyle: { color: '#0B4B69', borderColor: '#1A8BFF', borderWidth: 1 } },
|
||||||
|
],
|
||||||
|
label: { position: 'right', formatter: '{b}', show: false },
|
||||||
|
lineStyle: { color: 'source', curveness: 0.1, opacity: 0.5 },
|
||||||
|
force: { repulsion: 120, edgeLength: 80, gravity: 0.1 },
|
||||||
|
emphasis: { focus: 'adjacency', lineStyle: { width: 5 } },
|
||||||
|
data: [], links: [],
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
// B. 更新图表的函数
|
// --- 核心联动逻辑 ---
|
||||||
const updateGraphData = () => {
|
const updateViewForTimePoint = (timePoint) => {
|
||||||
if (!myChart) return;
|
// 1. 更新左侧列表的数据
|
||||||
// 从 `visibleLeaders` 计算当前应显示的节点和连线
|
visibleLeaders.value = allLeaderData.slice(0, timePoint);
|
||||||
const leadersSet = new Set(visibleLeaders.value.map(l => l.id));
|
|
||||||
const currentVisibleLinks = allGraphLinks.value.filter(link => leadersSet.has(link.source) && leadersSet.has(link.target));
|
// 2. 更新右侧图表的数据
|
||||||
const currentVisibleNodes = allGraphNodes.value.filter(node => leadersSet.has(node.id));
|
if (!myChart) return;
|
||||||
|
|
||||||
|
const leadersToShowIds = visibleLeaders.value.map(l => l.id);
|
||||||
|
const leadersSet = new Set(leadersToShowIds);
|
||||||
|
|
||||||
|
const visibleLinks = allGraphLinks.filter(link =>
|
||||||
|
leadersSet.has(link.source) && leadersSet.has(link.target) // 只显示当前领袖之间的连线
|
||||||
|
);
|
||||||
|
|
||||||
|
const visibleNodeIds = new Set(leadersToShowIds);
|
||||||
|
// 如果需要显示与领袖相连的普通用户,可以放开下面这段逻辑
|
||||||
|
// visibleLinks.forEach(link => {
|
||||||
|
// visibleNodeIds.add(link.source);
|
||||||
|
// visibleNodeIds.add(link.target);
|
||||||
|
// });
|
||||||
|
|
||||||
|
const visibleNodes = allGraphNodes.filter(node => visibleNodeIds.has(node.id));
|
||||||
|
|
||||||
// 使用计算出的数据更新图表
|
|
||||||
myChart.setOption({
|
myChart.setOption({
|
||||||
series: [{
|
series: [{ data: visibleNodes, links: visibleLinks }]
|
||||||
data: currentVisibleNodes,
|
|
||||||
links: currentVisibleLinks
|
|
||||||
}]
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// C. ECharts 初始化
|
// --- 生命周期与侦听器 ---
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (chartContainer.value) {
|
if (chartContainer.value) {
|
||||||
myChart = echarts.init(chartContainer.value);
|
myChart = echarts.init(chartContainer.value);
|
||||||
myChart.setOption({
|
myChart.setOption(chartOptions);
|
||||||
tooltip: {}, animationDurationUpdate: 1000,
|
updateViewForTimePoint(activeTimePoint.value); // 初始加载
|
||||||
series: [{
|
window.addEventListener('resize', resizeChart);
|
||||||
type: 'graph', layout: 'force', roam: true,
|
|
||||||
categories: [
|
|
||||||
{ name: '自媒体/政府', itemStyle: { color: '#E06300', shadowBlur: 20, shadowColor: '#E06300', borderWidth: 2, borderColor: '#FF8A2B' } },
|
|
||||||
{ name: '新闻媒体', itemStyle: { color: '#0A53B5', shadowBlur: 15, shadowColor: '#3AA1F8', borderWidth: 2, borderColor: '#3AA1F8' } },
|
|
||||||
],
|
|
||||||
label: { show: false }, lineStyle: { color: 'source', curveness: 0.1, opacity: 0.5 },
|
|
||||||
force: { repulsion: 150, edgeLength: 100, gravity: 0.1 },
|
|
||||||
data: [], links: [],
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
updateGraphData(); // 初始渲染
|
|
||||||
window.addEventListener('resize', myChart.resize);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// D. 侦听核心状态的变化,自动更新图表
|
onBeforeUnmount(() => {
|
||||||
watch(activeTimePoint, () => {
|
window.removeEventListener('resize', resizeChart);
|
||||||
if (myChart) {
|
if (myChart) myChart.dispose();
|
||||||
updateGraphData();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
watch(activeTimePoint, (newTimePoint) => {
|
||||||
if (myChart) {
|
updateViewForTimePoint(newTimePoint);
|
||||||
window.removeEventListener('resize', myChart.resize);
|
|
||||||
myChart.dispose();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resizeChart = () => {
|
||||||
|
if (myChart) myChart.resize();
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.main-container {
|
/* 全局布局 */
|
||||||
display: flex;
|
.opinion-leader-layout {
|
||||||
flex-direction: row;
|
width: 1200px;
|
||||||
gap: 10px;
|
height: 700px;
|
||||||
padding: 10px;
|
|
||||||
background-color: #031024;
|
background-color: #031024;
|
||||||
|
color: #cce7ff;
|
||||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
color: #fff;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #1a8bff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧面板样式 (从 OpinionLeaders.vue 合并) */
|
.main-header {
|
||||||
.left-panel { width: 350px; flex-shrink: 0; background-color: rgba(6, 45, 90, 0.3); border: 1px solid #1a8bff; display: flex; flex-direction: column; padding: 15px; border-radius: 4px; }
|
padding: 10px 20px;
|
||||||
.panel-title { font-size: 18px; font-weight: bold; text-align: center; padding: 10px; margin: 0 0 15px 0; background: linear-gradient(to right, rgba(58,161,248,0), rgba(58,161,248,0.3), rgba(58,161,248,0)); border-top: 1px solid #3aa1f8; border-bottom: 1px solid #3aa1f8; color: #fff; }
|
font-size: 14px;
|
||||||
.tabs { display: flex; margin-bottom: 15px; border-bottom: 2px solid #1a5a9c; }
|
background-color: rgba(10, 35, 68, 0.5);
|
||||||
.tabs button { background: none; border: none; color: #a9c2e0; padding: 8px 16px; font-size: 14px; cursor: pointer; transition: all 0.3s ease; position: relative; }
|
border-bottom: 1px solid #1a8bff;
|
||||||
.tabs button.active { color: #fff; font-weight: bold; }
|
}
|
||||||
.tabs button.active::after { content: ''; position: absolute; bottom: -2px; left: 0; width: 100%; height: 2px; background-color: #3aa1f8; }
|
|
||||||
.leader-list { flex-grow: 1; overflow-y: auto; color: #fff;}
|
.main-content {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧面板 */
|
||||||
|
.left-panel {
|
||||||
|
width: 350px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: rgba(6, 45, 90, 0.3);
|
||||||
|
border-right: 1px solid #1a8bff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
background: linear-gradient(to right, rgba(58,161,248,0), rgba(58,161,248,0.3), rgba(58,161,248,0));
|
||||||
|
border-top: 1px solid #3aa1f8;
|
||||||
|
border-bottom: 1px solid #3aa1f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-bottom: 2px solid #1a5a9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #a9c2e0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs button.active {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs button.active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #3aa1f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leader-list {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动条样式 */
|
||||||
.leader-list::-webkit-scrollbar { width: 4px; }
|
.leader-list::-webkit-scrollbar { width: 4px; }
|
||||||
.leader-list::-webkit-scrollbar-track { background: transparent; }
|
.leader-list::-webkit-scrollbar-track { background: transparent; }
|
||||||
.leader-list::-webkit-scrollbar-thumb { background: #3aa1f8; border-radius: 2px; }
|
.leader-list::-webkit-scrollbar-thumb { background: #3aa1f8; border-radius: 2px; }
|
||||||
.leader-item { display: flex; align-items: center; padding: 10px 5px; border-bottom: 1px solid rgba(58, 161, 248, 0.2); }
|
|
||||||
.avatar { width: 50px; height: 50px; border-radius: 50%; margin-right: 15px; flex-shrink: 0; }
|
|
||||||
.info { display: flex; flex-direction: column; gap: 5px; }
|
|
||||||
.name { display: flex; align-items: baseline; gap: 8px; }
|
|
||||||
.en-name { font-size: 16px; font-weight: bold; }
|
|
||||||
.cn-name { font-size: 14px; color: #a9c2e0; }
|
|
||||||
.stats { font-size: 12px; color: #a9c2e0; display: flex; gap: 20px; }
|
|
||||||
|
|
||||||
/* 右侧面板样式 (从 PelosiGraph.vue 合并) */
|
.leader-item {
|
||||||
.right-panel { flex-grow: 1; height: 540px; position: relative; }
|
display: flex;
|
||||||
.key-node-recognition { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
align-items: center;
|
||||||
.background-svg-wrapper { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
|
padding: 10px 5px;
|
||||||
.content-wrapper { position: relative; z-index: 2; display: flex; flex-direction: column; height: 100%; padding: 15px 20px; box-sizing: border-box; }
|
border-bottom: 1px solid rgba(58, 161, 248, 0.2);
|
||||||
.graph-title { text-align: center; font-size: 20px; font-weight: bold; color: #cce7ff; letter-spacing: 2px; margin: 0 0 5px 0; text-shadow: 0 0 5px rgba(58, 161, 248, 0.5); }
|
}
|
||||||
.chart-container { flex-grow: 1; width: 100%; height: calc(100% - 100px); }
|
|
||||||
.timeline-container { width: 100%; height: 50px; display: flex; align-items: center; justify-content: space-between; padding: 0 10px; box-sizing: border-box; }
|
.avatar {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 15px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.en-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cn-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #a9c2e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #a9c2e0;
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧面板 */
|
||||||
|
.right-panel {
|
||||||
|
flex-grow: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-node-recognition {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-svg-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; width: 100%; height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
position: relative; z-index: 2; display: flex; flex-direction: column;
|
||||||
|
height: 100%; padding: 15px 20px; box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
text-align: center; font-size: 20px; font-weight: bold; color: #cce7ff;
|
||||||
|
letter-spacing: 2px; margin: 0 0 5px 0; text-shadow: 0 0 5px rgba(58, 161, 248, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
flex-grow: 1; width: 100%;
|
||||||
|
height: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-container, .time-label, .timeline-track, .timeline-point-wrapper, .timeline-point, .active-pin {
|
||||||
|
/* 时间轴样式与上一版完全相同 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.timeline-container { width: 100%; height: 50px; display: flex; align-items: center; justify-content: space-between; padding: 0 10px;}
|
||||||
.time-label { font-size: 12px; color: #a9c2e0; }
|
.time-label { font-size: 12px; color: #a9c2e0; }
|
||||||
.timeline-track { flex-grow: 1; height: 4px; background: linear-gradient(90deg, #1b62a9, #3aa1f8, #1b62a9); margin: 0 15px; position: relative; display: flex; justify-content: space-between; align-items: center; }
|
.timeline-track { flex-grow: 1; height: 4px; background: linear-gradient(90deg, #1b62a9, #3aa1f8, #1b62a9); margin: 0 15px; position: relative; display: flex; justify-content: space-between; align-items: center;}
|
||||||
.timeline-point-wrapper { display: flex; align-items: center; justify-content: center; height: 20px; cursor: pointer; }
|
.timeline-point-wrapper { display: flex; align-items: center; justify-content: center; height: 20px; cursor: pointer;}
|
||||||
.timeline-point { width: 10px; height: 10px; background-color: #8dc5ff; border-radius: 50%; border: 1px solid #cce7ff; transition: transform 0.3s ease; position: relative; }
|
.timeline-point { width: 10px; height: 10px; background-color: #8dc5ff; border-radius: 50%; border: 1px solid #cce7ff; transition: transform 0.3s ease; position: relative;}
|
||||||
.timeline-point-wrapper:hover .timeline-point { transform: scale(1.5); }
|
.timeline-point-wrapper:hover .timeline-point { transform: scale(1.5); }
|
||||||
.timeline-point.active { background-color: #ffc94d; border-color: #fff; transform: scale(1.3); }
|
.timeline-point.active { background-color: #ffc94d; border-color: #fff; transform: scale(1.3); }
|
||||||
.active-pin { width: 20px; height: 24px; background-image: url('data:image/svg+xml;utf8,<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 24L15 14H5L10 24Z" fill="%23FFC94D"/><path d="M1.5 8C1.5 4.41015 4.41015 1.5 8 1.5H12C15.5899 1.5 18.5 4.41015 18.5 8V11C18.5 12.6569 17.1569 14 15.5 14H4.5C2.84315 14 1.5 12.6569 1.5 11V8Z" fill="%23FFC94D" stroke="white"/></svg>'); position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); }
|
.active-pin { width: 20px; height: 24px; background-image: url('data:image/svg+xml;utf8,<svg width="20" height="24" viewBox="0 0 20 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 24L15 14H5L10 24Z" fill="%23FFC94D"/><path d="M1.5 8C1.5 4.41015 4.41015 1.5 8 1.5H12C15.5899 1.5 18.5 4.41015 18.5 8V11C18.5 12.6569 17.1569 14 15.5 14H4.5C2.84315 14 1.5 12.6569 1.5 11V8Z" fill="%23FFC94D" stroke="white"/></svg>'); position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%);}
|
||||||
</style>
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user