提交 c5add0d2 authored 作者: coderBryanFu's avatar coderBryanFu

update

......@@ -77,3 +77,22 @@ export function getCoopRestrictionDomain(params) {
params
})
}
// 合作限制-获取合作限制列表接口
/**
* @param {area} 涉及领域
* @param {currentPage} 当前页码
* @param {pageSize} 每页数量
* @param {sortOrder} 排序顺序
* @param {source} 合作来源
* @param {sortField} 排序字段
* @param {type} 合作限制类型
* @header token
*/
export function getCoopRestrictionList(params) {
return request({
method: 'GET',
url: `/api/cooperationlimitinfo/statList`,
params
})
}
\ No newline at end of file
// 智库概览信息
import request from "@/api/request.js";
// 获取新闻资讯
/**
* @param {moduleId}
*/
export function getNews(moduleId = "0105") {
return request({
method: 'GET',
url: `/api/commonFeature/news/${moduleId}`,
})
}
/**
* 社交媒体
*/
export function getSocialMediaInfo(moduleId = "0105") {
return request({
method: "GET",
url: `/api/commonFeature/remarks/${moduleId}`
})
}
// 获取法案风险信号
/**
* @param {moduleId}
*/
export function getBillRiskSignal(moduleId = "0105") {
return request({
method: 'GET',
url: `/api/commonFeature/riskSignal/${moduleId}`,
})
}
//创新主体主页:统计不同创新主体类型对应的主体数量
export function getCountSubjectType() {
return request({
......@@ -41,3 +76,104 @@ export function getOverallRanking(params) {
params
})
}
//创新主体主页:领域布局
export function getResearchField(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/researchField`,
params
})
}
//创新主体主页:主体类型分类领域布局
export function getResearchFieldSubjectType(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/researchFieldSubjectType`,
params
})
}
/***********详情页 */
//创新主体详情:基本信息
export function getInfo(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/info/${params.id}`,
params
})
}
//最新动态
export function getDynamics(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/dynamics/${params.orgId}`,
params
})
}
//创新主体详情:历史事件轴
export function getEventList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/eventList/${params.id}`,
params
})
}
//创新主体详情:重点人物
export function getPersonList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/personList/${params.id}`,
params
})
}
//创新主体科研实力:专利数量统计
export function getPatentList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/patentList/${params.id}`,
params
})
}
// 创新主体科研实力:论文数量统计
export function getPaperList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/paperList/${params.id}`,
params
})
}
//创新主体科研实力:经费增长情况
export function getFundGrowth(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/fundGrowth/${params.id}`,
params
})
}
//创新主体科研实力:经费来源
export function getFundFromList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/fundFromList/${params.id}`,
params
})
}
//创新主体科研实力:经费分配
export function getFundToList(params) {
return request({
method: 'GET',
url: `/api/innovateSubject/fundToList/${params.id}`,
params
})
}
......@@ -13,7 +13,7 @@ const innovationSubjectRoutes = [
}
},
{
path: "/InnovativeInstitutions",
path: "/InnovativeInstitutions/:id",
name: "InnovativeInstitutions",
component: InnovationInstitution,
meta: {
......
......@@ -39,13 +39,8 @@
{{ `共有${total}条${box1BtnActive === 1 ? "涉华" : "全部"}背景` }}
</div>
<div class="page-box">
<el-pagination
background
layout="prev, pager, next"
:total="total"
v-model:current-page="currentPage"
@current-change="handleGetBillBackground"
/>
<el-pagination background layout="prev, pager, next" :total="total" v-model:current-page="currentPage"
@current-change="handleGetBillBackground" />
</div>
</div>
</div>
......@@ -119,21 +114,13 @@
</div> -->
<div class="right-box1-main">
<div class="right-box1-main-top">
<el-icon
style="margin-top: 20px; cursor: pointer"
size="20"
:color="currentIndex > 0 ? '#5f656c' : '#ccc'"
@click="handlePrev"
><CaretLeft
/></el-icon>
<el-icon style="margin-top: 20px; cursor: pointer" size="20"
:color="currentIndex > 0 ? '#5f656c' : '#ccc'" @click="handlePrev">
<CaretLeft />
</el-icon>
<div class="user-list-container">
<div class="user-list-wrapper" :style="{ transform: `translateX(-${currentIndex * 110}px)` }">
<div
class="user-box"
v-for="(item, index) in personList"
:key="index"
@click="handleClickUser(item)"
>
<div class="user-box" v-for="(item, index) in personList" :key="index" @click="handleClickUser(item)">
<div class="img-box">
<img :src="item.image" alt="" />
<div class="icon1">
......@@ -149,21 +136,14 @@
</div>
</div>
</div>
<el-icon
style="margin-top: 20px; cursor: pointer"
size="20"
:color="currentIndex < personList.length - 4 ? '#5f656c' : '#ccc'"
@click="handleNext"
><CaretRight
/></el-icon>
<el-icon style="margin-top: 20px; cursor: pointer" size="20"
:color="currentIndex < personList.length - 4 ? '#5f656c' : '#ccc'" @click="handleNext">
<CaretRight />
</el-icon>
</div>
<div class="right-box1-main-bottom">
<WordCloudMap
:data="wordCloudData"
:selectedName="selectedIndustryName"
shape="circle"
@wordClick="handleWordClick"
/>
<WordCloudMap :data="wordCloudData" :selectedName="selectedIndustryName" shape="circle"
@wordClick="handleWordClick" />
</div>
</div>
</div>
......@@ -320,7 +300,7 @@ const handleGetBillBackground = async () => {
console.log("立法背景", res);
backgroundList.value = res.data.content;
total.value = res.data.totalElements; // 假设API返回totalElements
} catch (error) {}
} catch (error) { }
};
// 获取相关事件
......@@ -345,7 +325,7 @@ const handleGetRelatedEvent = async () => {
item.image = event5;
}
});
} catch (error) {}
} catch (error) { }
};
// 议员相关性分析
......@@ -386,7 +366,7 @@ const handleGetBillPersonAnalyze = async isOppose => {
wordCloudData.value = [];
aboutUserList.value = [];
}
} catch (error) {}
} catch (error) { }
};
......@@ -408,7 +388,7 @@ const handleGetBillPersonAnalyzeDy = async () => {
icon: userIcon,
icon1: m.party === "共和党" ? userIcon2 : userIcon1
}));
} catch (error) {}
} catch (error) { }
};
onMounted(() => {
......@@ -424,10 +404,12 @@ onMounted(() => {
height: 100%;
box-sizing: border-box;
display: flex;
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 18px;
width: 8px;
......@@ -435,6 +417,7 @@ onMounted(() => {
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 14px;
......@@ -445,15 +428,18 @@ onMounted(() => {
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
top: 14px;
right: 84px;
display: flex;
.btn {
margin-left: 8px;
}
}
.header-right {
position: absolute;
top: 14px;
......@@ -461,9 +447,11 @@ onMounted(() => {
display: flex;
justify-content: flex-end;
gap: 4px;
.icon {
width: 28px;
height: 28px;
img {
width: 100%;
height: 100%;
......@@ -471,14 +459,17 @@ onMounted(() => {
}
}
}
.background-wrap-left {
width: 1150px;
margin-top: 16px;
.background-wrap-left-box1 {
width: 1150px;
height: 415px;
background: #fff;
border-radius: 4px;
.box1-main {
.box1-main-center {
margin: 0 22px;
......@@ -488,6 +479,7 @@ onMounted(() => {
flex-wrap: wrap;
justify-content: space-between;
align-content: flex-start;
.box1-main-item {
width: 544px;
height: 48px;
......@@ -497,6 +489,7 @@ onMounted(() => {
background: rgba(255, 255, 255, 1);
display: flex;
margin-top: 8px;
.id {
width: 24px;
height: 24px;
......@@ -509,6 +502,7 @@ onMounted(() => {
font-weight: 400;
margin: 12px 16px 12px 18px;
}
.title {
width: 440px;
height: 48px;
......@@ -520,6 +514,7 @@ onMounted(() => {
text-align: left;
overflow: hidden;
}
.share {
margin-left: 13px;
margin-top: 16px;
......@@ -527,6 +522,7 @@ onMounted(() => {
height: 16px;
padding: 2px;
cursor: pointer;
img {
width: 100%;
height: 100%;
......@@ -534,11 +530,13 @@ onMounted(() => {
}
}
}
.box1-main-footer {
margin: 30px 22px 0 22px;
height: 22px;
display: flex;
justify-content: space-between;
.info {
height: 22px;
line-height: 22px;
......@@ -551,18 +549,21 @@ onMounted(() => {
}
}
}
.background-wrap-left-box2 {
margin-top: 15px;
width: 1150px;
height: 415px;
background: #fff;
border-radius: 4px;
.box2-main {
// margin-top: 9px;
width: 1110px;
margin-left: 23px;
height: 349px;
overflow-y: auto;
.box2-main-item {
width: 1103px;
height: 60px;
......@@ -570,21 +571,26 @@ onMounted(() => {
box-sizing: border-box;
padding: 6px 8px;
display: flex;
&:hover {
background: rgba(225, 225, 225, 0.3);
}
.left {
width: 64px;
height: 48px;
border-radius: 2px;
img {
width: 64px;
height: 48px;
}
}
.center {
margin-left: 14px;
width: 900px;
.title {
height: 22px;
color: rgba(59, 65, 75, 1);
......@@ -598,6 +604,7 @@ onMounted(() => {
text-overflow: ellipsis;
white-space: nowrap;
}
.content {
margin-top: 2px;
height: 22px;
......@@ -613,6 +620,7 @@ onMounted(() => {
white-space: nowrap;
}
}
.right {
margin-left: 25px;
line-height: 60px;
......@@ -626,14 +634,17 @@ onMounted(() => {
}
}
}
.box2-footer {
margin-top: 7px;
display: flex;
justify-content: center;
.btn-more {
width: 108px;
height: 32px;
cursor: pointer;
img {
width: 100%;
height: 100%;
......@@ -642,6 +653,7 @@ onMounted(() => {
}
}
}
.background-wrap-right {
margin-right: 18px;
margin-left: 16px;
......@@ -650,22 +662,27 @@ onMounted(() => {
height: 845px;
background: #fff;
border-radius: 4px;
.background-wrap-right-main {
.right-box1 {
height: 365px;
.right-box1-header {
height: 22px;
margin-left: 22px;
display: flex;
.icon {
margin: 1px 12px 3px 0;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.title {
height: 22px;
color: rgba(95, 101, 108, 1);
......@@ -677,37 +694,45 @@ onMounted(() => {
text-align: left;
}
}
.right-box1-main {
width: 502px;
margin-top: 17px;
.right-box1-main-top {
margin-left: 16px;
width: 544px;
display: flex;
justify-content: space-between;
.user-list-container {
width: 440px; // 4个 user-box 的宽度 (110 * 4)
overflow: hidden;
.user-list-wrapper {
display: flex;
transition: transform 0.3s ease;
}
}
.user-box {
width: 110px;
flex-shrink: 0;
height: 80px;
.img-box {
width: 48px;
height: 48px;
position: relative;
margin: 0 auto;
img {
width: 100%;
height: 100%;
border-radius: 50%; // 圆形头像
object-fit: cover;
}
.icon1 {
position: absolute;
left: 5px;
......@@ -717,11 +742,13 @@ onMounted(() => {
border-radius: 10px;
padding: 2px;
background: rgba(255, 255, 255, 0.8);
img {
width: 100%;
height: 100%;
}
}
.icon2 {
position: absolute;
right: 5px;
......@@ -731,12 +758,14 @@ onMounted(() => {
border-radius: 10px;
padding: 2px;
background: rgba(255, 255, 255, 0.8);
img {
width: 100%;
height: 100%;
}
}
}
.name {
margin-top: 10px;
height: 14px;
......@@ -756,6 +785,7 @@ onMounted(() => {
}
}
}
.right-box1-main-bottom {
margin: 17px 16px 0 16px;
border-top: 1px solid rgba(243, 243, 244, 1);
......@@ -781,8 +811,10 @@ onMounted(() => {
// }
}
}
.right-box1-footer {
display: flex;
.left {
width: 70px;
height: 18px;
......@@ -794,6 +826,7 @@ onMounted(() => {
margin-left: 26px;
margin-top: 23px;
}
.right {
flex: 1;
display: flex;
......@@ -801,6 +834,7 @@ onMounted(() => {
margin-right: 20px;
margin-top: 20px;
justify-content: space-between;
.right-tag {
padding: 1px 8px 1px 8px;
// box-sizing: border-box;
......@@ -812,21 +846,25 @@ onMounted(() => {
font-size: 14px;
font-weight: 400;
}
.tag1 {
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
color: rgba(22, 119, 255, 1);
}
.tag2 {
border: 1px solid rgba(217, 247, 190, 1);
background: rgba(246, 255, 237, 1);
color: rgba(82, 196, 26, 1);
}
.tag3 {
border: 1px solid rgba(255, 204, 199, 1);
background: rgba(255, 241, 240, 1);
color: rgba(255, 77, 79, 1);
}
.tag4 {
border: 1px solid rgba(255, 241, 184, 1);
background: rgba(255, 251, 230, 1);
......@@ -835,22 +873,27 @@ onMounted(() => {
}
}
}
.right-box2 {
height: 423px;
.right-box2-header {
height: 22px;
margin-left: 22px;
padding-top: 10px;
display: flex;
.icon {
margin: 1px 12px 3px 0;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.title {
height: 22px;
color: rgba(95, 101, 108, 1);
......@@ -860,6 +903,7 @@ onMounted(() => {
line-height: 22px;
letter-spacing: 0px;
text-align: left;
.title-active {
color: var(--color-main-active);
font-family: Microsoft YaHei;
......@@ -868,6 +912,7 @@ onMounted(() => {
}
}
}
.right-box2-center {
height: 345px;
overflow: auto;
......@@ -875,6 +920,7 @@ onMounted(() => {
margin-top: 19px;
margin-left: 16px;
width: 544px;
.user-box {
cursor: pointer;
width: 538px;
......@@ -883,22 +929,27 @@ onMounted(() => {
border-bottom: 1px solid rgba(241, 243, 246, 1);
margin-bottom: 0;
display: flex;
&:last-child {
border-bottom: none;
}
.user-left {
margin-left: 14px;
margin-top: 12px;
.img-box {
width: 56px;
height: 56px;
position: relative;
img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}
.icon1 {
position: absolute;
left: -2px;
......@@ -912,12 +963,14 @@ onMounted(() => {
display: flex;
align-items: center;
justify-content: center;
img {
width: 14px;
height: 14px;
border-radius: 0;
}
}
.icon2 {
position: absolute;
right: -2px;
......@@ -931,6 +984,7 @@ onMounted(() => {
display: flex;
align-items: center;
justify-content: center;
img {
width: 14px;
height: 14px;
......@@ -939,12 +993,15 @@ onMounted(() => {
}
}
}
.user-right {
margin-left: 16px;
flex: 1;
:deep(.text-ellipsis) {
white-space: normal;
}
.name {
margin-top: 10px;
height: 24px;
......@@ -954,6 +1011,7 @@ onMounted(() => {
font-size: 16px;
font-weight: 700;
}
.content {
margin-top: 4px;
color: #666;
......@@ -972,6 +1030,7 @@ onMounted(() => {
}
}
}
// .right-box2-footer {
// margin-top: 7px;
// display: flex;
......@@ -994,15 +1053,18 @@ onMounted(() => {
:deep(.el-steps--simple) {
padding: 6px 10px;
}
:deep(.el-timeline-item) {
padding-bottom: 5px !important;
}
:deep(.el-timeline-item__timestamp) {
color: rgba(95, 101, 108, 1) !important;
font-family: Microsoft YaHei !important;
font-size: 14px !important;
font-weight: 600 !important;
}
.timeline-content {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
......
......@@ -1524,24 +1524,26 @@ watch(box8selectetedTime, () => {
});
const handleToPosi = id => {
let top = 0;
switch (id) {
case "position2":
top = isShow.value ? 634 : 980;
break;
case "position3":
top = isShow.value ? 1204 : 1550;
break;
case "position4":
top = isShow.value ? 2334 : 2680;
break;
default:
top = 0;
const element = document.getElementById(id);
if (element && containerRef.value) {
// 1. 如果是从完整搜索框跳转,先强制切换状态稳定布局
if (!isShow.value) {
isShow.value = true;
}
// 2. 使用 nextTick 等待 DOM 布局(如高度切换)完成后再进行坐标计算
nextTick(() => {
const containerRect = containerRef.value.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
// 使用 getBoundingClientRect 计算元素相对于容器顶部的绝对距离
const top = elementRect.top - containerRect.top + containerRef.value.scrollTop;
containerRef.value.scrollTo({
top: top,
behavior: "smooth"
});
});
}
};
const tabList = ref([
......@@ -1838,6 +1840,7 @@ onUnmounted(() => {});
overflow-y: auto;
}
.home-box {
position: relative;
width: 100%;
height: 100%;
background: url("./assets/images/background.png");
......
......@@ -116,62 +116,6 @@ const handleClickLeftSiderBtn = (item) => {
}
})
}
const progressList = ref([
{
tip: true,
date: "7月5日",
title:
"特朗普于美国独立日签署法案,​公法编号Pub. L. No. 119-21。白宫举行庆典,B-2轰炸机飞越上空,象征“美国新时代”开启。",
degree: "特别重大风险",
rank: 4,
},
{
tip: true,
date: "7月4日",
title:
"众议院最终表决218票赞成 vs 214票反对,修订版法案以4票优势通过,2名共和党议员倒戈,民主党全员反对。",
degree: "重大风险",
rank: 3,
},
{
tip: true,
date: "7月3日",
title:
"民主党领袖杰弗里斯发表 ​8小时45分钟​ 演讲(众议院现代史最长),抗议法案“劫贫济富”,但仍未阻止表决。",
degree: "较大风险",
rank: 2,
},
{
tip: true,
date: "7月2日",
title:
"众议院以 ​219:213​ 通过程序规则,为最终表决铺路。此前4名共和党议员反对程序规则,议长约翰逊紧急游说挽回1票。",
degree: "低风险",
rank: 1,
},
{
tip: false,
date: "7月1日",
title:
"参议院最终表决投票51:50​,副总统万斯(JD Vance)投出关键票打破平局。3名共和党参议员倒戈(蒂利斯、保罗、柯林斯)。",
degree: "",
},
]);
const wordCloudData = [
{ name: "财政保守主义", value: 100 },
{ name: "移民与边境安全", value: 5 },
{ name: "削减市民福利", value: 77 },
{ name: "债务驱动型经济", value: 35 },
{ name: "传统能源", value: 96 },
{ name: "与马斯克公开冲突", value: 57 },
{ name: "共和党财政鹰派", value: 72 },
{ name: "财政问责法案", value: 18 },
{ name: "强硬边境政策", value: 34 },
{ name: "扩大军费", value: 16 },
{ name: "债务与赤字警告", value: 72 },
{ name: "批评美国债务膨胀", value: 58 },
];
onMounted(() => {});
</script>
......
......@@ -335,6 +335,14 @@ onMounted(() => {
margin-bottom: 12px;
display: flex;
align-items: flex-start;
&::before {
content: "·";
font-size: 24px;
line-height: 20px;
font-weight: bold;
margin-right: 16px;
color: rgb(59, 65, 75);
}
.ul-title {
flex-shrink: 0;
width: 120px;
......@@ -413,6 +421,16 @@ onMounted(() => {
line-height: 24px;
color: rgb(59, 65, 75);
margin-bottom: 10px;
display: flex;
align-items: center;
&::before {
content: "·";
font-size: 24px;
line-height: 20px;
font-weight: bold;
margin-right: 12px;
color: rgb(59, 65, 75);
}
}
}
.left-bottom-content {
......
<template>
<div class="reslib-page">
<div class="reslib-page" ref="reslibContainer">
<div class="nav">
<div
v-for="item in navList"
:key="item"
:key="item.id"
class="nav-item"
:class="{ active: item === activeItem }"
@click="activeItem = item"
:class="{ active: item.id === activeItem }"
@click="activeItem = item.id"
>
{{ item }}
{{ item.name }}
</div>
</div>
<el-select v-model="value" placeholder="Select" class="select">
<el-select v-model="value" placeholder="排序方式" class="select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<div class="main">
......@@ -21,13 +21,21 @@
<div class="left-title">数据来源</div>
<div class="left-content">
<div v-for="item in dataList" :key="item.id" class="left-item">
<input type="checkbox" checked />{{ item.name }}
<input
type="checkbox"
:value="String(item.id)"
v-model="selectedSources"
/>{{ item.name }}
</div>
</div>
<div class="left-title cl1">涉及领域</div>
<div class="left-content">
<div v-for="(item, i) in dataList2" :key="item.id" class="left-item">
<input type="checkbox" :checked="i === 0" />{{ item.name }}
<div v-for="item in dataList2" :key="item.id" class="left-item">
<input
type="checkbox"
:value="String(item.id)"
v-model="selectedDomains"
/>{{ item.name }}
</div>
</div>
</div>
......@@ -47,15 +55,14 @@
<div class="domain">
<div v-for="(domain, i) in item.domain" :key="i" class="domain-item">{{ domain }}</div>
</div>
<div class="type" :class="{ type1: item.type === '行政令', type2: item.type === '法案' }">
<div class="type" :class="getTypeClass(item.type)">
{{ item.type }}
</div>
</div>
</div>
</div>
<div class="line"></div>
<div class="page">
<div class="count">共1205项调查</div>
<div class="count">{{ total }}项调查</div>
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
......@@ -72,202 +79,245 @@
</template>
<script setup>
import { ref } from "vue";
import { ref, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { getCoopRestrictionList } from "@/api/coopRestriction/coopRestriction";
import defaultImg from "../../assets/images/default-icon2.png";
// 合作限制-获取合作限制列表接口
const getMainDataList = async () => {
const params = {
currentPage: currentPage.value - 1, // 后端通常从0开始计页
pageSize: pageSize.value,
sortField: "date"
};
if (!selectedDomains.value.includes("0")) {
params.area = selectedDomains.value.join(",");
}
if (!selectedSources.value.includes("0")) {
params.source = selectedSources.value.join(",");
}
if (activeItem.value !== "0") {
params.type = activeItem.value;
}
if (value.value) {
params.sortOrder = value.value;
}
try {
const res = await getCoopRestrictionList(params);
if (res && res.code === 200) {
mainDataList.value = (res.data.content || []).map(item => ({
id: item.limitId,
title: item.limitName,
content: item.limitIntroduction,
date: item.limitDate,
domain: item.limitArea || [],
type: item.limitMeans,
// 使用默认图片
img: defaultImg
}));
total.value = res.data.totalElements || 0;
} else {
mainDataList.value = [];
total.value = 0;
}
} catch (error) {
console.error("获取合作限制列表失败:", error);
mainDataList.value = [];
total.value = 0;
}
};
import whitehouse from "./assets/白宫.png";
import guohui from "./assets/国会.png";
import guotuanquanbu from "./assets/国土安全部.png";
import guowuyuan from "./assets/国务院.png";
import weishengyanjiuyuan from "./assets/卫生研究院.png";
import zhongyiyuan from "./assets/众议院.png";
import fda from "./assets/FDA.png";
const router = useRouter();
const handleClick = item => {
router.push({
const routeData = router.resolve({
path: "/cooperationRestrictions/detail",
query: {
id: item.id
}
});
window.open(routeData.href, "_blank");
};
const navList = ref(["全部限制", "学术交流限制", "科研合作限制", "人才流动限制", "数据共享限制"]);
const activeItem = ref("全部限制");
const navList = ref([
{ id: "0", name: "全部限制" },
{ id: "01", name: "学术交流限制" },
{ id: "02", name: "科研合作限制" },
{ id: "03", name: "人才流动限制" },
{ id: "04", name: "数据共享限制" }
]);
const activeItem = ref("0");
const value = ref("发布时间");
const value = ref("");
const options = [
{
value: "发布时间",
label: "发布时间"
value: "asc",
label: "正序"
},
{
value: "发布日期",
label: "发布日期"
value: "desc",
label: "倒序"
}
];
const dataList = ref([
{
id: 1,
id: "0",
name: "全部来源"
},
{
id: 2,
id: "02",
name: "法案"
},
{
id: 3,
name: "政令"
id: "01",
name: "政令"
},
{
id: 4,
id: "03",
name: "政府公告"
}
]);
const selectedSources = ref(["0"]);
const dataList2 = ref([
{
id: 1,
id: 0,
name: "全部领域"
},
{
id: 2,
id: 1,
name: "人工智能"
},
{
id: 2,
name: "生物科技"
},
{
id: 3,
name: "集成电路"
name: "新一代信息技术"
},
{
id: 4,
name: "通信网络"
name: "量子科技"
},
{
id: 5,
name: "量子科技"
name: "新能源"
},
{
id: 6,
name: "能源"
name: "集成电路"
},
{
id: 7,
name: "生物科技"
name: "海洋"
},
{
id: 8,
name: "航空航天"
name: "先进制造"
},
{
id: 9,
name: "海洋"
}
]);
const mainDataList = ref([
{
id: 1,
title: "美国国土安全部:取消哈佛大学SEVP认证,禁止其招收国际新生",
content:
"国土安全部长诺姆以安全审查为由,正式终止哈佛大学“学生与交流访问学者项目”(SEVP)认证,导致该校丧失2025~2026学年招收国际新生的资格。",
date: "2025年10月15日",
domain: ["人才流动限制"],
type: "行政令",
img: guotuanquanbu
},
{
id: 2,
title: "美国白宫:通过应对哈佛大学的风险来增强国家安全",
content:
"学生交换签证计划(SEVP)根本依赖于学术机构的诚信、透明度以及对相关监管框架的完全遵守。 这是出于关键的国家安全原因。 联邦调查局(FBI)长期警告称,外国对手和竞争对手利用美国高等教育的便捷通道,窃取技术信息和产品,利用昂贵的研发项目来实现自身野心,并出于政治或其他原因传...",
date: "2025年9月30日",
domain: ["人才流动限制"],
type: "行政令",
img: whitehouse
},
{
id: 3,
title: "美国白宫:限制外国公民入境,以保护美国免受外国恐怖分子及其他国家安全和公共安全威胁",
content: "美国商务部宣称将“至少每年”更新一次管制规则,以堵塞已发现的政策“漏洞”。",
date: "2025年9月15日",
domain: ["人才流动限制"],
type: "行政令",
img: whitehouse
},
{
id: 4,
title: "美国白宫:提升生物研究的安全性与保障",
content: "停止联邦资助外国实体在关注国家(如中国)进行的危险功能增益研究。",
date: "2025年9月15日",
domain: ["人才流动限制", "生物科技"],
type: "行政令",
img: whitehouse
name: "新材料"
},
{
id: 5,
title: "美国国会:《2026财年拨款法案》审议通过重启“中国行动计划”",
content: "美国众议院拨款委员会在审议《2026财年拨款法案》时,加入条款要求司法部重启已被拜登政府叫停的“中国行动计划”。",
date: "2025年9月1日",
domain: ["人才流动限制"],
type: "法案",
img: guohui
id: 10,
name: "航空航天"
},
{
id: 6,
title: "美国FDA:紧急叫停涉及将美国公民活体细胞送往中国等“敌对国家”实验室的临床试验",
content: "涉及将人类生物样本(如活体细胞)转移至中国进行基因工程等研究的临床试验项目,相关研究被叫停。",
date: "2025年8月26日",
domain: ["数据共享限制", "生物科技"],
type: "行政令",
img: fda
id: 11,
name: "深海"
},
{
id: 7,
title: "美国众议院“美中战略竞争特设委员会”:要求杜克大学终止昆山杜克大学合作项目",
content: "指控其“通过先进摄像系统研究协助中国导弹制导技术发展”,并质疑美国学生被用于中国宣传。",
date: "2025年8月22日",
domain: ["科研合作限制"],
type: "行政令",
img: zhongyiyuan
id: 12,
name: "极地"
},
{
id: 8,
title: "美国国务院:大规模撤销部分中国学生签证,重点审查STEM领域",
content:
"美国国务卿鲁比奥宣布将开始大规模撤销部分中国学生的签证,重点针对STEM(科学、技术、工程和数学)领域的学习者以及所谓“与中国政府有联系”的人员,并全面提升对中国大陆和香港地区申请者的签证审查标准",
date: "2025年8月15日",
domain: ["人才流动限制"],
type: "行政令",
img: guowuyuan
id: 13,
name: "太空"
},
{
id: 9,
title: "美国FDA:针对两家中国第三方检测机构的数据完整性问题采取行动",
content:
"FDA因发现数据伪造或无效问题,向两家中国第三方检测公司(天津中联科技检测有限公司和苏州大学卫生与环境技术研究所)正式发出“一般信函”。",
date: "2025年10月15日",
domain: ["数据共享限制", "生物科技"],
type: "行政令",
img: fda
},
{
id: 10,
title: "美国国家卫生研究院:限制中国等国的研究人员访问其受控访问数据存储库",
content: "美国商务部宣称将“至少每年”更新一次管制规则,以堵塞已发现的政策“漏洞”。",
date: "2025年9月15日",
domain: ["数据共享限制", "生物科技"],
type: "行政令",
img: weishengyanjiuyuan
id: 14,
name: "核"
}
]);
const selectedDomains = ref(["0"]);
const mainDataList = ref([]);
const total = ref(0);
const pageSize = ref(10);
const currentPage = ref(1);
const reslibContainer = ref(null);
const getTypeClass = (type) => {
const map = {
'行政令': 'type1',
'法案': 'type2',
'政府公告': 'type3',
'政令': 'type4'
};
return map[type] || 'type-default';
};
const total = ref(1205);
const pageSize = ref(121);
const currentPage = ref(5);
const handlePageChange = p => {
currentPage.value = p;
// 翻页后平滑滚动到资源库顶部
if (reslibContainer.value) {
reslibContainer.value.scrollIntoView({ behavior: "smooth" });
}
};
onMounted(() => {
getMainDataList();
});
watch(
[activeItem, selectedSources, selectedDomains, value],
(newVal, oldVal) => {
const [newActive, newSources, newDomains] = newVal;
const [, oldSources, oldDomains] = oldVal;
if (newSources.includes("0") && newSources.length > 1) {
if (oldSources.includes("0")) {
selectedSources.value = newSources.filter(i => i !== "0");
return;
} else {
selectedSources.value = ["0"];
return;
}
} else if (newSources.length === 0) {
selectedSources.value = ["0"];
return;
}
if (newDomains.includes("0") && newDomains.length > 1) {
if (oldDomains.includes("0")) {
selectedDomains.value = newDomains.filter(i => i !== "0");
return;
} else {
selectedDomains.value = ["0"];
return;
}
} else if (newDomains.length === 0) {
selectedDomains.value = ["0"];
return;
}
if (currentPage.value === 1) {
getMainDataList();
} else {
currentPage.value = 1;
}
},
{ deep: true }
);
watch(currentPage, () => {
getMainDataList();
});
</script>
<style scoped lang="scss">
......@@ -277,8 +327,10 @@ const handlePageChange = p => {
}
.reslib-page {
width: 1600px;
height: 1565px;
min-height: 1565px;
height: auto;
position: relative;
padding-bottom: 50px;
.nav {
width: 808px;
height: 42px;
......@@ -310,11 +362,12 @@ const handlePageChange = p => {
}
.main {
width: 1600px;
height: 1489px;
height: auto;
min-height: 1489px;
display: flex;
.left {
width: 300px;
height: 584px;
height: 760px;
margin-right: 16px;
border-radius: 10px;
background-color: #fff;
......@@ -350,12 +403,12 @@ const handlePageChange = p => {
margin-top: 13px;
}
.left-content {
width: 109px;
height: 132px;
// width: 109px;
// height: 132px;
margin-left: 25px;
margin-top: 13px;
.left-item {
width: 89px;
// width: 89px;
height: 30px;
margin-bottom: 4px;
font-size: 16px;
......@@ -397,7 +450,8 @@ const handlePageChange = p => {
}
.right {
width: 1284px;
height: 1489px;
height: auto;
min-height: 1489px;
border-radius: 10px;
background-color: #fff;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
......@@ -427,46 +481,58 @@ const handlePageChange = p => {
}
.right-main {
width: 1284px;
height: 1441px;
padding: 22px 43px 15px 20px;
height: auto;
min-height: 1441px;
padding: 22px 43px 80px 20px;
position: relative;
.main-content {
width: 1221px;
height: 1345px;
height: auto;
min-height: 1345px;
.main-item {
width: 1221px;
height: auto;
min-height: 116px;
max-height: 140px;
margin-bottom: 16px;
margin-bottom: 24px;
position: relative;
.date {
display: flex;
&::after {
content: "";
position: absolute;
top: 4px;
left: 10px;
top: 37px;
bottom: -37px;
left: 91px;
width: 2px;
background-color: rgb(230, 231, 232);
z-index: 1;
}
&:last-child::after {
display: none;
}
.date {
flex-shrink: 0;
width: 62px;
height: 68px;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(5, 95, 194);
text-align: right;
margin-top: 13px;
}
.img {
flex-shrink: 0;
width: 24px;
height: 24px;
position: absolute;
top: 13px;
left: 90px;
margin: 13px 21px 0 18px;
position: relative;
z-index: 100;
}
.box {
width: 1086px;
flex: 1;
min-height: 91px;
max-height: 114px;
position: absolute;
top: 10px;
left: 135px;
position: relative;
padding-top: 10px;
.title {
font-size: 20px;
font-weight: 700;
......@@ -493,7 +559,7 @@ const handlePageChange = p => {
font-family: "Microsoft YaHei";
line-height: 24px;
position: absolute;
top: 0px;
top: 10px;
right: 0px;
}
.type1 {
......@@ -504,6 +570,18 @@ const handlePageChange = p => {
background-color: rgba(206, 79, 81, 0.1);
color: rgb(206, 79, 81);
}
.type3 {
background-color: rgba(5, 95, 194, 0.1);
color: rgb(5, 95, 194);
}
.type4 {
background-color: rgba(103, 194, 58, 0.1);
color: rgb(103, 194, 58);
}
.type-default {
background-color: rgba(144, 147, 153, 0.1);
color: rgb(144, 147, 153);
}
.domain {
margin-bottom: 15px;
display: flex;
......@@ -522,14 +600,6 @@ const handlePageChange = p => {
}
}
}
.line {
height: 1150px;
border: 2px solid rgb(235, 238, 242);
position: absolute;
top: 75px;
left: 120px;
z-index: 1;
}
.page {
width: 1221px;
height: 40px;
......
<template>
<div class="coop-page">
<!-- 吸顶简易搜索框 -->
<div class="search-header" v-show="isShow">
<div class="home-main-header-center">
<input v-model="input" class="search-input" placeholder="搜索合作限制" />
<div class="search-btn-small" @click="handleSearch">
<div class="search-icon">
<img src="./assets/icons/search-icon.png" alt="" />
</div>
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-btn-box">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">最新动态</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">数据总览</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">资源库</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
<!-- 面包屑 -->
<div class="breadcrumb">
<div class="breadcrumb" v-show="!isShow">
<div class="breadcrumb-box">
<div class="breadcrumb-item">国家科技安全</div>
<div class="breadcrumb-item">&nbsp;>&nbsp;</div>
......@@ -11,9 +50,9 @@
</div>
</div>
<!-- 主页面 -->
<div class="main-content">
<div class="main-content" ref="homeMainRef" :class="{ 'scroll-main': isShow }">
<!-- 搜索栏部分 -->
<div class="search">
<div class="search" v-show="!isShow">
<div class="search-main">
<input v-model="input" placeholder="搜索合作限制" class="search-input" />
<div class="search-btn">
......@@ -36,25 +75,25 @@
</div>
</div> -->
<div class="search-bottom">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">最新动态</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position2')">
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position3')">
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">数据总览</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position4')">
<div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">资源库</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
......@@ -95,21 +134,52 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted, nextTick } from "vue";
import { useRouter } from "vue-router";
import comTitle from "./common/comTitle.vue";
import newData from "./components/dataNew/index.vue";
import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue";
import scrollToTop from "@/utils/scrollToTop";
import { useContainerScroll } from "@/hooks/useScrollShow";
// 搜索框
const input = ref("");
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
const router = useRouter();
// 搜索功能
const handleSearch = () => {
console.log("搜索内容:", input.value);
};
// 锚点跳转
const handleToPosi = id => {
const element = document.getElementById(id);
if (element && homeMainRef.value) {
// 如果当前还未显示吸顶搜索栏,先强制切换状态以稳定布局
if (!isShow.value) {
isShow.value = true;
}
// 使用 nextTick 确保 DOM 状态更新(高度变化生效)后再计算
nextTick(() => {
const containerRect = homeMainRef.value.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
// 使用 getBoundingClientRect 计算元素相对于容器顶部的绝对距离,不受嵌套布局影响
const top = elementRect.top - containerRect.top + homeMainRef.value.scrollTop;
homeMainRef.value.scrollTo({
top: top,
behavior: "smooth"
});
});
}
};
// 返回首页
const handleBackHome = () => {
router.push({
......@@ -152,6 +222,7 @@ const handleBackHome = () => {
}
}
.main-content {
position: relative;
overflow: auto;
width: 100%;
height: calc(100% - 64px);
......@@ -188,7 +259,11 @@ const handleBackHome = () => {
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(132, 136, 142);
color: rgba(59, 65, 75, 1);
&::placeholder {
color: #a8abb2;
}
}
.search-btn {
margin-right: -3px;
......@@ -335,5 +410,132 @@ const handleBackHome = () => {
}
}
}
.search-header {
width: 100%;
height: 144px;
background: #fff;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
overflow: hidden;
.home-main-header-center {
margin-top: 20px;
margin-left: 200px;
width: 800px;
height: 48px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
padding: 1px;
position: relative;
border: 1px solid transparent;
display: flex;
align-items: center;
&:hover {
border: 1px solid var(--color-main-active);
}
.search-input {
border: none;
outline: none;
width: 800px;
height: 46px;
background-color: transparent;
font-size: 16px;
padding: 12px 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(132, 136, 142);
}
.search-btn-small {
position: absolute;
right: -1px;
top: 0px;
width: 120px;
height: 46px;
border-radius: 10px;
background: var(--color-main-active);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
.search-icon {
width: 18px;
height: 18px;
img {
width: 100%;
height: 100%;
}
}
.search-text {
margin-left: 8px;
height: 22px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
}
}
}
.home-main-header-btn-box {
margin-top: 20px;
margin-left: 200px;
display: flex;
gap: 16px;
.btn {
display: flex;
align-items: center;
gap: 9px;
width: 160px;
height: 48px;
border: 1px solid #aed6ff;
box-sizing: border-box;
border-radius: 24px;
background: #e7f3ff;
cursor: pointer;
position: relative;
&:hover {
background: #cae3fc;
}
.btn-text {
width: 80px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 48px;
margin-left: 36px;
text-align: center;
}
.btn-icon {
position: absolute;
top: 16px;
right: 19px;
width: 6px;
height: 12px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.scroll-main {
height: calc(100% - 144px) !important;
}
}
</style>
......@@ -627,8 +627,7 @@
</template>
<script setup>
import { onMounted, ref, computed, reactive, shallowRef, watch } from "vue";
import scrollToTop from "@/utils/scrollToTop";
import { onMounted, ref, computed, reactive, shallowRef, watch, nextTick } from "vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
......@@ -686,13 +685,23 @@ import {
const handleToPosi = id => {
const element = document.getElementById(id);
if (element && homeMainRef.value) {
// 1. 如果是从完整搜索框跳转,先强制切换状态稳定布局
if (!isShow.value) {
isShow.value = true;
}
// 2. 使用 nextTick 等待 DOM 布局(如高度切换)完成后再进行坐标计算
nextTick(() => {
const containerRect = homeMainRef.value.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
// 使用 getBoundingClientRect 计算元素相对于容器顶部的绝对距离,不受嵌套布局影响
const top = elementRect.top - containerRect.top + homeMainRef.value.scrollTop;
homeMainRef.value.scrollTo({
top: top,
behavior: "smooth"
});
});
}
};
......@@ -2159,6 +2168,7 @@ const handleMediaClick = item => {
overflow-y: hidden;
.home-main {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
......
......@@ -103,36 +103,37 @@
</div>
</div>
<div class="right-title">
<div class="filter-row">
<!-- <div class="filter-left">
<el-select v-model="filterEntity" placeholder="受制裁实体" style="width: 184px">
<el-option label="受制裁实体" value="1" />
</el-select>
</div> -->
<div class="filter-right">
<el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select v-model="filterField" placeholder="全部领域" style="width: 150px; margin: 0 12px 0 16px">
<el-option label="全部领域" value="" />
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input v-model="searchKeyword" placeholder="搜索实体" style="width: 150px" :suffix-icon="Search" />
</div>
</div>
<div class="stats-row">
<!-- <div class="tabs">
<div class="tabs">
<div class="tab-btn" :class="{ active: activeTab === 'add' }" @click="activeTab = 'add'">新增实体</div>
<div class="tab-btn" :class="{ active: activeTab === 'remove' }" @click="activeTab = 'remove'">移除实体</div>
</div> -->
</div>
<div class="stats-info">
<div class="stat-item">
<span class="dot red"></span>
<span class="text">新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
addRuleCount }}</span>家)</span>
</div>
<!-- <div class="stat-item">
<div class="stat-item">
<span class="dot green"></span>
<span class="text">移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span class="num green">{{ removeRuleCount }}</span>家)</span>
</div> -->
</div>
</div>
</div>
<div class="filter-row">
<div class="filter-left">
<!-- <el-select v-model="filterEntity" placeholder="受制裁实体" style="width: 184px">
<el-option label="受制裁实体" value="1" />
</el-select> -->
</div>
<div class="filter-right">
<el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select v-model="filterField" placeholder="全部领域" style="width: 150px; margin: 0 12px 0 16px">
<el-option label="全部领域" value="" />
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-input v-model="searchKeyword" placeholder="搜索实体" style="width: 150px" :suffix-icon="Search" />
</div>
</div>
</div>
......@@ -185,6 +186,7 @@
<script setup>
import { ref, defineProps, computed, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { DArrowRight, Search } from "@element-plus/icons-vue";
import { debounce } from "lodash";
import title from "../../assets/title.png"
......@@ -195,14 +197,10 @@ import RuleSubsidiaryDialog from "../../../v2.0EntityList/components/sanctionsOv
// 跳转公司详情页
const handleCompClick = item => {
// console.log("item", item);
// const route = router.resolve({
// path: "/companyPages",
// query: {
// id: item.entityId
// }
// });
// window.open(route.href, "_blank");
if (!item.entityId) {
ElMessage.warning("暂无数据");
return;
}
const curRoute = router.resolve({ name: "companyPages", params: { id: item.entityId } });
window.open(curRoute.href, "_blank");
};
......@@ -817,10 +815,9 @@ onMounted(() => {
.filter-row {
display: flex;
// 隐藏使用right,解除使用space-between
justify-content: right;
align-items: center;
margin-bottom: 20px;
// margin-bottom: 20px;
:deep(.el-input__inner) {
font-size: 16px;
......@@ -850,8 +847,8 @@ onMounted(() => {
.stats-row {
display: flex;
// 隐藏使用right,解除使用space-between
justify-content: right;
margin-bottom: 20px;
justify-content: space-between;
align-items: center;
.tabs {
......
......@@ -158,16 +158,16 @@
<div class="box2-main">
<div class="box2-main-item" v-for="(item, index) in warningList" :key="index">
<div class="item-left" :class="{
itemLeftStatus1: item.status === '一般风险',
itemLeftStatus2: item.status === '重大风险'
itemLeftStatus1: item.signalLevel === '一般风险',
itemLeftStatus2: item.signalLevel === '重大风险'
}">
{{ item.status }}
{{ item.signalLevel }}
</div>
<div class="item-right">
<div class="text">
{{ item.title }}
{{ item.signalTitle }}
</div>
<div class="time">{{ item.time }}</div>
<div class="time">{{ item.signalTime }}</div>
</div>
</div>
</div>
......@@ -194,14 +194,14 @@
<div class="box3-main">
<div class="box3-item" v-for="(news, index) in newsList" :key="index">
<div class="left">
<img :src="news.img" alt="" />
<img :src="news.newsImage" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="title">{{ news.title }}</div>
<div class="time">{{ news.from }}</div>
<div class="title">{{ news.newsTitle }}</div>
<div class="time">{{ news.newsDate + '.' + news.newsOrg }}</div>
</div>
<div class="right-footer">{{ news.content }}</div>
<div class="right-footer">{{ news.newsContent }}</div>
</div>
</div>
</div>
......@@ -216,14 +216,14 @@
<div class="box4-main">
<div class="box4-main-item" v-for="(item, index) in messageList" :key="index">
<div class="left">
<img :src="item.img" alt="" />
<img :src="item.personImage" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="name">{{ item.name }}</div>
<div class="time">{{ item.time }}</div>
<div class="name">{{ item.personName }}</div>
<div class="time">{{ item.time + '.' + item.orgName }}</div>
</div>
<div class="content">{{ item.content }}</div>
<div class="content">{{ item.remarks }}</div>
</div>
</div>
</div>
......@@ -240,7 +240,7 @@
</div>
</div>
<div class="select-box">
<el-select v-model="releaseTime" placeholder="2022" style="width: 120px"
<el-select v-model="releaseTime" pl aceholder="2022" style="width: 120px"
@change="handleGetOverallRanking">
<el-option v-for="item in releaseTimeList" :key="item.value" :label="item.label"
:value="item.value" />
......@@ -308,7 +308,8 @@
</div>
</div>
<div class="select-box">
<el-select v-model="releaseTime" placeholder="2022" style="width: 120px">
<el-select v-model="releaseTime" placeholder="2022" style="width: 120px"
@change="handleGetResearchField(), handleGetResearchFieldSubjectType()">
<el-option v-for="item in releaseTimeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
......@@ -324,7 +325,7 @@
</div>
<div class="center-footer-layout-content">
<div class="center-footer-layout-content-item">
<EChart :option="pieOption" autoresize :style="{ height: '370px', width: '750px' }" />
<EChart :option="pieOption(barOptionData)" autoresize :style="{ height: '370px', width: '750px' }" />
<div class="center-footer-elx-footer">
<img src="./assets/images/ai.png" alt="" />
<div class="center-footer-elx-footer-text">
......@@ -335,7 +336,8 @@
</div>
<div style="width: 1px;height: 432px;background: rgba(234, 236, 238, 1);"></div>
<div class="center-footer-layout-content-item">
<EChart :option="raderOption" autoresize :style="{ height: '370px', width: '750px' }" />
<EChart :option="raderOption(raderOptionData)" autoresize
:style="{ height: '370px', width: '750px' }" />
<div class="center-footer-elx-footer">
<img src="./assets/images/ai.png" alt="" />
<div class="center-footer-elx-footer-text">
......@@ -365,7 +367,8 @@
</div>
<div class="home-main-footer-main">
<div style=" width: 1600px;">
<div class="footer-main-item" v-for="item in universityList" :key="item.name" @click="handleClickToDetail">
<div class="footer-main-item" v-for="item in universityList" :key="item.name"
@click="handleClickToDetail(item.orgId)">
<img :src="item.logoUrl" style="height: 32px; width: 32px;" />
<div class="item-text">{{ item.orgName }}</div>
<div class="item-text2">{{ item.address }}</div>
......@@ -409,11 +412,16 @@ import getCalendarHeatChart from "./utils/cleandarHeat";
import EChart from "@/components/Chart/index.vue";
import { pieOption, raderOption } from "./utils/charts";
import {
getNews,
getSocialMediaInfo,
getBillRiskSignal,
getCountGeography,
getCountSubjectType,
getSubjectTypeList,
findListBySubjectTypeId,
getOverallRanking
getOverallRanking,
getResearchField,
getResearchFieldSubjectType
} from "@/api/innovationSubject/overview.js";
import setChart from "@/utils/setChart";
......@@ -555,9 +563,11 @@ const handleGetCountGeography = async () => {
}
};
// 点击查看详情
const handleClickToDetail = () => {
const route = router.resolve("/InnovativeInstitutions");
window.open(route.href, "_blank");
const handleClickToDetail = (university) => {
const curRoute = router.resolve({ name: "InnovativeInstitutions", params: { id: university } });
window.open(curRoute.href, "_blank");
// const route = router.resolve("/InnovativeInstitutions");
// window.open(route.href, "_blank");
};
// 查看更多风险信号
......@@ -610,7 +620,17 @@ const warningList = ref([
status: "重大风险"
}
]);
const handleGetBillRiskSignal = async () => {
try {
const res = await getBillRiskSignal();
console.log("风险信号", res);
if (res.code === 200 && res.data) {
warningList.value = res.data
}
} catch (error) {
console.error("获取风险信号error", error);
}
};
// 新闻资讯
const newsList = ref([
{
......@@ -644,6 +664,18 @@ const newsList = ref([
from: "11-2 · ​福克斯新闻网"
}
]);
const handleGetNews = async () => {
try {
const res = await getNews();
console.log("新闻资讯", res);
if (res.code === 200 && res.data) {
newsList.value = res.data
}
} catch (error) {
console.error("获取新闻资讯error", error);
}
};
// 社交媒体
const messageList = ref([
......@@ -666,7 +698,17 @@ const messageList = ref([
content: `提出特朗普政府的AI政策强调技术开放与快速应用,但可能以牺牲安全防范为代价,开启了“潘多拉魔盒”。`
}
]);
const handleGetSocialMediaInfo = async () => {
try {
const res = await getSocialMediaInfo();
console.log("社交媒体", res);
if (res.code === 200 && res.data) {
messageList.value = res.data
}
} catch (error) {
console.error("获取社交媒体error", error);
}
};
// 政令涉及领域
const chart1Data = ref([
......@@ -884,6 +926,43 @@ const releaseTimeList = ref([
]);
const categoryList = ref(["创新主体排名", "研究布局"]);
const activeCate = ref("创新主体排名");
//研究领域布局情况
const barOptionData = ref([])
const handleGetResearchField = async () => {
try {
let params = {
year: releaseTime.value,
}
const res = await getResearchField(params);
console.log("研究领域布局情况", res);
if (res.code === 200 && res.data) {
// 提取 names 和 values
const names = res.data.map(item => item.areaName);
const values = res.data.map(item => item.amount); // 或者使用 item.percent,根据需求选择
const total = res.data.reduce((sum, item) => sum + item.amount, 0);
barOptionData.value = {
names, values, total
}
}
} catch (error) {
console.error("获取研究领域布局情况error", error);
}
};
const raderOptionData = ref([])
const handleGetResearchFieldSubjectType = async () => {
try {
let params = {
year: releaseTime.value,
}
const res = await getResearchFieldSubjectType(params);
console.log("研究领域布局情况", res);
if (res.code === 200 && res.data) {
raderOptionData.value = res.data
}
} catch (error) {
console.error("获取研究领域布局情况error", error);
}
};
const areaList = ref([
{
......@@ -894,7 +973,6 @@ const areaList = ref([
const categoryList1 = ref(["研究型大学", "国家实验室", "科技企业", "国防承包商"]);
const activeCate1 = ref('');
//类型列表
const handleGetSubjectTypeList = async () => {
try {
......@@ -959,12 +1037,17 @@ const handleToPosi = id => {
};
onMounted(async () => {
handleGetNews()
handleGetSocialMediaInfo()
handleGetBillRiskSignal()
await handleGetSubjectTypeList()
handleGetCountGeography()
handleGetCountSubjectType()
handleGetOverallRanking()
handleFindListBySubjectTypeId()
handleGetResearchField()
handleGetResearchFieldSubjectType()
let chart1 = getPieChart(chart1Data.value, colorList);
setChart(chart1, "chart1");
});
......@@ -1621,7 +1704,7 @@ onMounted(async () => {
display: flex;
.text {
width: 348px;
width: 310px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
......
......@@ -2,14 +2,14 @@
<div class="wrap">
<div class="header">
<div class="header-left">
<img src="./assets/images/icon-harvard.png" alt="" />
<img :src="institutionInfo.logoUrl" alt="" />
</div>
<div class="header-right">
<div class="title">{{ institutionInfo.name }}</div>
<div class="en-title">{{ institutionInfo.enName }}</div>
<div class="desc">{{ institutionInfo.desc }}</div>
<div class="title">{{ institutionInfo.orgName }}</div>
<div class="en-title">{{ institutionInfo.orgNameEn }}</div>
<div class="desc">{{ institutionInfo.orgIntroduction }}</div>
<div class="tag-box">
<div class="tag" v-for="(tag, index) in institutionInfo.tagList" :key="index">
<div class="tag" v-for="(tag, index) in institutionInfo.taglist" :key="index">
{{ tag }}
</div>
</div>
......@@ -18,17 +18,12 @@
<div class="icon">
<img src="@/assets/images/links-icon.png" alt="" />
</div>
<div class="text">{{ "查看官网" }}</div>
<div class="text" @click="toURL(institutionInfo.url)">{{ "查看官网" }}</div>
</div>
</div>
<div class="tab-box">
<div
class="tab"
@click="handleClickTab(item, index)"
:class="{ tabActive: item.active }"
v-for="(item, index) in tabList"
:key="index"
>
<div class="tab" @click="handleClickTab(item, index)" :class="{ tabActive: item.active }"
v-for="(item, index) in tabList" :key="index">
{{ item.name }}
</div>
</div>
......@@ -47,6 +42,11 @@ import overView from "./overView/index.vue";
import researchStrength from "./researchStrength/index.vue";
import cooperationStatus from "./cooperationStatus/index.vue";
import otherStatus from "./otherStatus/index.vue";
import { useRouter } from "vue-router";
import {
getInfo
} from "@/api/innovationSubject/overview.js";
const router = useRouter();
const institutionInfo = ref({
name: "哈佛大学",
......@@ -55,6 +55,23 @@ const institutionInfo = ref({
tagList: ["常春藤", "精英摇篮"]
});
const handleGetInfo = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getInfo(params);
console.log("创新主体详情", res);
if (res.code === 200 && res.data) {
institutionInfo.value = res.data
}
} catch (error) {
console.error("获取创新主体详情error", error);
}
};
const toURL = (url) => {
}
const activeTabName = ref("学校详情");
const tabList = ref([
......@@ -83,6 +100,11 @@ const handleClickTab = (val, index) => {
tabList.value[index].active = true;
activeTabName.value = val.name;
};
onMounted(async () => {
handleGetInfo()
});
</script>
<style lang="scss" scoped>
......@@ -93,6 +115,7 @@ const handleClickTab = (val, index) => {
background-repeat: no-repeat;
background-size: 100% auto;
padding-top: 16px;
.header {
width: 1600px;
height: 200px;
......@@ -104,17 +127,21 @@ const handleClickTab = (val, index) => {
background: rgba(255, 255, 255, 0.8);
display: flex;
position: relative;
.header-left {
width: 160px;
height: 160px;
margin: 20px;
img {
width: 100%;
height: 100%;
}
}
.header-right {
margin-left: 24px;
.title {
margin-top: 26px;
height: 42px;
......@@ -126,6 +153,7 @@ const handleClickTab = (val, index) => {
letter-spacing: 0px;
text-align: left;
}
.en-title {
margin-top: 8px;
height: 24px;
......@@ -137,6 +165,7 @@ const handleClickTab = (val, index) => {
letter-spacing: 0px;
text-align: left;
}
.desc {
margin-top: 6px;
height: 24px;
......@@ -148,10 +177,12 @@ const handleClickTab = (val, index) => {
letter-spacing: 0px;
text-align: justify;
}
.tag-box {
margin-top: 14px;
display: flex;
gap: 8px;
.tag {
height: 24px;
padding: 0px 8px;
......@@ -166,6 +197,7 @@ const handleClickTab = (val, index) => {
}
}
}
.header-btn {
position: absolute;
top: 26px;
......@@ -179,14 +211,17 @@ const handleClickTab = (val, index) => {
justify-content: center;
align-items: center;
cursor: pointer;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
height: 22px;
color: rgba(255, 255, 255, 1);
......@@ -197,6 +232,7 @@ const handleClickTab = (val, index) => {
}
}
}
.tab-box {
width: 1600px;
height: 64px;
......@@ -209,6 +245,7 @@ const handleClickTab = (val, index) => {
display: flex;
justify-content: space-between;
align-items: center;
.tab {
width: 397px;
height: 54px;
......@@ -223,10 +260,12 @@ const handleClickTab = (val, index) => {
font-weight: 400;
letter-spacing: 0px;
cursor: pointer;
&:hover {
background: rgba(231, 243, 255, 1);
}
}
.tabActive {
border: 2px solid rgba(174, 214, 255, 1);
background: rgba(231, 243, 255, 1);
......@@ -235,6 +274,7 @@ const handleClickTab = (val, index) => {
font-weight: 700;
}
}
.main {
width: 1600px;
margin: 16px auto;
......
<!--学校详情-->
<template>
<div class="detail-wrap">
<div class="left box">
......@@ -5,9 +6,10 @@
<div class="header-left"></div>
<div class="title">最新动态</div>
<div class="header-right">
<el-checkbox checked="true" label="只看涉华动态" />
<div class="btnRightActive">机构动态</div>
<div class="btnRight">主官动态</div>
<el-checkbox :checked="cRelated" label="只看涉华动态" @change="handleGetDynamics()" />
<div :class="dynamicsType === 'org' ? 'btnRightActive' : 'btnRight'" @click="changedynamicsType()">机构动态</div>
<div :class="dynamicsType === 'person' ? 'btnRightActive' : 'btnRight'" @click="changedynamicsType()">主官动态
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
......@@ -18,7 +20,7 @@
</div>
<div class="left-main">
<div class="left-main-item" v-for="(item, index) in curList" :key="index">
<div class="line" ></div>
<div class="line"></div>
<div class="time">
<div class="timetext">{{ item.time }}</div>
<div class="timetext">{{ item.date }}</div>
......@@ -39,10 +41,11 @@
</div>
<div class="left-footer">
<div class="info">
{{ `共有153项动态` }}
{{ `共有` + total + `项动态` }}
</div>
<div class="page-box">
<el-pagination background layout="prev, pager, next" :total="153" />
<el-pagination background layout="prev, pager, next" :total="total" v-model:current-page="currentPage"
@current-change="handleGetDynamics" />
</div>
</div>
</div>
......@@ -62,24 +65,24 @@
</div>
<div class="right-main">
<div class="img-box">
<img :src="basicInfo.image" alt="" />
<img :src="basicInfo.logoUrl" alt="" />
</div>
<div class="info-box">
<div class="info-item">
<div class="info-item-left">{{ "成立时间:" }}</div>
<div class="info-item-right">{{ basicInfo.shijian }}</div>
<div class="info-item-right">{{ basicInfo.establishmentDate }}</div>
</div>
<div class="info-item">
<div class="info-item-left">{{ "总部地址:" }}</div>
<div class="info-item-right">{{ basicInfo.dizhi }}</div>
<div class="info-item-right">{{ basicInfo.address }}</div>
</div>
<div class="info-item">
<div class="info-item-left">{{ "组织性质:" }}</div>
<div class="info-item-right">{{ basicInfo.xingzhi }}</div>
<div class="info-item-right">{{ basicInfo.companyType }}</div>
</div>
<div class="info-item">
<div class="info-item-left">{{ "在校人数:" }}</div>
<div class="info-item-right">{{ basicInfo.stuNum }}</div>
<div class="info-item-right">{{ basicInfo.numberOfEmployees }}</div>
</div>
<div class="info-item">
<div class="info-item-left">{{ "教职工数:" }}</div>
......@@ -102,9 +105,9 @@
{{ "重点人物:" }}
</div>
<div class="user-content">
<div class="user-item" v-for="(item, index) in basicInfo.keyUser" :key="index">
<div class="user-item" v-for="(item, index) in personList" :key="index">
<div class="user-item-left">
<img :src="item.img" alt="" />
<img :src="item.avatarUrl" alt="" />
</div>
<div class="user-item-right">
<div class="name">{{ item.name }}</div>
......@@ -139,9 +142,9 @@
<div class="timeline"></div>
<div class="historyList">
<div class="historyPoint" v-for="item in historyList">
<div class="historytime">{{ item.time }}</div>
<div class="historytime">{{ item.createTime }}</div>
<img src="./assets/images/timepoint.png" alt="" style="z-index: 2;" />
<div class="historyitem">{{ item.event }}</div>
<div class="historyitem">{{ item.content }}</div>
</div>
</div>
</div>
......@@ -152,22 +155,32 @@
<script setup>
import { ref, computed, onMounted } from "vue";
import { useRouter } from "vue-router";
import {
getDynamics,
getInfo,
getEventList,
getPersonList
} from "@/api/innovationSubject/overview.js";
import Img from "./assets/images/img.png";
import User1 from "./assets/images/user1.png";
import User2 from "./assets/images/user2.png";
import User3 from "./assets/images/user3.png";
import User4 from "./assets/images/user4.png";
const router = useRouter();
//只看涉华动态
const cRelated = ref(false)
const dynamicsType = ref('org')
const basicInfo = ref({
image: Img,
shijian: "1636年",
dizhi: "剑桥市,马萨诸塞州,美国",
xingzhi:"私立研究型大学",
stuNum:"约22,000人",
teaNum:"约2,400人",
rate:"#1",
tag:["癌症精准医疗","气候变化解决方案","人工智能伦理","可持续发展能源"],
xingzhi: "私立研究型大学",
stuNum: "约22,000人",
teaNum: "约2,400人",
rate: "#1",
tag: ["癌症精准医疗", "气候变化解决方案", "人工智能伦理", "可持续发展能源"],
keyUser: [
{
name: "艾伦·M·加伯",
......@@ -290,51 +303,128 @@ const curList = ref([
}
]);
const changedynamicsType = () => {
dynamicsType.value === 'person' ? dynamicsType.value = 'org' : dynamicsType.value = 'person'
handleGetDynamics()
}
const handleGetInfo = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getInfo(params);
console.log("创新主体基本信息", res);
if (res.code === 200 && res.data) {
basicInfo.value = res.data
}
} catch (error) {
console.error("获取基本信息error", error);
}
};
const personList = ref([])
//重点人物
const handleGetPersonList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getPersonList(params);
console.log("重点人物", res);
if (res.code === 200 && res.data) {
personList.value = res.data
}
} catch (error) {
console.error("获取重点人物error", error);
}
};
const total = ref(0)
const currentPage = ref(1)
const handleGetDynamics = async () => {
try {
let params = {
currentPage: 1,
pageSize: 10,
cRelated: cRelated.value ? null : 'N',
dynamicsType: dynamicsType.value,
orgId: router.currentRoute._value.params.id
}
const res = await getDynamics(params);
console.log("创新主体最新动态", res);
if (res.code === 200 && res.data) {
curList.value = res.data.content
total.value = res.data.totalElements
}
} catch (error) {
console.error("获取最新动态error", error);
}
};
const awardsList = ref([
{
name:"诺贝尔奖",
num:161
name: "诺贝尔奖",
num: 161
},
{
name:"图灵奖",
num:18
name: "图灵奖",
num: 18
},
{
name:"菲尔兹奖",
num:14
name: "菲尔兹奖",
num: 14
},
{
name:"美国院士",
num:273
name: "美国院士",
num: 273
}
]);
const historyList = ref([
{
time:"2023年",
event:"克劳丁·盖伊成为首位非裔女校长"
time: "2023年",
event: "克劳丁·盖伊成为首位非裔女校长"
},
{
time:"1999年",
event:"拉德克利夫学院转型为“拉德克利夫高等研究院”"
time: "1999年",
event: "拉德克利夫学院转型为“拉德克利夫高等研究院”"
},
{
time:"1977年",
event:"哈佛与拉德克利夫学院正式合并招生"
time: "1977年",
event: "哈佛与拉德克利夫学院正式合并招生"
},
{
time:"1950年",
event:"逐步废除招生中的宗教、种族限制,学生群体多元化"
time: "1950年",
event: "逐步废除招生中的宗教、种族限制,学生群体多元化"
},
{
time:"1908年",
event:"哈佛商学院成立,首创MBA案例教学法"
time: "1908年",
event: "哈佛商学院成立,首创MBA案例教学法"
},
{
time:"1869年",
event:"查尔斯·W·艾略特任校长40年,推行选修制、重建法学院、改革医学院、新建商学院等"
time: "1869年",
event: "查尔斯·W·艾略特任校长40年,推行选修制、重建法学院、改革医学院、新建商学院等"
}
]);
const handleGetEventList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getEventList(params);
console.log("历史动态", res);
if (res.code === 200 && res.data) {
historyList.value = res.data
}
} catch (error) {
console.error("获取历史动态error", error);
}
};
onMounted(async () => {
handleGetInfo()
handleGetDynamics()
handleGetEventList()
handleGetPersonList()
});
</script>
<style lang="scss" scoped>
......@@ -342,16 +432,19 @@ const historyList = ref([
display: flex;
gap: 16px;
padding-bottom: 30px;
.box {
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
}
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 18px;
width: 8px;
......@@ -359,6 +452,7 @@ const historyList = ref([
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 14px;
margin-top: 14px;
......@@ -370,11 +464,13 @@ const historyList = ref([
letter-spacing: 0px;
text-align: left;
}
.header-btn-box {
position: absolute;
top: 14px;
right: 52px;
display: flex;
.btn {
margin-left: 8px;
height: 28px;
......@@ -391,12 +487,14 @@ const historyList = ref([
font-weight: 400;
cursor: pointer;
}
.btnActive {
border: 1px solid rgba(10, 87, 166, 1);
background: rgba(246, 250, 255, 1);
color: rgba(10, 87, 166, 1);
}
}
.header-info {
height: 22px;
position: absolute;
......@@ -404,16 +502,19 @@ const historyList = ref([
top: 17px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 3px;
width: 14px;
height: 14px;
margin-right: 8px;
img {
width: 100%;
height: 100%;
}
}
.text {
height: 22px;
color: rgba(95, 101, 108, 1);
......@@ -423,6 +524,7 @@ const historyList = ref([
line-height: 22px;
}
}
.header-right {
position: absolute;
top: 14px;
......@@ -430,15 +532,18 @@ const historyList = ref([
display: flex;
justify-content: flex-end;
gap: 8px;
.icon {
width: 28px;
height: 28px;
img {
width: 100%;
height: 100%;
}
}
.checkboxRight{
.checkboxRight {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
......@@ -447,7 +552,8 @@ const historyList = ref([
letter-spacing: 0px;
text-align: justify;
}
.btnRightActive{
.btnRightActive {
width: 80px;
height: 28px;
display: flex;
......@@ -466,7 +572,8 @@ const historyList = ref([
letter-spacing: 0px;
text-align: center;
}
.btnRight{
.btnRight {
width: 80px;
height: 28px;
display: flex;
......@@ -487,12 +594,15 @@ const historyList = ref([
}
}
}
.left {
width: 1064px;
height: 2083px;
.left-main {
border-top: 1px solid rgba(234, 236, 238, 1);
height: 1905px;
.left-main-item {
display: flex;
margin-top: 20px;
......@@ -500,6 +610,7 @@ const historyList = ref([
margin-left: 31px;
height: 130px;
position: relative;
.line {
background: #e6e7e8;
width: 2px;
......@@ -508,10 +619,12 @@ const historyList = ref([
top: 24px;
left: 106px;
}
.time {
display: flex;
flex-direction: column;
.timetext{
.timetext {
width: 79px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
......@@ -522,20 +635,25 @@ const historyList = ref([
text-align: right;
}
}
.icon {
width: 24px;
height: 24px;
img {
width: 100%;
height: 100%;
}
}
.info {
width: 862px;
.header {
height: 26px;
display: flex;
justify-content: space-between;
.title {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -544,6 +662,7 @@ const historyList = ref([
line-height: 26px;
}
}
.content {
margin-top: 8px;
color: rgba(95, 101, 108, 1);
......@@ -552,10 +671,12 @@ const historyList = ref([
font-weight: 400;
line-height: 24px;
}
.tag-box {
margin-top: 12px;
display: flex;
gap: 8px;
.tag {
height: 28px;
padding: 0 8px;
......@@ -571,6 +692,7 @@ const historyList = ref([
}
}
}
.left-footer {
height: 75px;
border-top: 1px solid rgba(234, 236, 238, 1);
......@@ -580,32 +702,40 @@ const historyList = ref([
padding: 0 20px;
}
}
.rightcontent{
.rightcontent {
display: flex;
flex-direction: column;
width: 520px;
gap: 16px;
.right {
width: 520px;
height: 874px;
.right-main {
width: 469px;
margin: 3px auto;
.img-box {
width: 469px;
height: 240px;
img {
width: 100%;
height: 100%;
}
}
.info-box {
margin-top: 26px;
padding-bottom: 50px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
.info-item {
display: flex;
margin-top: 12px;
.info-item-left {
width: 88px;
height: 24px;
......@@ -617,6 +747,7 @@ const historyList = ref([
letter-spacing: 1px;
text-align: left;
}
.info-item-right {
width: 356px;
color: rgba(59, 65, 75, 1);
......@@ -627,12 +758,14 @@ const historyList = ref([
letter-spacing: 0px;
text-align: left;
}
.taglist{
.taglist {
width: 358px;
display: flex;
gap: 8px;
flex-wrap: wrap;
.tagdirec{
.tagdirec {
height: 24px;
justify-content: center;
align-items: center;
......@@ -652,8 +785,10 @@ const historyList = ref([
}
}
}
.user-box {
padding-top: 19px;
.user-header {
height: 24px;
color: rgba(59, 65, 75, 1);
......@@ -664,24 +799,29 @@ const historyList = ref([
letter-spacing: 1px;
text-align: left;
}
.user-content {
margin-top: 19px;
display: flex;
flex-wrap: wrap;
gap: 16px 73px;
justify-content: start;
.user-item {
height: 49px;
display: flex;
gap: 8px;
.user-item-left {
width: 48px;
height: 48px;
img {
width: 100%;
height: 100%;
}
}
.user-item-right {
.name {
height: 24px;
......@@ -693,6 +833,7 @@ const historyList = ref([
letter-spacing: 0px;
text-align: left;
}
.position {
height: 24px;
color: rgba(95, 101, 108, 1);
......@@ -709,13 +850,14 @@ const historyList = ref([
}
}
}
.right-Num{
.right-Num {
display: flex;
flex-wrap: wrap;
width: 520px;
gap: 8px;
.numbox{
.numbox {
display: flex;
width: 256px;
height: 80px;
......@@ -725,12 +867,14 @@ const historyList = ref([
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
align-items: center;
.iconrec{
.iconrec {
width: 4px;
height: 49px;
background: rgba(5, 95, 194, 1);
}
.awards{
.awards {
margin-left: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -740,7 +884,8 @@ const historyList = ref([
letter-spacing: 0px;
text-align: left;
}
.awardsNum{
.awardsNum {
margin-left: 72px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
......@@ -752,7 +897,8 @@ const historyList = ref([
}
}
}
.right-history{
.right-history {
width: 520px;
height: 1009px;
border: 1px solid rgba(234, 236, 238, 1);
......@@ -760,18 +906,21 @@ const historyList = ref([
/* 业务系统/模块阴影 */
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.historytimeline{
.historytimeline {
display: flex;
margin-left: 37px;
margin-top: 29px;
.timeline{
.timeline {
position: absolute;
width: 8px;
height: 910px;
background: url("./assets/images/arrow.png") repeat;
}
.historyList{
.historyList {
margin-left: -2px;
display: flex;
......@@ -779,11 +928,12 @@ const historyList = ref([
margin-top: 42px;
gap: 56px;
.historyPoint{
.historyPoint {
width: 458px;
height: 89px;
gap: 6px;
.historytime{
.historytime {
margin-left: 24px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
......@@ -793,7 +943,8 @@ const historyList = ref([
letter-spacing: 1px;
text-align: left;
}
.historyitem{
.historyitem {
margin-left: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
......@@ -813,7 +964,7 @@ const historyList = ref([
::v-deep .el-checkbox__label{
::v-deep .el-checkbox__label {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
......@@ -821,6 +972,5 @@ const historyList = ref([
line-height: 24px;
padding-left: 3px;
padding-right: 10px;
}
}
</style>
\ No newline at end of file
<!--科研实力-->
<template>
<div class="detail-wrap">
<div class="row">
......@@ -15,7 +16,7 @@
</div>
</div>
<div class="statisticsChart">
<Echarts :option="barOption" height="100%"></Echarts>
<Echarts :option="barOption(patentList)" height="100%"></Echarts>
</div>
<div class="statisticsAI">
<div class="AIbox">
......@@ -39,7 +40,7 @@
</div>
</div>
<div class="statisticsChart">
<Echarts :option="lineChart" height="100%"></Echarts>
<Echarts :option="lineChart(paperList)" height="100%"></Echarts>
</div>
<div class="statisticsAI">
<div class="AIbox">
......@@ -89,7 +90,7 @@
</div>
</div>
<div class="statisticsChart">
<Echarts :option="lineChart1" height="100%"></Echarts>
<Echarts :option="lineChart1(fundGrowth)" height="100%"></Echarts>
</div>
<div class="statisticsAI">
<div class="AIbox">
......@@ -115,7 +116,7 @@
</div>
</div>
<div class="statisticsChart">
<Echarts :option="pieOption1" height="100%"></Echarts>
<Echarts :option="pieOption1(fundFromList)" height="100%"></Echarts>
</div>
<div class="statisticsAI">
<div class="AIbox">
......@@ -146,7 +147,7 @@
letter-spacing: 0px;
text-align: right;">(亿美元)</div>
<div class="statisticsChart" style="height: 298px; ">
<Echarts :option="horizontalBaroption" height="100%" ></Echarts>
<Echarts :option="horizontalBaroption(fundToList)" height="100%"></Echarts>
</div>
<div class="statisticsAI">
<div class="AIbox">
......@@ -164,8 +165,103 @@
import { ref, computed, onMounted } from "vue";
import Echarts from "@/components/Chart/index.vue";
import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalBaroption } from "../../utils/charts.js";
import {
getPatentList,
getPaperList, getFundGrowth, getFundFromList, getFundToList
} from "@/api/innovationSubject/overview.js";
import { useRouter } from "vue-router";
const router = useRouter();
//专利数量统计
const patentList = ref([])
const handleGetPatentList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getPatentList(params);
console.log("专利数量统计", res);
if (res.code === 200 && res.data) {
patentList.value = res.data
}
} catch (error) {
console.error("获取专利数量统计error", error);
}
};
//论文数量统计
const paperList = ref([])
const handleGetPaperList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getPaperList(params);
console.log("论文数量统计", res);
if (res.code === 200 && res.data) {
paperList.value = res.data
}
} catch (error) {
console.error("获取论文数量统计error", error);
}
};
//经费增长情况
const fundGrowth = ref([])
const handleGetFundGrowth = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getFundGrowth(params);
console.log("经费增长情况", res);
if (res.code === 200 && res.data) {
fundGrowth.value = res.data
}
} catch (error) {
console.error("获取经费增长情况error", error);
}
};
//经费来源
const fundFromList = ref([])
const handleGetFundFromList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getFundFromList(params);
console.log("经费来源", res);
if (res.code === 200 && res.data) {
fundFromList.value = res.data
}
} catch (error) {
console.error("获取经费来源error", error);
}
};
//学院经费分配排序
const fundToList = ref([])
const handlegGetFundToList = async () => {
try {
let params = {
id: router.currentRoute._value.params.id
}
const res = await getFundToList(params);
console.log("学院经费分配排序", res);
if (res.code === 200 && res.data) {
fundToList.value = res.data
}
} catch (error) {
console.error("获取学院经费分配排序error", error);
}
};
onMounted(async () => {
handleGetPatentList()
handleGetPaperList()
handleGetFundGrowth()
handleGetFundFromList()
handlegGetFundToList()
});
</script>
......@@ -175,16 +271,19 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
flex-direction: column;
gap: 16px;
padding-bottom: 30px;
.box {
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
}
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 18px;
width: 8px;
......@@ -192,6 +291,7 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 14px;
margin-top: 14px;
......@@ -203,11 +303,13 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
letter-spacing: 0px;
text-align: left;
}
.header-btn-box {
position: absolute;
top: 14px;
right: 52px;
display: flex;
.btn {
margin-left: 8px;
height: 28px;
......@@ -224,12 +326,14 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
font-weight: 400;
cursor: pointer;
}
.btnActive {
border: 1px solid rgba(10, 87, 166, 1);
background: rgba(246, 250, 255, 1);
color: rgba(10, 87, 166, 1);
}
}
.header-info {
height: 22px;
position: absolute;
......@@ -237,16 +341,19 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
top: 17px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 3px;
width: 14px;
height: 14px;
margin-right: 8px;
img {
width: 100%;
height: 100%;
}
}
.text {
height: 22px;
color: rgba(95, 101, 108, 1);
......@@ -256,6 +363,7 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
line-height: 22px;
}
}
.header-right {
position: absolute;
top: 14px;
......@@ -263,15 +371,18 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
display: flex;
justify-content: flex-end;
gap: 8px;
.icon {
width: 28px;
height: 28px;
img {
width: 100%;
height: 100%;
}
}
.checkboxRight{
.checkboxRight {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
......@@ -280,7 +391,8 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
letter-spacing: 0px;
text-align: justify;
}
.btnRightActive{
.btnRightActive {
width: 80px;
height: 28px;
display: flex;
......@@ -299,7 +411,8 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
letter-spacing: 0px;
text-align: center;
}
.btnRight{
.btnRight {
width: 80px;
height: 28px;
display: flex;
......@@ -320,20 +433,24 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
}
}
}
.row{
.row {
display: flex;
width: 1600px;
height: 500px;
gap: 16px;
.statisticsItem{
.statisticsItem {
width: 792px;
height: 500px;
.statisticsChart{
.statisticsChart {
width: 736px;
height: 321px;
margin: 20px auto;
}
.statisticsAI{
.statisticsAI {
margin: 0 auto;
width: 760px;
height: 64px;
......@@ -348,7 +465,8 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
.AIbox{
.AIbox {
width: 736px;
height: 52px;
......@@ -359,7 +477,8 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
align-items: center;
gap: 13px;
padding: 2px 0px 2px 0px;
.AItext{
.AItext {
width: 667px;
height: 48px;
display: flex;
......@@ -381,9 +500,4 @@ import { barOption, lineChart, raderOption1, lineChart1, pieOption1, horizontalB
}
}
</style>
\ No newline at end of file
import * as echarts from "echarts";
var data = [
{
name: "人工智能与信息技术",
value: 27
},
{
name: "生物医学与健康",
value: 22
},
{
name: "能源与环境技术",
value: 18
},
{
name: "先进制造与材料",
value: 15
},
{
name: "国家安全与国防",
value: 12
},
{
name: "航空航天",
value: 8
}
];
// 计算总和
function getTotal(data) {
return data.reduce((sum, item) => sum + item.value, 0);
return data.reduce((sum, item) => sum + item.value, 0);
};
export const pieOption = {
export const pieOption = (data) => {
console.log(data, 'datadatadata')
// 颜色数组
const colors = [
'rgba(105, 177, 255, 1)',
'rgba(255, 236, 61, 1)',
'rgba(135, 232, 222, 1)',
'rgba(133, 165, 255, 1)',
'rgba(255, 120, 117, 1)',
'rgba(179, 127, 235, 1)',
'rgba(255, 187, 120, 1)',
'rgba(120, 255, 180, 1)',
'rgba(255, 150, 150, 1)'
];
const seriesData = data.names.map((name, index) => ({
name,
value: data.values[index],
itemStyle: { color: colors[index % colors.length] }
}));
const option = {
tooltip: {
trigger: 'item'
},
......@@ -40,18 +33,18 @@ export const pieOption = {
x: 'right',
y: 'center',
align: 'left',
left:'50%',
data: data.map(item => item.name),
left: '50%',
data: data.names,
textStyle: { // 图例字体样式
color: "rgba(59, 65, 75, 1)",
fontSize: 16
},
formatter: function(name) {
var total = getTotal(data);
var item = data.find(item => item.name === name);
var percentage = ((item.value / total) * 100).toFixed(2);
return `${name} ${percentage}%`;
}
// formatter: function (name) {
// var total = data.total;
// var item = data.find(item => item.name === name);
// var percentage = ((item.value / total) * 100).toFixed(2);
// return `${name} ${percentage}%`;
// }
},
series: [
{
......@@ -68,41 +61,12 @@ export const pieOption = {
labelLine: {
show: false
},
data: [
{
name: "人工智能与信息技术",
value: 27,
itemStyle:{color: 'rgba(105, 177, 255, 1)'}
},
{
name: "生物医学与健康",
value: 22,
itemStyle:{color: 'rgba(255, 236, 61, 1)'}
},
{
name: "能源与环境技术",
value: 18,
itemStyle:{color: 'rgba(135, 232, 222, 1)'}
},
{
name: "先进制造与材料",
value: 15,
itemStyle:{color: 'rgba(133, 165, 255, 1)'}
},
{
name: "国家安全与国防",
value: 12,
itemStyle:{color: 'rgba(255, 120, 117, 1)'}
},
{
name: "航空航天",
value: 8,
itemStyle:{color: 'rgba(179, 127, 235, 1)'}
}
]
data: seriesData
}
]
};
};
return option
}
var data1 = [
{
......@@ -127,23 +91,41 @@ var data1 = [
}
];
export const pieOption1 = (data) => {
// 提取部门名称和对应的金额
const colors = [
'rgba(105, 177, 255, 1)',
'rgba(255, 236, 61, 1)',
'rgba(135, 232, 222, 1)',
'rgba(133, 165, 255, 1)',
'rgba(255, 120, 117, 1)'
];
// 提取部门名称和对应的金额
const departmentNames = data.map(item => item.departmentName);
const amounts = data.map(item => item.amount);
// 计算总金额
const getTotal = (data) => {
return data.reduce((total, item) => total + item.amount, 0);
};
export const pieOption1 = {
// 饼图配置
const option = {
legend: {
orient: 'vertical',
x: 'right',
y: 'center',
align: 'left',
left:'60%',
data: data1.map(item => item.name),
left: '60%',
data: departmentNames,
textStyle: { // 图例字体样式
color: "rgba(59, 65, 75, 1)",
fontSize: 16
},
formatter: function(name) {
var total = getTotal(data1);
var item = data1.find(item => item.name === name);
var percentage = ((item.value / total) * 100).toFixed(2);
formatter: function (name) {
var total = getTotal(data);
var item = data.find(item => item.departmentName === name);
var percentage = ((item.amount / total) * 100).toFixed(2);
return `${name} ${percentage}%`;
}
},
......@@ -158,66 +140,71 @@ export const pieOption1 = {
show: false,
position: 'center'
},
labelLine: {
show: false
},
data: [
{
name: "捐赠基金",
value: 27,
itemStyle:{color: 'rgba(105, 177, 255, 1)'}
},
{
name: "政府拨款",
value: 22,
itemStyle:{color: 'rgba(255, 236, 61, 1)'}
},
{
name: "企业合作",
value: 18,
itemStyle:{color: 'rgba(135, 232, 222, 1)'}
},
{
name: "学费收入",
value: 15,
itemStyle:{color: 'rgba(133, 165, 255, 1)'}
},
{
name: "其他来源",
value: 12,
itemStyle:{color: 'rgba(255, 120, 117, 1)'}
}
]
data: data.map((item, index) => ({
name: item.departmentName,
value: item.amount,
itemStyle: { color: colors[index % colors.length] } // 使用颜色列表
}))
}
]
};
};
return option;
}
export const raderOption = (data) => {
// 提取所有可能的 areaName
const allAreaNames = new Set();
data.forEach(subject => {
subject.areaVoList.forEach(area => {
allAreaNames.add(area.areaName);
});
});
const indicatorNames = Array.from(allAreaNames);
// 定义雷达图的指标最大值(可以根据实际数据调整)
const indicatorMaxValues = {};
indicatorNames.forEach(name => {
indicatorMaxValues[name] = 5; // 假设每个指标的最大值为 5
});
export const raderOption = {
// 生成雷达图的 indicator 配置
const radarIndicators = indicatorNames.map(name => ({
name,
max: indicatorMaxValues[name]
}));
// 为每个 subjectTypeName 生成雷达图数据
const radarSeriesData = data.map(subject => {
const values = indicatorNames.map(indicator => {
const area = subject.areaVoList.find(a => a.areaName === indicator);
return area ? area.amount : 0; // 如果存在该指标,取其 amount 值,否则为 0
});
return {
value: values,
name: subject.subjectTypeName,
areaStyle: { color: `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, 0.2)` }
};
});
// 雷达图配置
const option = {
title: { text: '' },
legend: {
icon: 'circle',
orient: 'vertical', // 纵向排列
right: 50, // 贴右边
top: 'center', // 垂直居中
align: 'left', // 文字在图标左侧
textStyle: { // 图例字体样式
orient: 'vertical',
right: 50,
top: 'center',
align: 'left',
textStyle: {
color: "rgba(59, 65, 75, 1)",
fontSize: "16px"
}
},
radar: {
radius: '60%', // 关键:缩小整个雷达
indicator: [
{ name: '能源领域', max: 6500 },
{ name: '集成电路', max: 16000 },
{ name: '人工智能', max: 30000 },
{ name: '通信网络', max: 38000 },
{ name: '量子科技', max: 52000 },
{ name: '生物科技', max: 25000 }
],
radius: '60%',
indicator: radarIndicators,
axisName: {
formatter: '{value}',
color: 'rgba(59, 65, 75, 1)',
......@@ -229,32 +216,13 @@ export const raderOption = {
{
name: 'Budget vs spending',
type: 'radar',
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: '研究型大学',
areaStyle: { color: 'rgba(5, 95, 194, 0.2)' }
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: '国家实验室',
areaStyle: { color: 'rgba(250, 140, 22, 0.2)' }
},
{
value: [4000, 14000, 18000, 21000, 32000, 10000],
name: '科技企业',
areaStyle: { color: 'rgba(179, 127, 235, 0.2)' }
},
{
value: [4000, 14000, 18000, 21000, 32000, 10000],
name: '国防承包商',
areaStyle: { color: 'rgba(33, 129, 57, 0.2)' }
}
]
data: radarSeriesData
}
]
};
return option;
}
export const raderOption1 = {
grid: {
top: '3%',
......@@ -293,9 +261,13 @@ export const raderOption1 = {
]
}
]
};
export const barOption = {
};
export const barOption = (data) => {
// 提取年份和对应的专利数量
const years = data.map(item => item.year.toString());
const counts = data.map(item => item.countNum);
// 柱状图配置
const option = {
tooltip: {
trigger: "axis",
axisPointer: {
......@@ -309,7 +281,6 @@ export const barOption = {
left: '1%',
containLabel: true
},
xAxis: [{
axisLine: {
lineStyle: {
......@@ -317,8 +288,7 @@ export const barOption = {
color: "rgba(231, 243, 255, 1)"
}
},
axisTick:
{ show: false },
axisTick: { show: false },
type: "category",
boundaryGap: [100, 100],
axisLabel: {
......@@ -326,9 +296,8 @@ export const barOption = {
// fontSize: 22,
// fontWeight: 400
},
data: ["2016","2017","2018","2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: years, // 动态设置 xAxis 数据
}],
yAxis: {
type: "value",
axisLine: {
......@@ -336,7 +305,6 @@ export const barOption = {
type: "dashed"
}
},
axisLabel: {
color: "rgba(95, 101, 108, 1)",
// fontSize: 22,
......@@ -349,38 +317,41 @@ export const barOption = {
type: "dashed",
color: "rgba(231, 243, 255, 1)"
},
}
},
series: [
{
name: "专利数量",
data: [80, 90, 110, 130, 140, 160, 200,250,300,340],
data: counts, // 动态设置 series 数据
type: "bar",
barWidth: 20,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(46, 165, 255, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(46, 165, 255, 0)" }
])
// borderRadius: [6, 6, 0, 0]
},
label:{
show:false,
position:'top',
textStyle:{
fontSize:'20px',
fontWeight:400,
color:'rgba(255, 255, 255, 1)'
},
label: {
show: false,
position: 'top',
textStyle: {
fontSize: '20px',
fontWeight: 400,
color: 'rgba(255, 255, 255, 1)'
}
}
}
]
};
};
return option;
export const lineChart = {
}
export const lineChart = (data) => {
// 提取年份和对应的专利数量
const years = data.map(item => item.year.toString());
const counts = data.map(item => item.countNum);
// 折线图配置
const option = {
tooltip: {
trigger: "axis",
axisPointer: {
......@@ -403,12 +374,13 @@ export const lineChart = {
axisLine: {
show: false
},
data: ["2016","2017","2018","2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: years, // 动态设置 xAxis 数据
},
yAxis: {
type: 'value',
splitLine: {
show: true, lineStyle: {
show: true,
lineStyle: {
type: "dashed",
color: "#E7F3FF"
}
......@@ -416,31 +388,32 @@ export const lineChart = {
axisLine: {
show: false
},
},
color: ['rgba(255, 149, 77, 1)'],
series: [
{
data: [50,60,80,100,110,140,180,260,280,330],
data: counts, // 动态设置 series 数据
type: 'line',
emphasis: {
focus: 'series'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255, 149, 77, 0.5)' // 起始颜色:深色
}, {
offset: 1,
color: 'rgba(255, 149, 77, 0)' // 结束颜色:浅色且透明度降低
}])
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 149, 77, 0.5)' }, // 起始颜色:深色
{ offset: 1, color: 'rgba(255, 149, 77, 0)' } // 结束颜色:浅色且透明度降低
])
},
}
]
};
export const lineChart1 = {
};
return option;
}
export const lineChart1 = (data) => {
// 提取年份和对应的 fundAmount
const years = data.map(item => item.year.toString());
const fundAmounts = data.map(item => item.fundAmount);
// 折线图配置
const option = {
tooltip: {
trigger: "axis",
axisPointer: {
......@@ -463,12 +436,13 @@ export const lineChart1 = {
axisLine: {
show: false
},
data: ["2016","2017","2018","2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: years, // 动态设置 xAxis 数据
},
yAxis: {
type: 'value',
splitLine: {
show: true, lineStyle: {
show: true,
lineStyle: {
type: "dashed",
color: "#E7F3FF"
}
......@@ -476,35 +450,37 @@ export const lineChart1 = {
axisLine: {
show: false
},
},
color: ['rgba(33, 129, 57, 1)'],
series: [
{
data: [50,60,80,100,110,140,180,260,280,330],
data: fundAmounts, // 动态设置 series 数据
type: 'line',
emphasis: {
focus: 'series'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(33, 129, 57, 0.5)' // 起始颜色:深色
}, {
offset: 1,
color: 'rgba(33, 129, 57, 0)' // 结束颜色:浅色且透明度降低
}])
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(33, 129, 57, 0.5)' }, // 起始颜色:深色
{ offset: 1, color: 'rgba(33, 129, 57, 0)' } // 结束颜色:浅色且透明度降低
])
},
}
]
};
};
return option;
}
const nameList = ["教育学院","文理学院","法学院","商学院","工程学院","医学院"];
const valueList = [21,21,25,79,95,109];
const nameList = ["教育学院", "文理学院", "法学院", "商学院", "工程学院", "医学院"];
const valueList = [21, 21, 25, 79, 95, 109];
export const horizontalBaroption = (data) => {
// 提取部门名称和对应的金额
const departmentNames = data.map(item => item.departmentName);
const amounts = data.map(item => item.amount);
export const horizontalBaroption = {
// 水平柱状图配置
const option = {
grid: {
top: '0',
right: '3%',
......@@ -522,7 +498,7 @@ export const horizontalBaroption = {
},
yAxis: {
type: 'category',
data: nameList,
data: departmentNames, // 动态设置 yAxis 数据
splitLine: {
show: false
},
......@@ -538,7 +514,7 @@ export const horizontalBaroption = {
},
series: [{
type: 'bar',
data: valueList.map((item, index) => {
data: amounts.map((item, index) => {
return {
value: item,
label: {
......@@ -556,31 +532,20 @@ export const horizontalBaroption = {
itemStyle: {
color: function (params) {
if (params.dataIndex < 3) {
return new echarts.graphic.LinearGradient(0, 0, 1, 0,
[{
offset: 0,
color: 'rgba(22, 119, 255, 0)'
},
{
offset: 1,
color: '#1778ff'
}
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(22, 119, 255, 0)' },
{ offset: 1, color: '#1778ff' }
]);
} else {
return new echarts.graphic.LinearGradient(0, 0, 1, 0,
[{
offset: 0,
color: 'rgba(206, 79, 81, 0)'
},
{
offset: 1,
color: '#ce4f51'
}
return new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: 'rgba(206, 79, 81, 0)' },
{ offset: 1, color: '#ce4f51' }
]);
}
},
barBorderRadius: 4,
}
}]
};
return option;
}
\ No newline at end of file
<template>
<div class="coop-page">
<!-- 吸顶简易搜索框 -->
<div class="search-header" v-show="isShow">
<div class="home-main-header-center">
<input v-model="input" class="search-input" placeholder="搜索规则限制" />
<div class="search-btn-small" @click="handleSearch">
<div class="search-icon">
<img src="./assets/icons/search-icon.png" alt="" />
</div>
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-btn-box">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">最新动态</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">数据总览</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">资源库</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
<!-- 面包屑 -->
<div class="breadcrumb">
<div class="breadcrumb" v-show="!isShow">
<div class="breadcrumb-box">
<div class="breadcrumb-item">国家科技安全</div>
<div class="breadcrumb-item">&nbsp;>&nbsp;</div>
......@@ -11,9 +50,9 @@
</div>
</div>
<!-- 主页面 -->
<div class="main-content">
<div class="main-content" ref="homeMainRef" :class="{ 'scroll-main': isShow }">
<!-- 搜索栏部分 -->
<div class="search">
<div class="search" v-show="!isShow">
<div class="search-main">
<input v-model="input" placeholder="搜索规则限制" class="search-input" />
<div class="search-btn">
......@@ -40,25 +79,25 @@
</div>
</div> -->
<div class="search-bottom">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">最新动态</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position2')">
<div class="btn" @click="handleToPosi('position2')">
<div class="btn-text">咨询要闻</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position3')">
<div class="btn" @click="handleToPosi('position3')">
<div class="btn-text">数据总览</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position4')">
<div class="btn" @click="handleToPosi('position4')">
<div class="btn-text">资源库</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
......@@ -99,7 +138,7 @@
</template>
<script setup>
import { ref } from "vue";
import { ref, nextTick } from "vue";
import { useRouter } from "vue-router";
import comTitle from "./common/comTitle.vue";
import newData from "./components/dataNew/index.vue";
......@@ -107,14 +146,44 @@ import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue";
import scrollToTop from "@/utils/scrollToTop";
import { useContainerScroll } from "@/hooks/useScrollShow";
// 搜索框
const input = ref("");
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
const router = useRouter();
// 搜索功能
const handleSearch = () => {
console.log("搜索内容:", input.value);
};
// 锚点跳转
const handleToPosi = id => {
const element = document.getElementById(id);
if (element && homeMainRef.value) {
// 如果当前还未显示吸顶搜索栏,先强制切换状态以稳定布局
if (!isShow.value) {
isShow.value = true;
}
// 使用 nextTick 确保 DOM 状态更新(高度变化生效)后再计算
nextTick(() => {
const containerRect = homeMainRef.value.getBoundingClientRect();
const elementRect = element.getBoundingClientRect();
// 使用 getBoundingClientRect 计算元素相对于容器顶部的绝对距离,不受嵌套布局影响
const top = elementRect.top - containerRect.top + homeMainRef.value.scrollTop;
homeMainRef.value.scrollTo({
top: top,
behavior: "smooth"
});
});
}
};
// 返回首页
const handleBackHome = () => {
router.push({
......@@ -157,12 +226,18 @@ const handleBackHome = () => {
}
}
.main-content {
position: relative;
overflow: auto;
width: 100%;
height: calc(100% - 64px);
background: url("./assets/images/background.png");
background-size: 100% 100%;
padding: 44px 160px 30px 160px;
&.scroll-main {
height: calc(100% - 144px);
}
.search {
width: 960px;
height: 168px;
......@@ -184,15 +259,19 @@ const handleBackHome = () => {
.search-input {
border: none;
outline: none;
width: 800px;
width: 838px;
height: 48px;
background-color: transparent;
font-size: 16px;
font-size: 14px;
padding: 12px 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(132, 136, 142);
line-height: 22px;
color: rgba(59, 65, 75, 1);
&::placeholder {
color: #a8abb2;
}
}
.search-btn {
cursor: pointer;
......@@ -204,53 +283,18 @@ const handleBackHome = () => {
margin-right: -3px;
border-radius: 8px;
background-color: rgb(5, 95, 194);
font-size: 18px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
line-height: 22px;
color: #fff;
img {
width: 22px;
height: 22px;
width: 18px;
height: 18px;
margin-right: 8px;
}
}
}
.search-center {
width: 600px;
height: 57px;
margin: 36px auto;
display: flex;
align-items: center;
justify-content: space-between;
.search-item {
box-sizing: border-box;
width: 120px;
height: 57px;
.search-item-num {
width: 120px;
height: 22px;
font-size: 36px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 22px;
color: rgb(5, 95, 194);
text-align: center;
cursor: pointer;
}
.search-item-name {
width: 120px;
height: 24px;
margin-top: 11px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
text-align: center;
}
}
}
.search-bottom {
width: 688px;
height: 48px;
......@@ -339,5 +383,129 @@ const handleBackHome = () => {
}
}
}
.search-header {
width: 100%;
height: 144px;
background: #fff;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
overflow: hidden;
.home-main-header-center {
margin-top: 20px;
margin-left: 200px;
width: 800px;
height: 48px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
padding: 1px;
position: relative;
display: flex;
align-items: center;
border: 1px solid transparent;
&:hover {
border: 1px solid var(--color-main-active);
}
.search-input {
border: none;
outline: none;
width: 800px;
height: 46px;
background-color: transparent;
font-size: 14px;
padding: 12px 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 22px;
color: rgba(59, 65, 75, 1);
&::placeholder {
color: #a8abb2;
}
}
.search-btn-small {
position: absolute;
right: -1px;
top: 0px;
width: 120px;
height: 46px;
border-radius: 10px;
background: var(--color-main-active);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
.search-icon {
width: 18px;
height: 18px;
img {
width: 100%;
height: 100%;
}
}
.search-text {
margin-left: 8px;
height: 22px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
}
}
}
.home-main-header-btn-box {
margin-top: 20px;
margin-left: 200px;
display: flex;
gap: 16px;
.btn {
display: flex;
align-items: center;
gap: 9px;
width: 160px;
height: 48px;
border: 1px solid #aed6ff;
box-sizing: border-box;
border-radius: 24px;
background: #e7f3ff;
cursor: pointer;
position: relative;
&:hover {
background: #cae3fc;
}
.btn-text {
width: 80px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 48px;
margin-left: 36px;
text-align: center;
}
.btn-icon {
position: absolute;
top: 16px;
right: 19px;
width: 6px;
height: 12px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
}
</style>
......@@ -98,11 +98,11 @@
<div class="bottom-main">
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="select-box-header" style=" display: flex;">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<div class="select-main" style="padding: 25px;">
<div class="checkbox-group">
<!-- <el-checkbox v-for="(item, index) in areaList" :key="index" v-model="selectedAreaList" :label="item"
class="filter-checkbox">
......@@ -918,6 +918,7 @@ onMounted(() => {
.select-box {
margin-top: 21px;
.paixu-btn {
display: flex;
width: 120px;
......@@ -970,6 +971,26 @@ onMounted(() => {
}
}
}
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
.title {
color: rgba(5, 95, 194, 1);
margin-left: 17px;
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论