提交 acee33d4 authored 作者: 李智林's avatar 李智林

Merge branch 'master' into 'lzl-dev'

# Conflicts: # src/router/index.js
......@@ -14,6 +14,8 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@traptitech/markdown-it-katex": "^3.6.0",
"axios": "^1.12.2",
"d3": "^7.9.0",
"d3-cloud": "^1.2.7",
"echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0",
......@@ -2364,6 +2366,21 @@
"node": ">=12"
}
},
"node_modules/d3-cloud": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/d3-cloud/-/d3-cloud-1.2.7.tgz",
"integrity": "sha512-8TrgcgwRIpoZYQp7s3fGB7tATWfhckRb8KcVd1bOgqkNdkJRDGWfdSf4HkHHzZxSczwQJdSxvfPudwir5IAJ3w==",
"license": "BSD-3-Clause",
"dependencies": {
"d3-dispatch": "^1.0.3"
}
},
"node_modules/d3-cloud/node_modules/d3-dispatch": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
"integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==",
"license": "BSD-3-Clause"
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz",
......
......@@ -23,6 +23,8 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@traptitech/markdown-it-katex": "^3.6.0",
"axios": "^1.12.2",
"d3": "^7.9.0",
"d3-cloud": "^1.2.7",
"echarts": "^5.4.3",
"echarts-liquidfill": "^3.1.0",
"echarts-wordcloud": "^2.1.0",
......
......@@ -7,11 +7,11 @@
<div class="brand-icon">
<img src="@/assets/icons/header-logo.png" alt="" />
</div>
<div class="brand-text">
<div class="brand-text" @click="handleToHome">
<div class="text-ch">某方向风险监测预警系统</div>
<div class="text-en">
<!-- <div class="text-en">
National Science and Technology Security Risk Monitoring and Early Warning System
</div>
</div> -->
</div>
</div>
<!-- <div class="nav-menu">
......@@ -101,6 +101,12 @@ import AiBox from "./components/AiBox.vue";
const router = useRouter();
const handleToHome = () => {
router.push({
path: '/overview'
})
}
const isShowAiBox = ref(false);
const closeAiBox = () => {
......@@ -153,7 +159,6 @@ body {
display: flex;
justify-content: center;
align-items: center;
padding: 0 24px;
background: white;
color: #333;
height: 100%;
......@@ -161,7 +166,7 @@ body {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
position: relative;
box-sizing: border-box;
height: 96px;
height: 64px;
}
.main-container {
......@@ -176,8 +181,7 @@ body {
align-items: center;
gap: 12px;
position: absolute;
left: 160px;
left: 13px;
.brand-icon {
width: 48px;
height: 48px;
......@@ -195,13 +199,14 @@ body {
}
.brand-text {
cursor: pointer;
.text-ch {
height: 42px;
height: 37px;
color: rgba(10, 18, 30, 1);
font-family: Microsoft YaHei;
font-size: 32px;
font-size: 28px;
font-weight: 700;
line-height: 42px;
line-height: 37px;
}
.text-en {
......@@ -264,7 +269,7 @@ body {
border-radius: 6px;
color: #333;
position: absolute;
right: 159px;
right: 37px;
.email {
width: 20px;
......@@ -300,7 +305,7 @@ body {
.ai-btn {
position: absolute;
top: 50%;
bottom: 20%;
right: 46px;
cursor: pointer;
......@@ -315,6 +320,7 @@ body {
}
.text {
margin-top: -15px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -328,22 +334,23 @@ body {
.ai-dialog {
position: absolute;
right: 100px;
top: 50px;
top: 100px;
z-index: 9999;
}
}
.el-header {
padding: 0;
height: 96px;
height: 64px;
position: relative;
z-index: 1;
}
.el-main {
padding: 0;
height: calc(100vh - 96px);
height: calc(100vh - 64px);
overflow: hidden;
overflow-y: auto;
background-color: rgba(246, 251, 255, 1);
}
</style>
......@@ -269,7 +269,7 @@ onUnmounted(() => {
<style lang="scss" scoped>
.ai-wrapper {
width: 548px;
height: 1048px;
height: 800px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
......@@ -326,7 +326,7 @@ onUnmounted(() => {
}
.main {
height: 830px;
height: 580px;
// background: rgba(225, 225, 225, 0.5);
width: 520px;
margin: 10px auto;
......
......@@ -5,11 +5,16 @@
<slot name="header-top"></slot>
</div>
<div class="container-header">
<div class="header-left">
<div class="header-left" :style="{ paddingLeft: block ? 0 : '14px' }">
<slot name="header-left">
<!-- <div class="blue-title-block"></div> -->
<el-image :src="titleIcon" class="header-icon" fit="contain" />
<div v-if="block" class="blue-title-block"></div>
<el-image v-else :src="titleIcon" class="header-icon" fit="contain" />
<div :class="headerTitleClasses">{{ title }}</div>
<div v-if="props.headerNum > 0" class="num-box">
<div class="num">
{{ headerNum }}
</div>
</div>
</slot>
</div>
<div class="header-right">
......@@ -57,6 +62,14 @@ const props = defineProps({
titleType: {
type: String,
default: ""
},
block: {
type: Boolean,
default: false
},
headerNum: {
type: Number,
default: 0
}
});
......@@ -95,6 +108,26 @@ const headerTitleClasses = computed(() => [
display: flex;
align-items: center;
padding-left: 14px;
.num-box {
height: 48px;
width: 35px;
background: rgba(206, 79, 81, 1);
display: flex;
justify-content: flex-start;
align-items: center;
.num {
width: 24px;
height: 20px;
line-height: 20px;
text-align: center;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 12px;
// border: 1px solid rgba(255, 255, 255, 1);
border-radius: 100px;
background: rgba(255, 255, 255, 0.3);
}
}
}
.header-icon {
......@@ -106,9 +139,9 @@ const headerTitleClasses = computed(() => [
.blue-title-block {
width: 8px;
height: 16px;
background-color: var(--base-color);
background-color: $base-color;
/* border-radius: 3px; */
margin-right: 14px;
margin-right: 4px;
}
.header-title {
......@@ -127,7 +160,7 @@ const headerTitleClasses = computed(() => [
}
.header-title-danger {
background: red;
background: rgba(206, 79, 81, 1);
color: white;
}
......
<template>
<div class="policy-list">
<div v-for="item in props.policyList" :key="item.id" class="policy-item">
<el-image :src="$withFallbackImage(item.imageUrl, item.content) " class="item-cover" fit="cover"/>
<div class="item-details">
<h3 class="item-title">{{ item.name }}</h3>
<div class="item-content"> {{ item.times }} · {{ item.content }} <el-icon><Link /></el-icon></div>
<div class="item-tags">
<el-tag v-for="tag in item.tags" :key="tag" class="custom-tag">{{ tag }}</el-tag>
<div
v-for="(item, index) in policyList"
:key="index"
class="policy-item"
>
<div class="item-left">
<div class="report-cover">
<img :src="$withFallbackImage(item.imageUrl, index)" alt="Report Cover" />
</div>
<div
v-if="item.relatedBill"
class="related-bill-box"
:class="`status-bg-${item.status}`"
>
<span>{{ item.relatedBill.text }}</span>
<div class="status-badge" :class="`status-color-${item.status}`">
<span class="badge-dot"></span>
{{ getStatusInfo(item.status).text }}
</div>
</div>
<div class="item-right">
<h3 class="item-title">
{{ item.content }}
</h3>
<div class="item-meta">
<span class="meta-date">{{ formatDate(item.times) }}</span>
<span class="meta-divider">·</span>
<span class="meta-source">
{{ item.name }}
<el-icon class="link-icon"><TopRight /></el-icon>
</span>
</div>
<div class="item-tags" v-if="item.tags && item.tags.length">
<span v-for="(tag, tIndex) in item.tags" :key="tIndex" class="tag-pill">
{{ tag }}
</span>
</div>
<div v-else class="related-bill-box status-bg-unimplemented">
<span>不存在相关提案。</span>
<div class="status-badge status-color-unimplemented">
<span class="badge-dot"></span>
{{ item.status }}
<div class="item-actions" v-if="item.statusRaw">
<div
v-for="(statusItem, sIndex) in parseStatus(item.statusRaw)"
:key="sIndex"
class="status-link"
>
<span class="status-type">{{ statusItem.type }}</span>
<span class="status-year">{{ statusItem.year }}</span>
<span class="status-name">{{ statusItem.name }}</span>
<el-icon class="arrow-icon"><Right /></el-icon>
</div>
</div>
</div>
......@@ -33,35 +48,53 @@
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Link } from '@element-plus/icons-vue'
const props = defineProps({
policyList: {
type: Array,
default: () => []
}
})
// --- Status Styling Helper ---
const getStatusInfo = (status) => {
switch (status) {
case 'implemented':
return { text: '已实施', color: '#e66657', bgColor: '#fdeeed' };
case 'partial':
return { text: '部分实施', color: '#d38f24', bgColor: '#fcf3e4' };
case 'unimplemented':
return { text: '未实施', color: '#409eff', bgColor: '#ecf5ff' };
default:
return { text: '未知', color: '#909399', bgColor: '#f4f4f5' };
}
};
<script setup lang="ts">
import { TopRight, Right } from '@element-plus/icons-vue'
interface PolicyItem {
content: string;
statusRaw: string; // 原始的长字符串
name: string;
times: string;
tags?: string[];
coverUrl?: string;
}
const props = defineProps<{
policyList: PolicyItem[]
}>()
// 格式化日期:2025-06-26 -> 2025年6月26日
const formatDate = (dateStr: string) => {
if (!dateStr) return '';
const date = new Date(dateStr);
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日`;
}
// 解析状态字符串
// 输入: "法案 2024 《芯片科学法案》; 政令 2025 《推动美国...》"
// 输出: 数组对象
const parseStatus = (raw: string) => {
if (!raw) return [];
// 按分号分割多个条目
const items = raw.split(/[;;]/).map(s => s.trim()).filter(s => s);
return items.map(itemStr => {
// 简单正则匹配: "类型 年份 《名称》"
// 注意:这里假设数据格式比较规范,实际需根据后端数据调整
// 尝试移除书名号进行提取
const cleanStr = itemStr.replace(/[《》]/g, '');
const parts = cleanStr.split(' ');
return {
type: parts[0] || '政策',
year: parts[1] || '',
name: parts.slice(2).join(' ') || cleanStr // 剩余部分作为名称
}
});
}
</script>
<style scoped>
/* --- Policy List Styles --- */
.policy-list {
display: flex;
flex-direction: column;
......@@ -70,139 +103,143 @@ const getStatusInfo = (status) => {
.policy-item {
display: flex;
padding: 20px 0;
border-bottom: 1px solid #e4e7ed;
border-bottom: 1px solid #ebeef5;
gap: 16px;
transition: background-color 0.2s;
}
.policy-item:last-child {
border-bottom: none;
}
.item-cover {
width: 80px;
height: 80px;
margin-right: 20px;
/* 左侧封面 */
.item-left {
flex-shrink: 0;
border-radius: 4px;
border: 1px solid #ebeef5;
}
.item-details {
flex-grow: 1;
.report-cover {
width: 60px;
height: 80px;
background-color: #f2f3f5;
border: 1px solid #e4e7ed;
border-radius: 2px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.report-cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 右侧内容 */
.item-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* 1. 标题 */
.item-title {
margin: 0 0 6px 0;
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0 0 8px;
cursor: pointer;
font-weight: 700;
color: #1a1a1a;
line-height: 1.4;
cursor: pointer;
}
.item-title:hover {
color: #409eff;
color: #409EFF;
}
.item-content {
color: #909399;
font-size: 14px;
/* 2. 元数据 */
.item-meta {
display: flex;
align-items: center;
font-size: 13px;
color: #606266;
margin-bottom: 8px;
}
.item-tags {
margin-bottom: 12px;
}
.custom-tag {
margin-right: 8px;
background-color: #f0f2f5;
color: #606266;
border-color: #e4e7ed;
font-size: 12px;
.meta-divider {
margin: 0 8px;
font-weight: bold;
}
.related-bill-box {
.meta-source {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
border-radius: 6px;
font-size: 14px;
line-height: 1.4;
gap: 4px;
cursor: pointer;
}
.meta-source:hover {
color: #409EFF;
}
.status-badge {
.link-icon {
font-size: 12px;
}
/* 3. 标签 */
.item-tags {
display: flex;
align-items: center;
font-size: 13px;
padding: 4px 8px;
border-radius: 4px;
white-space: nowrap;
font-weight: 500;
gap: 8px;
margin-bottom: 10px;
}
.badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: currentColor;
margin-right: 6px;
.tag-pill {
background-color: #f2f3f5;
color: #5e6d82;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
/* Dynamic status colors */
.status-bg-implemented {
background-color: #fdeeed;
/* 4. 底部状态链接 */
.item-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.status-color-implemented {
color: #e66657;
.status-link {
display: inline-flex;
align-items: center;
background-color: #ecf5ff; /* 浅蓝色背景 */
color: #409EFF; /* 蓝色文字 */
padding: 4px 12px;
border-radius: 4px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.status-bg-partial {
background-color: #fcf3e4;
.status-link:hover {
background-color: #d9ecff;
}
.status-color-partial {
color: #d38f24;
.status-type {
font-weight: bold;
margin-right: 4px;
}
.status-bg-unimplemented {
background-color: #ecf5ff;
.status-year {
margin-right: 4px;
}
.status-color-unimplemented {
color: #409eff;
.status-name {
margin-right: 4px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.policy-item {
flex-direction: column;
gap: 12px;
padding: 16px 0;
}
.item-cover {
width: 100%;
height: 120px;
margin-right: 0;
margin-bottom: 12px;
}
.item-title {
font-size: 15px;
}
.related-bill-box {
flex-direction: column;
align-items: flex-start;
gap: 8px;
padding: 12px;
}
.status-badge {
align-self: flex-end;
}
.arrow-icon {
margin-left: 4px;
font-size: 12px;
}
</style>
</style>
\ No newline at end of file
......@@ -82,7 +82,7 @@ import PolicyList from './PolicyList.vue';
import CardTitle from './CardTitle.vue';
import { getOverviewPolicy } from '@/api'
import PolicyOverview from '@/views/thinkTank/components/PolicyOverview.vue'
import { mockPolicyList } from '@/views/thinkTank/mockData';
const props = defineProps({
showSearch: {
type: Boolean,
......@@ -120,7 +120,8 @@ const getPolicies = async () => {
researchTypeIds: activeTechField.value,
statusList: activeStatus.value,
})
policies.value = data
// policies.value = data
policies.value = mockPolicyList
}
......
......@@ -2,6 +2,8 @@ import { createRouter, createWebHistory } from "vue-router";
//中美博弈概览
import overView from "@/views/overView/index.vue";
//GJ概览
import gjOverView from "@/views/gjOverView/index.vue";
//新闻速览
import newsBrief from "@/views/newsBrief/index.vue"
// 风险信号
......@@ -51,6 +53,9 @@ import MarketSingleCaseDeepdig from "@/views/marketAccessRestrictions/singleCase
// 投融资限制
import Finance from "@/views/finance/index.vue";
// 新闻事件分析
import NewsAnalysis from "@/views/newsAnalysis/index.vue";
// 智能写报
import WrittingAsstaint from "@/views/writtingAsstaint/index.vue";
// 门户
......@@ -93,6 +98,15 @@ const routes = [
title: "中美博弈概览"
}
},
// GJ概览页面路由
{
path: "/gjOverView",
name: "gjOverView",
component: gjOverView,
meta: {
title: "国家概览"
}
},
//新闻速览页面路由
{
path: "/newsBrief",
......@@ -102,7 +116,7 @@ const routes = [
title: "新闻速览"
}
},
//风险信号页面路由
//风险信号页面路由
{
path: "/riskSignal",
name: "riskSignal",
......@@ -439,67 +453,167 @@ const routes = [
},
// 门户
{
path: "/portal1",
name: "portal1",
component: Portal1,
meta: {
title: "门户1"
}
},
{
path: "/portal2",
name: "portal2",
component: Portal2,
meta: {
title: "门户2"
}
},
// 市场准入限制首页
{
path: "/marketAccessRestrictions",
name: "MarketAccessRestrictions",
component: MarketAccessRestrictions,
meta: {
title: "市场准入限制"
}
},
{
path: "/marketAccessLayout",
name: "MarketAccessLayout",
component: MarketAccessLayout,
redirect: "/marketAccessLayout/overview",
meta: {
title: "市场准入限制布局"
},
children: [
{
path: "overview",
name: "MarketAccessOverview",
component: MarketAccessOverview,
meta: {
title: "调查概况"
}
},
{
path: "case",
name: "MarketAccessCase",
component: MarketAccessCase,
meta: {
title: "调查案件"
}
}
]
},
{
path: "/marketSingleCaseLayout",
name: "MarketSingleCaseLayout",
component: MarketSingleCaseLayout,
redirect: "/marketSingleCaseLayout/overview",
meta: {
title: "单次调查案件布局"
},
children: [
{
path: "overview",
name: "MarketSingleCaseOverview",
component: MarketSingleCaseOverview,
meta: {
title: "调查简介"
}
},
{
path: "deepdig",
name: "MarketSingleCaseDeepdig",
component: MarketSingleCaseDeepdig,
meta: {
title: "深度挖掘"
}
}
]
},
// 综合搜索
{
path: "/comprehensiveSearch",
name: "comprehensiveSearch",
component: ComprehensiveSearch,
meta: {
title: "综合搜索"
}
},
{
path: "/searchResults",
name: "searchResults",
component: SearchResults,
meta: {
title: "搜索结果"
}
},
{
path: "/chat",
name: "chat",
component: Chat,
meta: {
title: "智能问答"
}
},
// 合作限制
{
path: "/cooperationRestrictions",
name: "CooperationRestrictions",
component: CooperationRestrictions,
meta: {
title: "合作限制"
}
},
// 合作限制详情
{
path: "/coopRestriction/detail",
name: "CooperationRestrictionsDetail",
component: CooperationRestrictionsDetail,
meta: {
title: "合作限制详情"
}
},
// 出口管制转移过来的页面
{
path: "/exportControl/analysis",
name: "analysis",
component: () => import("@/views/exportControl/analysis/index.vue"),
meta: {
title: "分析页"
}
},
{
path: "/exportControl/infoplatform",
name: "infoplatform",
component: () => import("@/views/exportControl/infoPlatform/index.vue"),
meta: {
title: "信息平台"
}
},
{
path: "/exportControl/rulelimit",
name: "rulelimit",
component: () => import("@/views/exportControl/ruleLimit/index.vue"),
meta: {
title: "规则限制"
}
},
{
path: "/exportControl/ruledetail",
name: "ruledetail",
component: () => import("@/views/exportControl/ruleDetail/index.vue"),
meta: {
title: "规则详情"
}
},
{
path: "/exportControl/researchfunding",
name: "researchfunding",
component: () => import("@/views/exportControl/researchFunding/index.vue"),
meta: {
title: "科研资助"
}
},
// 投融资限制
{
path: "/finance",
name: "finance",
component: Finance,
meta: {
title: "投融资限制"
}
},
// 新闻事件分析
{
path: "/newsAnalysis",
name: "newsAnalysis",
component: NewsAnalysis,
meta: {
title: "新闻事件分析"
}
},
// 门户
{
path: "/portal1",
name: "portal1",
component: Portal1,
meta: {
title: "门户1"
}
},
{
path: "/portal2",
name: "portal2",
component: Portal2,
meta: {
title: "门户2"
}
},
// 综合搜索
{
path: "/comprehensiveSearch",
name: "comprehensiveSearch",
component: ComprehensiveSearch,
meta: {
title: "综合搜索"
}
},
{
path: "/searchResults",
name: "searchResults",
component: SearchResults,
meta: {
title: "搜索结果"
}
}
];
const router = createRouter({
......
:root {
--color-main-active: rgba(10, 87, 166, 1);
--color-main-active: rgba(5, 95, 194, 1);
--color-main-primay: rgba(59, 65, 75, 1);
--color-bg-hover: #e7f3ff;
/* 普通按钮颜色 */
--btn-plain-border-color: rgba(230, 231, 232, 1);
--btn-plain-bg-color: rgba(255, 255, 255, 1);
......
// 平滑滚动到指定元素
const scrollToCenter = (elementId) => {
const element = document.getElementById(elementId);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
export default scrollToCenter
\ No newline at end of file
// 平滑滚动到指定元素
const scrollToTop = (elementId) => {
const element = document.getElementById(elementId);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
export default scrollToTop
\ No newline at end of file
......@@ -118,17 +118,6 @@
</div>
</div>
</div>
<div class="tool-box">
<div class="tool1">
<img src="./assets/icons/tool-icon1.png" alt="" />
</div>
<div class="tool2">
<img src="./assets/icons/tool-icon2.png" alt="" />
</div>
<div class="tool3">
<img src="./assets/icons/tool-icon3.png" alt="" />
</div>
</div>
</div>
</template>
......@@ -136,23 +125,6 @@
import { ref, onMounted } from "vue";
import router from "@/router";
import GJKJAQImg from "./assets/images/icon1.png";
import GJKJAQActiveImg from "./assets/images/icon1.png";
import KJLYImg from "./assets/images/icon2.png";
import KJLYActiveImg from "./assets/images/icon2.png";
import KJYSImg from "./assets/images/icon3.png";
import KJYSActiveImg from "./assets/images/icon3.png";
import ZDKJAQImg from "./assets/images/icon4.png";
import ZDKJAQActiveImg from "./assets/images/icon4.png";
import ZBTXImg from "./assets/images/icon5.png";
import ZBTXActiveImg from "./assets/images/icon5.png";
import ZJKImg from "./assets/images/icon6.png";
import ZJKActiveImg from "./assets/images/icon6.png";
import XTGLImg from "./assets/images/icon7.png";
import XTGLActiveImg from "./assets/images/icon7.png";
import search from "./assets/images/search.png";
import mail from "./assets/images/mail.png";
import icon1 from "./assets/icons/icon1.png";
import icon1Active from "./assets/icons/icon1_active.png";
import icon2 from "./assets/icons/icon2.png";
......@@ -162,12 +134,6 @@ import icon3Active from "./assets/icons/icon3_active.png";
import icon4 from "./assets/icons/icon4.png";
import icon4Active from "./assets/icons/icon4_active.png";
import headerIcon1 from "./assets/icons/header-icon1.png";
import headerIcon2 from "./assets/icons/header-icon2.png";
import headerIcon3 from "./assets/icons/header-icon3.png";
import headerIcon4 from "./assets/icons/header-icon4.png";
import headerIcon5 from "./assets/icons/header-icon5.png";
const activeName = ref("分析报告");
const handleSwitchActiveName = (name) => {
......@@ -222,13 +188,6 @@ const handleClickMainHeaderBtn = (item) => {
router.push(item.path);
};
const activeNavIndex = ref(0);
const handleClickNav = (index, item) => {
activeNavIndex.value = index;
router.push(item.path);
};
onMounted(() => {
if(window.sessionStorage.getItem('activeTitle')) {
activeTitle.value = window.sessionStorage.getItem('activeTitle')
......@@ -239,7 +198,6 @@ onMounted(() => {
<style lang="scss" scoped>
.layout-container {
width: 1920px;
// height: 1080px;
height: 1016px;
background: rgba(249, 250, 252, 1);
position: relative;
......@@ -623,49 +581,5 @@ onMounted(() => {
}
}
}
.tool-box {
position: fixed;
z-index: 10000;
bottom: 80px;
left: 0;
width: 48px;
height: 144px;
border-radius: 0px 10px 10px 0px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.tool1 {
width: 17px;
height: 18px;
margin-top: 17px;
margin-left: 16px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.tool2 {
width: 22px;
height: 20px;
margin-top: 26px;
margin-left: 14px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.tool3 {
width: 20px;
height: 20px;
margin-top: 25px;
margin-left: 15px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
}
</style>
\ No newline at end of file
......@@ -635,7 +635,7 @@ onMounted(() => {
<style lang="scss" scoped>
.introduction-wrap {
width: 100%;
height: 100%;
height: 880px;
display: flex;
.box-header {
height: 56px;
......@@ -768,8 +768,8 @@ onMounted(() => {
}
.item-right4 {
// width: 500px;
margin-top: -2px;
height: 26px;
margin-top: -4px;
height: 28px;
display: flex;
padding-left: -10px;
border: 2px solid #5f656c;
......
......@@ -24,22 +24,22 @@ const getbarChart = (data) => {
0,
0,
1, [{
offset: 0,
color: "#3e7697",
},
{
offset: 1,
color: "#152d47",
},
],
offset: 0,
color: "#3e7697",
},
{
offset: 1,
color: "#152d47",
},
],
false
),
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: "category",
......@@ -59,37 +59,37 @@ const getbarChart = (data) => {
nameTextStyle: { fontSize: "14px" },
show: false,
axisLine: {
lineStyle: {
color: "#ccc",
: {
color: "#ccc",
},
show: false,
show: false,
},
splitLine: {
lineStyle: {
color: "#888",
splitLine: {
: {
color: "#888",
},
show: false,
show: false,
},
},
series: [
{
type: "bar",
barWidth: "36px",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(67,188,255,1)" },
{ offset: 1, color: "rgba(67,188,255,0.5)" },
]),
},
label: {
show: true,
position: 'top'
},
data: data.datay
}
],
series: [
{
type: "bar",
barWidth: "36px",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(67,188,255,1)" },
{ offset: 1, color: "rgba(67,188,255,0.5)" },
]),
},
label: {
show: true,
position: 'top'
},
data: data.datay
}
],
};
}
......
......@@ -586,7 +586,7 @@ onMounted(async () => {
.tag {
height: 18px;
line-height: 18px;
padding: 1px 8px 1px 8px;
padding: 0 8px;
border-radius: 4px;
margin-left: 5px;
font-size: 12px;
......
......@@ -2,13 +2,14 @@
<div :class="['clickable-card', { disabled: disabled }]" @click="handleClick">
<span class="card-text">{{ text }}</span>
<!-- <span class="arrow-icon"></span> -->
<el-icon><ArrowRight /></el-icon>
<div class="icon">
<el-icon><ArrowRightBold /></el-icon>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
import { ArrowRight } from "@element-plus/icons-vue";
import { ArrowRight, ArrowRightBold } from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
const router = useRouter();
......@@ -51,7 +52,7 @@ const handleClick = () => {
};
</script>
<style scoped>
<style lang="scss" scoped>
.clickable-card {
width: 160px;
height: 48px;
......@@ -69,6 +70,12 @@ const handleClick = () => {
font-size: 20px;
font-weight: 500;
box-sizing: border-box;
position: relative;
.icon{
position: absolute;
top: 14px;
right: 12px;
}
}
.clickable-card:hover {
......
......@@ -19,7 +19,6 @@
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
const props = defineProps({
listData: {
......
......@@ -2,12 +2,14 @@
<div :class="['clickable-card', { disabled: disabled }]" @click="handleClick">
<span class="card-text">{{ text }}</span>
<!-- <span class="arrow-icon"></span> -->
<el-icon><ArrowRight /></el-icon>
<div class="icon">
<el-icon><ArrowRightBold /></el-icon>
</div>
</div>
</template>
<script setup>
import { ArrowRight } from "@element-plus/icons-vue";
import { ArrowRight, ArrowRightBold } from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
const router = useRouter();
......@@ -50,7 +52,7 @@ const handleClick = () => {
};
</script>
<style scoped>
<style lang="scss" scoped>
.clickable-card {
width: 160px;
height: 48px;
......@@ -68,6 +70,12 @@ const handleClick = () => {
font-size: 20px;
font-weight: 500;
box-sizing: border-box;
position: relative;
.icon{
position: absolute;
top: 14px;
right: 12px;
}
}
.clickable-card:hover {
......
......@@ -19,7 +19,6 @@
</template>
<script setup>
import { defineProps, defineEmits } from "vue";
const props = defineProps({
listData: {
......
......@@ -41,8 +41,8 @@ defineProps({
}
.title-text {
font-size: 20px;
font-weight: bold;
font-size: 32px;
font-weight: 700;
margin-left: 20px;
white-space: nowrap;
}
......
<!-- 国家科技博弈支撑资源分析 -->
<template>
<div class="thematic-box">
<div style="width:100%;text-align:center;">
<div class="thematic-title" style="display:inline-block;">
国家科技博弈优劣势分析
</div>
</div>
<div class="divider"></div>
<div class="chart-content" style="margin-top: 32px;">
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">技术能力对比</div>
<div class="item-header-btn">
查看数据源 >
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char8">
</div>
<div style="width: 100%;">
<div class="comparison-btn">
领域综合能力排名位势
</div>
</div>
</div>
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-radar.png"></img>
<div class="item-header-text">产业能力对比</div>
<div class="item-header-btn">
查看数据源 >
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100%-40px) ;margin: 20px;" id="char9">
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import router from '@/router/index'
import * as echarts from "echarts";
import DivideHeader from "@/components/DivideHeader.vue";
import Timeline from "../component/Timeline.vue";
import getBarChart from '../js/barChart.js'
import getBarChart3 from '../js/barChart3.js'
import radarChart2 from '../js/radarChart2.js'
import getMultiLineChart2 from "../js/multiLineChart2.js";
//科技人才对比分析
const char4Data = ref({
name: ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚'],
value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79],
});
//科研投入对比分析
const char5Data = ref({
name: ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚'],
value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79],
});
const names = ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚']
const data1 = [120, 200, 150, 80, 70, 100]
const data2 = [90, 230, 180, 110, 100, 30]
const data3 = [20, 130, 120, 160, 80, 11]
// 绘制echarts图表
const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId);
chartDom.removeAttribute("_echarts_instance_");
let chart = echarts.init(chartDom);
chart.setOption(option);
return chart;
};
const chart8Data = ref({
title: [
"2014",
"2015",
"2016",
"2017",
"2018",
"2019",
"2020",
"2021",
"2022",
"2023",
"2024",
"2025"
],
data: [
{
name: "美国",
value: [145, 52, 84, 99, 71, 96, 128, 144, 140, 168, 188, 172]
},
{
name: "中国",
value: [6, 3, 4, 6, 11, 5, 2, 14, 16, 27, 28, 44]
},
{
name: "日本",
value: [149, 58, 85, 95, 72, 108, 133, 148, 139, 180, 187, 125]
},
{
name: "欧盟",
value: [152, 48, 91, 88, 74, 103, 129, 141, 136, 171, 185, 19]
},
{
name: "韩国",
value: [138, 61, 77, 105, 68, 112, 135, 159, 147, 183, 192, 148]
},
{
name: "人才限制",
value: [142, 53, 82, 97, 69, 100, 130, 150, 142, 173, 189, 177]
},
]
});
onMounted(() => {
let char8 = getBarChart3(names, data1, data2, data3, false);
setChart(char8, "char8");
let char7 = radarChart2()
setChart(char7, "char7");
//制裁手段强度变化折线图
let chart9 = getMultiLineChart2(chart8Data.value.title, chart8Data.value.data[0].value, chart8Data.value.data[1].value, chart8Data.value.data[2].value, chart8Data.value.data[3].value, chart8Data.value.data[4].value, chart8Data.value.data[5].value);
setChart(chart9, "char9");
});
</script>
<style lang="scss" scoped>
.thematic-box {
margin-top: 64px;
height: 578px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: linear-gradient(180.00deg, rgba(246, 250, 255, 1), rgba(246, 250, 255, 0) 100%);
.divide {
width: 100%;
margin: 0 auto;
margin-top: 52px;
margin-bottom: 36px;
}
}
.thematic-title {
/* Common/导航按钮 */
width: 284px;
height: 48px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 4;
padding: 9px 16px 9px 16px;
background: rgba(5, 95, 194, 1);
color: rgba(255, 255, 255, 1);
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.thematic-content {
/* 容器 480 */
width: calc(100% - 40px);
height: 640px;
margin: 35px 20px 20px 20px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.timeLine-box {
height: 100%;
width: calc(100% - 50px);
.time-item-box {
width: 30%;
height: 165px;
}
}
.item-header-divider {
/* 矩形 249 */
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.thematic-btn-left {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-right {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-icon {
height: 48px;
width: 100%;
margin-top: calc(50% - 24px);
}
}
.item-header {
height: 48px;
line-height: 48px;
width: 100%;
display: flex;
.item-header-icon {
width: 20px;
height: 20px;
margin: 15px;
}
.item-header-text {
width: calc(100% - 170px);
font-size: 20px;
font-weight: 700;
line-height: 26px;
background: rgba(255, 255, 255, 0.65);
color: #055fc2;
font-family: Microsoft YaHei;
font-size: 20px;
line-height: 48px;
text-align: left;
}
.item-header-btn {
color: rgba(5, 95, 194, 1);
cursor: pointer;
margin-right: 27px;
width: 100px;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
letter-spacing: 0px;
text-align: right;
}
}
.divider {
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.comparison-btn {
/* 容器 648 */
width: calc(100% - 70px);
margin-left: 35px;
height: 30px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10;
padding: 0px 24px 0px 24px;
border-radius: 15px;
background: rgba(231, 243, 255, 1);
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: center;
}
.chart-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
width: 100%;
height: 452px;
padding: 0 20px;
}
.chart-box {
height: 452px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
background: rgba(255, 255, 255, 1);
}
.radar2Data-line {
display: flex;
width: 100%;
height: 30px;
margin-top: 18px;
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
align-items: center;
/* 垂直居中 */
}
.radar2Data-circle {
/* 椭圆 79 */
width: 12px;
height: 12px;
border-radius: 50%;
}
</style>
<!-- 国家科技战略博弈手段分析 -->
<template>
<div class="thematic-box">
<div style="width:100%;text-align:center;">
<div class="thematic-title" style="display:inline-block;">
国家科技战略博弈手段分析
</div>
</div>
<div class="divider"></div>
<div class="thematic-content">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科技博弈历程</div>
</div>
<div class="item-header-divider"></div>
<div style="display: flex;height: calc(100% - 60px);">
<Timeline :data="course" text-key="title" id-key="seq" />
</div>
</div>
<div class="chart-content">
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">制裁手段强度变化</div>
<div class="item-header-btn">
查看数据源>
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 400px;width: 100%;" id="char1">
</div>
</div>
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">制裁领域分布情况</div>
<div class="item-header-btn">
查看数据源>
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 350px;width: calc(100% - 40px) ;margin: 20px;" id="char2">
</div>
</div>
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">制裁要素排行分析</div>
<div class="item-header-btn">
查看数据源>
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 450px;width: calc(100% - 40px) ;margin: 20px;" id="char3">
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import router from '@/router/index'
import * as echarts from "echarts";
import DivideHeader from "@/components/DivideHeader.vue";
import Timeline from "../component/Timeline.vue";
import getMultiLineChart1 from "../js/multiLineChart1.js";
import radarChart1 from '../js/radarChart.js'
import pictorialBarChar from '../js/pictorialBarChar.js'
//科技博弈历程
const course = ref([
{
time: "2025-05-23",
title: "日本加入对华设备管制联盟",
content: "日本经济产业省正式宣布,将23种高性能半导体制造设备纳入出口管制范围,于7月23...",
unit: "日本"
},
{
time: "2025-05-23",
title: "中国发起关键原材料反制",
content: "中国商务部、海关总署宣布为维护国家安全和利益,自8月1日起对镓、锗相关物项实施...",
unit: "中国"
},
{
time: "2025-08-19",
title: "中国突破7nm芯片制造",
content: "华为开售Mate 60 Pro手机。其搭载的麒麟9000s芯片使用DUV光刻技术制造。",
unit: "中国"
},
{
time: "2025-08-20",
title: "中国加速成熟制程产能扩张引发欧美担忧",
content: "中国在成熟制程半导体(28nm及以上)的产能正急剧扩张。此举引发美国和欧洲联盟的...",
unit: "中国"
},
{
time: "2025-08-17",
title: "美国进一步收紧对华AI芯片出口限制",
content: "美国商务部工业和安全局(BIS)宣布更新“先进计算芯片规则”,大幅收紧对向中...",
unit: "美国"
},
{
time: "2025-12-21",
title: "美国调查中国传统芯片产能",
content: "美国商务部宣布,将启动一项针对美国关键行业对中国制造的成熟制程依赖程度的调查...",
unit: "美国"
}
]);
const chart1Data = ref({
title: [
"2024-09",
"2024-10",
"2024-11",
"2024-12",
"2025-01",
"2025-02",
"2025-03",
"2025-04",
"2025-05",
"2025-06",
"2025-07",
"2025-08"
],
data: [
{
name: "科技法案",
value: [145, 52, 84, 99, 71, 96, 128, 144, 140, 168, 188, 172]
},
{
name: "出口管制",
value: [6, 3, 4, 6, 11, 5, 2, 14, 16, 27, 28, 44]
},
{
name: "科技政令",
value: [149, 58, 85, 95, 72, 108, 133, 148, 139, 180, 187, 175]
},
{
name: "合作限制",
value: [152, 48, 91, 88, 74, 103, 129, 141, 136, 171, 185, 169]
},
{
name: "市场准入",
value: [138, 61, 77, 105, 68, 112, 135, 159, 147, 183, 192, 178]
},
{
name: "人才限制",
value: [142, 53, 82, 97, 69, 100, 130, 150, 142, 173, 189, 177]
},
]
});
const names = ['Jan', 'Feb', 'Mar', 'Apr', 'May']
const data1 = [120, 200, 150, 80, 70]
const data2 = [90, 230, 180, 110, 100]
// 绘制echarts图表
const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId);
chartDom.removeAttribute("_echarts_instance_");
let chart = echarts.init(chartDom);
chart.setOption(option);
return chart;
};
onMounted(() => {
course.value.sort((a, b) => new Date(a.time) - new Date(b.time));
//制裁手段强度变化折线图
let chart1 = getMultiLineChart1(chart1Data.value.title, chart1Data.value.data[0].value, chart1Data.value.data[1].value, chart1Data.value.data[2].value, chart1Data.value.data[3].value, chart1Data.value.data[4].value, chart1Data.value.data[5].value);
setChart(chart1, "char1");
let char2 = radarChart1()
setChart(char2, "char2");
let char3 = pictorialBarChar(names, data1, data2, false);
setChart(char3, "char3");
// pictorialBarChar
});
</script>
<style lang="scss" scoped>
.thematic-box {
margin-top: 36px;
height: 1233px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: linear-gradient(180.00deg, rgba(246, 250, 255, 1), rgba(246, 250, 255, 0) 100%);
.divide {
width: 100%;
margin: 0 auto;
margin-top: 52px;
margin-bottom: 36px;
}
}
.thematic-title {
/* Common/导航按钮 */
width: 284px;
height: 48px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 4;
padding: 9px 16px 9px 16px;
background: rgba(5, 95, 194, 1);
color: rgba(255, 255, 255, 1);
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.thematic-content {
/* 容器 480 */
width: calc(100% - 40px);
height: 640px;
margin: 35px 20px 20px 20px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.timeLine-box {
height: 100%;
width: calc(100% - 50px);
.time-item-box {
width: 30%;
height: 165px;
}
}
.item-header-divider {
/* 矩形 249 */
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.thematic-btn-left {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-right {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-icon {
height: 48px;
width: 100%;
margin-top: calc(50% - 24px);
}
}
.item-header {
height: 48px;
line-height: 48px;
width: 100%;
display: flex;
.item-header-icon {
width: 20px;
height: 20px;
margin: 15px;
}
.item-header-text {
width: calc(100% - 170px);
font-size: 20px;
font-weight: 700;
line-height: 26px;
background: rgba(255, 255, 255, 0.65);
color: #055fc2;
font-family: Microsoft YaHei;
font-size: 20px;
line-height: 48px;
text-align: left;
}
.item-header-btn {
color: rgba(5, 95, 194, 1);
cursor: pointer;
margin-right: 27px;
width: 92px;
}
}
.divider {
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.chart-content {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
width: 100%;
height: 452px;
padding: 0 20px;
}
.chart-box {
height: 452px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
background: rgba(255, 255, 255, 1);
}
</style>
<!-- 国家科技博弈支撑资源分析 -->
<template>
<div class="thematic-box">
<div style="width:100%;text-align:center;">
<div class="thematic-title" style="display:inline-block;">
国家科技战略博弈手段分析
</div>
</div>
<div class="divider"></div>
<div class="chart-content" style="margin-top: 32px;">
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科技人才对比分析</div>
<div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img>
数据来源:美国某某发展基金会
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char4">
</div>
<div style="width: 100%;">
<div class="comparison-btn">
R&D人员总数对比
</div>
</div>
</div>
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科研投入对比分析</div>
<div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img>
数据来源:美国某某发展基金会
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char5">
</div>
<div style="width: 100%;">
<div class="comparison-btn">
R&D支出总额对比
</div>
</div>
</div>
</div>
<div class="chart-content" style="margin-top: 24px;">
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">创新主体对比分析</div>
<div class="item-header-btn">
<img class="item-header-icon" src="@/assets/images/icon/header-btn.png"></img>
数据来源:美国某某发展基金会
</div>
</div>
<div class="divider"></div>
<div style="display: flex;height: 320px;width: calc(100% - 40px) ;margin: 20px;" id="char6">
</div>
<div style="width: 100%;">
<div class="comparison-btn">
R&全球顶尖创新主体数量对比
</div>
</div>
</div>
<div class="chart-box">
<div class="item-header">
<img class="item-header-icon" src="@/assets/images/icon/thematic-card-header-time.png"></img>
<div class="item-header-text">科学数据对比分析</div>
<div class="item-header-btn">
<img class="item-header-ibtn-con" src="@/assets/images/icon/header-btn.png"></img>
数据来源:美国某某发展基金会
</div>
</div>
<div class="divider"></div>
<div style="display: flex;">
<div style="display: flex;height: 320px;width: calc(50% - 40px) ;margin: 20px;" id="char7">
</div>
<div style="width: 50%;padding-top: 50px;">
<div v-for="value in radar2Data" class="radar2Data-line">
<div class="radar2Data-circle" :style="{ backgroundColor: value.color }"></div>
<div style=" width: 40px;margin: 0 7px">{{ value.name }}</div>
<div style="width: calc(100% - 150px) ;padding: 10px;"> <el-progress :percentage="value.percent"
:color="value.color" :show-text="false" /></div>
<div style=" width: 70px;text-align: right;">{{ value.percent < 50 ? '低依赖' : value.percent < 80
? '中度依赖' : '高度依赖' }} </div>
</div>
</div>
</div>
<div style="width: 100%;">
<div class="comparison-btn">
科学数据外部依赖情况对比
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import router from '@/router/index'
import * as echarts from "echarts";
import DivideHeader from "@/components/DivideHeader.vue";
import Timeline from "../component/Timeline.vue";
import getBarChart from '../js/barChart.js'
import getBarChart2 from '../js/barChart2.js'
import radarChart2 from '../js/radarChart2.js'
//科技人才对比分析
const char4Data = ref({
name: ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚'],
value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79],
});
//科研投入对比分析
const char5Data = ref({
name: ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚'],
value: [83.76, 76.72, 73.89, 72.16, 66.24, 65.47, 63.98, 62.12, 44.38, 24.79],
});
const names = ['中国', '日本', '欧盟', '韩国', '加拿大', '澳大利亚']
const data1 = [120, 200, 150, 80, 70, 100]
const data2 = [90, 230, 180, 110, 100, 30]
const data3 = [20, 130, 120, 160, 80, 11]
// 绘制echarts图表
const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId);
chartDom.removeAttribute("_echarts_instance_");
let chart = echarts.init(chartDom);
chart.setOption(option);
return chart;
};
const radar2Data = ref([
{
name: '中国',
percent: 20,
color: '#CF4F51',
text: ''
}, {
name: '美国', color: '#0560C3',
percent: 30,
text: ''
}, {
name: '欧盟', color: '#14A8A8',
percent: 40,
text: ''
}, {
name: '英国', color: '#722FD1',
percent: 70,
text: ''
}, {
name: '日本', color: '#FA8C15',
percent: 80,
text: ''
}, {
name: '韩国', color: '#69B2FF',
percent: 90,
text: ''
}
])
onMounted(() => {
//科技人才对比分析
let char4 = getBarChart(char4Data.value.name, char4Data.value.value, true);
setChart(char4, "char4");
//科研投入对比分析
let char5 = getBarChart(char5Data.value.name, char5Data.value.value, true);
setChart(char5, "char5");
let char6 = getBarChart2(names, data1, data2, data3, false);
setChart(char6, "char6");
let char7 = radarChart2()
setChart(char7, "char7");
});
</script>
<style lang="scss" scoped>
.thematic-box {
margin-top: 36px;
height: 1050px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: linear-gradient(180.00deg, rgba(246, 250, 255, 1), rgba(246, 250, 255, 0) 100%);
.divide {
width: 100%;
margin: 0 auto;
margin-top: 52px;
margin-bottom: 36px;
}
}
.thematic-title {
/* Common/导航按钮 */
width: 284px;
height: 48px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 4;
padding: 9px 16px 9px 16px;
background: rgba(5, 95, 194, 1);
color: rgba(255, 255, 255, 1);
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.thematic-content {
/* 容器 480 */
width: calc(100% - 40px);
height: 640px;
margin: 35px 20px 20px 20px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.timeLine-box {
height: 100%;
width: calc(100% - 50px);
.time-item-box {
width: 30%;
height: 165px;
}
}
.item-header-divider {
/* 矩形 249 */
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.thematic-btn-left {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-right {
height: 100%;
width: 24px;
padding-top: 130px;
}
.thematic-btn-icon {
height: 48px;
width: 100%;
margin-top: calc(50% - 24px);
}
}
.item-header {
height: 48px;
line-height: 48px;
width: 100%;
display: flex;
align-items: center;
.item-header-icon {
width: 20px;
height: 20px;
margin: 15px;
}
.item-header-text {
width: 165px;
font-size: 20px;
font-weight: 700;
line-height: 26px;
background: rgba(255, 255, 255, 0.65);
color: #055fc2;
font-family: Microsoft YaHei;
font-size: 20px;
line-height: 48px;
text-align: left;
}
.item-header-btn {
display: flex;
color: rgba(132, 136, 142, 1);
width: calc(100% - 210px);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
letter-spacing: 0px;
text-align: right;
}
.item-header-ibtn-con {
width: 16px;
height: 16px;
margin-top: 15px;
}
}
.divider {
width: 100%;
height: 1px;
background: rgba(231, 243, 255, 1);
}
.comparison-btn {
/* 容器 648 */
width: calc(100% - 70px);
margin-left: 35px;
height: 30px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 10;
padding: 0px 24px 0px 24px;
border-radius: 15px;
background: rgba(231, 243, 255, 1);
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: center;
}
.chart-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
width: 100%;
height: 452px;
padding: 0 20px;
}
.chart-box {
height: 452px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: var(---, 10px);
background: rgba(255, 255, 255, 1);
}
.radar2Data-line {
display: flex;
width: 100%;
height: 30px;
margin-top: 18px;
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
align-items: center;
/* 垂直居中 */
}
.radar2Data-circle {
/* 椭圆 79 */
width: 12px;
height: 12px;
border-radius: 50%;
}
</style>
<template>
<div class="timeline-wrapper">
<button class="year-box">2025</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="card" :class="[cardPos(item), 'right-side']" :style="widthStyle()"
@click="$emit('click-card', item)">
<img :src="`/icon/${item.unit}.png`" class="icon"></img>
<div class="title">{{ item.time }}</div>
<div class="title" :style="{
color: item.unit === '中国' ? ' #CF4F51' : ''
}">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TimeLine',
props: {
data: { type: Array, required: true },
textKey: { type: String, default: 'text' },
idKey: { type: String, default: 'id' }
},
methods: {
widthStyle() {
console.log(`100 / ${this.data.length - 1}}vw`)
let w = 100 / (this.data.length - 1)
return { width: ` calc(${w}vw - 20px)` };
},
/* 水平位置:按索引均匀分布 */
leftStyle(i) {
return { left: `${(i * 100) / (this.data.length - 1)}vw` };
},
/* 卡片上下位置:unit=0 -> 下侧,其余 -> 上侧 */
cardPos(item) {
return item.unit === '中国' ? 'down' : 'up';
},
/* 延伸线方向 = 卡片方向 */
linePos(item) {
return this.cardPos(item);
}
}
};
</script>
<style scoped>
/* 以下样式完全沿用你已有的,无需改动 */
.timeline-wrapper {
display: flex;
align-items: center;
width: 100%;
padding: 0 40px;
width: calc(100vw - 300px);
overflow: auto;
}
.year-box {
width: 80px;
height: 36px;
border-radius: 4px;
background: #055fc2;
color: #fff;
font-size: 18px;
border: none
}
.timeline-box {
flex: 1;
position: relative;
margin-left: 15px;
}
.line {
position: absolute;
left: 0;
right: 0;
top: 50%;
height: 6px;
background-image: url("@/assets/images/bg/timeLine-bg.jpg");
transform: translateY(-50%);
background-size: auto 100%;
}
.node {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
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;
margin: 0 auto;
position: relative
} */
.dot::after {
content: '';
position: absolute;
left: 50%;
transform: translateX(-1px);
width: 1px;
/* background: #409eff */
background: var(--i);
}
.dot.up::after {
bottom: 100%;
height: 180px
}
.dot.down::after {
top: 100%;
height: 180px
}
.card {
position: absolute;
height: 180px;
padding: 8px 20px;
font-size: 14px;
cursor: pointer
}
.card.up {
bottom: 20px
}
.card.down {
top: 20px;
}
.icon {
margin-left: -22px;
}
.title {
color: #055fc2;
font-size: 18px;
font-weight: 700;
line-height: 26px
}
.content {
color: #5f656c;
font-size: 16px;
line-height: 24px
}
</style>
\ No newline at end of file
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论