提交 58c59aa9 authored 作者: 付康's avatar 付康

合并分支 'lzl-dev' 到 'master'

Lzl dev 查看合并请求 !99
<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>
......@@ -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;
......
......@@ -2,31 +2,30 @@
<template>
<div class="content-wrapper">
<div class="btn-box">
<div v-for="(value, index) in buttonsData" class="btn-item" :style="{ background: value.background }">
<!-- <div class="btn-left">
<img class="btn-left-icon" :src="`/public/icon/ZM/btn-icon2-${index}.png`"
style="width: 22px; height: 19px;" />
<div class="btn-left-text" >{{ value.text }}</div>
</div> -->
<div :style="{
height: '60px',
width: '60px',
backgroundImage: 'url(' + `/public/icon/ZM/btn-icon2-${index}.png` + ')',
backgroundSize: 'cover'
}">
{{ value.text }}
</div>
<div class="btn-right">
{{ value.count + "次" }}
<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>
<span>美对华领域打压遏制数量趋势</span>
</div>
<div class="title-right">
<el-select v-model="select1" placeholder="按月统计" class="custom-select">
......@@ -35,50 +34,394 @@
</div>
</div>
<div class="charts-content">
<div id="chartRef" style=" width: 1453px; height: 354px"></div>
<div id="chartRef" class="chart-container"></div>
</div>
</div>
<div>
<div style="width: 792px; height: 700px" class="main-charts">
<div class="charts-title" style="padding: 8px">
<div class="bottom-content">
<div class="news-section main-charts">
<div class="charts-title news-title">
<div class="title-left">
<img src="./icon/icon-1.png" alt="" />
<span>美对华制裁措施数量趋势</span>
<img src="./icon/icon-2.png" alt="" style="width: 22px; height: 18px" />
<span>美对华领域打压遏制最新动态</span>
</div>
</div>
<div v-for="value in newsList" class="news-item">
<div class="news-item-title">
<div style="width: 400px; display: flex; gap: 8px">
<div v-for="tag in value.tags" :key="tag" class="tag" :style="{
color: getTagColor(tag).textColor,
backgroundColor: getTagColor(tag).bgColor,
border: '1px solid ' + getTagColor(tag).textColor
}">
{{ tag }}
<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>
<div class="date">
{{ value.date + " · " + value.type }}
<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 class="content-title">
{{ value.title }}
</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>
<div class="content-text">
{{ value.content }}
<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 style="width: 792px; height: 700px"></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 { ref, onMounted } 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: "集成电路",
......@@ -149,28 +492,73 @@ const box5Data = ref({
data: [
{
name: "集成电路",
color: "rgba(9, 88, 217, 0.7)", // 定义颜色
value: [88, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30]
color: "#0052D9",
value: [87, 78, 74, 67, 60, 59, 63, 66, 63, 58, 56, 62]
},
{
name: "人工智能",
color: "rgba(51, 153, 255, 0.7)", // 定义颜色
value: [20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
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: "rgba(255, 187, 51, 0.7)", // 定义颜色
value: [30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85]
color: "#FF9F1C",
value: [10, 22, 22, 34, 48, 51, 46, 55, 55, 60, 68, 70]
},
{
name: "量子科技",
color: "rgba(117, 73, 255, 0.7)", // 定义颜色
value: [40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
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: "rgba(102, 102, 102, 0.7)", // 定义颜色
value: [50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105]
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]
}
]
});
......@@ -251,6 +639,24 @@ const getTagColor = 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");
......@@ -263,10 +669,15 @@ onMounted(() => {
height: 2132px;
}
.btn-wrapper {
position: relative;
width: 1601px;
}
.btn-box {
margin-top: 16px;
width: 1601px;
height: auto;
width: 100%;
height: 176px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
......@@ -276,6 +687,28 @@ onMounted(() => {
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;
......@@ -284,16 +717,35 @@ onMounted(() => {
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
display: flex;
align-items: center;
justify-content: space-between; // 调整为两端对齐
padding: 0 10px; // 添加内边距以便文本和图标有空间
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: 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 {
......@@ -306,13 +758,6 @@ onMounted(() => {
margin-right: 8px; // 图标和文本之间的间距
}
.btn-left-text {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 30px; // 根据图标大小调整这个值
}
.main-charts {
margin-top: 16px;
width: 1601px;
......@@ -322,7 +767,6 @@ onMounted(() => {
box-shadow: 0 0 20px rgba(25, 69, 130, 0.1);
display: flex;
flex-direction: column;
margin-bottom: 22px;
.charts-title {
width: 100%;
......@@ -413,6 +857,246 @@ onMounted(() => {
}
}
.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;
......@@ -430,24 +1114,12 @@ onMounted(() => {
background: rgba(255, 255, 255, 0.65);
.news-item-title {
line-height: 30px;
display: flex;
justify-content: space-between;
width: 689px;
.tag {
/* 数据展示/Tag标签/亮色/绿 */
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 8;
padding: 1px 8px 1px 8px;
box-sizing: border-box;
border-radius: 4px;
display: none;
}
.date {
......@@ -486,21 +1158,240 @@ onMounted(() => {
}
.content-text {
color: rgba(95, 101, 108, 1);
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: justify;
white-space: nowrap;
/* 禁止换行 */
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;
/* 隐藏溢出内容 */
text-overflow: ellipsis;
/* 溢出部分显示省略号 */
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>
......@@ -10,18 +10,17 @@ const getMultiLineChart = (data) => {
const echartsSeries = series.map((item, index) => ({
name: item.name,
type: 'line',
// areaStyle: {
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// {
// offset: 0,
// color: item.color || `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.7)` // 随机颜色
// },
// {
// offset: 1,
// color: item.color ? `${item.color.replace('0.7', '0')}` : `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0)` // 随机颜色
// }
// ])
// },
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#fff', // 图表圆点内部为白色
borderColor: item.color, // 圆点边框为系列颜色
borderWidth: 2
},
lineStyle: {
width: 2,
color: item.color
},
emphasis: {
focus: 'series'
},
......@@ -29,38 +28,84 @@ const getMultiLineChart = (data) => {
}));
return {
color: series.map(item => item.color),
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
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: '1%',
bottom: '0%',
left: '1%',
right: '2%',
bottom: '5%',
left: '2%',
containLabel: true
},
legend: {
show: true,
top: 10,
left: 'center'
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)'
}
},
color: series.map(item => item.color || `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.7)`), // 动态颜色
xAxis: [
{
type: 'category',
boundaryGap: false,
data: title
data: title,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#999',
fontSize: 12,
margin: 15
}
}
],
yAxis: [
{
type: 'value'
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
......
......@@ -559,7 +559,7 @@ onMounted(() => {
<style lang="scss" scoped>
.content-wrapper {
width: 1666px;
height: 2132px;
height: 1076px;
// background: orange;
.header {
width: 1664px;
......@@ -567,6 +567,7 @@ onMounted(() => {
display: flex;
justify-content: center;
gap: 16px;
margin: 0 auto;
position: relative;
.header-arrow-left {
position: absolute;
......
......@@ -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;
}
}
}
......
......@@ -20,7 +20,7 @@
<AllGovernment v-if="activeNav === '全政府'" />
<AddDomain v-if="activeNav === '全领域'" />
<AllUnion v-if="activeNav === '全联盟'" />
<AllElement v-if="activeNav === '全要素'" />
<AllElement v-if="activeNav === '全要素'" />
</div>
</template>
......@@ -47,8 +47,8 @@ const handleNavClick = name => {
<style lang="scss" scoped>
.content-wrapper {
// width: 1601px;
width: 1666px;
height: 2203px;
width: 1601px;
height: auto;
margin: 0 auto;
.main-nav {
width: 1601px;
......
......@@ -8,7 +8,7 @@
</div>
<div style="display: flex; height: 650px; width: 100%">
<div style="width: 50%">
<div style="display: flex; justify-content: space-between; margin-right: 50px; ine-height: 32px">
<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">
......@@ -61,9 +61,9 @@
<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" />
<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-right: 22px; width: 22px; height: 19px" />
<img :src="`/public/icon/ZM/btn-icon-arrow.png`" style="margin-left: auto; margin-right: 22px; width: 13px; height: 12px" />
</div>
</div>
</div>
......@@ -263,9 +263,9 @@ const btnList = ref([
<style lang="scss" scoped>
.content-wrapper {
width: 100%;
width: 1600px;
height: 1600px;
margin: 0 auto;
.card-box {
width: 1600px;
height: 700px;
......@@ -277,7 +277,7 @@ const btnList = ref([
background: rgba(255, 255, 255, 1);
.card-title {
width: 1602px;
width: 100%;
height: 48px;
display: flex;
......@@ -412,7 +412,7 @@ const btnList = ref([
.btn-box {
margin-top: 16px;
width: 1601px;
width: 100%;
height: auto;
/* 高度自适应内容 */
display: flex;
......@@ -420,7 +420,7 @@ const btnList = ref([
/* 行方向布局 */
flex-wrap: wrap;
/* 允许换行 */
justify-content: space-between;
justify-content: flex-start;
align-items: stretch;
/* 使所有按钮高度一致 */
gap: 16px 16px;
......@@ -430,8 +430,7 @@ const btnList = ref([
}
.btn-item {
width: calc(20% - 16px);
/* 宽度为容器宽度的20%减去一个间隙的宽度 */
width: 307px;
height: 60px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
......@@ -442,7 +441,7 @@ const btnList = ref([
display: flex;
/* 使用flex布局 */
color: rgba(255, 255, 255, 1);
justify-content: space-between;
justify-content: flex-start;
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 16px;
......
......@@ -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">
......@@ -159,7 +165,7 @@ const sections = ref([
},
{
title: "对实体清单的更新及修订",
date: "12-18",
date: "12-19",
waveBall: [
{
percent: 10, // 估算的百分比
......@@ -179,6 +185,7 @@ const sections = ref([
},
{
title: "SDN清单更新",
date: "12-15",
waveBall: [
{
percent: 15, // 估算的百分比
......@@ -198,7 +205,7 @@ const sections = ref([
},
{
title: "232调查:商用飞机和喷气发动机进口对国家安全的...",
date: "12-18",
date: "12-15",
waveBall: [
{
percent: 3, // 估算的百分比
......@@ -269,6 +276,12 @@ const handleCarouselChange = index => {
}
};
const handleToRiskManage = () => {
// 这里的路由路径请根据实际情况修改
// router.push('/riskSignalManage');
console.log('跳转到风险信号管理');
};
const handleSwithCurNews = name => {
if (name === "left") {
carouselRef.value.prev();
......@@ -290,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 {
......@@ -327,7 +343,7 @@ onMounted(() => {
letter-spacing: 0px;
text-align: left;
display: flex;
align-items: center;
img {
/* 矢量 347 */
width: 22px;
......@@ -357,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);
}
}
}
}
......@@ -373,7 +409,7 @@ onMounted(() => {
}
.section-title {
margin: 16px;
margin: 8px 16px;
/* 容器 1559 */
width: 125px;
height: 31px;
......@@ -383,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;
......@@ -398,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%);
}
}
}
......@@ -441,6 +493,7 @@ onMounted(() => {
.risk-signals {
width: 769px;
height: 470px;
overflow-y: auto;
overflow-x: hidden;
box-sizing: border-box;
......
<!--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>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论