提交 a2c79cbe authored 作者: caijian's avatar caijian

智库UI更新

上级 635c86d8
<template>
<div class="policy-tracker-container">
<PolicyOverview />
<!-- 顶部搜索栏 -->
<div class="top-search-bar" v-if="props.showSearch">
<div class="search-left">
......@@ -80,6 +81,7 @@ import { Search } from '@element-plus/icons-vue';
import PolicyList from './PolicyList.vue';
import CardTitle from './CardTitle.vue';
import { getOverviewPolicy } from '@/api'
import PolicyOverview from '@/views/thinkTank/components/PolicyOverview.vue'
const props = defineProps({
showSearch: {
......
......@@ -184,6 +184,7 @@
import { ref, computed } from 'vue'
import { ArrowRight, Download } from '@element-plus/icons-vue'
import CardTitle from '@/components/CardTitle.vue'
import PolicyOverview from '../components/PolicyOverview.vue'
// props
const props = defineProps({
......
......@@ -77,7 +77,7 @@ const currentComponent = computed(() => {
onMounted(() => {
getThinkTankSummary({ id: route.params.id }).then(res => {
console.log(res.data);
summary.value = res.data;
summary.value = res.data || {};
});
});
</script>
......
<template>
<div class="dashboard-container">
<div class="top-row">
<el-card class="chart-card" shadow="hover">
<template #header>
<div class="card-header">
<div class="title">
<el-icon color="#409EFF"><TrendCharts /></el-icon>
<span>政策建议趋势分布</span>
</div>
<el-select v-model="lineChartPeriod" size="small" style="width: 100px">
<el-option label="近十年" value="10years" />
<el-option label="近五年" value="5years" />
</el-select>
</div>
</template>
<div ref="lineChartRef" class="chart-container"></div>
</el-card>
<el-card class="chart-card" shadow="hover">
<template #header>
<div class="card-header">
<div class="title">
<el-icon color="#409EFF"><PieChart /></el-icon>
<span>政策建议领域分布</span>
</div>
<el-select v-model="pieChartYear" size="small" style="width: 100px">
<el-option label="2025年" value="2025" />
<el-option label="2024年" value="2024" />
</el-select>
</div>
</template>
<div ref="pieChartRef" class="chart-container"></div>
</el-card>
</div>
<div class="bottom-row">
<el-card class="chart-card" shadow="hover">
<template #header>
<div class="card-header">
<div class="title">
<el-icon color="#409EFF"><Share /></el-icon>
<span>智库资金流向</span>
</div>
</div>
</template>
<div ref="sankeyChartRef" class="chart-container"></div>
</el-card>
<el-card class="chart-card" shadow="hover">
<template #header>
<div class="card-header">
<div class="title">
<el-icon color="#409EFF"><Opportunity /></el-icon>
<span>智库研究热点</span>
</div>
<el-select v-model="hotspotPeriod" size="small" style="width: 100px">
<el-option label="近一年" value="1year" />
<el-option label="近半年" value="halfyear" />
</el-select>
</div>
</template>
<div class="list-container">
<div v-for="(item, index) in hotspotList" :key="index" class="list-item">
<div class="rank-wrapper">
<span :class="['rank-num', index < 3 ? 'top-rank' : 'normal-rank']">
{{ index + 1 }}
</span>
</div>
<div class="content-text" :title="item.title">{{ item.title }}</div>
<div class="report-count">{{ item.count }}份报告 ></div>
</div>
</div>
</el-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts';
import {
TrendCharts,
PieChart,
Share,
Opportunity
} from '@element-plus/icons-vue';
// --- 状态管理 ---
const lineChartPeriod = ref('10years');
const pieChartYear = ref('2025');
const hotspotPeriod = ref('1year');
// --- DOM 引用 ---
const lineChartRef = ref(null);
const pieChartRef = ref(null);
const sankeyChartRef = ref(null);
// --- 模拟数据 ---
// 列表数据
const hotspotList = ref([
{ title: '人工智能领域竞争发展', count: 11 },
{ title: '美元未来与能源出口挂钩', count: 7 },
{ title: '量子领域国家合作', count: 5 },
{ title: '限制中国产燃油进口', count: 5 },
{ title: '禁止政府部门采购受控半导体或服务', count: 4 },
{ title: '禁止向部分中国实体提供资金', count: 3 },
{ title: '中国生产电池', count: 3 },
{ title: '重视新兴中国技术公司威胁', count: 2 },
{ title: '禁止卫星出口至中国', count: 1 },
{ title: '华为设备', count: 1 },
]);
// ECharts 实例
let lineChart = null;
let pieChart = null;
let sankeyChart = null;
// --- 初始化图表 ---
const initLineChart = () => {
if (!lineChartRef.value) return;
lineChart = echarts.init(lineChartRef.value);
const option = {
color: ['#409EFF', '#67C23A', '#909399', '#E6A23C', '#73C0DE', '#F56C6C'],
tooltip: { trigger: 'axis' },
legend: {
data: ['人工智能', '集成电路', '量子科技', '生物科技', '通信网络', '能源'],
bottom: 'top',
icon: 'circle'
},
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
boundaryGap: false,
data: ['2014', '2015', '2016', '2017', '2018', '2019', '2020', '2021', '2022', '2023', '2024', '2025']
},
yAxis: { type: 'value' },
series: [
{ name: '人工智能', type: 'line', smooth: true, areaStyle: { opacity: 0.1 }, data: [150, 80, 90, 100, 80, 100, 130, 150, 140, 170, 190, 175] },
{ name: '集成电路', type: 'line', smooth: true, areaStyle: { opacity: 0.1 }, data: [70, 100, 125, 115, 135, 150, 168, 165, 165, 172, 172, 185] },
{ name: '量子科技', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10] }, // 模拟低数据
{ name: '生物科技', type: 'line', smooth: true, data: [10, 5, 5, 10, 10, 15, 20, 30, 28, 30, 45, 45] },
{ name: '通信网络', type: 'line', smooth: true, areaStyle: { opacity: 0.1 }, data: [40, 35, 45, 50, 50, 48, 55, 75, 65, 65, 68, 60] },
{ name: '能源', type: 'line', smooth: true, areaStyle: { opacity: 0.1 }, data: [100, 75, 65, 85, 65, 75, 85, 120, 118, 150, 140, 110] },
]
};
lineChart.setOption(option);
};
const initPieChart = () => {
if (!pieChartRef.value) return;
pieChart = echarts.init(pieChartRef.value);
const option = {
tooltip: { trigger: 'item' },
legend: {
orient: 'vertical',
left: '5%',
top: 'center',
itemGap: 20,
formatter: (name) => {
// 简单的 mock 数据映射,实际应从 data 动态获取
const dataMap = {
'人工智能': '50项 21%',
'集成电路': '46项 18%',
'通信网络': '40项 16%',
'能源': '32项 14%',
'量子科技': '31项 12%',
'生物科技': '30项 11%',
'其他': '24项 8%'
};
return `{name|${name}}\n{val|${dataMap[name]}}`;
},
textStyle: {
rich: {
name: { fontWeight: 'bold', fontSize: 14, padding: [0, 0, 5, 0] },
val: { color: '#666', fontSize: 12 }
}
}
},
series: [
{
name: '领域分布',
type: 'pie',
radius: ['45%', '70%'],
center: ['65%', '50%'], // 向右偏移,留位置给 Legend
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: { show: false }, // 隐藏连接线上的标签,使用 Legend
data: [
{ value: 50, name: '人工智能', itemStyle: { color: '#409EFF' } },
{ value: 46, name: '集成电路', itemStyle: { color: '#F56C6C' } },
{ value: 40, name: '通信网络', itemStyle: { color: '#a0cfff' } },
{ value: 32, name: '能源', itemStyle: { color: '#E6A23C' } },
{ value: 31, name: '量子科技', itemStyle: { color: '#cceeff' } },
{ value: 30, name: '生物科技', itemStyle: { color: '#d1bdf2' } },
{ value: 24, name: '其他', itemStyle: { color: '#f2f2f2' } }
]
}
]
};
pieChart.setOption(option);
};
const initSankeyChart = () => {
if (!sankeyChartRef.value) return;
sankeyChart = echarts.init(sankeyChartRef.value);
// 简化的桑基图节点和连接
const nodes = [
{ name: '美国', itemStyle: { color: '#1f5cb8' } },
{ name: '英国', itemStyle: { color: '#6f42c1' } },
{ name: '加拿大', itemStyle: { color: '#d9534f' } },
// 中间层
{ name: '美国能源部', itemStyle: { color: '#a4c639' } },
{ name: '美国财政部', itemStyle: { color: '#f06292' } },
{ name: '美国国务院', itemStyle: { color: '#b08d55' } },
{ name: '美国国际开发署', itemStyle: { color: '#4285f4' } },
{ name: '美国内政部', itemStyle: { color: '#8cae9e' } },
{ name: '美国国家民主基金会', itemStyle: { color: '#a1887f' } },
// 右侧层 (部分)
{ name: '麻省理工学院科技评论', itemStyle: { color: '#7cb342' } },
{ name: '麦肯锡全球研究院', itemStyle: { color: '#cddc39' } },
{ name: '卡内基国际和平研究院', itemStyle: { color: '#afb42b' } },
{ name: '战略与国际研究中心', itemStyle: { color: '#827717' } },
{ name: '兰德公司', itemStyle: { color: '#689f38' } },
];
const links = [
{ source: '美国', target: '美国能源部', value: 50 },
{ source: '美国', target: '美国财政部', value: 40 },
{ source: '美国', target: '美国国务院', value: 60 },
{ source: '美国', target: '美国国际开发署', value: 20 },
{ source: '美国', target: '美国内政部', value: 15 },
{ source: '英国', target: '美国国务院', value: 10 },
{ source: '加拿大', target: '美国能源部', value: 5 },
// Flow to right
{ source: '美国能源部', target: '麻省理工学院科技评论', value: 20 },
{ source: '美国能源部', target: '麦肯锡全球研究院', value: 25 },
{ source: '美国财政部', target: '卡内基国际和平研究院', value: 30 },
{ source: '美国国务院', target: '战略与国际研究中心', value: 40 },
{ source: '美国国务院', target: '兰德公司', value: 15 },
];
const option = {
tooltip: { trigger: 'item', triggerOn: 'mousemove' },
series: [
{
type: 'sankey',
layout: 'none',
emphasis: { focus: 'adjacency' },
nodeAlign: 'left',
data: nodes,
links: links,
lineStyle: { color: 'gradient', curveness: 0.5, opacity: 0.3 },
label: { position: 'right', color: '#666', fontSize: 10 }
}
]
};
sankeyChart.setOption(option);
};
// --- 生命周期钩子 ---
onMounted(() => {
nextTick(() => {
initLineChart();
initPieChart();
initSankeyChart();
});
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
lineChart?.dispose();
pieChart?.dispose();
sankeyChart?.dispose();
});
const handleResize = () => {
lineChart?.resize();
pieChart?.resize();
sankeyChart?.resize();
};
</script>
<style scoped>
.dashboard-container {
background-color: #f5f7fa;
padding: 20px;
display: flex;
flex-direction: column;
gap: 20px;
height: 100vh; /* 或根据需要调整高度 */
box-sizing: border-box;
}
/* 通用布局类 */
.top-row, .bottom-row {
display: grid;
grid-template-columns: 1.5fr 1fr; /* 2列等宽 */
gap: 20px;
flex: 1; /* 上下平分高度 */
min-height: 0; /* 防止溢出 */
}
.chart-card {
display: flex;
flex-direction: column;
height: 100%;
border-radius: 8px;
}
/* Element Plus Card 样式覆盖 */
:deep(.el-card__header) {
padding: 15px 20px;
border-bottom: 1px solid #ebeef5;
}
:deep(.el-card__body) {
flex: 1;
padding: 10px;
position: relative;
height: 0; /* 关键:配合 flex:1 让内部图表自适应高度 */
}
/* 头部样式 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: bold;
color: #303133;
}
/* 图表容器 */
.chart-container {
width: 100%;
height: 100%;
}
/* 列表样式 */
.list-container {
height: 100%;
overflow-y: auto;
padding: 0 10px;
}
.list-item {
display: flex;
align-items: center;
padding: 12px 0;
border-bottom: 1px dashed #eee;
font-size: 14px;
color: #606266;
}
.list-item:last-child {
border-bottom: none;
}
.rank-wrapper {
width: 30px;
text-align: center;
margin-right: 10px;
}
.rank-num {
font-weight: bold;
font-family: Arial, sans-serif;
}
.top-rank {
color: #E6A23C; /* 前三名金色 */
font-size: 16px;
}
.normal-rank {
color: #909399; /* 其他灰色 */
font-size: 14px;
}
.content-text {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #303133;
cursor: pointer;
}
.content-text:hover {
color: #409EFF;
}
.report-count {
color: #909399;
font-size: 12px;
margin-left: 10px;
}
/* 响应式调整 (可选) */
@media (max-width: 1024px) {
.top-row, .bottom-row {
grid-template-columns: 1fr; /* 小屏幕变为单列 */
}
.dashboard-container {
height: auto; /* 允许滚动 */
}
.chart-card {
height: 400px; /* 固定高度 */
}
}
</style>
\ No newline at end of file
<template>
<div class="dashboard-container">
<el-row :gutter="20">
<el-col :span="8" :xs="24">
<el-card shadow="hover" class="dashboard-card">
<template #header>
<div class="card-header">
<div class="title-group">
<el-icon class="icon-blue"><PieChart /></el-icon>
<span class="title">提出建议领域分布</span>
</div>
<span class="link">查看数据源 ></span>
</div>
</template>
<div class="card-body">
<div class="filter-row">
<el-select v-model="yearLeft" size="small" class="year-select">
<el-option label="2025年" value="2025" />
<el-option label="2024年" value="2024" />
</el-select>
</div>
<div ref="leftChartRef" class="chart-container"></div>
</div>
</el-card>
</el-col>
<el-col :span="8" :xs="24">
<el-card shadow="hover" class="dashboard-card">
<template #header>
<div class="card-header">
<div class="title-group">
<el-icon class="icon-orange"><Flag /></el-icon>
<span class="title">相关政策领域分布</span>
</div>
<span class="link">查看数据源 ></span>
</div>
</template>
<div class="card-body">
<div class="filter-row">
<el-select v-model="yearMiddle" size="small" class="year-select">
<el-option label="2025年" value="2025" />
<el-option label="2024年" value="2024" />
</el-select>
</div>
<div class="policy-list">
<div v-for="(item, index) in currentPolicyList" :key="index" class="policy-item">
<div class="item-label">
<span class="dot" :style="{ backgroundColor: item.color }"></span>
<span class="name">{{ item.name }}</span>
</div>
<div class="progress-track">
<div
class="progress-bar"
:style="{ width: item.percent + '%', backgroundColor: item.color }"
></div>
</div>
<div class="item-value">
<span class="count">{{ item.current }}项 / {{ item.total }}项</span>
<span class="percent">{{ item.percent }}%</span>
</div>
</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8" :xs="24">
<el-card shadow="hover" class="dashboard-card">
<template #header>
<div class="card-header">
<div class="title-group">
<el-icon class="icon-orange"><TrendCharts /></el-icon>
<span class="title">热门研究方向变化趋势</span>
</div>
<span class="link">查看数据源 ></span>
</div>
</template>
<div class="card-body">
<div class="filter-row right-filter">
<el-select v-model="yearRight" size="small" class="year-select">
<el-option label="2025年" value="2025" />
<el-option label="2024年" value="2024" />
</el-select>
</div>
<div ref="rightChartRef" class="chart-container"></div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick, watch, computed } from 'vue'
import { PieChart, Flag, TrendCharts } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
// ==================== 1. 模拟数据源 (Mock Data) ====================
// 视觉颜色常量 (为了还原图片,不同图表用了不同色系)
const COLORS = {
pie: ['#409EFF', '#F56C6C', '#a0cfff', '#E6A23C', '#b3e19d', '#d1edc4', '#909399'],
line: {
ic: '#409EFF', // 集成电路
ai: '#67C23A', // 人工智能 (青绿)
comm: '#8e44ad', // 通信 (紫)
quantum: '#E6A23C', // 量子 (橙)
bio: '#a0cfff', // 生物 (淡蓝)
energy: '#F56C6C' // 能源 (红)
}
}
const mockDatabase = {
'2025': {
// 左侧环形图数据
pieData: [
{ value: 50, name: '集成电路' },
{ value: 46, name: '人工智能' },
{ value: 40, name: '通信网络' },
{ value: 32, name: '量子科技' },
{ value: 31, name: '生物科技' },
{ value: 30, name: '能源' },
{ value: 24, name: '其他' }
],
// 中间列表数据
policyData: [
{ name: '集成电路', current: 2, total: 10, percent: 20, color: '#c0392b' },
{ name: '人工智能', current: 1, total: 6, percent: 17, color: '#2e7d32' },
{ name: '通信网络', current: 2, total: 7, percent: 26, color: '#f39c12' },
{ name: '量子科技', current: 1, total: 2, percent: 50, color: '#c0392b' },
{ name: '生物科技', current: 3, total: 7, percent: 43, color: '#c0392b' },
{ name: '能源', current: 11, total: 20, percent: 55, color: '#c0392b' }
],
// 右侧折线图数据
lineData: {
xAxis: ['2023\nQ1', '2023\nQ3', '2024\nQ1', '2024\nQ3', '2025\nQ1', '2025\nQ3'],
series: {
'集成电路': [32, 13, 20, 28, 37, 40],
'量子科技': [29, 18, 15, 19, 14, 9],
'生物科技': [15, 17, 24, 23, 16, 24],
'能源': [20, 22, 12, 14, 19, 5],
'通信网络': [3, 3, 1, 6, 4, 2],
'人工智能': [5, 15, 25, 10, 15, 18]
}
}
},
'2024': {
pieData: [
{ value: 40, name: '集成电路' },
{ value: 35, name: '人工智能' },
{ value: 45, name: '通信网络' },
{ value: 20, name: '量子科技' },
{ value: 25, name: '生物科技' },
{ value: 35, name: '能源' },
{ value: 15, name: '其他' }
],
policyData: [
{ name: '集成电路', current: 5, total: 8, percent: 62, color: '#c0392b' },
{ name: '人工智能', current: 3, total: 5, percent: 60, color: '#2e7d32' },
{ name: '通信网络', current: 1, total: 10, percent: 10, color: '#f39c12' },
{ name: '量子科技', current: 0, total: 2, percent: 0, color: '#c0392b' },
{ name: '生物科技', current: 4, total: 6, percent: 66, color: '#c0392b' },
{ name: '能源', current: 8, total: 15, percent: 53, color: '#c0392b' }
],
lineData: {
xAxis: ['2022\nQ1', '2022\nQ3', '2023\nQ1', '2023\nQ3', '2024\nQ1', '2024\nQ3'],
series: {
'集成电路': [10, 20, 25, 30, 35, 32],
'量子科技': [5, 8, 12, 15, 20, 29],
'生物科技': [20, 18, 15, 17, 15, 15],
'能源': [15, 14, 18, 20, 22, 12],
'通信网络': [8, 7, 6, 5, 3, 1],
'人工智能': [12, 18, 20, 22, 15, 25]
}
}
}
}
// ==================== 2. 状态管理 ====================
const yearLeft = ref('2025')
const yearMiddle = ref('2025')
const yearRight = ref('2025')
const leftChartRef = ref(null)
const rightChartRef = ref(null)
let leftChart = null
let rightChart = null
// 计算属性:获取当前中间列表的数据
const currentPolicyList = computed(() => {
return mockDatabase[yearMiddle.value]?.policyData || []
})
// ==================== 3. 图表逻辑 ====================
// --- 左侧环形图 ---
const updateLeftChart = () => {
if (!leftChart || !leftChartRef.value) return
const data = mockDatabase[yearLeft.value]?.pieData
if (!data) return
const option = {
color: COLORS.pie,
tooltip: { trigger: 'item' },
legend: { show: false }, // 隐藏图例,直接显示Label
series: [
{
name: '建议领域',
type: 'pie',
radius: ['45%', '70%'],
center: ['50%', '55%'],
avoidLabelOverlap: true,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: '{b}\n{c}项 {d}%',
lineHeight: 16,
color: '#606266',
fontSize: 12
},
labelLine: {
show: true,
length: 15,
length2: 10
},
data: data
}
]
}
leftChart.setOption(option)
// 确保图表正确渲染
leftChart.resize()
}
// --- 右侧折线图 ---
const updateRightChart = () => {
if (!rightChart) return
const data = mockDatabase[yearRight.value].lineData
const option = {
tooltip: { trigger: 'axis' },
legend: {
data: ['集成电路', '人工智能', '通信网络', '量子科技', '生物科技', '能源'],
top: 0,
left: 0,
icon: 'circle',
itemWidth: 8,
itemHeight: 8,
textStyle: { fontSize: 10, padding: [0, 2, 0, 0] }
},
grid: {
left: '2%',
right: '3%',
bottom: '3%',
top: '28%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false, // 折线图点在轴线上
data: data.xAxis,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { color: '#909399', fontSize: 10 }
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: { type: 'dotted', color: '#ebeef5' }
},
axisLabel: { color: '#909399' }
},
series: [
{
name: '集成电路', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.ic },
data: data.series['集成电路']
},
{
name: '量子科技', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.quantum },
data: data.series['量子科技']
},
{
name: '生物科技', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.bio },
data: data.series['生物科技']
},
{
name: '能源', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.energy },
data: data.series['能源']
},
{
name: '通信网络', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.comm },
data: data.series['通信网络']
},
{
name: '人工智能', type: 'line', smooth: false, symbol: 'circle', symbolSize: 6,
itemStyle: { color: COLORS.line.ai },
data: data.series['人工智能']
}
]
}
rightChart.setOption(option, true) // true = 不合并,完全重绘,避免旧线条残留
}
// ==================== 4. 生命周期与监听 ====================
// 监听年份变化,刷新图表
watch(yearLeft, () => updateLeftChart())
watch(yearRight, () => updateRightChart())
// 中间列表由 computed 自动处理,无需 watch
const resizeHandler = () => {
leftChart?.resize()
rightChart?.resize()
}
onMounted(() => {
nextTick(() => {
// 确保 DOM 完全渲染后再初始化
setTimeout(() => {
// 初始化实例
if (leftChartRef.value) {
leftChart = echarts.init(leftChartRef.value)
updateLeftChart()
// 确保图表正确渲染
leftChart.resize()
}
if (rightChartRef.value) {
rightChart = echarts.init(rightChartRef.value)
updateRightChart()
// 确保图表正确渲染
rightChart.resize()
}
window.addEventListener('resize', resizeHandler)
}, 100)
})
})
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler)
leftChart?.dispose()
rightChart?.dispose()
})
</script>
<style lang="scss" scoped>
// 变量定义
$card-height: 360px;
$bg-color: #f5f7fa;
$text-main: #303133;
$text-sub: #909399;
$border-color: #ebeef5;
.dashboard-container {
// background-color: $bg-color;
padding: 20px;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
.dashboard-card {
height: $card-height;
display: flex;
flex-direction: column;
border: none;
border-radius: 8px;
background: #fff;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
margin-bottom: 10px; // 移动端换行间距
:deep(.el-card__header) {
padding: 15px 20px;
border-bottom: 1px solid #f0f0f0;
}
:deep(.el-card__body) {
flex: 1;
padding: 15px 20px;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
}
// 头部样式
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.title-group {
display: flex;
align-items: center;
gap: 8px;
.title {
font-weight: 700;
font-size: 16px;
color: $text-main;
}
.el-icon { font-size: 18px; }
.icon-blue { color: #409EFF; }
.icon-orange { color: #E6A23C; }
}
.link {
font-size: 12px;
color: #409EFF;
cursor: pointer;
transition: opacity 0.3s;
&:hover { opacity: 0.8; }
}
}
// 过滤器区域
.filter-row {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
z-index: 5;
.year-select {
width: 90px;
}
&.right-filter {
position: absolute;
right: 20px;
top: 15px; // 调整位置以避开图例
}
}
// ECharts 容器
.chart-container {
width: 100%;
flex: 1;
min-height: 200px; // 确保有最小高度,让图表可以渲染
height: 0; // 配合 flex: 1 使用
}
// 中间进度列表样式
.policy-list {
display: flex;
flex-direction: column;
justify-content: space-between; // 上下均匀分布
height: 100%;
padding-bottom: 5px;
.policy-item {
display: flex;
align-items: center;
font-size: 12px;
color: #606266;
.item-label {
width: 85px; // 固定宽度对齐
display: flex;
align-items: center;
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
flex-shrink: 0;
}
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
}
}
.progress-track {
flex: 1;
height: 6px;
background-color: #f2f3f5;
border-radius: 3px;
margin: 0 15px;
overflow: hidden;
.progress-bar {
height: 100%;
border-radius: 3px;
transition: width 0.5s ease; // 宽度变化动画
}
}
.item-value {
width: 110px;
display: flex;
justify-content: space-between;
color: $text-sub;
.count { font-family: Arial, sans-serif; }
.percent {
width: 35px;
text-align: right;
font-family: Arial, sans-serif;
}
}
}
}
</style>
\ No newline at end of file
......@@ -33,7 +33,37 @@
<div class="stat-label">热点科技领域</div>
</div>
</div>
<div class="home-main-header-footer-link">
<ClickableCard text="最新动态" link="/billHome" target="_blank" />
<ClickableCard text="资讯要闻" link="/billHome" target="_blank" />
<ClickableCard text="数据总览" link="/billHome" target="_blank" />
<ClickableCard text="资源库" link="/billHome" target="_blank" />
</div>
<!-- 智库展示区域 -->
<div class="think-tanks-section">
<div v-for="tank in thinkTanks" :key="tank.id" class="think-tank-card" @click="handleClick(tank)">
<div class="tank-header">
<div class="tank-logo">
<el-image :src="$withFallbackImage(tank.logo, tank.id)" :alt="tank.name" />
</div>
<div class="tank-country">{{ tank.country }}</div>
</div>
<div class="tank-name">{{ tank.name }}</div>
<div class="tank-description" :title="tank.describe">{{ tank.describe }}</div>
<div class="tank-tags">
<el-tag v-for="tag in tank.tags" :key="tag" type="primary" size="small">
{{ tag }}
</el-tag>
</div>
</div>
<div class="view-all">
<span class="view-all-link">查看全部智库 ></span>
</div>
</div>
<DivideHeader style="margin: 30px auto" :titleText="'最新动态'"></DivideHeader>
<!-- 主要内容区域 -->
<div class="main-content">
<!-- 左侧数据分布 -->
......@@ -82,30 +112,61 @@
</el-button>
</div>
</div>
<!-- 智库展示区域 -->
<div class="think-tanks-section">
<div v-for="tank in thinkTanks" :key="tank.id" class="think-tank-card" @click="handleClick(tank)">
<div class="tank-header">
<div class="tank-logo">
<el-image :src="$withFallbackImage(tank.logo, tank.id)" :alt="tank.name" />
</div>
<div class="tank-country">{{ tank.country }}</div>
</div>
<div class="tank-name">{{ tank.name }}</div>
<div class="tank-description" :title="tank.describe">{{ tank.describe }}</div>
<div class="tank-tags">
<el-tag v-for="tag in tank.tags" :key="tag" type="primary" size="small">
{{ tag }}
</el-tag>
</div>
</div>
<div class="view-all">
<span class="view-all-link">查看全部智库 ></span>
</div>
</div>
<DivideHeader style="margin: 30px auto" :titleText="'资讯要闻'"></DivideHeader>
<!-- 资讯要闻 -->
<div class="center-center">
<div class="box3">
<div class="box3-header">
<div class="box3-header-left">
<div class="box3-header-icon">
<img src="@/views/bill/billHome/assets/images/box3-header-icon.png" alt="" />
</div>
<div class="box3-header-title">{{ "新闻资讯" }}</div>
<div class="more">{{ "更多 +" }}</div>
</div>
</div>
<div class="box3-main">
<div class="box3-item" v-for="(news, index) in newsList" :key="index">
<div class="left">
<img :src="news.img" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="title">{{ news.title }}</div>
<div class="time">{{ news.from }}</div>
</div>
<div class="right-footer">{{ news.content }}</div>
</div>
</div>
</div>
</div>
<div class="box4">
<div class="box4-header">
<div class="header-icon">
<img src="@/views/bill/billHome/assets/images/box4-header-icon.png" alt="" />
</div>
<div class="header-title">{{ "智库人物动态" }}</div>
<div class="more">{{ "更多 +" }}</div>
</div>
<div class="box4-main">
<div class="box4-main-item" v-for="(item, index) in messageList" :key="index">
<div class="left">
<img :src="item.img" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="name">{{ item.name }}</div>
<div class="time">{{ item.time }}</div>
</div>
<div class="content">{{ item.content }}</div>
</div>
</div>
</div>
</div>
</div>
<DivideHeader style="margin: 30px auto" :titleText="'数据总览'"></DivideHeader>
<DataOverview />
<DivideHeader style="margin: 30px auto" :titleText="'资源库'"></DivideHeader>
<ReportList />
</div>
</template>
......@@ -122,13 +183,79 @@ import mit from "@/assets/images/mit.png";
import itif from "@/assets/images/itif.png";
import mckinsley from "@/assets/images/mckinsey.png";
import { getThinkTankList } from "@/api";
import ClickableCard from "@/views/finance/components/link.vue";
import DivideHeader from "@/components/DivideHeader.vue";
import DataOverview from "./components/DataOverview.vue";
import News1 from "@/views/bill/billHome/assets/images/news1.png";
import News2 from "@/views/bill/billHome/assets/images/news2.png";
import News3 from "@/views/bill/billHome/assets/images/news3.png";
import News4 from "@/views/bill/billHome/assets/images/news4.png";
import News5 from "@/views/bill/billHome/assets/images/news5.png";
import Message1 from "@/views/bill/billHome/assets/images/message-icon1.png";
import Message2 from "@/views/bill/billHome/assets/images/message-icon2.png";
import Message3 from "@/views/bill/billHome/assets/images/message-icon3.png";
const router = useRouter();
const searchText = ref("");
const activeTime = ref("本月");
const worldMapRef = ref(null);
// 新闻资讯
const newsList = ref([
{
img: News1,
title: "美政府停摆仍持续,拨款法案存缺陷,但两党磋商露曙光",
content: `美国政府停摆已持续34天,距离历史上最长的停摆纪录仅差一天,参议院已先后13次尝试...`,
from: "11-4 · 华盛顿邮报"
},
{
img: News2,
title: "美参议院通过决议,要求终止特朗普全球关税政策",
content: `参议院以51票赞成、47票反对通过一项决议,旨在终止特朗普实施的全面关税政策,四名......`,
from: "11-4 · 纽约时报"
},
{
img: News3,
title: "美众院通过950亿美元对外援助法案,包含对台军援",
content: `国会众议院在4月通过了大规模对外援助法案,其中包括为“印太安全”提供资金的条款,......`,
from: "11-3 · 洛杉矶时报"
},
{
img: News4,
title: "“大而美”法案在激烈争议中通过",
content: `特朗普力推的大规模税收与支出法案在国会以微弱优势通过。该法案因大幅削减医疗补助和......`,
from: "11-3 · 今日美国"
},
{
img: News5,
title: "美政府“停摆”追平历史最长纪录,民生多领域受重创",
content: `联邦政府“停摆”进入第35天,追平历史纪录。食品救济项目资金中断,数百万低收入民......`,
from: "11-2 · ​福克斯新闻网"
}
]);
// 社交媒体
const messageList = ref([
{
img: Message1,
name: "唐纳德·特朗普",
time: "15:23 · 发布于真实社交",
content: `埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。`
},
{
img: Message2,
name: "埃隆·马斯克",
time: "14:49 · 发布于X",
content: `如果这个疯狂的支出法案获得通过,‘美国党’将在第二天成立。`
},
{
img: Message3,
name: "塞巴斯蒂安·马拉比",
time: "11:05 · 发布于X",
content: `提出特朗普政府的AI政策强调技术开放与快速应用,但可能以牺牲安全防范为代价,开启了“潘多拉魔盒”。`
}
]);
// 世界地图数据 - 各国智库数量统计
const mapData = ref([
{ name: "United States", value: 6, itemStyle: { color: "#ff4757" } },
......@@ -400,6 +527,237 @@ onMounted(() => {
background-position: center -100px;
background-size: 100% 100%;
min-height: 100vh;
.home-main-header-footer-link {
display: flex;
gap: 30px;
padding: 30px 0;
justify-content: center;
}
.center-center {
margin-top: 21px;
height: 450px;
display: flex;
.box3 {
width: 792px;
height: 450px;
box-shadow: 0px 0px 15px 0px rgba(25, 69, 130, 0.2);
background: rgba(255, 255, 255, 1);
.box3-header {
height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
position: relative;
.box3-header-left {
display: flex;
.box3-header-icon {
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box3-header-title {
margin-top: 16px;
margin-left: 19px;
height: 22px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
}
.more {
width: 49px;
height: 24px;
position: absolute;
top: 14px;
right: 27px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
}
}
.box3-main {
height: 402px;
overflow-y: auto;
overflow-x: hidden;
padding-top: 6px;
.box3-item {
display: flex;
height: 77px;
width: 749px;
margin-left: 21px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
.left {
width: 72px;
height: 48px;
margin-top: 15px;
img {
width: 100%;
height: 100%;
}
}
.right {
width: 657px;
margin-left: 20px;
.right-top {
width: 657px;
display: flex;
justify-content: space-between;
.title {
margin-top: 13px;
width: 520px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time {
flex: 1;
text-align: right;
height: 22px;
margin-top: 19px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
.right-footer {
width: 657px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.box4 {
margin-left: 20px;
width: 792px;
height: 450px;
box-shadow: 0px 0px 15px 0px rgba(25, 69, 130, 0.2);
background: rgba(255, 255, 255, 1);
.box4-header {
width: 792px;
height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
box-sizing: border-box;
padding-left: 22px;
position: relative;
.header-icon {
margin-top: 15px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.header-title {
margin-top: 16px;
margin-left: 18px;
height: 22px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
}
.more {
width: 49px;
height: 24px;
position: absolute;
top: 14px;
right: 27px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
}
.box4-main {
height: 402px;
overflow-y: auto;
box-sizing: border-box;
padding-top: 8px;
.box4-main-item {
margin-top: 16px;
display: flex;
margin-left: 21px;
.left {
margin-top: 5px;
width: 36px;
height: 36px;
img {
width: 100%;
height: 100%;
}
}
.right {
margin-left: 10px;
width: 690px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
background: rgba(246, 250, 255, 1);
padding: 10px 15px;
.right-top {
display: flex;
justify-content: space-between;
.name {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.time {
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
}
}
.content {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
}
}
}
}
.search-section {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论