提交 e8a57e68 authored 作者: 李顺's avatar 李顺

[update]:新增全联盟页面

import request from "@/api/request.js";
// 图表解读(流式)
/**
* @param {text}
*/
export function getChartAnalysis(data) {
return request({
method: 'POST',
url: `/aiAnalysis/chart_interpretation`,
data,
})
}
\ No newline at end of file
<template>
<div class="snake-timeline-container">
<!-- Left Navigation Button -->
<div class="nav-btn left" @click="scrollLeft" :class="{ disabled: isStart }">
<img src="@/assets/icons/card-btn-left.png" alt="Previous" />
</div>
<!-- Scrollable Content -->
<div class="timeline-wrapper" ref="scrollContainer" @scroll="checkScroll">
<div class="timeline-track">
<!-- Connecting Line -->
<div class="connecting-line"></div>
<!-- Cards -->
<div
v-for="(item, index) in timelineData"
:key="index"
class="timeline-card-item"
:class="{ 'item-top': index % 2 !== 0, 'item-bottom': index % 2 === 0 }"
>
<!-- Date Label -->
<div class="date-label">
<span class="year">{{ item.year }}</span>
<span class="month">{{ item.month }}</span>
</div>
<!-- Node/Dot -->
<div class="timeline-node"></div>
<!-- Card Content -->
<div class="card-content">
<div class="tags">
<span
v-for="(tag, tIndex) in item.tags"
:key="tIndex"
class="tag"
:class="getTagClass(tag)"
>
{{ tag }}
</span>
</div>
<h3 class="title" :title="item.title">{{ item.title }}</h3>
<p class="description">{{ item.description }}</p>
<div class="footer">
<span class="date">{{ item.fullDate }}</span>
<span class="separator">·</span>
<span class="source">{{ item.source }}</span>
<span class="separator">·</span>
<span class="category">{{ item.category }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Right Navigation Button -->
<div class="nav-btn right" @click="scrollRight" :class="{ disabled: isEnd }">
<img src="@/assets/icons/card-btn-right.png" alt="Next" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const scrollContainer = ref(null);
const isStart = ref(true);
const isEnd = ref(false);
const timelineData = ref([
{
year: '2025年',
month: '5月',
tags: ['人工智能', '航空航天'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '5月',
tags: ['人工智能'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '5月',
tags: ['集成电路'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '6月',
tags: ['新材料', '量子科技'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '6月',
tags: ['人工智能', '航空航天'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '7月',
tags: ['新材料', '能源'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '7月',
tags: ['人工智能', '航空航天'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '8月',
tags: ['能源'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
},
{
year: '2025年',
month: '8月',
tags: ['新材料', '量子科技'],
title: '《国家量子倡议再授权法案》发布',
description: '计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...',
fullDate: '11月15日',
source: '参议院',
category: '科技法案'
}
]);
const getTagClass = (tag) => {
// Simple logic to assign colors based on tag name or random
const map = {
'人工智能': 'tag-red',
'航空航天': 'tag-blue',
'集成电路': 'tag-blue-dark',
'新材料': 'tag-cyan',
'量子科技': 'tag-purple',
'能源': 'tag-green'
};
return map[tag] || 'tag-default';
};
const scrollLeft = () => {
if (scrollContainer.value) {
scrollContainer.value.scrollBy({ left: -400, behavior: 'smooth' });
}
};
const scrollRight = () => {
if (scrollContainer.value) {
scrollContainer.value.scrollBy({ left: 400, behavior: 'smooth' });
}
};
const checkScroll = () => {
if (!scrollContainer.value) return;
const { scrollLeft, scrollWidth, clientWidth } = scrollContainer.value;
isStart.value = scrollLeft <= 0;
isEnd.value = Math.ceil(scrollLeft + clientWidth) >= scrollWidth;
};
onMounted(() => {
checkScroll();
window.addEventListener('resize', checkScroll);
});
onUnmounted(() => {
window.removeEventListener('resize', checkScroll);
});
</script>
<style lang="scss" scoped>
.snake-timeline-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding: 20px 40px; /* Space for buttons */
box-sizing: border-box;
background-color: transparent;
.nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
z-index: 10;
width: 24px;
height: 48px;
&.left {
left: 0;
}
&.right {
right: 0;
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.timeline-wrapper {
width: 100%;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
&::-webkit-scrollbar {
display: none;
}
.timeline-track {
display: flex;
position: relative;
padding: 20px 0;
min-width: max-content;
align-items: center; /* Center items vertically relative to the line */
height: 550px; /* Sufficient height for top and bottom cards */
}
}
.connecting-line {
position: absolute;
top: 50%;
left: 20px;
right: 20px;
height: 2px;
background-color: #A9C4DF;
z-index: 0;
transform: translateY(-50%);
}
.timeline-card-item {
position: relative;
flex: 0 0 320px;
margin-right: 40px;
display: flex;
flex-direction: column;
align-items: flex-start; /* Align content to the left or center */
justify-content: flex-start;
height: 100%;
&:last-child {
margin-right: 0;
}
/* Common Node Styles */
.timeline-node {
width: 12px;
height: 12px;
background-color: #2F88FF;
border: 2px solid #fff;
border-radius: 50%;
box-shadow: 0 0 0 2px #2F88FF;
position: absolute;
top: 50%;
left: 24px; /* Align with card content padding */
z-index: 1;
transform: translateY(-50%);
}
/* Date Label Styles */
.date-label {
background: #EAF4FF;
color: #2F88FF;
padding: 4px 12px;
border-radius: 8px;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.2;
position: absolute;
left: 0;
.year {
font-size: 14px;
}
.month {
font-size: 16px;
}
}
/* Card Content Styles */
.card-content {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border: 1px solid #eee;
transition: transform 0.2s;
width: 100%;
position: absolute;
left: 0;
&:hover {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
}
/* ... inner content styles remain same ... */
.tags {
display: flex;
gap: 8px;
margin-bottom: 12px;
flex-wrap: wrap;
.tag {
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
border: 1px solid currentColor;
&.tag-red { color: #FF4D4F; background: #FFF1F0; border-color: #FFA39E; }
&.tag-blue { color: #1890FF; background: #E6F7FF; border-color: #91D5FF; }
&.tag-blue-dark { color: #2F54EB; background: #F0F5FF; border-color: #ADC6FF; }
&.tag-cyan { color: #13C2C2; background: #E6FFFB; border-color: #87E8DE; }
&.tag-purple { color: #722ED1; background: #F9F0FF; border-color: #D3ADF7; }
&.tag-green { color: #52C41A; background: #F6FFED; border-color: #B7EB8F; }
&.tag-default { color: #666; background: #F5F5F5; border-color: #D9D9D9; }
}
}
.title {
font-size: 16px;
font-weight: bold;
color: #333;
margin: 0 0 8px 0;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.description {
font-size: 13px;
color: #666;
margin: 0 0 16px 0;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.footer {
display: flex;
align-items: center;
font-size: 12px;
color: #999;
.separator {
margin: 0 4px;
}
}
}
/* Bottom Alignment (Even) */
&.item-bottom {
.date-label {
top: 50%;
transform: translateY(-55px); /* Just above the line */
}
.card-content {
top: 50%;
margin-top: 20px; /* Below the line */
}
}
/* Top Alignment (Odd) */
&.item-top {
.date-label {
top: 50%;
transform: translateY(15px); /* Just below the line */
}
.card-content {
bottom: 50%;
margin-bottom: 20px; /* Above the line */
}
}
}
}
</style>
......@@ -2,7 +2,6 @@
import ZMGame from "@/views/ZMGame/index.vue";
const ZMGameRoutes = [
//创新主体
{
path: "/ZMGame",
name: "ZMGame",
......
......@@ -2,7 +2,6 @@
import ZMOverview from "@/views/ZMOverView/index.vue";
const ZMOverviewRoutes = [
//创新主体
{
path: "/ZMOverView",
name: "ZMOverView",
......@@ -14,4 +13,4 @@ const ZMOverviewRoutes = [
]
export default ZMOverviewRoutes
export default ZMOverviewRoutes
\ No newline at end of file
......@@ -21,11 +21,12 @@ const props = defineProps({
<style scoped lang="scss">
.com-title {
width: 100%;
width: 1601px;
height: 42px;
display: flex;
align-items: center;
margin-bottom: 36px;
// margin-bottom: 36px;
margin: 0 auto;
.cl1 {
width: 24px;
height: 30px;
......
<!--科技要闻-->
<!--全领域-->
<template>
<div class="content-wrapper">
全领域
<div class="btn-wrapper">
<div class="btn-box">
<div v-for="(value, index) in buttonsData" class="btn-item" :style="{ background: value.background }">
<div
:style="{
backgroundImage: 'url(' + `/public/icon/ZM/btn-icon2-${index}.png` + ')'
}"
class="btn-left-text"
>
{{ value.text }}
</div>
<div class="btn-right">
{{ value.count + "次" }}
</div>
</div>
</div>
<img :src="leftBtn" alt="" class="left-btn" />
<img :src="rightBtn" alt="" class="right-btn" />
</div>
<div class="main-charts">
<div class="charts-title">
<div class="title-left">
<img src="./icon/icon-1.png" alt="" />
<span>美对华领域打压遏制数量趋势</span>
</div>
<div class="title-right">
<el-select v-model="select1" placeholder="按月统计" class="custom-select">
<el-option label="按月统计" value="" />
</el-select>
</div>
</div>
<div class="charts-content">
<div id="chartRef" class="chart-container"></div>
</div>
</div>
<div class="bottom-content">
<div class="news-section main-charts">
<div class="charts-title news-title">
<div class="title-left">
<img src="./icon/icon-2.png" alt="" style="width: 22px; height: 18px" />
<span>美对华领域打压遏制最新动态</span>
</div>
</div>
<div class="news-content">
<div v-for="value in newsList" class="news-item">
<div class="news-item-title">
<div class="tag-container">
<div v-for="tag in value.tags" :key="tag" :class="getTagClass(tag)">
{{ tag }}
</div>
</div>
<div class="date">
{{ value.date + " · " + value.type }}
</div>
</div>
<div class="content-title">
{{ value.title }}
</div>
<el-tooltip
effect="dark"
:content="value.content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="content-text">
{{ value.content }}
</div>
</el-tooltip>
</div>
</div>
</div>
<div class="empty-section">
<div class="bottom-item">
<div class="bottom-item-title">
<img :src="icon3" alt="" />
<span>美对华领域打压遏制排行</span>
</div>
</div>
<div class="select-box">
<div class="rank-btns">
<div class="rank-btn" :class="{ active: rankType === 'institution' }" @click="rankType = 'institution'">
对我打压机构
</div>
<div class="rank-btn" :class="{ active: rankType === 'enterprise' }" @click="rankType = 'enterprise'">
受打压企业
</div>
<div class="rank-btn" :class="{ active: rankType === 'school' }" @click="rankType = 'school'">
受打压院校
</div>
</div>
<el-select v-model="selectedField" placeholder="全部领域" class="field-select">
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="main-box">
<div v-for="(item, index) in rankList" :key="index" class="rank-item">
<div class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</div>
<img :src="defaultImg" alt="" class="rank-icon" />
<div class="rank-name">{{ item.name }}</div>
<div class="rank-progress-container">
<div class="rank-progress-bar" :style="{ width: getProgressWidth(item.count) }"></div>
</div>
<div class="rank-count">{{ item.count }}</div>
</div>
</div>
</div>
</div>
<div class="line-time">
<div class="bottom-item">
<div class="bottom-item-title">
<img :src="icon4" alt="" />
<span>美对我领域打压遏制时间线</span>
</div>
<el-select v-model="selectedFieldTimeline" placeholder="全部领域" class="field-select">
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="line-main">
<div class="nav-btn left"><img :src="leftBtn" alt="" /></div>
<div class="svg-container">
<svg :viewBox="`0 0 ${svgWidth} ${svgHeight}`" width="100%" height="100%">
<defs>
<marker
id="arrow"
markerWidth="10"
markerHeight="10"
refX="8"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L0,6 L9,3 z" fill="#e8f2ff" />
</marker>
</defs>
<path :d="snakePath" fill="none" stroke="#e8f2ff" stroke-width="2" />
<g v-for="(label, index) in axisDates" :key="'date-' + index">
<foreignObject :x="label.x" :y="label.y" width="80" height="60" style="overflow: visible">
<div class="axis-date-label">
<div class="year">{{ label.year }}</div>
<div class="month">{{ label.month }}</div>
</div>
</foreignObject>
</g>
<g v-for="(node, index) in timelineNodes" :key="index">
<line :x1="node.x" :y1="node.y" :x2="node.x" :y2="node.y + 150" stroke="#1677ff" stroke-width="1" />
<circle :cx="node.x" :cy="node.y" r="4" fill="#fff" stroke="#1677ff" stroke-width="3" />
<foreignObject
:x="node.contentX"
:y="node.contentY"
:width="node.contentWidth"
:height="node.contentHeight"
style="overflow: visible"
>
<div class="timeline-content-item">
<div class="item-tags">
<span v-for="tag in node.tags" :key="tag" :class="getTagClass(tag)">{{ tag }}</span>
</div>
<div class="item-title">{{ node.title }}</div>
<el-tooltip
effect="dark"
:content="node.content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="item-desc">{{ node.content }}</div>
</el-tooltip>
<div class="item-footer">{{ node.info }}</div>
</div>
</foreignObject>
</g>
</svg>
</div>
<div class="nav-btn right"><img :src="rightBtn" alt="" /></div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import { ref, computed, onMounted } from "vue";
import setChart from "@/utils/setChart";
import getMultiLineChart from "./multiLineChart";
import leftBtn from "../../assets/left-btn.png";
import rightBtn from "../../assets/right-btn.png";
import icon3 from "./icon/icon-3.png";
import icon4 from "./icon/icon-4.png";
import defaultImg from "../../../../assets/images/default-icon2.png";
const select1 = ref("");
const rankType = ref("institution");
const selectedField = ref("");
const selectedFieldTimeline = ref("");
const timelineScrollX = ref(0);
const timelineContainerWidth = 1601;
const timelineList = ref([
{
date: "2025年 5月",
tags: ["人工智能", "航空航天"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 6月",
tags: ["人工智能"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 7月",
tags: ["集成电路"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 8月",
tags: ["新材料", "量子科技"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 9月",
tags: ["人工智能", "航空航天"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 10月",
tags: ["新材料", "能源"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 11月",
tags: ["人工智能", "航空航天"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 12月",
tags: ["新材料", "能源"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
},
{
date: "2025年 12月",
tags: ["集成电路"],
title: "《国家量子倡议再授权法案》发布",
content: "计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA...",
info: "11月15日 · 参议院 · 科技法案"
}
]);
const maxPerRow = 3;
const nodeGapX = 450;
const rowHeight = 215;
const startX = 190;
const startY = 45;
const axisDates = computed(() => {
const dates = [];
if (timelineList.value.length > 0) {
dates.push({
year: timelineList.value[0].date.split(" ")[0],
month: timelineList.value[0].date.split(" ")[1],
x: startX - 140,
y: startY - 25
});
const lastNode = timelineNodes.value[timelineNodes.value.length - 1];
const isEvenRow = lastNode.row % 2 === 0;
const endX = isEvenRow ? lastNode.x + 400 : lastNode.x - 140;
dates.push({
year: timelineList.value[timelineList.value.length - 1].date.split(" ")[0],
month: timelineList.value[timelineList.value.length - 1].date.split(" ")[1],
x: endX,
y: lastNode.y - 25
});
}
return dates;
});
const timelineNodes = computed(() => {
return timelineList.value.map((item, index) => {
const row = Math.floor(index / maxPerRow);
const col = index % maxPerRow;
let x;
if (row % 2 === 0) {
x = startX + col * nodeGapX;
} else {
x = startX + (maxPerRow - 1 - col) * nodeGapX;
}
const y = startY + row * rowHeight;
const contentX = x + 20;
const contentY = y + 10;
return {
...item,
x,
y,
row,
col,
contentX,
contentY,
contentWidth: 320,
contentHeight: 140
};
});
});
const getColorName = tag => {
const tagColorMap = {
航空航天: "blue",
生物科技: "blue",
集成电路: "blue",
能源: "green",
新材料: "green",
人工智能: "red"
};
if (tagColorMap[tag]) return tagColorMap[tag];
const colors = ["blue", "green", "red", "orange", "purple", "cyan"];
let hash = 0;
for (let i = 0; i < tag.length; i++) {
hash = tag.charCodeAt(i) + ((hash << 5) - hash);
}
return colors[Math.abs(hash) % colors.length];
};
const getTagClass = tag => {
return "tag-item " + `tag-${getColorName(tag)}`;
};
const snakePath = computed(() => {
if (timelineNodes.value.length === 0) return "";
let path = `M ${startX - 100} ${startY}`;
path += ` L ${timelineNodes.value[0].x} ${timelineNodes.value[0].y}`;
for (let i = 0; i < timelineNodes.value.length - 1; i++) {
const curr = timelineNodes.value[i];
const next = timelineNodes.value[i + 1];
if (curr.row !== next.row) {
const radius = rowHeight / 2;
if (curr.row % 2 === 0) {
const turnX = curr.x + 400;
path += ` L ${turnX} ${curr.y}`;
path += ` A ${radius} ${radius} 0 0 1 ${turnX} ${next.y}`;
path += ` L ${next.x} ${next.y}`;
} else {
const turnX = curr.x - 100;
path += ` L ${turnX} ${curr.y}`;
path += ` A ${radius} ${radius} 0 0 0 ${turnX} ${next.y}`;
path += ` L ${next.x} ${next.y}`;
}
} else {
path += ` L ${next.x} ${next.y}`;
}
}
const last = timelineNodes.value[timelineNodes.value.length - 1];
if (last.row % 2 === 0) {
path += ` L ${last.x + 400} ${last.y}`;
} else {
path += ` L ${last.x - 100} ${last.y}`;
}
return path;
});
const svgWidth = computed(() => {
return timelineContainerWidth;
});
const svgHeight = computed(() => {
const rows = Math.ceil(timelineList.value.length / maxPerRow);
return startY + rows * rowHeight;
});
const scrollLeft = () => {
if (timelineScrollX.value < 0) {
timelineScrollX.value += nodeGapX;
}
};
const scrollRight = () => {
const maxScroll = -(timelineNodes.value.length * nodeGapX - timelineContainerWidth + startX + 100);
if (timelineScrollX.value > maxScroll) {
timelineScrollX.value -= nodeGapX;
}
};
const fieldOptions = ref([
{ label: "全部领域", value: "" },
{ label: "集成电路", value: "集成电路" },
{ label: "人工智能", value: "人工智能" },
{ label: "量子科技", value: "量子科技" }
]);
const buttonsData = [
{
text: "集成电路",
count: 101,
background: "linear-gradient(180.00deg, rgba(255, 102, 102, 1),rgba(255, 102, 102, 0.5) 100%)"
},
{
text: "人工智能",
count: 132,
background: "linear-gradient(180.00deg, rgba(51, 153, 255, 1),rgba(51, 153, 255, 0.5) 100%)"
},
{
text: "新一代信息技术",
count: 105,
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
},
{
text: "量子科技",
count: 97,
background: "linear-gradient(180.00deg, rgba(117, 73, 255, 1),rgba(117, 73, 255, 0.5) 100%)"
},
{
text: "先进制造",
count: 84,
background: "linear-gradient(180.00deg, rgba(102, 102, 102, 1),rgba(102, 102, 102, 0.5) 100%)"
},
{
text: "航空航天",
count: 77,
background: "linear-gradient(180.00deg, rgba(51, 102, 255, 1),rgba(51, 102, 255, 0.5) 100%)"
},
{
text: "生物科技",
count: 62,
background: "linear-gradient(180.00deg, rgba(102, 204, 204, 1),rgba(102, 204, 204, 0.5) 100%)"
},
{
text: "新能源",
count: 60,
background: "linear-gradient(180.00deg, rgba(102, 187, 51, 1),rgba(102, 187, 51, 0.5) 100%)"
},
{
text: "新材料",
count: 43,
background: "linear-gradient(180.00deg, rgba(255, 102, 51, 1),rgba(255, 102, 51, 0.5) 100%)"
},
{
text: "海洋",
count: 25,
background: "linear-gradient(180.00deg, rgba(153, 204, 255, 1),rgba(153, 204, 255, 0.5) 100%)"
}
];
const box5Data = ref({
title: [
"2024-12",
"2025-1",
"2025-2",
"2025-3",
"2025-4",
"2025-5",
"2025-6",
"2025-7",
"2025-8",
"2025-9",
"2025-10",
"2025-11"
],
data: [
{
name: "集成电路",
color: "#0052D9",
value: [87, 78, 74, 67, 60, 59, 63, 66, 63, 58, 56, 62]
},
{
name: "生物科技",
color: "#00A79D",
value: [13, 13, 8, 13, 20, 37, 34, 25, 22, 20, 27, 18]
},
{
name: "量子科技",
color: "#7B61FF",
value: [18, 16, 12, 16, 16, 26, 30, 29, 25, 25, 33, 25]
},
{
name: "新一代信息技术",
color: "#FF9F1C",
value: [10, 22, 22, 34, 48, 51, 46, 55, 55, 60, 68, 70]
},
{
name: "人工智能",
color: "#E34D59",
value: [25, 34, 39, 45, 53, 54, 50, 47, 50, 54, 56, 51]
},
{
name: "通信网络",
color: "#0052D9",
value: [22, 26, 31, 31, 38, 33, 26, 38, 36, 40, 45, 47]
},
{
name: "新能源",
color: "#2BA471",
value: [53, 44, 43, 41, 34, 29, 57, 44, 61, 67, 61, 61]
},
{
name: "先进制造",
color: "#363B42",
value: [70, 75, 78, 75, 75, 80, 73, 51, 71, 77, 80, 89]
},
{
name: "航空航天",
color: "#3762F0",
value: [18, 16, 12, 16, 16, 26, 30, 29, 25, 25, 33, 25]
},
{
name: "海洋",
color: "#76D1FF",
value: [13, 13, 8, 13, 20, 37, 34, 25, 22, 20, 27, 18]
},
{
name: "新材料",
color: "#FFD900",
value: [10, 22, 22, 34, 48, 51, 46, 55, 55, 60, 68, 70]
},
{
name: "深海",
color: "#002060",
value: [22, 26, 31, 31, 38, 33, 26, 38, 36, 40, 45, 47]
},
{
name: "极地",
color: "#A6A6A6",
value: [53, 44, 43, 41, 34, 29, 57, 44, 61, 67, 61, 61]
},
{
name: "核",
color: "#FFB3B3",
value: [25, 34, 39, 45, 53, 54, 50, 47, 50, 54, 56, 51]
}
]
});
const newsList = ref([
{
tags: ["航空航天", "能源"],
title: "联邦参议院:通过2026年国防授权法案",
date: "2025年12月18日",
type: "法案",
content: "2026年国防授权法案(NDAA)已于2025年12月18日由美国总统特朗普签署成为法律,法案..."
},
{
tags: ["新材料", "生物科技"],
title: "美国FDA:针对两家中国第三方检测机构的数据完整性问题采取行动",
date: "2025年12月18日",
type: "法案",
content: "FDA因数据造假或无效问题,向两家中国第三方检测公司(天津中联科技检测有限公司和苏...)"
},
{
tags: ["人工智能", "集成电路"],
title: "美国FCC:出台电信设备市场准入认证新规",
date: "2025年12月18日",
type: "法案",
content: "将国家安全部门审查全面嵌入设备授权程序,对实验室资质、测试标准以及供应链设置严格限制。"
},
{
tags: ["人工智能", "集成电路"],
title: "商务部工业与安全局:对实体清单的修订",
date: "2025年12月18日",
type: "法案",
content: "美国商务部工业与安全局公布对中国半导体出口管制措施新规则,将140家中国半导体公司列入..."
},
{
tags: ["集成电路"],
title: "国际贸易委员会:外国制造的半导体器件及其下游产品和组件",
date: "2025年12月18日",
type: "法案",
content: "美国国际贸易委员会(ITC)投票决定对特定半导体器件及其下游计算产品和组件(Certain ...)"
}
]);
const tagColors = [
{
text: "航空航天",
textColor: "#1677FF", // 蓝色文字
bgColor: "#E6F7FF" // 浅蓝色背景
},
{
text: "能源",
textColor: "#1677FF",
bgColor: "#E6F7FF"
},
{
text: "新材料",
textColor: "#5993EE",
bgColor: "#E6F7FF"
},
{
text: "生物科技",
textColor: "#5993EE",
bgColor: "#E6F7FF"
},
{
text: "人工智能",
textColor: "#D9001B",
bgColor: "#FFECEC"
},
{
text: "集成电路",
textColor: "#1677FF",
bgColor: "#E6F7FF"
}
];
const getTagColor = tagName => {
const foundTag = tagColors.find(tag => tag.text === tagName);
return foundTag ? { textColor: foundTag.textColor, bgColor: foundTag.bgColor } : { textColor: "#000", bgColor: "#fff" };
};
const rankList = ref([
{ name: "美国商务部", count: 45 },
{ name: "美国财政部", count: 38 },
{ name: "美国白宫", count: 36 },
{ name: "美国国务院", count: 34 },
{ name: "美国战争部", count: 33 },
{ name: "联邦参议院", count: 31 },
{ name: "美国美国国土安全部", count: 28 },
{ name: "美国贸易代表办公室", count: 20 },
{ name: "联邦通信委员会", count: 16 },
{ name: "美国食品药品监督管理局", count: 12 }
]);
const maxCount = Math.max(...rankList.value.map(item => item.count));
const getProgressWidth = count => {
return (count / maxCount) * 100 + "%";
};
onMounted(() => {
let Chart = getMultiLineChart(box5Data.value);
setChart(Chart, "chartRef");
});
</script>
<style lang="scss" scoped>
.content-wrapper {
width: 1666px;
height: 2132px;
width: 1666px;
height: 2132px;
}
.btn-wrapper {
position: relative;
width: 1601px;
}
.btn-box {
margin-top: 16px;
width: 100%;
height: 176px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch;
gap: 16px;
padding: 0;
}
.left-btn {
width: 24px;
height: 48px;
position: absolute;
top: 50%;
left: -33px;
transform: translateY(-50%);
cursor: pointer;
z-index: 10;
}
.right-btn {
width: 24px;
height: 48px;
position: absolute;
top: 50%;
right: -33px;
transform: translateY(-50%);
cursor: pointer;
z-index: 10;
}
.btn-item {
/* 全领域-总统计 */
width: 307px;
height: 80px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
.btn-left-text {
width: fit-content;
min-width: 60px;
height: 60px;
background-repeat: no-repeat;
background-position: center;
background-size: contain;
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 60px;
letter-spacing: 0px;
text-align: left;
color: rgba(255, 255, 255, 1);
//不换行
white-space: nowrap;
display: flex;
align-items: center;
font-style: italic;
}
.btn-right {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
color: rgba(255, 255, 255, 1);
font-style: italic;
}
}
.btn-left {
position: relative;
display: flex;
align-items: center;
}
.btn-left-icon {
margin-right: 8px; // 图标和文本之间的间距
}
.main-charts {
margin-top: 16px;
width: 1601px;
height: 500px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
display: flex;
flex-direction: column;
.charts-title {
width: 100%;
height: 48px;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 17px;
padding-right: 35px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.title-left {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 24px;
color: rgb(5, 95, 194);
}
}
.title-right {
display: flex;
height: 48px;
align-items: end;
gap: 12px;
padding-right: 17px;
.custom-select {
width: 160px;
:deep(.el-input) {
height: 32px;
.el-input__wrapper {
background-color: #fff;
box-shadow: 0 0 0 1px #dcdfe6 inset;
border-radius: 4px;
padding: 0 12px;
height: 32px;
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #409eff inset;
}
}
.el-input__inner {
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
height: 32px;
line-height: 24px;
&::placeholder {
color: rgb(95, 101, 108);
}
}
}
}
}
}
.charts-content {
flex: 1;
width: 100%;
padding: 20px 40px 20px 40px;
box-sizing: border-box;
.chart-container {
width: 100%;
height: 100%;
}
}
}
.bottom-content {
display: flex;
gap: 17px;
.news-section {
width: 792px;
height: 700px;
.news-title {
padding: 8px;
}
.tag-container {
display: flex;
align-items: center;
gap: 8px;
.tag-item {
padding: 2px 8px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
border: 1px solid transparent;
&.tag-blue {
color: rgba(9, 88, 217, 1);
background: rgba(230, 244, 255, 1);
border-color: rgba(186, 224, 255, 1);
}
&.tag-green {
color: rgba(56, 158, 13, 1);
background: rgba(246, 255, 237, 1);
border-color: rgba(217, 247, 190, 1);
}
&.tag-red {
color: rgba(245, 34, 45, 1);
background: rgba(255, 241, 240, 1);
border-color: rgba(255, 163, 158, 1);
}
&.tag-orange {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
border-color: rgba(255, 213, 145, 1);
}
&.tag-purple {
color: rgba(114, 46, 209, 1);
background: rgba(249, 240, 255, 1);
border-color: rgba(211, 173, 247, 1);
}
&.tag-cyan {
color: rgba(19, 194, 194, 1);
background: rgba(230, 255, 251, 1);
border-color: rgba(135, 232, 222, 1);
}
}
}
}
.empty-section {
width: 792px;
height: 700px;
margin-top: 16px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
.bottom-item {
width: 100%;
height: 48px;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 17px;
padding-right: 35px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.bottom-item-title {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 24px;
color: rgb(5, 95, 194);
}
}
}
.select-box {
width: 691px;
height: 32px;
margin: 17px auto 36px auto;
display: flex;
justify-content: space-between;
align-items: center;
.rank-btns {
display: flex;
gap: 8px;
.rank-btn {
padding: 4px 12px;
border-radius: 4px;
border: 1px solid rgb(230, 231, 232);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: rgb(95, 101, 108);
cursor: pointer;
background-color: #fff;
&.active {
color: rgb(5, 95, 194);
border-color: rgb(5, 95, 194);
background-color: rgba(231, 243, 255, 1);
}
}
}
.field-select {
width: 160px;
:deep(.el-input) {
.el-input__wrapper {
height: 32px;
padding: 0 12px;
box-sizing: border-box;
background-color: #fff;
border-radius: 4px;
.el-input__inner {
height: 32px;
line-height: 32px;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
&::placeholder {
color: rgb(95, 101, 108);
}
}
}
}
}
}
.main-box {
width: 100%;
height: 567px;
padding: 24px 51px 51px 27px;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 21px;
overflow-y: auto;
.rank-item {
display: flex;
align-items: center;
height: 30px;
.rank-num {
width: 24px;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
margin-right: 12px;
text-align: center;
color: #3b414b;
&.rank-1 {
color: #d94b4b;
}
&.rank-2 {
color: #e3935d;
}
&.rank-3 {
color: #ebd348;
}
}
.rank-icon {
width: 30px;
height: 30px;
margin-right: 12px;
}
.rank-name {
width: 180px;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 700;
color: rgb(59, 65, 75);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rank-progress-container {
flex: 1;
height: 12px;
margin: 0 20px;
background: transparent;
display: flex;
align-items: center;
.rank-progress-bar {
height: 100%;
border-radius: 6px;
background: linear-gradient(90deg, rgba(55, 98, 240, 0) 0%, rgba(55, 98, 240, 0.8) 100%);
}
}
&:nth-child(1) .rank-progress-bar {
background: linear-gradient(90deg, rgba(217, 75, 75, 0) 0%, rgba(217, 75, 75, 0.8) 100%);
}
&:nth-child(2) .rank-progress-bar {
background: linear-gradient(90deg, rgba(227, 147, 93, 0) 0%, rgba(227, 147, 93, 0.8) 100%);
}
&:nth-child(3) .rank-progress-bar {
background: linear-gradient(90deg, rgba(235, 211, 72, 0) 0%, rgba(235, 211, 72, 0.8) 100%);
}
.rank-count {
width: 60px;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 700;
color: rgb(59, 65, 75);
text-align: right;
}
}
}
}
}
.news-content {
overflow-y: auto;
overflow-x: hidden;
}
.news-item {
/* 全政府-动态 (四全-最新动态) */
width: 737px;
height: 124px;
margin: 0 28px;
/* 自动布局 */
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 16px 24px 16px 24px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
background: rgba(255, 255, 255, 0.65);
.news-item-title {
display: flex;
justify-content: space-between;
width: 689px;
.tag {
display: none;
}
.date {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: right;
}
}
.content-title {
/* 容器 1576 */
width: 689px;
height: 30px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 12;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
.content-text {
width: 689px;
height: 30px;
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
color: rgba(59, 65, 75, 1);
margin-top: 8px;
/* 单行省略 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
cursor: pointer;
}
}
.line-time {
margin-top: 16px;
width: 1601px;
height: 700px;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
.bottom-item {
width: 100%;
height: 48px;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 17px;
padding-right: 35px;
box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.bottom-item-title {
display: flex;
align-items: center;
img {
width: 18px;
height: 18px;
margin-right: 14px;
}
span {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 24px;
color: rgb(5, 95, 194);
}
}
.field-select {
width: 160px;
:deep(.el-input) {
.el-input__wrapper {
height: 32px;
padding: 0 12px;
box-sizing: border-box;
background-color: #fff;
border-radius: 4px;
.el-input__inner {
height: 32px;
line-height: 32px;
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
&::placeholder {
color: rgb(95, 101, 108);
}
}
}
}
}
}
.line-main {
width: 100%;
height: 652px;
position: relative;
.nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
z-index: 10;
img {
width: 24px;
height: 48px;
}
&.left {
left: 0;
}
&.right {
right: 0;
}
}
.svg-container {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.axis-date-label {
background: rgba(231, 243, 255, 1);
padding: 4px 2px;
border-radius: 8px;
text-align: center;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: auto;
min-width: 60px;
.year,
.month {
font-family: "Microsoft YaHei";
font-size: 18px;
font-weight: 700;
line-height: 24px;
color: rgb(5, 95, 194);
margin-bottom: 0;
}
}
.timeline-content-item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
.item-tags {
display: flex;
gap: 8px;
.tag-item {
padding: 2px 8px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
border: 1px solid transparent; // Ensure border width is set for all
&.tag-blue {
color: rgba(9, 88, 217, 1);
background: rgba(230, 244, 255, 1);
border-color: rgba(186, 224, 255, 1);
}
&.tag-green {
color: rgba(56, 158, 13, 1);
background: rgba(246, 255, 237, 1);
border-color: rgba(217, 247, 190, 1);
}
&.tag-red {
color: rgba(245, 34, 45, 1);
background: rgba(255, 241, 240, 1);
border-color: rgba(255, 163, 158, 1);
}
&.tag-orange {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
border-color: rgba(255, 213, 145, 1);
}
&.tag-purple {
color: rgba(114, 46, 209, 1);
background: rgba(249, 240, 255, 1);
border-color: rgba(211, 173, 247, 1);
}
&.tag-cyan {
color: rgba(19, 194, 194, 1);
background: rgba(230, 255, 251, 1);
border-color: rgba(135, 232, 222, 1);
}
}
}
.item-title {
font-size: 18px;
font-weight: 700;
color: rgb(59, 65, 75);
font-family: "Microsoft YaHei";
line-height: 24px;
}
.item-desc {
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
line-height: 24px;
font-family: "Microsoft YaHei";
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
cursor: pointer;
}
.item-footer {
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
line-height: 24px;
font-family: "Microsoft YaHei";
margin-top: 4px;
}
}
}
}
</style>
<style>
.common-prompt-popper.el-popper {
padding: 8px 16px !important;
border-radius: 10px !important;
background-color: rgb(59, 65, 75) !important;
font-size: 16px !important;
font-weight: 400 !important;
font-family: "Microsoft YaHei" !important;
line-height: 30px !important;
color: #fff !important;
border: none !important;
}
.common-prompt-popper.el-popper .el-popper__arrow::before {
background-color: rgb(59, 65, 75) !important;
border-color: rgb(59, 65, 75) !important;
}
</style>
import * as echarts from 'echarts';
const getMultiLineChart = (data) => {
// 提取标题和系列数据
// const { title, series } = data;
const title = data.title
const series = data.data
// 动态生成 series 配置
const echartsSeries = series.map((item, index) => ({
name: item.name,
type: 'line',
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#fff', // 图表圆点内部为白色
borderColor: item.color, // 圆点边框为系列颜色
borderWidth: 2
},
lineStyle: {
width: 2,
color: item.color
},
emphasis: {
focus: 'series'
},
data: item.value
}));
return {
color: series.map(item => item.color),
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.9)',
textStyle: {
color: '#666'
},
extraCssText: 'box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 4px;'
},
grid: {
top: '15%',
right: '2%',
bottom: '5%',
left: '2%',
containLabel: true
},
legend: {
show: true,
top: 0,
left: 'center',
icon: 'circle',
itemWidth: 12,
itemHeight: 12,
data: series.map(item => ({
name: item.name,
itemStyle: {
color: item.color, // 强制图例使用实心系列颜色
borderWidth: 0
}
})),
textStyle: {
fontFamily: 'Microsoft YaHei',
fontSize: 16,
fontWeight: 400,
lineHeight: 24,
color: 'rgb(95, 101, 108)'
}
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: title,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999',
fontSize: 12,
margin: 15
}
}
],
yAxis: [
{
type: 'value',
min: 0,
max: 100,
interval: 20,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999',
fontSize: 12
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#E0E6F1'
}
}
}
],
series: echartsSeries
};
};
export default getMultiLineChart;
\ No newline at end of file
<!--科技要闻-->
<template>
<div class="content-wrapper">
全要素
<div class="header">
<div class="header-arrow-left">
<img src="../../assets/left-btn.png" alt="" />
</div>
<div class="header-arrow-right">
<img src="../../assets/right-btn.png" alt="" />
</div>
<div
class="header-item"
:class="{
headerItem1: index === 0,
headerItem2: index === 1,
headerItem3: index === 2,
headerItem4: index === 3,
headerItem5: index === 4
}"
v-for="(item, index) in headerList"
:key="index"
>
<div class="name">{{ item.name }}</div>
<div class="num">{{ item.value }}</div>
</div>
</div>
<div class="main">
<div class="left">
<div class="box box1">
<div class="box1-header">
<div class="icon">
<img src="./assets/images/box-header-icon1.png" alt="" />
</div>
<div class="title">{{ "最新动态" }}</div>
</div>
<div class="box1-main">
<div class="box1-item" v-for="(item, index) in box1DataList" :key="index">
<div class="box1-item-left">{{ index + 1 }}</div>
<div class="box1-item-right">
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
<div class="tag-box">
<div class="tag" v-for="(val, idx) in item.tagList" :key="idx">{{ val }}</div>
</div>
<div class="box1-item-right-footer">
<div class="time">{{ item.time }}</div>
<div class="area-box">
<div class="area" v-for="(vall, idxx) in item.areaList" :key="idxx">{{ vall.name }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="box box2">
<div class="box2-header">
<div class="icon">
<img src="./assets/images/box-header-icon2.png" alt="" />
</div>
<div class="title">{{ "美对我要素打压情况" }}</div>
</div>
<div class="box2-main">
<div class="inner-box1">
<div class="left">
<div class="left-main">
<div
class="left-item"
:class="{ leftItemActive: box2LeftActiveIndex === index }"
v-for="(item, index) in box2DataList"
:key="index"
@click="handleClickBox2Item(index)"
>
<div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.title }}</div>
</div>
</div>
<div class="left-footer">
<el-pagination
background
layout="prev, pager, next"
:total="box2Total"
:page-size="box2PageSize"
v-model:current-page="box2CurrentPage"
@current-change="handleGetBox2DataList"
/>
</div>
</div>
<div class="right">
<div class="title">{{ box2DataList[box2LeftActiveIndex].title }}</div>
<div class="tag-box">
<div
class="tag"
v-for="(item, index) in box2DataList[box2LeftActiveIndex].tagList"
:key="index"
>
{{ item }}
</div>
</div>
<div class="content">{{ box2DataList[box2LeftActiveIndex].content }}</div>
<div class="area-box">
<div
class="area"
v-for="(item, index) in box2DataList[box2LeftActiveIndex].areaList"
:key="index"
>
{{ item.name }}
</div>
</div>
<div class="footer">
{{
`${box2DataList[box2LeftActiveIndex].time} · ${box2DataList[box2LeftActiveIndex].org} · ${box2DataList[box2LeftActiveIndex].type}`
}}
</div>
</div>
</div>
<div class="inner-box2">
<div class="chart-header">{{ "关键词云" }}</div>
<div class="box2Chart" id="box2Chart"></div>
</div>
</div>
</div>
<div class="box box3">
<div class="box3-header">
<div class="icon">
<img src="./assets/images/box-header-icon2.png" alt="" />
</div>
<div class="title">{{ "美自身要素发展情况" }}</div>
</div>
<div class="box3-main">
<div class="inner-box1">
<div class="left">
<div class="left-main">
<div
class="left-item"
:class="{ leftItemActive: box3LeftActiveIndex === index }"
v-for="(item, index) in box3DataList"
:key="index"
@click="handleClickBox3Item(index)"
>
<div class="id">{{ index + 1 }}</div>
<div class="text">{{ item.title }}</div>
</div>
</div>
<div class="left-footer">
<el-pagination
background
layout="prev, pager, next"
:total="box3Total"
:page-size="box3PageSize"
v-model:current-page="box3CurrentPage"
@current-change="handleGetBox3DataList"
/>
</div>
</div>
<div class="right">
<div class="title">{{ box3DataList[box3LeftActiveIndex].title }}</div>
<div class="tag-box">
<div
class="tag"
v-for="(item, index) in box3DataList[box3LeftActiveIndex].tagList"
:key="index"
>
{{ item }}
</div>
</div>
<div class="content">{{ box3DataList[box3LeftActiveIndex].content }}</div>
<div class="area-box">
<div
class="area"
v-for="(item, index) in box3DataList[box3LeftActiveIndex].areaList"
:key="index"
>
{{ item.name }}
</div>
</div>
<div class="footer">
{{
`${box3DataList[box3LeftActiveIndex].time} · ${box3DataList[box3LeftActiveIndex].org} · ${box3DataList[box3LeftActiveIndex].type}`
}}
</div>
</div>
</div>
<div class="inner-box2">
<div class="chart-header">{{ "关键词云" }}</div>
<div class="box3Chart" id="box3Chart"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import setChart from "@/utils/setChart";
import getWordCloudChart from "./uitls/worldCloudChart";
const headerList = ref([
{
name: "科研仪器",
value: 132
},
{
name: "科研机构",
value: 104
},
{
name: "科研人才",
value: 177
},
{
name: "科研数据",
value: 60
},
{
name: "科研经费",
value: 45
}
]);
const box1DataList = ref([
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
},
{
title: '更新"中国涉军企业清单"(NS-CMIC List)',
content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
tagList: ["科研机构", "科研经费"],
time: "2025年12月18日",
areaList: [
{
name: "量子科技"
},
{
name: "量子科技"
}
]
}
]);
const box2DataList = ref([
{
title: "防止受关注国家访问美国敏感数据的规定",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定2",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定3",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定4",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定5",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
}
]);
const box2LeftActiveIndex = ref(0);
const handleClickBox2Item = index => {
box2LeftActiveIndex.value = index;
};
const box2Total = ref(25);
const box2CurrentPage = ref(1);
const box2PageSize = ref(5);
const handleGetBox2DataList = async () => {};
const box2ChartData = ref([
{
name: "限制中国获取能源技术",
value: 35
},
{
name: "加强美国在核能领域得到领导力",
value: 12
},
{
name: "关注核聚变能源研究",
value: 15
},
{
name: "发展风能",
value: 21
},
{
name: "实施能源税收延期",
value: 18
},
{
name: "限制采购中国产电池",
value: 5
},
{
name: "评估中美现代化技术",
value: 11
},
{
name: "应对中国制造2025战略",
value: 9
},
{
name: "抵制外国人才争夺",
value: 16
}
]);
const box3DataList = ref([
{
title: "防止受关注国家访问美国敏感数据的规定",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定2",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定3",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定4",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
},
{
title: "防止受关注国家访问美国敏感数据的规定5",
tagList: ["科研机构", "科研经费"],
content:
'美国司法部发布最终规则,明确禁止将"美国人"的敏感个人数据(包括基因组、生物识别、健康、地理位置及财务数据)大规模交易给中国等"受关注国家"的实体与个人。此规定旨在防止...',
areaList: [
{
name: "人工智能"
},
{
name: "生物科技"
}
],
time: "2025年12月18日",
org: "司法部",
type: "行政令"
}
]);
const box3LeftActiveIndex = ref(0);
const handleClickBox3Item = index => {
box3LeftActiveIndex.value = index;
};
const box3Total = ref(25);
const box3CurrentPage = ref(1);
const box3PageSize = ref(5);
const handleGetBox3DataList = async () => {};
const box3ChartData = ref([
{
name: "限制中国获取能源技术",
value: 35
},
{
name: "加强美国在核能领域得到领导力",
value: 12
},
{
name: "关注核聚变能源研究",
value: 15
},
{
name: "发展风能",
value: 21
},
{
name: "实施能源税收延期",
value: 18
},
{
name: "限制采购中国产电池",
value: 5
},
{
name: "评估中美现代化技术",
value: 11
},
{
name: "应对中国制造2025战略",
value: 9
},
{
name: "抵制外国人才争夺",
value: 16
}
]);
onMounted(() => {
let box2Chart = getWordCloudChart(box2ChartData.value);
setChart(box2Chart, "box2Chart");
let box3Chart = getWordCloudChart(box3ChartData.value);
setChart(box3Chart, "box3Chart");
});
</script>
<style lang="scss" scoped>
.content-wrapper {
width: 1666px;
height: 2132px;
width: 1600px;
// height: 2132px;
.header {
width: 1600px;
height: 176px;
margin: 0 auto;
display: flex;
justify-content: center;
gap: 16px;
margin: 0 auto;
position: relative;
.header-arrow-left {
position: absolute;
left: -33px;
top: 64px;
width: 24px;
height: 48px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.header-arrow-right {
position: absolute;
right: -33px;
top: 64px;
width: 24px;
height: 48px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.header-item {
width: 307px;
height: 178px;
border-radius: 10px;
.name {
margin-top: 53px;
margin-left: 112px;
width: 174px;
height: 39px;
color: rgba(255, 255, 255, 1);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 30px;
font-weight: 400;
line-height: 39px;
letter-spacing: 0px;
text-align: left;
}
.num {
margin-top: -8px;
margin-left: 112px;
width: 174px;
height: 39px;
color: rgba(255, 255, 255, 1);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 30px;
font-weight: 400;
line-height: 39px;
letter-spacing: 0px;
text-align: left;
}
}
.headerItem1 {
background: url("./assets/images/bg1.png");
}
.headerItem2 {
background: url("./assets/images/bg2.png");
}
.headerItem3 {
background: url("./assets/images/bg3.png");
}
.headerItem4 {
background: url("./assets/images/bg4.png");
}
.headerItem5 {
background: url("./assets/images/bg5.png");
}
}
.main {
width: 1598px;
height: 884px;
margin: 0 auto;
margin-top: 14px;
display: flex;
justify-content: space-between;
.box {
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.65);
}
.left {
width: 472px;
.box1 {
width: 472px;
height: 884px;
overflow: hidden;
.box1-header {
height: 48px;
display: flex;
box-sizing: border-box;
border-bottom: 1px solid rgba(255, 255, 255, 1);
background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%);
.icon {
width: 22px;
height: 18px;
margin-top: 15px;
margin-left: 16px;
img {
width: 100%;
height: 100%;
}
}
.title {
width: 89px;
height: 31px;
margin-top: 8px;
margin-left: 11px;
color: var(--color-main-active);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 24px;
font-weight: 400;
line-height: 31px;
letter-spacing: 0px;
text-align: left;
}
}
.box1-main {
height: 836px;
overflow: hidden;
overflow-y: auto;
background: rgba(255, 255, 255, 0.65);
.box1-item {
width: 430px;
padding: 12px 0;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
margin: 0 auto;
display: flex;
.box1-item-left {
width: 30px;
height: 30px;
border-radius: 50px;
background: rgba(246, 250, 255, 1);
line-height: 30px;
text-align: center;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
}
.box1-item-right {
width: 388px;
margin-left: 12px;
.title {
height: 30px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
.content {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
.tag-box {
margin-top: 4px;
height: 28px;
display: flex;
gap: 8px;
.tag {
width: 80px;
height: 28px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
text-align: center;
line-height: 28px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
letter-spacing: 0px;
}
}
.box1-item-right-footer {
height: 30px;
margin-top: 4px;
display: flex;
justify-content: space-between;
align-items: center;
.time {
width: 124px;
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
.area-box {
height: 24px;
display: flex;
gap: 8px;
justify-content: flex-end;
.area {
height: 24px;
padding: 0 8px;
line-height: 24px;
text-align: center;
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 14px;
font-weight: 400;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
}
}
}
}
}
}
}
.right {
width: 1110px;
.box2 {
width: 1110px;
height: 434px;
.box2-header {
height: 48px;
box-sizing: border-box;
border-bottom: 1px solid rgba(255, 255, 255, 1);
background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%);
display: flex;
.icon {
width: 17px;
height: 16.5px;
margin-left: 19px;
margin-top: 16px;
img {
width: 100%;
height: 100%;
}
}
.title {
margin-top: 8px;
margin-left: 13px;
width: 199px;
height: 31px;
color: var(--color-main-active);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 24px;
font-weight: 400;
line-height: 31px;
letter-spacing: 0px;
text-align: left;
}
}
.box2-main {
height: 386px;
background: rgba(255, 255, 255, 0.65);
display: flex;
justify-content: center;
gap: 16px;
.inner-box1 {
width: 640px;
height: 368px;
overflow: hidden;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
display: flex;
.left {
width: 320px;
border-right: 1px solid rgba(234, 236, 238, 1);
.left-main {
margin-top: 9px;
height: 270px;
.left-item {
height: 54px;
border: 1px solid transparent;
display: flex;
align-items: center;
&:hover {
background: rgba(246, 250, 255, 1);
}
cursor: pointer;
.id {
margin-left: 16px;
width: 24px;
height: 24px;
border-radius: 50px;
background: rgba(231, 243, 255, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: center;
}
.text {
width: 260px;
height: 30px;
margin-left: 12px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.leftItemActive {
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
}
}
.left-footer {
height: 80px;
display: flex;
justify-content: center;
align-items: center;
}
}
.right {
width: 320px;
.title {
width: 283px;
min-height: 24px;
max-height: 48px;
margin: 16px auto 18px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.tag-box {
width: 283px;
margin: 0 auto;
height: 28px;
display: flex;
gap: 8px;
.tag {
height: 28px;
padding: 0 8px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
line-height: 28px;
text-align: center;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
}
}
.content {
margin: 0 auto;
margin-top: 8px;
width: 283px;
height: 150px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
.area-box {
width: 283px;
margin: 0 auto;
margin-top: 28px;
height: 24px;
display: flex;
justify-content: flex-start;
gap: 8px;
.area {
height: 24px;
padding: 0 8px;
line-height: 24px;
text-align: center;
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 14px;
font-weight: 400;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
}
.footer {
width: 283px;
margin: 0 auto;
margin-top: 8px;
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
}
}
.inner-box2 {
width: 412px;
height: 368px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
overflow: hidden;
position: relative;
.chart-header {
position: absolute;
top: 14px;
left: 20px;
width: 90px;
height: 26px;
border-radius: 20px;
background: var(--color-main-active);
text-align: center;
line-height: 26px;
color: #fff;
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
line-height: 26px;
letter-spacing: 0px;
}
.box2Chart {
width: 412px;
height: 368px;
}
}
}
}
.box3 {
margin-top: 16px;
width: 1110px;
height: 434px;
.box3-header {
height: 48px;
box-sizing: border-box;
border-bottom: 1px solid rgba(255, 255, 255, 1);
background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%);
display: flex;
.icon {
width: 17px;
height: 16.5px;
margin-left: 19px;
margin-top: 16px;
img {
width: 100%;
height: 100%;
}
}
.title {
margin-top: 8px;
margin-left: 13px;
width: 199px;
height: 31px;
color: var(--color-main-active);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 24px;
font-weight: 400;
line-height: 31px;
letter-spacing: 0px;
text-align: left;
}
}
.box3-main {
height: 386px;
background: rgba(255, 255, 255, 0.65);
display: flex;
justify-content: center;
gap: 16px;
.inner-box1 {
width: 640px;
height: 368px;
overflow: hidden;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
display: flex;
.left {
width: 320px;
border-right: 1px solid rgba(234, 236, 238, 1);
.left-main {
margin-top: 9px;
height: 270px;
.left-item {
height: 54px;
border: 1px solid transparent;
display: flex;
align-items: center;
&:hover {
background: rgba(246, 250, 255, 1);
}
cursor: pointer;
.id {
margin-left: 16px;
width: 24px;
height: 24px;
border-radius: 50px;
background: rgba(231, 243, 255, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: center;
}
.text {
width: 260px;
height: 30px;
margin-left: 12px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.leftItemActive {
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
}
}
.left-footer {
height: 80px;
display: flex;
justify-content: center;
align-items: center;
}
}
.right {
width: 320px;
.title {
width: 283px;
min-height: 24px;
max-height: 48px;
margin: 16px auto 18px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.tag-box {
width: 283px;
margin: 0 auto;
height: 28px;
display: flex;
gap: 8px;
.tag {
height: 28px;
padding: 0 8px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
line-height: 28px;
text-align: center;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
}
}
.content {
margin: 0 auto;
margin-top: 8px;
width: 283px;
height: 150px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
.area-box {
width: 283px;
margin: 0 auto;
margin-top: 28px;
height: 24px;
display: flex;
justify-content: flex-start;
gap: 8px;
.area {
height: 24px;
padding: 0 8px;
line-height: 24px;
text-align: center;
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 14px;
font-weight: 400;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
}
.footer {
width: 283px;
margin: 0 auto;
margin-top: 8px;
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
}
}
.inner-box2 {
width: 412px;
height: 368px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
overflow: hidden;
position: relative;
.chart-header {
position: absolute;
top: 14px;
left: 20px;
width: 90px;
height: 26px;
border-radius: 20px;
background: var(--color-main-active);
text-align: center;
line-height: 26px;
color: #fff;
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 20px;
font-weight: 400;
line-height: 26px;
letter-spacing: 0px;
}
.box3Chart {
width: 412px;
height: 368px;
}
}
}
}
}
}
}
</style>
const getWordCloudChart = (data) => {
const option = {
grid: {
left: 0,
top: 0,
right: 0,
bottom: 0,
},
series: [
{
type: "wordCloud",
width: '80%',
height: '80%',
shape: "rect", //
// 其他形状你可以使用形状路径
// 或者自定义路径
// shape: 'circle' // 圆形(默认)
// shape: 'rect' // 矩形
// shape: 'roundRect' // 圆角矩形
// shape: 'triangle' // 三角形
// shape: 'diamond' // 菱形
// shape: 'pentagon' // 五边形
// shape: 'star' // 星形
// shape: 'cardioid' // 心形
gridSize: 15, // 网格大小,影响词间距。
sizeRange: [10, 30], // 定义词云中文字大小的范围
rotationRange: [0, 0],
rotationStep: 15,
drawOutOfBound: false, // 是否超出画布
// 字体
textStyle: {
// normal: {
// color: function () {
// return 'rgb(' + [
// Math.round(Math.random() * 160),
// Math.round(Math.random() * 160),
// Math.round(Math.random() * 160)
// ].join(',') + ')';
// }
// },
color: function () {
let colors = [
"rgba(189, 33, 33, 1)",
"rgba(232, 151, 21, 1)",
"rgba(220, 190, 68, 1)",
"rgba(96, 58, 186, 1)",
"rgba(32, 121, 69, 1)",
"rgba(22, 119, 255, 1)",
];
return colors[parseInt(Math.random() * colors.length)];
},
emphasis: {
shadowBlur: 5,
shadowColor: "#333",
},
},
// 设置词云数据
data: data,
},
],
}
return option
}
export default getWordCloudChart
\ No newline at end of file
......@@ -151,11 +151,46 @@
</div>
<div class="bottom-content">
<div class="timeline-container">
<div class="timeline-item">
<div ref="mainChartRef" :style="{ width: '100%', height: totalHeight + 'px' }"></div>
<div class="timeline-list">
<div v-for="(dept, index) in filteredTimelineList" :key="index" class="dept-row">
<div class="dept-info">
<img :src="defaultImg" alt="" class="dept-icon" />
<div class="dept-text">
<div class="dept-name">{{ dept.name }}</div>
<div class="dept-count">{{ dept.count }}</div>
</div>
</div>
<div class="events-container">
<div v-for="(event, eIndex) in dept.events" :key="eIndex" class="event-card">
<div class="card-top-line" :class="getLineColorClass(event.level)"></div>
<div class="event-header">
<div class="event-date">{{ event.date }}</div>
<div v-if="event.level" class="traffic-light">
<div class="light" :class="{ active: event.level === 'green', green: true }"></div>
<div class="light" :class="{ active: event.level === 'yellow', yellow: true }"></div>
<div class="light" :class="{ active: event.level === 'red', red: true }"></div>
</div>
</div>
<el-tooltip
effect="dark"
:content="event.content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="event-content">{{ event.content }}</div>
</el-tooltip>
<div class="event-tags">
<span v-for="(tag, tIndex) in event.tags" :key="tIndex" class="tag" :class="getTagClass(tag)">
{{ tag }}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="timelineBar">
<div ref="sliderChartRef" style="width: 100%; height: 100%"></div>
<div class="timelineBar" :style="{ backgroundImage: `url(${timelineBg})` }">
<div ref="sliderChartRef" style="width: 100%; height: 100%; touch-action: none;"></div>
</div>
</div>
</div>
......@@ -173,6 +208,7 @@ import icon1 from "../../assets/icon1.png";
import icon2 from "../../assets/icon2.png";
import icon3 from "../../assets/icon3.png";
import icon4 from "../../assets/icon4.png";
import timelineBg from "../../assets/timeline.png";
import defaultImg from "../../../../assets/images/default-icon2.png";
const cardList = ref([
......@@ -282,30 +318,219 @@ const dynamicList = ref([
}
]);
const getTagClass = tag => {
// 1. 定义已知标签的固定颜色映射
const getColorName = (tag) => {
const tagColorMap = {
航空航天: "tag-blue",
生物科技: "tag-blue",
集成电路: "tag-blue",
能源: "tag-green",
新材料: "tag-green",
人工智能: "tag-red"
航空航天: "blue",
生物科技: "blue",
集成电路: "blue",
能源: "green",
新材料: "green",
人工智能: "red"
};
// 2. 如果是已知标签,直接返回对应颜色
if (tagColorMap[tag]) return tagColorMap[tag];
// 3. 如果是未知标签,使用简单的字符串哈希算法分配一个稳定的颜色
const colors = ["tag-blue", "tag-green", "tag-red"];
const colors = ["blue", "green", "red", "orange", "purple", "cyan"];
let hash = 0;
for (let i = 0; i < tag.length; i++) {
hash = tag.charCodeAt(i) + ((hash << 5) - hash);
}
// 取绝对值并取模,确保同一个字符串永远得到同一个颜色类
return colors[Math.abs(hash) % colors.length];
};
const getTagClass = tag => {
return `tag-${getColorName(tag)}`;
};
const getLineColorClass = (level) => {
if (level) {
return `line-${level}`;
}
return `line-default`;
};
const dateRange = ref([0, 0]);
const filteredTimelineList = computed(() => {
if (!dateRange.value || dateRange.value[0] === 0) return timelineList.value;
const [start, end] = dateRange.value;
return timelineList.value.map(dept => {
const events = dept.events.filter(e => {
const t = parseDate(e.date);
return t >= start && t <= end;
});
return { ...dept, events };
});
});
const timelineList = ref([
{
name: "商务部",
count: 77,
events: [
{
date: "2025年1月24日",
content: "商务部发布《人工智能扩散框架》",
tags: ["人工智能"],
level: "red"
},
{
date: "2025年2月10日",
content: "工业与安全局出口管制指南,限制先进AI芯片及相关技术...",
tags: ["集成电路"]
},
{
date: "2025年2月15日",
content: "商务部宣布实施美国人工智能出口项目",
tags: ["人工智能"]
},
{
date: "2025年4月10日",
content: "针对中国产海运、钢、铝、铜矿产及制品加征关税",
tags: ["新材料"],
level: "yellow"
},
{
date: "2025年4月15日",
content: "工业与安全局新增实体清单,包含45家中国科技企业",
tags: ["人工智能"],
level: "red"
}
]
},
{
name: "国务院",
count: 49,
events: [
{
date: "2025年1月26日",
content: "联合声明—战略人工智能伙伴关系",
tags: ["人工智能"],
level: "green"
},
{
date: "2025年2月1日",
content: "商务部宣布实施美国人工智能出口项目商务部宣布实施美...",
tags: ["人工智能"]
},
{
date: "2025年2月13日",
content: "国务院宣布终止5项美中文化交流计划,包括政策制定者...",
tags: ["量子科技", "航空航天"],
level: "red"
},
{
date: "2025年3月8日",
content: "国务院宣布进一步调整对中国留学生的签证政策,主要针...",
tags: ["人工智能"]
},
{
date: "2025年4月19日",
content: "联合声明—战略人工智能伙伴关系",
tags: ["人工智能"],
level: "green"
}
]
},
{
name: "财政部",
count: 49,
events: [
{
date: "2025年1月28日",
content: "财政部执行税收抵免新规与供应链审查,终止电动汽车税...",
tags: ["能源"],
level: "red"
},
{
date: "2025年2月11日",
content: '将6名中国个人和10家中国实体加入"特别指定国民清单"',
tags: ["集成电路"]
},
{
date: "2025年2月27日",
content: '向EDA巨头发出"正告函",暂停处理对华出口3nm及以下...',
tags: ["集成电路"],
level: "red"
},
{
date: "2025年4月10日",
content: "财政部准备向美国9家人工智能企业投资210亿元",
tags: ["人工智能"],
level: "green"
}
]
},
{
name: "贸易代表办公室",
count: 49,
events: [
{
date: "2025年1月25日",
content: '对“中国相关船舶”进入美国港口征收“入港费”,并分...',
tags: ["能源"],
level: "yellow"
},
{
date: "2025年2月2日",
content: "对进口自中国的半导体产品加征关税",
tags: ["集成电路"]
},
{
date: "2025年2月20日",
content: '向EDA巨头发出"正告函",暂停处理对华出口3nm及以下...',
tags: ["集成电路"]
},
{
date: "2025年3月23日",
content: "财政部准备向美国9家人工智能企业投资210亿元",
tags: ["人工智能"],
level: "green"
},
{
date: "2025年4月21日",
content: "财政部拟议出台《境外投资规程》,限制对华高科技投...",
tags: ["人工智能"],
level: "yellow"
}
]
},
{
name: "商务部",
count: 77,
events: [
{
date: "2025年1月24日",
content: "商务部发布《人工智能扩散框架》",
tags: ["人工智能"],
level: "red"
},
{
date: "2025年2月10日",
content: "工业与安全局出口管制指南,限制先进AI芯片及相关技术...",
tags: ["集成电路"]
},
{
date: "2025年2月15日",
content: "商务部宣布实施美国人工智能出口项目",
tags: ["人工智能"]
},
{
date: "2025年4月10日",
content: "针对中国产海运、钢、铝、铜矿产及制品加征关税",
tags: ["新材料"],
level: "yellow"
},
{
date: "2025年4月15日",
content: "工业与安全局新增实体清单,包含45家中国科技企业",
tags: ["人工智能"],
level: "red"
}
]
}
]);
const rankingList = ref([
{
depts: ["商务部", "财政部", "国务院"],
......@@ -343,10 +568,7 @@ const rankingList = ref([
const chartRef = ref(null);
let myChart = null;
const mainChartRef = ref(null);
const sliderChartRef = ref(null);
const totalHeight = ref(600);
let mainChart = null;
let sliderChart = null;
const parseDate = (str) => {
......@@ -366,114 +588,23 @@ const timelineAllData = computed(() => {
return all.sort((a, b) => parseDate(b.date) - parseDate(a.date));
});
const updateTimeline = (startTime, endTime) => {
const filtered = timelineAllData.value.filter(item => {
const t = parseDate(item.date);
return t >= startTime && t <= endTime;
});
const itemHeight = 80;
totalHeight.value = Math.max(filtered.length * itemHeight, 552);
setTimeout(() => {
if (!mainChart) return;
mainChart.resize();
const option = {
grid: {
left: 150,
right: 50,
top: 20,
bottom: 20
},
tooltip: {
show: true,
formatter: (params) => {
const item = filtered[params.dataIndex];
return `${item.date}<br/>${item.title}`;
}
},
singleAxis: {
type: 'category',
orient: 'vertical',
data: filtered.map((_, i) => i),
top: 20,
bottom: 20,
left: 120,
width: '80%',
axisLabel: {
interval: 0,
formatter: (value, index) => {
if (!filtered[index]) return '';
return filtered[index].date;
},
align: 'right',
margin: 20,
textStyle: {
color: '#666',
fontSize: 14
}
},
axisLine: {
lineStyle: {
color: '#ccc',
width: 2
}
},
axisPointer: {
animation: true,
label: {
show: true
}
},
splitLine: { show: false }
},
series: [
{
type: 'scatter',
coordinateSystem: 'singleAxis',
data: filtered.map((_, i) => [i, 0]),
symbolSize: 14,
itemStyle: {
color: '#2f79c4',
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
formatter: (params) => {
const item = filtered[params.dataIndex];
return item.title;
},
position: 'right',
offset: [20, 0],
color: '#333',
fontSize: 16,
fontWeight: 'bold',
width: 400,
overflow: 'truncate'
}
}
]
};
mainChart.setOption(option);
}, 50);
};
const initTimeline = () => {
if (!mainChartRef.value) return;
mainChart = echarts.init(mainChartRef.value);
updateTimeline(0, Date.now() + 365 * 24 * 3600 * 1000 * 10);
};
const initSlider = () => {
if (!sliderChartRef.value) return;
sliderChart = echarts.init(sliderChartRef.value);
const dates = timelineAllData.value.map(item => parseDate(item.date));
const minDate = Math.min(...dates);
const maxDate = Math.max(...dates);
const rangeMin = minDate - 30 * 24 * 3600 * 1000;
const rangeMax = maxDate + 30 * 24 * 3600 * 1000;
// 设定总范围:2010年1月1日 - 当前年份年底
const currentYear = new Date().getFullYear();
const rangeMin = new Date(2018, 0, 1).getTime();
const rangeMax = new Date(currentYear, 11, 31).getTime();
// 设定默认选中范围:2025-01-01 到 2025-04-25
const defaultStart = new Date(2025, 0, 1).getTime();
const defaultEnd = new Date(2025, 3, 25).getTime();
// 初始化 dateRange 为默认选中范围
dateRange.value = [defaultStart, defaultEnd];
const option = {
grid: {
......@@ -497,12 +628,38 @@ const initSlider = () => {
type: 'slider',
xAxisIndex: 0,
filterMode: 'weakFilter',
height: 30,
bottom: 10,
handleSize: '100%',
showDetail: true,
start: 0,
end: 100
height: '100%',
top: 0,
bottom: 0,
left: 20,
right: 20,
// 默认选中范围
startValue: defaultStart,
endValue: defaultEnd,
backgroundColor: 'transparent',
borderColor: 'transparent',
fillerColor: 'rgba(246, 250, 255, 1)',
dataBackground: {
lineStyle: { color: 'transparent' },
areaStyle: { color: 'transparent' }
},
selectedDataBackground: {
lineStyle: { color: 'transparent' },
areaStyle: { color: 'transparent' }
},
labelFormatter: (value) => {
const date = new Date(value);
const y = date.getFullYear();
const m = (date.getMonth() + 1).toString().padStart(2, '0');
const d = date.getDate().toString().padStart(2, '0');
return `${y}-${m}-${d}`;
},
textStyle: {
color: '#5F656C',
fontSize: 12
},
// 解决 passive event 警告的关键配置
preventDefaultMouseMove: false
}
],
series: [
......@@ -510,30 +667,22 @@ const initSlider = () => {
type: 'scatter',
data: dates.map(d => [d, 0]),
symbolSize: 5,
itemStyle: { color: '#ccc' }
itemStyle: { color: 'transparent' }
}
]
};
sliderChart.setOption(option);
sliderChart.on('dataZoom', function () {
sliderChart.on('dataZoom', () => {
const opt = sliderChart.getOption();
const startValue = opt.dataZoom[0].startValue;
const endValue = opt.dataZoom[0].endValue;
let startT, endT;
if (startValue != null && endValue != null) {
startT = startValue;
endT = endValue;
} else {
const range = rangeMax - rangeMin;
const startP = opt.dataZoom[0].start;
const endP = opt.dataZoom[0].end;
startT = rangeMin + range * startP / 100;
endT = rangeMin + range * endP / 100;
if (opt.dataZoom && opt.dataZoom[0]) {
const start = opt.dataZoom[0].startValue;
const end = opt.dataZoom[0].endValue;
if (start && end) {
dateRange.value = [start, end];
}
}
updateTimeline(startT, endT);
});
};
......@@ -646,11 +795,9 @@ const initChart = () => {
onMounted(() => {
initChart();
initTimeline();
initSlider();
window.addEventListener("resize", () => {
myChart && myChart.resize();
mainChart && mainChart.resize();
sliderChart && sliderChart.resize();
});
});
......@@ -1215,7 +1362,204 @@ const prev = () => {
padding: 8px 8px 8px 8px;
background-color: rgb(247, 248, 249);
border: 1px solid rgb(234, 236, 238);
.timeline-item {
display: flex;
flex-direction: column;
.timeline-list {
flex: 1;
overflow-y: auto;
padding-right: 8px;
padding-bottom: 8px;
.dept-row {
display: flex;
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
.dept-info {
width: 200px;
flex-shrink: 0;
display: flex;
align-items: center;
background-color: #fff;
border-radius: 4px;
margin-right: 8px;
padding: 12px 16px;
box-sizing: border-box;
height: 130px;
.dept-icon {
width: 48px;
height: 50px;
margin-right: 8px;
}
.dept-text {
.dept-name {
font-family: "Microsoft YaHei";
font-size: 18px;
font-weight: 700;
color: rgb(59, 65, 75);
line-height: 24px;
}
.dept-count {
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
color: rgb(59, 65, 75);
line-height: 24px;
}
}
}
.events-container {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
overflow-x: auto;
// 隐藏滚动条
&::-webkit-scrollbar {
display: none;
}
.event-card {
min-width: 240px;
max-width: 240px;
height: 130px;
background-color: #fff;
border-radius: 4px;
padding: 12px;
box-sizing: border-box;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
flex-shrink: 0;
.card-top-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3px;
&.line-blue { background-color: #2f79c4; }
&.line-green { background-color: rgb(33, 129, 57); }
&.line-red { background-color: rgb(206, 79, 81); }
&.line-orange { background-color: #fa8c16; }
&.line-yellow { background-color: rgb(232, 189, 11); }
&.line-purple { background-color: #722ed1; }
&.line-cyan { background-color: #13c2c2; }
&.line-default { background-color: rgb(234, 236, 238); }
}
.event-header {
display: flex;
justify-content: space-between;
align-items: center;
.event-date {
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 700;
color: rgb(59, 65, 75);
line-height: 30px;
}
.traffic-light {
display: flex;
gap: 2px;
background-color: rgb(247, 248, 249);
padding: 2px 2px;
border-radius: 20px;
.light {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: rgb(234, 236, 238);
&.green.active {
background-color: rgb(33, 129, 57);
}
&.yellow.active {
background-color: rgb(232, 189, 11);
}
&.red.active {
background-color: rgb(206, 79, 81);
}
}
}
}
.event-content {
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
color: rgb(59, 65, 75);
line-height: 24px;
// margin-bottom: auto;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.event-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 4px;
.tag {
padding: 2px 8px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
border: 1px solid;
&.tag-blue {
color: rgba(9, 88, 217, 1);
background: rgba(230, 244, 255, 1);
border-color: rgba(186, 224, 255, 1);
}
&.tag-green {
color: rgba(56, 158, 13, 1);
background: rgba(246, 255, 237, 1);
border-color: rgba(217, 247, 190, 1);
}
&.tag-red {
color: rgba(245, 34, 45, 1);
background: rgba(255, 241, 240, 1);
border-color: rgba(255, 163, 158, 1);
}
&.tag-orange {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
border-color: rgba(255, 213, 145, 1);
}
&.tag-purple {
color: rgba(114, 46, 209, 1);
background: rgba(249, 240, 255, 1);
border-color: rgba(211, 173, 247, 1);
}
&.tag-cyan {
color: rgba(19, 194, 194, 1);
background: rgba(230, 255, 251, 1);
border-color: rgba(135, 232, 222, 1);
}
}
}
}
}
}
}
.timelineBar {
width: 100%;
height: 552px;
overflow-y: auto;
......@@ -1227,6 +1571,9 @@ const prev = () => {
width: 100%;
height: 51px;
background-color: rgba(255, 255, 255, 1);
background-size: 100% 20px;
background-repeat: no-repeat;
background-position: center;
}
}
}
......
......@@ -2,18 +2,18 @@
<template>
<div class="content-wrapper">
<div class="main-nav">
<div
v-for="item in navList"
:key="item.name"
class="nav-item"
:class="{ active: activeNav === item.name }"
@click="handleNavClick(item.name)"
>
<div class="item-content">
<img v-if="activeNav === item.name" :src="right" class="active-icon" alt="" />
<span>{{ item.name }}</span>
</div>
<img v-if="activeNav === item.name" :src="background" class="active-bg" alt="" />
<div
v-for="item in navList"
:key="item.name"
class="nav-item"
:class="{ active: activeNav === item.name }"
@click="handleNavClick(item.name)"
>
<div class="item-content">
<img v-if="activeNav === item.name" :src="right" class="active-icon" alt="" />
<span>{{ item.name }}</span>
</div>
<img v-if="activeNav === item.name" :src="background" class="active-bg" alt="" />
</div>
</div>
<!-- 切换不同的组件 -->
......@@ -35,88 +35,84 @@ import AddDomain from "./components/addDomain/index.vue";
import AllUnion from "./components/allUnion/index.vue";
import AllElement from "./components/allElement/index.vue";
const navList = ref([
{ name: "全政府" },
{ name: "全领域" },
{ name: "全联盟" },
{ name: "全要素" }
]);
const navList = ref([{ name: "全政府" }, { name: "全领域" }, { name: "全联盟" }, { name: "全要素" }]);
const activeNav = ref("全政府");
const handleNavClick = (name) => {
activeNav.value = name;
const handleNavClick = name => {
activeNav.value = name;
};
</script>
<style lang="scss" scoped>
.content-wrapper {
width: 1601px;
height: 2203px;
margin: 0 auto;
.main-nav {
width: 100%;
height: 55px;
width: 1601px;
height: auto;
margin: 0 auto;
.main-nav {
width: 1600px;
height: 55px;
// margin: 0 auto;
padding: 4px 5px;
display: flex;
align-items: center;
display: flex;
align-items: center;
border-radius: 10px;
background-color: rgba(255, 255, 255, 0.65);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
box-sizing: border-box;
gap: 8px;
box-sizing: border-box;
gap: 8px;
margin-bottom: 16px;
.nav-item {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
// transition: all 0.3s;
.nav-item {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
position: relative;
// transition: all 0.3s;
.item-content {
display: flex;
align-items: center;
z-index: 1;
.item-content {
display: flex;
align-items: center;
z-index: 1;
.active-icon {
width: 18px;
height: 18px;
margin-right: 8px;
}
.active-icon {
width: 18px;
height: 18px;
margin-right: 8px;
}
span {
font-family: 'YouSheBiaoTiHei';
font-size: 24px;
font-weight: 400;
line-height: 31px;
color: rgb(59, 65, 75);
}
}
span {
font-family: "YouSheBiaoTiHei";
font-size: 24px;
font-weight: 400;
line-height: 31px;
color: rgb(59, 65, 75);
}
}
.active-bg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
height: 60px;
z-index: 0;
}
.active-bg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
height: 60px;
z-index: 0;
}
&.active {
background-color: rgba(246, 250, 255, 1);
border: 1px solid rgba(174, 214, 255, 1);
border-radius: 10px;
span {
color: rgb(5, 95, 194);
}
}
}
}
&.active {
background-color: rgba(246, 250, 255, 1);
border: 1px solid rgba(174, 214, 255, 1);
border-radius: 10px;
span {
color: rgb(5, 95, 194);
}
}
}
}
}
</style>
<template>
<div class="timeline-wrapper">
<button class="arrow left" :disabled="index <= 0" @click="index--">
{{ '<' }}
</button>
{{ '<' }} </button>
<div class="timeline-box">
<div class="line" style="width:100vw ;" />
<!-- 一次性渲染全部节点 -->
<div v-for="(item, i) in data" :key="item[idKey]" class="node" :style="leftStyle(i)">
<!-- 圆环 -->
<div class="dot" :class="linePos(item)" :style="{ '--i': item.unit === '中国' ? ' #E29697' : '#69A0DA' }" />
<div class="dot">
<div class="big-circle" :style="{
backgroundColor: item.unit === '中国' ? ' #CF4F51' : '#CDDFF3'
}">
<div class="small-circle" :style="{
backgroundColor: item.unit === '中国' ? ' #E29697' : '#69A0DA'
}"></div>
</div>
</div>
<div class="timeline-box">
<div class="line"></div>
<div v-for="(item, i) in showList" :key="item[idKey]" class="node" :style="leftOffset(i)">
<div class="node" :style="leftOffset(i)">
<!-- 圆环 -->
<div class="dot" :class="linePos(i, flip)"></div>
<!-- 卡片 -->
<div class="card" :class="[cardPos(item), 'right-side']" @click="$emit('click-card', item)">
<div style="justify-content: space-between;display: flex;width: 300px;">
<div class="tag">{{ item.tag }}</div>
<img :src="`/icon/${item.unit}.png`" class="icon"></img>
<!-- 卡片:放到线右侧 -->
<div class="card" :class="[cardPos(i, flip), 'right-side']" @click="$emit('click-card', item)">
</div>
<div class="title" :style="{
<div class="time">
{{ item.time }}
</div>
<div class="title">
{{ item.title }}
<img class="item-header-icon" src="@/assets/images/icon/copy.png" style="cursor: pointer;" />
</div>
<div class="content">
{{ item.content }}
}">{{ item.title }}</div>
<div class="time">{{ item.time }}</div>
<!-- <div class="title" :style="{
color: item.unit === '中国' ? ' #CF4F51' : ''
}">{{ item.title }}</div>
<div class="time">{{ item.time }}</div> -->
<div class="content">{{ item.content }}</div>
</div>
</div>
</div>
</div>
</div>
<button class="arrow right" :disabled="index >= total - 5" @click="index++">
{{ '>' }}
</button>
<button class="arrow right" :disabled="index >= total - 5" @click="index++">
{{ '>' }}
</button>
</div>
</template>
......@@ -39,18 +49,9 @@
export default {
name: 'TimeLine',
props: {
data: { // 父组件传入的数组
type: Array,
required: true
},
textKey: { // 要显示的文本字段
type: String,
default: 'text'
},
idKey: { // 唯一标识字段
type: String,
default: 'id'
}
data: { type: Array, required: true },
textKey: { type: String, default: 'text' },
idKey: { type: String, default: 'id' }
},
data() {
return { index: 0 };
......@@ -65,37 +66,49 @@ export default {
flip() { return this.index % 2 === 1; }
},
methods: {
leftOffset(i) {
return { left: `${(i * 100) / 5}%` };
// widthStyle() {
// console.log(`100 / ${this.data.length - 1}}vw`)
// let w = 100 / (this.data.length - 1)
// return { width: ` calc(${w}vw - 20px)` };
// },
/* 水平位置:按索引均匀分布 */
leftStyle(i) {
// let pos = ``
// if (i === 0) {
// pos = 0
// } else {
// this.linePos(this.data[i - 1]) !== this.linePos(this.data[i]) ? pos = { left: `${(i * 270) - 125}px` } : pos = { left: `${(i * 270)}px` }
// }
return { left: `${(i * 327)}px` }
// return pos;
},
/* 上下层翻转(保留上次逻辑) */
cardPos(i, flip = false) {
return (i % 2) ^ flip ? 'down' : 'up';
/* 卡片上下位置:unit=0 -> 下侧,其余 -> 上侧 */
cardPos(item) {
return item.unit === '中国' ? 'down' : 'up';
},
/* 线延伸方向 = 卡片出现方向 */
linePos(i, flip = false) {
return this.cardPos(i, flip); // up / down
/* 延伸线方向 = 卡片方向 */
linePos(item) {
return this.cardPos(item);
}
}
};
</script>
<style scoped>
/* 样式与之前完全一致,不再重复 */
/* 以下样式完全沿用你已有的,无需改动 */
.timeline-wrapper {
display: flex;
align-items: center;
width: 100%;
position: relative;
padding: 0 40px;
width: 1500px;
overflow: auto;
}
.arrow {
position: absolute;
top: 170px;
/* 左右切换按钮 */
width: 24px;
height: 48px;
font-size: 24px;
......@@ -123,10 +136,21 @@ export default {
border-radius: 4px 0px 0px 4px;
}
.year-box {
width: 80px;
height: 36px;
border-radius: 4px;
background: #055fc2;
color: #fff;
font-size: 18px;
border: none
}
.timeline-box {
flex: 1;
height: 100%;
position: relative;
margin-left: 15px;
}
.line {
......@@ -142,101 +166,185 @@ export default {
background-size: auto 100%;
}
.node {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
z-index: 2;
z-index: 2
}
/* ===== 圆环基础 ===== */
.dot {
/* 任意尺寸/居中方式随意 */
margin: 0 auto;
position: relative;
display: flex;
justify-content: center;
align-items: center;
/* 仅示例 */
}
.big-circle {
width: 14px;
height: 14px;
border-radius: 50%;
/* 大圆颜色 */
display: flex;
justify-content: center;
align-items: center;
}
.small-circle {
width: 8px;
/* 小圆直径 */
height: 8px;
border-radius: 50%;
/* 小圆颜色 */
}
/* .dot {
width: 14px;
height: 14px;
border-radius: 50%;
border: 3px solid #409eff;
background: #fff;
position: relative;
margin: 0 auto;
z-index: 2;
}
position: relative
} */
/* ===== 延伸线 ===== */
.dot::after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-1px);
/* 居中细线 */
width: 1px;
background: #409eff;
/* background: #409eff */
background: var(--i);
}
/* 向上节点:线往下伸 */
.dot.up::after {
bottom: 100%;
height: 165px;
/* 圆环底部 → 卡片顶 */
height: 240px
}
/* 向下节点:线往上伸 */
.dot.down::after {
top: 100%;
height: 165px;
height: 240px
}
.card {
position: absolute;
height: 165px;
width: 320px;
padding: 8px 12px;
text-align: left;
cursor: pointer;
/* 容器 28 */
width: 327px;
height: 270px;
padding-left: 27px;
font-size: 14px;
cursor: pointer
}
.time {
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: justify;
margin-bottom: 10px;
.card.up {
bottom: 0px
}
.card.down {
top: 10px;
padding-top: 50px
}
.icon {
width: 28px;
height: 28px;
}
.title {
/* 美国进一步收紧对华AI芯片出口限制 */
width: 300px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: justify;
margin-bottom: 10px;
}
.content {
color: rgba(95, 101, 108, 1);
.tag {
/* 容器 1626 */
width: 120px;
height: 28px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
padding: 2px 8px 2px 8px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
text-align: center;
margin-bottom: 4px;
}
.card.up {
bottom: 20px;
.tag1 {
/* 容器 1626 */
width: 120px;
height: 28px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
padding: 2px 8px 2px 8px;
border-radius: 4px;
background: rgba(206, 79, 81, 0.1);
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.card.down {
top: 20px;
.time {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
.content {
width: 300px;
height: 150px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
</style>
\ No newline at end of file
<!--科技要闻-->
<!--ZM博弈概况-->
<template>
<div class="content-wrapper">
<div class="card-box">
......@@ -6,82 +6,266 @@
<img class="icon" src="../../assets/icons/title-icon1.png" />
<img class="text" src="../../assets/icons/title-text1.png" />
</div>
<div style="display: flex; height: 650px; width: 100%">
<div style="width: 50%">
<div style="display: flex; justify-content: space-between; margin-right: 50px; line-height: 32px;align-items: center;" >
<div style="display: flex; margin-left: 50px">
数据来源:
<el-select class="select-item" size="default" style="margin-left: 15px; width: 240px; height: 32px">
<el-option label="国家创新指数报告" value="last_year" />
</el-select>
</div>
<div style="display: flex">
<div class="china-icon"></div>
<div class="text">中国</div>
<div class="usa-icon"></div>
<div class="text">美国</div>
</div>
</div>
<div style="width: 672px; height: 486px; padding-top: 50px" id="char"></div>
</div>
<div style="width: 50%">
<el-table :data="tableData" style="width: 100%; margin-bottom: 20px" row-key="id" border default-expand-all>
<el-table-column prop="name" label="指标名称" sortable width="350" />
<el-table-column prop="percent" label="中国">
<template #default="scope">
<div class="progress-wrapper left" :style="{ '--i': '40px', '--j': '-20px', marginLeft: '20px' }">
<el-progress :percentage="scope.row.percent[0]" :stroke-width="20" class="left-progress"
:show-text="false" />
</div>
</template>
</el-table-column>
<el-table-column prop="percent" label="美国">
<template #default="scope">
<div class="progress-wrapper right" :style="{ '--i': '40px', marginRight: '20px' }">
<el-progress :percentage="scope.row.percent[0]" :stroke-width="20" class="right-progress"
:show-text="false" />
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<div class="card-box" style="margin-top: 16px;">
<div class="card-box" style="margin-top: 16px">
<div class="card-title">
<img class="icon" src="../../assets/icons/title-icon2.png" />
<img class="text" src="../../assets/icons/title-text2.png" />
</div>
<div style="display: flex;height: 530px;">
<div style="display: flex; height: 650px">
<Timeline :data="course" text-key="title" id-key="seq" />
</div>
</div>
<div class="btn-box">
<div v-for="(value, index) in btnList" class="btn-item" :style="{
background: value.background
}">
<img :src="`/public/icon/ZM/btn-icon-${index}.png`" style="width: 22px; height: 19px; margin: 0 22px" />
{{ value.text }}
<img :src="`/public/icon/ZM/btn-icon-arrow.png`" style="margin-left: auto; margin-right: 22px; width: 13px; height: 12px" />
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import * as echarts from "echarts";
import Timeline from "./Timeline.vue";
import tableShow from "./tableShow.vue";
import radarChart from "./radarChart3.js";
const course = ref([
{
time: "2025年1月",
title: "《AI扩散暂行最终规则》发布",
content: "拜登政府发布《AI扩散暂行最终规则》,建立三级许可制度。"
time: "2025-01-15",
title: "《国家量子倡议再授权法案》发布",
content:
"计划将国家量子倡议延长至2034年,新增研发中心、测试平台,并首次将NASA的量子研究纳入,旨在加速技术应用与人才发展。",
unit: "美国",
tag: "科技发展战略"
},
{
time: "2025年1月",
title: "特朗普宣布撤销拜登AI规则",
content: "拜登政府发布《AI扩散暂行最终规则》,建立三级许可制度。"
time: "2025-01-21",
title: "将美国实体列入'不可靠实体清单'并采取商品进口限制",
content: "根据《反外国制裁法》等,将17家美国实体列入清单,禁止其从事与中国有关的进出口与新增投资。",
unit: "中国",
tag: "反制措施"
},
{
time: "2025年1月",
title: "特朗普签署EO 143202",
content: "特朗普政府宣布撤销拜登AI规则,计划"
time: "2025-01-21",
title: "《国家创新驱动发展战略纲要》发布",
content: "纲领性文件,提出了建设世界科技强国的'三步走'战略目标,并系统部署了构建现代产业技术体系等八大战略任务。",
unit: "中国",
tag: "科技发展战略"
},
{
time: "2025年1月",
time: "2025-01-21",
title: "中国网信办约谈英伟达",
content: "中国网信办约谈英伟达,要求就H20算力芯片漏洞后门安全风险问题进行说明。"
content: "中国网信办约谈英伟达,要求就H20算力芯片漏洞后门安全风险问题进行说明。",
unit: "中国",
tag: "反制措施"
},
{
time: "2025-05-09",
title: "《2026财年中国防授权法案》发布",
content:
"年度国防预算法案,其中系统性地嵌入了大量对华竞争条款,首次将对外投资审查法律化,并在供应链、实体清单等方面加强限制。",
unit: "美国",
tag: "对华打压遏制"
},
{
time: "2025年7月23日",
title: "英伟达H20发放出口许可证",
content: "美国商务部为4月份被实质禁售的英伟达H20发放出口许可证。"
time: "2025-07-24",
title: "白宫'人工智能(AI)行动计划'",
content: "提出放宽监管、简化环境许可可以加快AI基础设施建设,并涉及促进AI出口、要求联邦采购的AI模型具备中立性等内容。",
unit: "美国",
tag: "科技发展战略"
},
{
time: "2025-07-24",
title: "2026财年《商务、司法、科学及相关支出法案》",
content: "法案为商务部工业与安全局(BIS)大幅增加预算,以加强出口管制执法,特别强调审查对华为、中芯国际的出口许可证。",
unit: "美国",
tag: "对华打压遏制"
}
]);
onMounted(() => {
const dom = document.getElementById("char");
const myChart = echarts.init(dom);
// 2. 传入容器,生成配置
const option = radarChart(
["创新资源", "知识创造", "企业创新", "创新绩效", "创新环境"],
[
[4200, 3000, 20000, 35000, 50000, 18000],
[1000, 42000, 32000, 35000, 50000, 18000]
]
);
myChart.setOption(option);
// setChart(option, "char7");
});
const tableData = ref([
{
id: 1,
name: "综合创新指数",
percent: [60, 40],
children: []
},
{
time: "2025年1月",
title: "《AI扩散暂行最终规则》发布",
content: "拜登政府发布《AI扩散暂行最终规则》,建立三级许可制度。"
id: 2,
name: "创新资源",
percent: [50, 50],
children: []
},
{
time: "2025年1月",
title: "特朗普宣布撤销拜登AI规则",
content: "拜登政府发布《AI扩散暂行最终规则》,建立三级许可制度。"
id: 3,
name: "研发人力",
percent: [70, 30],
children: [
{
id: 31,
name: "每万名就业人员中R&D人员全时当量",
percent: [65, 35],
children: []
},
{
id: 32,
name: "研究人员占R&D人员比例",
percent: [55, 45],
children: []
},
{
id: 33,
name: "科学与工程类毕业生比例",
percent: [70, 30],
children: []
}
]
},
{
time: "2025年1月",
title: "特朗普签署EO 143202",
content: "特朗普政府宣布撤销拜登AI规则,计划"
id: 4,
name: "研发经费",
percent: [60, 40],
children: [
{
id: 41,
name: "R&D经费支出总额(购买力平价)",
percent: [65, 35],
children: []
},
{
id: 42,
name: "R&D经费支出占GDP比重",
percent: [55, 45],
children: []
},
{
id: 43,
name: "基础研究经费占R&D经费比重",
percent: [45, 55],
children: []
}
]
},
{
time: "2025年1月",
title: "中国网信办约谈英伟达",
content: "中国网信办约谈英伟达,要求就H20算力芯片漏洞后门安全风险问题进行说明。"
id: 5,
name: "科研条件",
percent: [60, 40],
children: [
{
id: 51,
name: "国家级重大科技基础设施数量",
percent: [50, 50],
children: []
}
]
}
]);
const btnList = ref([
{
text: "制裁征候挖掘",
background: "linear-gradient(180.00deg, rgba(9, 88, 217, 1),rgba(9, 88, 217, 0.5) 100%)"
},
{
text: "制裁内容快速摘要",
background: "linear-gradient(180.00deg, rgba(255, 97, 97, 1),rgba(255, 97, 97, 0.5) 100%)"
},
{
text: "重点领域博弈历程分析",
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
},
{
text: "制裁手段关联分析",
background: "linear-gradient(180.00deg, rgba(48, 193, 195, 1),rgba(48, 193, 195, 0.5) 100%)"
},
{
text: "政要人物画像",
background: "linear-gradient(180.00deg, rgba(123, 52, 184, 1),rgba(123, 52, 184, 0.5) 100%)"
},
{
text: "人物关系挖掘",
background: "linear-gradient(180.00deg, rgba(255, 187, 51, 1),rgba(255, 187, 51, 0.5) 100%)"
},
{
text: "人物立场分析",
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)"
},
{
time: "2025年7月23日",
title: "英伟达H20发放出口许可证",
content: "美国商务部为4月份被实质禁售的英伟达H20发放出口许可证。"
text: "制裁内容比对分析",
background: "linear-gradient(180.00deg, rgba(93, 186, 255, 1),rgba(93, 186, 255, 0.5) 100%)"
}
]);
</script>
<style lang="scss" scoped>
.content-wrapper {
width: 100%;
height: 1416px;
width: 1600px;
height: 1600px;
margin: 0 auto;
.card-box {
width: 1600px;
height: 700px;
......@@ -93,8 +277,7 @@ const course = ref([
background: rgba(255, 255, 255, 1);
.card-title {
width: 1602px;
width: 100%;
height: 48px;
display: flex;
......@@ -114,4 +297,157 @@ const course = ref([
}
}
}
/* ========== 公共外壳 ========== */
.progress-wrapper {
position: relative;
flex: 1;
height: 10px;
}
/* 文字居中在彩色条内 */
.inner-text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
color: #fff;
font-weight: bold;
z-index: 2;
}
/* ========== 左侧:右侧斜切 + 左侧圆角 ========== */
/* 左侧文字 */
.left-text {
font-size: 12px;
color: #333;
margin-right: 8px;
white-space: nowrap;
}
.left-progress {
transform: scaleX(-1);
}
.left-progress :deep(.el-progress-bar__outer) {
border-radius: 0;
clip-path: inset(0 0 0 0);
background: rgba(231, 243, 255, 1);
overflow: hidden;
/* 读行内传进来的变量 */
height: var(--i) !important;
margin-left: var(--j) !important;
}
.left-progress :deep(.el-progress-bar__inner) {
border-radius: 0;
/* 移除圆角 */
clip-path: inset(0 0 0 0);
/* 设置为矩形,inset() 函数的四个参数都是0表示不裁剪 */
background: #055fc2;
/* 读行内传进来的变量 */
height: var(--i) !important;
margin-left: var(--j) !important;
}
/* ========== 右侧:左侧斜切 + 右侧圆角 ========== */
.right-progress :deep(.el-progress-bar__outer) {
border-radius: 0;
clip-path: inset(0 0 0 0);
background: #ffccc7;
overflow: hidden;
height: var(--i) !important;
}
.right-progress :deep(.el-progress-bar__inner) {
border-radius: 0;
clip-path: inset(0 0 0 0);
background: #ce4f51;
height: var(--i) !important;
}
:deep(.el-table .cell) {
padding: 0;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
}
.china-icon {
/* 椭圆 79 */
width: 12px;
height: 12px;
margin: 5px;
border-radius: 50%;
background: rgba(206, 79, 81, 1);
}
.usa-icon {
width: 12px;
height: 12px;
margin: 5px;
border-radius: 50%;
background: rgba(5, 95, 194, 1);
}
.text {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.btn-box {
margin-top: 16px;
width: 100%;
height: auto;
/* 高度自适应内容 */
display: flex;
flex-direction: row;
/* 行方向布局 */
flex-wrap: wrap;
/* 允许换行 */
justify-content: flex-start;
align-items: stretch;
/* 使所有按钮高度一致 */
gap: 16px 16px;
/* 设置按钮之间的间距 */
padding: 0;
/* 移除内边距,防止内容溢出 */
}
.btn-item {
width: 307px;
height: 60px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
// justify-content: center;
/* 使按钮内容居中 */
align-items: center;
/* 使按钮内容居中 */
display: flex;
/* 使用flex布局 */
color: rgba(255, 255, 255, 1);
justify-content: flex-start;
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: center;
}
</style>
import * as echarts from 'echarts';
/**
* 生成动态雷达图配置
* @param {string[]} nameList 指标名称(与 indicator 顺序保持一致)
* @param {number[][]} valueList 数据值数组,每组数据为一个数组
*/
const radarChart = (nameList, valueList) => {
// 计算最大值以设置雷达图的max值
const maxValue = Math.max(...valueList.map(values => Math.max(...values)));
const colorList = [
[5, 96, 195], // 蓝色
[208, 85, 87] // 红色
];
// 1. 基础雷达图配置
const option = {
title: { text: '' },
radar: {
radius: '75%', // 雷达图本身占容器 75%
indicator: nameList.map(name => ({
name: name,
max: maxValue * 1.2 // 设置最大值为数据最大值的120%
})),
axisName: {
color: '#ffffff',
fontSize: 18,
fontWeight: 700,
backgroundColor: '#0560C3',
borderRadius: 20,
padding: [8, 20],
formatter: function (txt) {
const len = txt.length;
if (len <= 5) return txt;
const br = Math.ceil(len / 2);
return txt.substring(0, br) + '\n' + txt.substring(br);
}
},
splitLine: {
lineStyle: { color: 'rgba(200,200,200,.6)' }
},
splitArea: {
show: true,
areaStyle: {
color: [
`#ffffff`,
`#F7F8F9`
]
}
},
},
series: valueList.map((values, index) => ({
name: `组${index + 1}`,
type: 'radar',
data: [
{
value: values,
name: `组${index + 1}`,
areaStyle: {
color: `rgba(${colorList[index][0]}, ${colorList[index][1]}, ${colorList[index][2]}, 0.2)` // 填充颜色
},
itemStyle: {
color: `rgba(${colorList[index][0]}, ${colorList[index][1]}, ${colorList[index][2]}, 1)`, // 圆环颜色
borderWidth: 1, // 边框宽度
backgroundColor: '#ffffff',
borderColor: `rgba(${colorList[index][0]}, ${colorList[index][1]}, ${colorList[index][2]}, 1)` // 边框颜色
},
}
]
}))
};
return option;
};
export default radarChart;
<template>
<div>
<el-table :data="tableData" style="width: 100%; margin-bottom: 20px" row-key="id" border default-expand-all>
<el-table-column prop="date" label="Date" sortable />
<el-table-column prop="name" label="Name" sortable />
<el-table-column prop="address" label="Address" sortable />
</el-table>
<el-table :data="tableData1" style="width: 100%" row-key="id" border lazy :load="load"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script lang="ts" setup>
interface User {
id: number
date: string
name: string
address: string
hasChildren?: boolean
children?: User[]
}
const load = (
row: User,
treeNode: unknown,
resolve: (data: User[]) => void
) => {
setTimeout(() => {
resolve([
{
id: 31,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 32,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
])
}, 1000)
}
const tableData: User[] = [
{
id: 1,
date: '2016-05-02',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 2,
date: '2016-05-04',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 3,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
children: [
{
id: 31,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 32,
date: '2016-05-01',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
],
},
{
id: 4,
date: '2016-05-03',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
]
const tableData1: User[] = [
{
id: 1,
date: '2016-05-02',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 2,
date: '2016-05-04',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 3,
date: '2016-05-01',
name: 'wangxiaohu',
hasChildren: true,
address: 'No. 189, Grove St, Los Angeles',
},
{
id: 4,
date: '2016-05-03',
name: 'wangxiaohu',
address: 'No. 189, Grove St, Los Angeles',
},
]
</script>
......@@ -70,10 +70,40 @@ const makeOption = () => {
},
label: {
show: true,
formatter: `${props.data.change}` + `${props.data.unit}`,
fontSize: 24,
color: `${props.color[5]}`,
insideColor: `${props.color[5]}`,
formatter: function () {
const change = props.data.change;
const unit = props.data.unit;
if (change === '无新增') {
return `{noChange|${change}}\n{b|共${props.data.count}${unit}}`
}
return `{num|${change}}{unit| ${unit}}\n{b|共${props.data.count}${unit}}`
},
rich: {
num: {
fontSize: 36,
fontWeight: 'bold',
color: `${props.color[5]}`,
padding: [-4, 0, 8, 0]
},
noChange: {
fontSize: 24,
fontWeight: 'bold',
color: `${props.color[5]}`,
padding: [0, 0, 8, 0]
},
unit: {
fontSize: 18,
fontWeight: 'normal',
color: `${props.color[5]}`,
padding: [0, 0, 12, 0],
verticalAlign: 'bottom'
},
b: {
fontSize: 16,
color: `${props.color[5]}`,
opacity: 0.8
}
}
}
}]
}
......
......@@ -11,7 +11,10 @@
<div class="stats">
<div v-for="value in sections[index].waveBall">
<WaveBall :percent="value.percent" :data="value" :color="section.waterColor" :size="128" />
<div class="waveBall-text">{{ value.title }}</div>
<div class="waveBall-text">
{{ value.title }}
<span v-if="value.change !== '无新增'" class="red-dot"></span>
</div>
</div>
</div>
<div class="bottm-box" :style="sections[index].waveBall.length === 2 ? 'width: 350px' : 'width:503px'">
......@@ -31,6 +34,9 @@
<div>风险信号</div>
<div class="num">12</div>
<div class="manage-btn" @click="handleToRiskManage">
风险信号管理 >
</div>
</div>
<div style="display: flex">
<div class="risk-signals">
......@@ -77,12 +83,16 @@
<img src="./icon/矩形 295.png" style="width: 96px; height: 96px" />
</div>
<div style="/* 矩形 351 */ width: 664px; height: 1px; background: rgba(234, 236, 238, 1)"></div>
<div class="carousel-content">{{ News.content }}</div>
<div class="news-carousel-content">{{ News.content }}</div>
<div style="/* 矩形 351 */ width: 664px; height: 1px; background: rgba(234, 236, 238, 1)"></div>
<!-- <div class="carousel-bottom">
<div class="carousel-bottom-left">{{ News.date + News.source }}</div>
</div> -->
<div class="carousel-bottom">
<div class="left">{{ News.date + News.source }}</div>
<div class="right">
<div v-for="tag in News.tags" class="tag">
{{ tag }}
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
......@@ -155,7 +165,7 @@ const sections = ref([
},
{
title: "对实体清单的更新及修订",
date: "12-18",
date: "12-19",
waveBall: [
{
percent: 10, // 估算的百分比
......@@ -175,6 +185,7 @@ const sections = ref([
},
{
title: "SDN清单更新",
date: "12-15",
waveBall: [
{
percent: 15, // 估算的百分比
......@@ -194,7 +205,7 @@ const sections = ref([
},
{
title: "232调查:商用飞机和喷气发动机进口对国家安全的...",
date: "12-18",
date: "12-15",
waveBall: [
{
percent: 3, // 估算的百分比
......@@ -242,7 +253,8 @@ const hotNewsList = ref([
content:
"2025年1月20日第14166号行政命令第2(a)条(《保护美国免受外国对抗者控制应用法案》对TikTok的适用)规定的执行延迟,并由2025年4月4日第14258号行政命令(延长TikTok执法延迟)和2025年6月19日第14310号行政命令(进一步延长TikTok执法延迟)所延长,进一步延长至2025年12月16日。在此期间,司法部不得采取任何行动执行《保护美国人免受外国对抗者控制应用法案》(以下简称“法案”)(公共法118-50,H部),也不得对任何违反该法案的实体处以任何处罚,包括分发、维护、更新(或促成分发,维护或更新任何外国对手控制的应用,如本法所定义。鉴于本指示,即使在上述规定期限届满后,司法部也应继续与相关部门合作,以确保美国国家安全和公民隐私权利不受威胁。",
date: "2025年9月16日",
source: "美国白宫·总统行政令"
source: "美国白宫·总统行政令",
tags: ['人工智能', '通信网络']
},
{
title: "美国白宫发布关于进一步延长TikTok执法宽限期的行政令",
......@@ -250,7 +262,8 @@ const hotNewsList = ref([
content:
"2025年1月20日第14166号行政命令第2(a)条(《保护美国免受外国对抗者控制应用法案》对TikTok的适用)规定的执行延迟,并由2025年4月4日第14258号行政命令(延长TikTok执法延迟)和2025年6月19日第14310号行政命令(进一步延长TikTok执法延迟)所延长,进一步延长至2025年12月16日。在此期间,司法部不得采取任何行动执行《保护美国人免受外国对抗者控制应用法案》(以下简称“法案”)(公共法118-50,H部),也不得对任何违反该法案的实体处以任何处罚,包括分发、维护、更新(或促成分发,维护或更新任何外国对手控制的应用,如本法所定义。鉴于本指示,即使在上述规定期限届满后,司法部也应继续与相关部门合作,以确保美国国家安全和公民隐私权利不受威胁。",
date: "2025年9月16日",
source: "美国白宫·总统行政令"
source: "美国白宫·总统行政令",
tags: ['人工智能', '通信网络']
}
]);
const curNews = ref({});
......@@ -263,6 +276,12 @@ const handleCarouselChange = index => {
}
};
const handleToRiskManage = () => {
// 这里的路由路径请根据实际情况修改
// router.push('/riskSignalManage');
console.log('跳转到风险信号管理');
};
const handleSwithCurNews = name => {
if (name === "left") {
carouselRef.value.prev();
......@@ -284,12 +303,15 @@ onMounted(() => {
.policy-monitoring {
font-family: Arial, sans-serif;
width: 1601px;
margin: 0 auto;
}
.header {
width: 1601px;
display: flex;
justify-content: space-around;
margin-bottom: 20px;
justify-content: space-between;
margin-bottom: 16px;
}
.content {
......@@ -317,11 +339,11 @@ onMounted(() => {
font-style: Regular;
font-size: 24px;
font-weight: 400;
line-height: 31px;
line-height: 48px;
letter-spacing: 0px;
text-align: left;
display: flex;
align-items: center;
img {
/* 矢量 347 */
width: 22px;
......@@ -351,6 +373,26 @@ onMounted(() => {
letter-spacing: 0px;
text-align: center;
}
.manage-btn {
margin-left: auto; /* 推到最右侧 */
margin-right: 16px; /* 适当的右边距 */
padding: 4px 12px;
border-radius: 20px;
background-color: rgba(206, 79, 81, 0.1);
border: 1px solid rgba(206, 79, 81, 0.1);
font-family: "Microsoft YaHei";
font-size: 16px;
font-weight: 400;
line-height: 24px;
color: rgb(206, 79, 81);
cursor: pointer;
&:hover {
background-color: rgba(206, 79, 81, 0.2);
}
}
}
}
......@@ -377,9 +419,8 @@ onMounted(() => {
/* 容器 519 */
height: 210px;
display: flex;
justify-content: left;
margin-top: 10px;
padding: 0 39px;
justify-content: space-between;
padding: 0 35px;
.waveBall-text {
margin-top: 17px;
......@@ -392,6 +433,23 @@ onMounted(() => {
line-height: 26px;
letter-spacing: 0px;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.red-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: rgba(255, 77, 79, 1);
margin-left: 8px;
// 如果需要绝对定位,可以使用下面的样式,但在 flex 布局中 margin-left 更简单
// position: absolute;
// right: -14px;
// top: 50%;
// transform: translateY(-50%);
}
}
}
......@@ -435,7 +493,7 @@ onMounted(() => {
.risk-signals {
width: 769px;
height: 568px;
height: 470px;
overflow-y: auto;
overflow-x: hidden;
box-sizing: border-box;
......@@ -583,7 +641,7 @@ onMounted(() => {
}
.carousel-item {
height: 480px;
height: 443px;
width: 736px;
.carousel-title {
......@@ -633,18 +691,16 @@ onMounted(() => {
}
}
.carousel-content {
.news-carousel-content {
width: 664px;
margin-top: 16px;
height: 296px;
height: 280px;
/* 调整为可用空间 */
overflow-y: auto;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
......@@ -652,7 +708,49 @@ onMounted(() => {
/* 容器 1566 */
width: 664px;
height: 34px;
display: flex;
justify-content: space-between;
.left {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: right;
}
.right {
.tag {
/* 数据展示/Tag标签/亮色/绿 */
width: 81px;
height: 20px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 8;
padding: 1px 8px 1px 8px;
box-sizing: border-box;
border: 1px solid rgba(255, 163, 158, 1);
border-radius: 4px;
background: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 14px;
font-weight: 400;
letter-spacing: 0px;
text-align: justify;
}
}
}
}
}
......@@ -671,4 +769,16 @@ onMounted(() => {
margin-bottom: 5px;
}
}
:deep(.el-carousel__container) {
height: 443px;
}
:deep(.el-carousel__item) {
height: 443px;
}
:deep(.el-carousel__item--card) {
height: 443px;
}
</style>
<!--ZM博弈概览页-->
<template>
<div class="home-wrapper">
<img :src="background" alt="" class="background-img">
<img :src="background" alt="" class="background-img" />
<div class="home-header">
<div class="header-left">
<HeaderMenu></HeaderMenu>
......@@ -22,41 +22,59 @@
</div>
</div>
</div>
<div class="data-select">
<div
v-for="item in dateList"
:key="item.type"
class="date-item"
:class="{ active: activeDate === item.type }"
@click="handleDateClick(item.type)"
>
<img :src="activeDate === item.type ? item.activeIcon : item.icon" alt="" />
<span>{{ item.name }}</span>
</div>
</div>
<div class="content-box">
<!-- 导航栏 -->
<div class="content-nav">
<div class="nav-title">中美科技博弈概览</div>
<div class="nav-btn">
<!-- 导航栏 -->
<div class="content-nav">
<div class="nav-title">中美科技博弈概览</div>
<div class="nav-btn">
<div v-for="item in navList" :key="item.name" class="btn-item" @click="handleScrollTo(item.id)">
<span>{{ item.name }}</span>
<img :src="right" alt="" />
</div>
</div>
</div>
<!-- 最新风险动态 -->
<commonTitle id="risk-dynamic" title="最新风险动态" style="margin-top: 48px;"></commonTitle>
</div>
<!-- 最新风险动态 -->
<commonTitle id="risk-dynamic" title="最新风险动态" style="margin-top: 48px; margin-bottom: 36px"></commonTitle>
<newRisk></newRisk>
<!-- 最新科技要闻 -->
<!-- <commonTitle id="tech-news" title="最新科技要闻" style="margin-top: 64px;"></commonTitle> -->
<!-- 美对华“四全”打压 -->
<commonTitle id="us-pressure" title="美对华“四全”打压" style="margin-top: 64px;"></commonTitle>
<!-- 最新科技要闻 -->
<!-- <commonTitle id="tech-news" title="最新科技要闻" style="margin-top: 64px;"></commonTitle> -->
<!-- 美对华“四全”打压 -->
<div id="us-pressure" class="us-pressure-section">
<div class="data-select">
<div
v-for="item in dateList"
:key="item.type"
class="date-item"
:class="{ active: activeDate === item.type }"
@click="handleDateClick(item.type)"
>
<img :src="activeDate === item.type ? item.activeIcon : item.icon" alt="" />
<span>{{ item.name }}</span>
</div>
</div>
<commonTitle title="美对华“四全”打压" style="margin-bottom: 36px"></commonTitle>
</div>
<fourSuppress></fourSuppress>
<!-- 中美博弈概况 -->
<commonTitle id="zm-overview" title="中美博弈概况" style="margin-top: 64px;"></commonTitle>
<!-- 中美博弈概况 -->
<commonTitle id="zm-overview" title="中美博弈概况" style="margin-top: 64px; margin-bottom: 36px"></commonTitle>
<gameProfile></gameProfile>
</div>
<div class="bottom-info">
<div class="info-item">
<div class="info-item-left">
<img :src="logo1" alt="" />
<div class="info-item-left-content">
<p>地址:北京市海淀区复兴路15号 邮编:100038</p>
<p>办公电话:010-58882033 办公传真:010-58882590</p>
<p>中国科学技术信息研究所 版权所有 京ICP备10027328号</p>
</div>
</div>
<div class="info-item-right">
<img :src="logo2" alt="" />
<img :src="logo3" alt="" />
</div>
</div>
</div>
</div>
</div>
</template>
......@@ -64,22 +82,23 @@
import { onMounted, ref, computed } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import background from "./assets/images/backgroundBT.png"
import background from "./assets/images/backgroundBT.png";
import HeaderMenu from "@/components/headerMenu.vue";
import right from "./assets/icons/right.png"
import commonTitle from "./commonTitle/comTitle.vue"
import week from "./assets/icons/week.png"
import weekActive from "./assets/icons/week-active.png"
import month from "./assets/icons/month.png"
import monthActive from "./assets/icons/month-active.png"
import year from "./assets/icons/year.png"
import yearActive from "./assets/icons/year-active.png"
import right from "./assets/icons/right.png";
import commonTitle from "./commonTitle/comTitle.vue";
import week from "./assets/icons/week.png";
import weekActive from "./assets/icons/week-active.png";
import month from "./assets/icons/month.png";
import monthActive from "./assets/icons/month-active.png";
import year from "./assets/icons/year.png";
import yearActive from "./assets/icons/year-active.png";
import logo1 from "./assets/images/logo1.png";
import logo2 from "./assets/images/logo2.png";
import logo3 from "./assets/images/logo3.png";
// 组件引入
import newRisk from "./components/newRisk/index.vue"
import fourSuppress from "./components/fourSuppress/index.vue"
import gameProfile from "./components/gameProfile/index.vue"
import newRisk from "./components/newRisk/index.vue";
import fourSuppress from "./components/fourSuppress/index.vue";
import gameProfile from "./components/gameProfile/index.vue";
const router = useRouter();
......@@ -90,13 +109,13 @@ const navList = ref([
{ name: "中美博弈概况", id: "zm-overview" }
]);
const handleScrollTo = (id) => {
const handleScrollTo = id => {
const element = document.getElementById(id);
const container = document.querySelector('.content-box');
const container = document.querySelector(".content-box");
if (element && container) {
const targetOffsetTop = element.offsetTop - container.offsetTop;
container.scrollTo({
top: targetOffsetTop,
top: targetOffsetTop,
behavior: "smooth"
});
}
......@@ -128,76 +147,31 @@ const dateList = ref([
{ name: "今年", type: "year", icon: year, activeIcon: yearActive }
]);
const activeDate = ref("week");
const handleDateClick = (type) => {
const handleDateClick = type => {
activeDate.value = type;
};
</script>
<style lang="scss" scoped>
* {
margin: 0;
padding: 0;
}
.home-wrapper {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
.data-select {
width: 120px;
height: 144px;
position: absolute;
top: 291px;
left: 0;
background-color: rgba(255, 255, 255, 0.65);
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
padding: 2px 2px 2px 0px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
display: flex;
flex-direction: column;
justify-content: space-around;
z-index: 10;
.date-item {
display: flex;
align-items: center;
padding-left: 18px;
height: 46px;
cursor: pointer;
// transition: all 0.3s;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
img {
width: 18px;
height: 18px;
margin-right: 18px;
}
span {
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
color: rgb(95, 101, 108);
}
&.active {
background-color: rgba(231, 243, 255, 1);
span {
font-weight: 700;
color: rgb(5, 95, 194);
}
}
}
}
.background-img {
width: 100%;
height: 700px;
position: absolute;
top: 64px;
left: 0;
z-index: -1;
}
position: relative;
.background-img {
width: 100%;
height: 700px;
position: absolute;
top: 64px;
left: 0;
z-index: -1;
}
.home-header {
height: 64px;
flex-shrink: 0;
......@@ -285,56 +259,151 @@ const handleDateClick = (type) => {
flex: 1;
width: 100%;
overflow-y: auto;
padding: 48px 160px;
.content-nav {
.nav-title {
font-size: 48px;
font-weight: 400;
line-height: 62px;
letter-spacing: 0px;
text-align: center;
color: rgb(5, 95, 194);
font-family: YouSheBiaoTiHei;
margin-bottom: 14px;
}
padding-top: 48px;
.us-pressure-section {
position: relative;
margin-top: 64px;
.data-select {
width: 120px;
height: 144px;
position: absolute;
top: 80px;
left: 0; /* 临时改为0,检查是否能显示 */
background-color: rgba(255, 255, 255, 0.65);
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
padding: 2px 2px 2px 0px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
display: flex;
flex-direction: column;
justify-content: space-around;
z-index: 10;
.date-item {
display: flex;
align-items: center;
padding-left: 18px;
height: 46px;
cursor: pointer;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
img {
width: 18px;
height: 18px;
margin-right: 18px;
}
span {
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
color: rgb(95, 101, 108);
}
&.active {
background-color: rgba(231, 243, 255, 1);
span {
font-weight: 700;
color: rgb(5, 95, 194);
}
}
}
}
}
.content-nav {
.nav-title {
font-size: 48px;
font-weight: 400;
line-height: 62px;
letter-spacing: 0px;
text-align: center;
color: rgb(5, 95, 194);
font-family: YouSheBiaoTiHei;
margin-bottom: 14px;
}
.nav-btn {
display: flex;
justify-content: center;
gap: 16px;
.nav-btn {
display: flex;
justify-content: center;
gap: 16px;
.btn-item {
display: flex;
align-items: center;
padding: 10px 24px 10px 32px;
background: #ffffff;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 50px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
cursor: pointer;
transition: all 0.3s;
.btn-item {
display: flex;
align-items: center;
padding: 10px 24px 10px 32px;
background: #ffffff;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 50px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
cursor: pointer;
transition: all 0.3s;
// &:hover {
// background: rgba(255, 255, 255, 0.8);
// transform: translateY(-2px);
// }
// &:hover {
// background: rgba(255, 255, 255, 0.8);
// transform: translateY(-2px);
// }
span {
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 26px;
color: rgb(5, 95, 194);
margin-right: 16px;
}
span {
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 26px;
color: rgb(5, 95, 194);
margin-right: 16px;
}
img {
width: 6px;
height: 12px;
}
}
}
}
img {
width: 6px;
height: 12px;
}
}
}
}
.bottom-info {
width: 100%;
height: 176px;
margin-bottom: 80px;
background-color: rgb(247, 248, 249);
.info-item {
width: 1601px;
height: 176px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
.info-item-left {
display: flex;
align-items: center;
img {
width: 134px;
height: 91px;
margin-right: 27px;
}
.info-item-left-content {
font-size: 16px;
font-weight: 400;
line-height: 32px;
color: rgb(59, 65, 75);
font-family: Microsoft YaHei;
}
}
.info-item-right {
display: flex;
align-items: center;
img:first-child {
width: 300px;
height: 48.8px;
margin-right: 24px;
}
img:last-child {
width: 300px;
height: 43.5px;
}
}
}
}
}
}
</style>
......@@ -299,9 +299,10 @@
{{ news.newsDate ? news.newsDate.slice(5) : "" }} - {{ news.newsOrg }}
</div>
</div>
<CommonPrompt :content="news.newsContent">
<div class="right-footer">{{ news.newsContent }}</div>
<!-- <CommonPrompt :content="news.newsContent">
<div class="right-footer">{{ news.newsContent }}</div>
</CommonPrompt>
</CommonPrompt> -->
</div>
</div>
</div>
......@@ -597,7 +598,7 @@
<el-checkbox class="filter-checkbox" label="全部领域"> 全部领域 </el-checkbox>
<el-checkbox
v-for="(area, index) in cateKuList"
:key="area.id"
:key="index"
:label="area.id"
class="filter-checkbox"
>
......@@ -615,7 +616,7 @@
<el-checkbox-group class="checkbox-group" v-model="activeDpList" @change="handleDpChange">
<el-checkbox
v-for="(dp, index) in dpList"
:key="dp.id"
:key="index"
:label="dp.id"
class="filter-checkbox"
>
......@@ -633,7 +634,7 @@
<el-checkbox-group class="checkbox-group" v-model="activeYyList" @change="handleYyChange">
<el-checkbox
v-for="(yy, index) in yyList"
:key="yy.id"
:key="index"
:label="yy.id"
class="filter-checkbox"
>
......@@ -652,7 +653,7 @@
<el-checkbox-group class="checkbox-group" v-model="activePubTime" @change="handlePubTimeChange">
<el-checkbox
v-for="(time, index) in pubTime"
:key="time.id"
:key="index"
:label="time.id"
class="filter-checkbox"
>
......@@ -2936,7 +2937,7 @@ onUnmounted(() => {
justify-content: flex-end;
}
.box7-main {
height: 380px;
height: 390px;
}
}
.box8 {
......
......@@ -6,7 +6,7 @@ const getDoublePieChart = (data1, data2) => {
series: [
{
type: 'pie',
radius: [45, 88],
radius: [35, 78],
// height: '100%',
left: 'center',
// width: '100%',
......@@ -32,7 +32,7 @@ const getDoublePieChart = (data1, data2) => {
},
{
type: 'pie',
radius: [90, 120],
radius: [80, 110],
height: '100%',
left: 'center',
width: '98%',
......@@ -45,18 +45,20 @@ const getDoublePieChart = (data1, data2) => {
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
minMargin: 5,
edgeDistance: 10,
lineHeight: 24,
lineHeight: 20,
rich: {
name: {
color: 'rgba(59, 65, 75, 1)',
fontFamily: 'Microsoft YaHei',
fontSize: 16,
fontSize: 14,
fontWeight: 'bold',
padding: [10,0,10,0]
},
time: {
fontSize: 16,
fontSize: 14,
fontFamily: 'Microsoft YaHei',
color: '#rgba(95, 101, 108, 1)'
color: '#rgba(95, 101, 108, 1)',
padding: [10,0,10,0]
}
}
},
......
......@@ -17,18 +17,20 @@ const getPieChart = (data, colorList) => {
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
minMargin: 5,
edgeDistance: 10,
lineHeight: 25,
lineHeight: 22,
rich: {
name: {
color: 'rgba(59, 65, 75, 1)',
fontFamily: 'Microsoft YaHei',
fontSize: 16,
fontWeight: 'bold',
padding: [10, 0, 10, 0]
},
time: {
fontSize: 16,
fontFamily: 'Microsoft YaHei',
color: '#rgba(95, 101, 108, 1)'
color: '#rgba(95, 101, 108, 1)',
padding: [10, 0, 10, 0]
}
}
......@@ -36,10 +38,10 @@ const getPieChart = (data, colorList) => {
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
maxSurfaceAngle: 88
},
labelLayout: function (params) {
const isLeft = params.labelRect.x < params.viewWidth / 2;
const isLeft = params.labelRect.x < 556 / 2;
const points = params.labelLinePoints;
// Update the end point.
points[2][0] = isLeft
......
......@@ -16,7 +16,7 @@ const getPieChart = (data) => {
formatter: '{name|{b}}\n{time|{d}%}',
minMargin: 5,
edgeDistance: 10,
lineHeight: 24,
lineHeight: 22,
rich: {
name: {
color: 'rgba(59, 65, 75, 1)',
......@@ -27,7 +27,7 @@ const getPieChart = (data) => {
time: {
fontSize: 16,
fontFamily: 'Microsoft YaHei',
color: '#rgba(95, 101, 108, 1)'
color: '#rgba(95, 101, 108, 1)',
}
}
},
......
......@@ -78,7 +78,7 @@
</template>
<script setup>
import { ref, defineProps, defineEmits, computed, watch } from "vue";
import { ref, computed, watch } from "vue";
import router from "@/router";
import { Close } from "@element-plus/icons-vue";
import defaultIcon from "@/assets/icons/default-icon1.png";
......
......@@ -620,13 +620,13 @@
<div class="item-right-header">
<div class="tag" :class="{tag1: item.SORTCODE === '337', tag2: item.SORTCODE === '301', tag3: item.SORTCODE === '232' }">{{ item.SORTCODE }}</div>
<div class="title">{{ item.SEARCHNAME }}</div>
<div class="status">
<div class="status" v-if="item.CASESTATUS">
<div class="status-icon"></div>
<div class="status-text">{{ item.CASESTATUS }}</div>
</div>
</div>
<div class="item-right-content">
{{ item.content }}
<div class="item-right-content" v-if="item.CONTENT">
{{ item.CONTENT }}
</div>
<div class="item-right-footer">
<div class="area-box" v-for="(val, idx) in item.searchArea" :key="idx">
......@@ -1600,9 +1600,6 @@ const handleGetSurveyList = async () => {
if (res.code === 200 && res.data) {
totalDiscussNum.value = res.data.totalElements;
surveyInfoList.value = res.data.content;
surveyInfoList.value.forEach(item => {
item.SORTIMAGEURL = `http://${item.SORTIMAGEURL}`
})
} else {
surveyInfoList.value = [];
}
......@@ -3179,7 +3176,8 @@ onMounted(async () => {
border-bottom: 1px solid rgba(230, 231, 232, 1);
// height: 780px;
.item {
height: 154px;
// height: 154px;
min-height: 94px;
display: flex;
cursor: pointer;
&:hover {
......@@ -3199,17 +3197,21 @@ onMounted(async () => {
}
.item-center {
width: 30px;
height: 154px;
// height: 154px;
.icon {
width: 30px;
height: 30px;
border-radius: 15px;
background: orange;
img{
width: 100%;
height: 100%;
}
}
.line {
margin-left: 14px;
width: 2px;
height: 124px;
min-height: 64px;
max-height: 124px;
border-radius: 1.5px;
background: rgba(234, 236, 238, 1);
}
......@@ -3261,13 +3263,17 @@ onMounted(async () => {
line-height: 26px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.status {
margin-left: 9px;
display: flex;
width: 76px;
height: 24px;
justify-content: space-between;
justify-content: flex-end;
gap: 5px;
align-items: center;
.status-icon {
width: 4px;
......@@ -3323,16 +3329,23 @@ onMounted(async () => {
}
.flag-box {
display: flex;
position: relative;
.flag {
width: 24px;
height: 24px;
border-radius: 12px;
border: 2px solid #fff;
overflow: hidden;
position: absolute;
img{
width: 100%;
height: 100%;
}
}
:nth-child(1) {
left: 18px;
z-index: 99;
}
}
}
}
......
......@@ -54,24 +54,19 @@ const getBarChart = (nameList, valueList) => {
},
barWidth: 20,
markPoint: {
symbol: 'circle',
symbolSize: 0,
// symbol: 'circle',
// symbolSize: 0,
data: (function () {
const data = [];
nameList.forEach((item, index) => {
data.push({
name: 'icon',
// value: '',
xAxis: index,
yAxis: valueList[index],
symbol: `image://${item.img}`,
symbolSize: [20, 20],
symbolOffset: [0, 10],
// itemStyle: {
// borderRadius: 10, // 设置为圆形
// borderColor: '#fff',
// borderWidth: 2
// }
symbolCircle: 10
});
});
return data;
......
......@@ -2,24 +2,31 @@
<template>
<div class="source-library-container">
<div class="source-library-grid">
<div v-for="(item, index) in PersonResource" :key="index" class="source-library-card" @click="handleClcikToCharacter(item.id)">
<div
v-for="(item, index) in PersonResource"
:key="index"
class="source-library-card"
@click="handleClcikToCharacter(item.id)"
>
<div class="source-library-avatar-wrapper">
<img :src="item.avatar" alt="" class="source-library-avatar" />
</div>
<div class="source-library-text-content">
<div style="width: 240px;height: 120px;display: flex; flex-direction: column;">
<div style="width: 240px; height: 120px; display: flex; flex-direction: column">
<h3 class="source-library-name">{{ item.name }}</h3>
<p class="source-library-title">{{ item.title }}</p>
<div class="taglist">
<p
class="source-library-tag"
v-for="value in item.tag"
:class="{tag1: value.typeId === '001',
v-for="(value, index) in item.tag"
:key="index"
:class="{
tag1: value.typeId === '001',
tag2: value.typeId === '002',
tag3: value.typeId === '003',
tag4: value.typeId === '004',
tag5: value.typeId === '005',
tag6: value.typeId === '006',
tag6: value.typeId === '006'
}"
>
{{ value.typeName }}
......@@ -30,7 +37,7 @@
</div>
</div>
<div class="page">
<div class="count">{{ `共 ${total} 项`}}</div>
<div class="count">{{ `共 ${total} 项` }}</div>
<el-pagination
@current-change="handleCurrentChange"
:page-size="pageSize"
......@@ -46,25 +53,26 @@
<script setup>
// 导入数据(建议使用更具语义的变量名)
import sourceLibraryData from "../json/source.json";
import { ref,onMounted,defineProps,watch } from "vue";
import { ref, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import DefaultIcon1 from '@/assets/icons/default-icon1.png'
import DefaultIcon2 from '@/assets/icons/default-icon2.png'
import DefaultIcon1 from "@/assets/icons/default-icon1.png";
import DefaultIcon2 from "@/assets/icons/default-icon2.png";
import {getPersonResource,getPersonSummaryInfo } from "@/api/technologyFigures/technologyFigures";
import { getPersonResource, getPersonSummaryInfo } from "@/api/technologyFigures/technologyFigures";
const props = defineProps({
typeId: {
type: String,
default: "000"
}
typeId: {
type: String,
default: "000"
}
});
watch(() => props.typeId, (val) => {
handlegetPersonResourceFn();
})
watch(
() => props.typeId,
val => {
handlegetPersonResourceFn();
}
);
const router = useRouter();
......@@ -88,11 +96,11 @@ const handlegetPersonResourceFn = async () => {
currentPage: currentPage.value - 1, // Standard Spring Boot page index is 0-based
pageSize: pageSize.value
};
if(props.typeId !== '000'){
if (props.typeId !== "000") {
params.personTypeId = props.typeId;
}
try {
const res = await getPersonResource(params,abortController.value.signal);
const res = await getPersonResource(params, abortController.value.signal);
console.log("人物资源库", res);
if (res.code === 200) {
if (res.data && res.data.content) {
......@@ -104,7 +112,6 @@ const handlegetPersonResourceFn = async () => {
avatar: item.personImage || DefaultIcon1
}));
total.value = res.data.totalElements;
} else {
PersonResource.value = [];
total.value = 0;
......@@ -129,7 +136,7 @@ const handleCurrentChange = page => {
};
// 跳转人物主页
const handleClcikToCharacter = async (id) => {
const handleClcikToCharacter = async id => {
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0;
......@@ -183,13 +190,9 @@ const handleClcikToCharacter = async (id) => {
};
onMounted(async () => {
handlegetPersonResourceFn();
handlegetPersonResourceFn();
});
const handlePageChange = p => {
currentPage.value = p;
};
......@@ -219,7 +222,6 @@ const handlePageChange = p => {
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
cursor: pointer;
}
.source-library-card:hover {
......@@ -232,25 +234,25 @@ const handlePageChange = p => {
}
.person-tags {
display: flex;
margin-top: -20px;
width: 42px;
padding-left: 28px;
text-align: center;
display: flex;
margin-top: -20px;
width: 42px;
padding-left: 28px;
text-align: center;
}
.person-tag-bg {
/* 椭圆 6 */
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
/* 椭圆 6 */
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
}
.tag-icon {
width: 24px;
height: 24px;
object-fit: contain;
width: 24px;
height: 24px;
object-fit: contain;
}
.source-library-avatar {
......@@ -263,7 +265,6 @@ const handlePageChange = p => {
.source-library-text-content {
width: 656px;
flex: 1;
}
.source-library-name {
......@@ -290,7 +291,7 @@ const handlePageChange = p => {
text-align: left;
}
.taglist{
.taglist {
display: flex;
gap: 10px;
margin-top: auto;
......
......@@ -129,6 +129,7 @@ import {
getThinkTankReportIndustry,
getThinkTankReportIndustryCloud
} from "@/api/thinkTank/overview";
import {getChartAnalysis} from '@/api/aiAnalysis/index'
import { useRouter } from "vue-router";
const router = useRouter();
......@@ -256,118 +257,6 @@ const majorOpinions = ref([
status: 2
}
]
},
{
id: 3,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "半导体",
status: 4
}
]
},
{
id: 4,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 5,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 6,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 7,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "产业脱钩",
status: 6
}
]
},
{
id: 8,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 9,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 10,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
}
]);
// 处理页码改变事件
......@@ -384,12 +273,25 @@ const handleGetThinkTankReportContent = async () => {
console.log("主要观点", res.data);
if (res.code === 200 && res.data) {
majorOpinions.value = res.data.content;
handleGetBox3AnalysisContent(majorOpinions.value)
total.value = res.data.totalElements
}
} catch (error) {
console.error("获取主要观点error", error);
}
};
// 获取图表分析内容
const box3AnalysisContent = ref('')
const handleGetBox3AnalysisContent = async (textJson) => {
const params = {
text: textJson
}
const res = await getChartAnalysis(params)
console.log('图表解析内容', res);
}
onMounted(() => {
handleGetThinkTankReportAbstract();
handleGetThinkTankReportContent();
......
......@@ -75,6 +75,11 @@ export default defineConfig({
target: 'http://106.12.150.59:18826/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/temporarySearch/, '')
},
'/aiAnalysis': {
target: 'http://8.140.26.4:15000/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/temporarySearch/, '')
}
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论