Commit b86b7619 authored by liyang's avatar liyang

feat:新增若干修改项

parent 57104fe8
This diff is collapsed.
...@@ -49,3 +49,13 @@ export function getImplementationProgress() { ...@@ -49,3 +49,13 @@ export function getImplementationProgress() {
} }
}) })
} }
export function getProgressDetail(id) {
return request({
url: '/portal/progress/' + id,
method: 'get',
headers: {
isToken: false
}
})
}
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
<li><router-link to="/portal/science-plan">科学计划与国际合作</router-link></li> <li><router-link to="/portal/science-plan">科学计划与国际合作</router-link></li>
<li><router-link to="/portal/progress">计划进展</router-link></li> <li><router-link to="/portal/progress">计划进展</router-link></li>
<li><router-link to="/portal/achievement">科研成果</router-link></li> <li><router-link to="/portal/achievement">科研成果</router-link></li>
<li><router-link to="/portal/application">应用与示范</router-link></li>
<li><router-link to="/portal/sdgsat1">SDGSAT-1专区</router-link></li> <li><router-link to="/portal/sdgsat1">SDGSAT-1专区</router-link></li>
<li><router-link to="/portal/notice">通知公告</router-link></li> <li><router-link to="/portal/notice">通知公告</router-link></li>
</ul> </ul>
......
...@@ -59,7 +59,6 @@ export default { ...@@ -59,7 +59,6 @@ export default {
{ path: '/portal/science-plan', title: '科学计划与国际合作' }, { path: '/portal/science-plan', title: '科学计划与国际合作' },
{ path: '/portal/progress', title: '计划进展' }, { path: '/portal/progress', title: '计划进展' },
{ path: '/portal/achievement', title: '科研成果' }, { path: '/portal/achievement', title: '科研成果' },
{ path: '/portal/application', title: '应用与示范' },
{ path: '/portal/sdgsat1', title: 'SDGSAT-1专区' }, { path: '/portal/sdgsat1', title: 'SDGSAT-1专区' },
{ path: '/portal/notice', title: '通知公告' } { path: '/portal/notice', title: '通知公告' }
] ]
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="banner-carousel"> <div class="banner-carousel">
<el-carousel height="500px" :interval="5000" arrow="hover" v-loading="loading" @change="handleCarouselChange"> <el-carousel height="500px" :interval="5000" arrow="hover" v-loading="loading" @change="handleCarouselChange">
<el-carousel-item v-for="(item, index) in banners" :key="index"> <el-carousel-item v-for="(item, index) in banners" :key="index">
<div class="carousel-item" :style="{ backgroundImage: 'url(' + item.image + ')' }"> <div class="carousel-item" :style="{ backgroundImage: 'url(' + item.image + ')' }" @click="goToDetail(item)">
</div> </div>
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
...@@ -61,6 +61,14 @@ export default { ...@@ -61,6 +61,14 @@ export default {
if (this.banners.length > 0) { if (this.banners.length > 0) {
this.currentBanner = this.banners[index] this.currentBanner = this.banners[index]
} }
},
goToDetail(item) {
if (item.id) {
this.$router.push({
path: '/portal/progress/detail',
query: { id: item.id }
})
}
} }
} }
} }
...@@ -113,6 +121,7 @@ export default { ...@@ -113,6 +121,7 @@ export default {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
position: relative; position: relative;
cursor: pointer;
&::before { &::before {
content: ''; content: '';
...@@ -123,6 +132,12 @@ export default { ...@@ -123,6 +132,12 @@ export default {
bottom: 0; bottom: 0;
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
} }
&:hover {
&::before {
background: rgba(0, 0, 0, 0.3);
}
}
} }
.carousel-content { .carousel-content {
......
...@@ -35,6 +35,12 @@ export const constantRoutes = [ ...@@ -35,6 +35,12 @@ export const constantRoutes = [
component: () => import('@/views/portal/Progress'), component: () => import('@/views/portal/Progress'),
meta: { title: '计划进展' } meta: { title: '计划进展' }
}, },
{
path: 'progress/detail',
name: 'PortalProgressDetail',
component: () => import('@/views/portal/ProgressDetail'),
meta: { title: '计划进展详情' }
},
{ {
path: 'achievement', path: 'achievement',
name: 'PortalAchievement', name: 'PortalAchievement',
...@@ -47,12 +53,6 @@ export const constantRoutes = [ ...@@ -47,12 +53,6 @@ export const constantRoutes = [
component: () => import('@/views/portal/AchievementDetail'), component: () => import('@/views/portal/AchievementDetail'),
meta: { title: '科研成果详情' } meta: { title: '科研成果详情' }
}, },
{
path: 'application',
name: 'PortalApplication',
component: () => import('@/views/portal/Application'),
meta: { title: '应用与示范' }
},
{ {
path: 'sdgsat1', path: 'sdgsat1',
name: 'PortalSDGSAT1', name: 'PortalSDGSAT1',
......
...@@ -60,6 +60,13 @@ ...@@ -60,6 +60,13 @@
> >
科学成果 科学成果
</div> </div>
<div
class="menu-item"
:class="{ active: currentMenu === 'sdgCases' }"
@click="handleMenuClick('sdgCases')"
>
SDG应用案例
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -67,7 +74,7 @@ ...@@ -67,7 +74,7 @@
<!-- 右侧内容区域 --> <!-- 右侧内容区域 -->
<div class="right-content"> <div class="right-content">
<div class="content-card" v-loading="loading"> <div class="content-card" v-loading="loading">
<div class="content-header" v-if="currentMenu === 'overview'"> <div class="content-header">
<span class="title">{{ currentTitle }}</span> <span class="title">{{ currentTitle }}</span>
</div> </div>
<div class="content-body"> <div class="content-body">
...@@ -138,6 +145,32 @@ ...@@ -138,6 +145,32 @@
</div> </div>
</section> </section>
</div> </div>
<!-- SDG应用案例内容 -->
<div v-if="currentMenu === 'sdgCases'" class="cases-content">
<div v-if="caseList.length === 0" class="empty-container">
<i class="el-icon-folder-opened"></i>
<p>暂无应用案例数据</p>
</div>
<div v-else class="cases-list">
<div class="case-item" v-for="(item, index) in caseList" :key="item.id || index">
<div class="case-header">
<h3 class="case-title">{{ item.title }}</h3>
<div class="case-tag">
<el-tag size="small" :type="item.tagType">{{ item.tag }}</el-tag>
</div>
</div>
<div class="case-thumbnail" v-if="item.thumbnail">
<img :src="item.thumbnail" :alt="item.title" />
</div>
<div class="case-intro" v-html="item.desc"></div>
<div class="case-meta">
<span><i class="el-icon-location"></i> {{ item.location }}</span>
<span><i class="el-icon-date"></i> {{ item.date }}</span>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -150,6 +183,7 @@ import { ...@@ -150,6 +183,7 @@ import {
getSdgsat1AchievementList, getSdgsat1AchievementList,
getAchievementOverview, getAchievementOverview,
} from "@/api/portal/achievement"; } from "@/api/portal/achievement";
import { getApplicationList } from "@/api/portal/application";
export default { export default {
name: "Achievement", name: "Achievement",
...@@ -167,13 +201,18 @@ export default { ...@@ -167,13 +201,18 @@ export default {
sdgsat1Total: 0, sdgsat1Total: 0,
sdgsat1PageSize: 10, sdgsat1PageSize: 10,
sdgsat1PageNum: 1, sdgsat1PageNum: 1,
// SDG应用案例数据
caseList: [],
}; };
}, },
computed: { computed: {
currentTitle() { currentTitle() {
return this.currentMenu === "overview" const titleMap = {
? "成果总体描述" overview: "成果总体描述",
: "科学成果"; sdgsat1: "科学成果",
sdgCases: "SDG应用案例",
};
return titleMap[this.currentMenu] || "";
}, },
}, },
mounted() { mounted() {
...@@ -227,8 +266,90 @@ export default { ...@@ -227,8 +266,90 @@ export default {
this.fetchAchievementOverview(); this.fetchAchievementOverview();
} else if (menu === "sdgsat1") { } else if (menu === "sdgsat1") {
this.fetchSdgsat1Achievements(); this.fetchSdgsat1Achievements();
} else if (menu === "sdgCases") {
this.getCaseList();
} }
}, },
// 获取SDG应用案例列表
getCaseList() {
this.loading = true;
const query = {
pageNum: 1,
pageSize: 100,
status: "0", // 只查询正常状态的
};
getApplicationList(query)
.then((response) => {
if (response.code === 200 && response.rows) {
// 转换后端数据为前端展示格式
this.caseList = response.rows.map((item) => ({
id: item.id,
title: item.caseName,
desc: item.introduction || item.remark || "暂无描述",
location: item.applicationUnit || "未知",
date: item.publishTime
? this.parseTime(item.publishTime, "{y}-{m}")
: "",
tag: item.applicationField || "应用案例",
tagType: this.getTagType(item.applicationField),
thumbnail: item.thumbnail,
}));
}
this.loading = false;
})
.catch(() => {
this.loading = false;
// 如果接口失败,使用默认静态数据
this.caseList = this.getDefaultCaseList();
});
},
// 根据应用领域获取标签类型
getTagType(field) {
if (!field) return "info";
const typeMap = {
气候变化: "primary",
生态环境: "success",
农业: "warning",
健康: "danger",
城市: "info",
水资源: "primary",
};
for (let key in typeMap) {
if (field.includes(key)) {
return typeMap[key];
}
}
return "info";
},
// 默认案例数据
getDefaultCaseList() {
return [
{
title: "气候变化监测系统应用",
desc: "在某省部署气候变化监测系统,实现气候变化指标的实时监测和预警,为应对气候变化提供科学依据。",
location: "某省",
date: "2023-06",
tag: "气候变化",
tagType: "primary",
},
{
title: "生态环境评估平台应用",
desc: "为某市提供生态环境评估服务,支持生态环境治理决策,有效改善区域生态环境质量。",
location: "某市",
date: "2023-08",
tag: "生态环境",
tagType: "success",
},
{
title: "农业产量预测系统应用",
desc: "在多个农业大县推广农业产量预测系统,提高农业生产效率,助力粮食安全保障。",
location: "多县",
date: "2023-09",
tag: "农业发展",
tagType: "warning",
},
];
},
}, },
}; };
</script> </script>
...@@ -425,6 +546,127 @@ export default { ...@@ -425,6 +546,127 @@ export default {
} }
} }
/* SDG应用案例样式 */
.cases-content {
.cases-list {
display: flex;
flex-direction: column;
gap: 20px;
.case-item {
background: #ffffff;
border-radius: 8px;
padding: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
&:hover {
transform: translateX(5px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.case-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 1px solid #e8e8e8;
.case-title {
font-size: 18px;
font-weight: 600;
color: #1e3c72;
margin: 0;
flex: 1;
}
.case-tag {
flex-shrink: 0;
margin-left: 15px;
}
}
.case-thumbnail {
display: flex;
justify-content: center;
margin-bottom: 15px;
img {
max-width: 100%;
max-height: 300px;
width: auto;
height: auto;
border-radius: 8px;
object-fit: contain;
}
}
.case-intro {
font-size: 15px;
line-height: 1.8;
color: #333333;
margin-bottom: 15px;
:deep(p) {
margin: 0 0 12px 0;
&:last-child {
margin-bottom: 0;
}
}
:deep(img) {
max-width: 100%;
height: auto;
border-radius: 6px;
margin: 10px 0;
}
:deep(h1), :deep(h2), :deep(h3), :deep(h4) {
color: #1e3c72;
margin: 16px 0 10px 0;
}
:deep(ul), :deep(ol) {
margin: 10px 0;
padding-left: 20px;
}
:deep(li) {
margin-bottom: 6px;
}
:deep(a) {
color: #1e3c72;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
.case-meta {
display: flex;
gap: 20px;
font-size: 13px;
color: #999999;
padding-top: 15px;
border-top: 1px dashed #e8e8e8;
span {
display: flex;
align-items: center;
gap: 5px;
i {
font-size: 14px;
}
}
}
}
}
}
.sdgsat1-achievements-list { .sdgsat1-achievements-list {
.achievements-list { .achievements-list {
display: flex; display: flex;
......
<template> <template>
<div class="achievement-detail-page"> <div class="achievement-detail-page">
<div class="page-container"> <!-- 面包屑导航 -->
<div class="back-button"> <div class="breadcrumb-container">
<el-button icon="el-icon-arrow-left" @click="goBack">返回列表</el-button> <div class="breadcrumb-wrapper">
<el-breadcrumb separator="/">
<el-breadcrumb-item><a href="/portal">首页</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="/portal/achievement">科研成果</a></el-breadcrumb-item>
<el-breadcrumb-item>成果详情</el-breadcrumb-item>
</el-breadcrumb>
<el-button class="back-btn" icon="el-icon-arrow-left" size="small" @click="goBack">返回</el-button>
</div> </div>
</div>
<div class="page-container">
<div class="detail-card" v-loading="loading"> <div class="detail-card" v-loading="loading">
<div class="detail-header"> <div class="detail-header">
<h1 class="detail-title">{{ achievement.achievementName }}</h1> <h1 class="detail-title">{{ achievement.achievementName }}</h1>
...@@ -109,16 +117,33 @@ export default { ...@@ -109,16 +117,33 @@ export default {
width: 100%; width: 100%;
min-height: calc(100vh - 200px); min-height: calc(100vh - 200px);
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding-bottom: 20px;
} }
.page-container { /* 面包屑导航 */
.breadcrumb-container {
// background: #f5f7fa;
padding: 20px 0;
// border-bottom: 1px solid #e0e0e0;
}
.breadcrumb-wrapper {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 40px 30px; padding: 0 30px;
display: flex;
justify-content: space-between;
align-items: center;
.back-btn {
margin-left: 20px;
}
} }
.back-button { .page-container {
margin-bottom: 30px; max-width: 1200px;
margin: 0 auto;
padding: 30px;
} }
.detail-card { .detail-card {
......
This diff is collapsed.
<template>
<div class="progress-detail-page">
<!-- 面包屑导航 -->
<div class="breadcrumb-container">
<div class="breadcrumb-wrapper">
<el-breadcrumb separator="/">
<el-breadcrumb-item><a href="/portal">首页</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="/portal/progress">计划进展</a></el-breadcrumb-item>
<el-breadcrumb-item>动态详情</el-breadcrumb-item>
</el-breadcrumb>
<el-button class="back-btn" icon="el-icon-arrow-left" size="small" @click="goBack">返回</el-button>
</div>
</div>
<div class="page-container">
<div class="detail-card" v-loading="loading">
<div class="detail-header">
<h1 class="detail-title">{{ progress.title }}</h1>
<div class="detail-meta">
<span v-if="progress.publishTime" class="meta-item">
<i class="el-icon-date"></i>
{{ progress.publishTime }}
</span>
<span v-if="progress.source" class="meta-item">
<i class="el-icon-office-building"></i>
{{ progress.source }}
</span>
</div>
</div>
<div class="detail-body">
<div class="detail-thumbnail" v-if="progress.coverImage">
<img :src="progress.coverImage" :alt="progress.title" />
</div>
<div class="detail-content">
<div class="content-section" v-if="progress.summary">
<h3>摘要</h3>
<p>{{ progress.summary }}</p>
</div>
<div class="content-section" v-if="progress.contentCn">
<h3>详情</h3>
<div class="content-text" v-html="progress.contentCn"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getProgressDetail } from '@/api/portal/progress'
export default {
name: 'ProgressDetail',
data() {
return {
progress: {},
loading: false
}
},
created() {
const id = this.$route.query.id
if (id) {
this.fetchProgressDetail(id)
} else {
this.$message.error('缺少动态ID')
this.goBack()
}
},
methods: {
async fetchProgressDetail(id) {
this.loading = true
try {
const response = await getProgressDetail(id)
if (response.code === 200) {
this.progress = response.data || {}
} else {
this.$message.error('获取动态详情失败')
}
} catch (error) {
console.error('获取动态详情失败:', error)
this.$message.error('获取动态详情失败')
} finally {
this.loading = false
}
},
goBack() {
this.$router.back()
}
}
}
</script>
<style lang="scss" scoped>
.progress-detail-page {
width: 100%;
min-height: calc(100vh - 200px);
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding-bottom: 20px;
}
/* 面包屑导航 */
.breadcrumb-container {
padding: 20px 0;
}
.breadcrumb-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 0 30px;
display: flex;
justify-content: space-between;
align-items: center;
.back-btn {
margin-left: 20px;
}
}
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 30px;
}
.detail-card {
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.detail-header {
padding: 40px 40px 30px;
border-bottom: 1px solid #f0f0f0;
.detail-title {
font-size: 32px;
font-weight: 700;
color: #1e3c72;
margin: 0 0 20px 0;
line-height: 1.4;
}
.detail-meta {
display: flex;
flex-wrap: wrap;
gap: 20px;
.meta-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #666666;
i {
font-size: 16px;
color: #2a5298;
}
}
}
}
.detail-body {
padding: 40px;
}
.detail-thumbnail {
width: 100%;
max-height: 500px;
border-radius: 8px;
overflow: hidden;
margin-bottom: 40px;
background: #f5f7fa;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.detail-content {
.content-section {
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
h3 {
font-size: 20px;
font-weight: 600;
color: #1e3c72;
margin: 0 0 20px 0;
padding-bottom: 10px;
border-bottom: 2px solid #2a5298;
}
.content-text {
font-size: 15px;
color: #333333;
line-height: 1.8;
text-align: justify;
::v-deep {
p {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 20px 0;
}
}
}
p {
font-size: 15px;
color: #333333;
line-height: 1.8;
margin: 0;
}
}
}
@media (max-width: 768px) {
.detail-header {
padding: 30px 20px;
.detail-title {
font-size: 24px;
}
}
.detail-body {
padding: 20px;
}
.detail-thumbnail {
max-height: 300px;
}
}
</style>
...@@ -402,10 +402,11 @@ text-transform: none; ...@@ -402,10 +402,11 @@ text-transform: none;
background: #e0e0e0; background: #e0e0e0;
img { img {
width: 100%; width: 100% !important;
height: 100%; height: 100% !important;
object-fit: cover; object-fit: cover;
transition: transform 0.3s ease; transition: transform 0.3s ease;
margin: 0 !important;
&:hover { &:hover {
transform: scale(1.05); transform: scale(1.05);
......
...@@ -47,12 +47,14 @@ public class PortalProgressController extends BaseController { ...@@ -47,12 +47,14 @@ public class PortalProgressController extends BaseController {
return AjaxResult.success(list); return AjaxResult.success(list);
} }
@Anonymous
@GetMapping("/projects") @GetMapping("/projects")
public AjaxResult getProjectList() { public AjaxResult getProjectList() {
List<SysProject> list = projectService.selectSysProjectList(new SysProject()); List<SysProject> list = projectService.selectSysProjectList(new SysProject());
return AjaxResult.success(list); return AjaxResult.success(list);
} }
@Anonymous
@GetMapping("/stats") @GetMapping("/stats")
public AjaxResult getProgressStats() { public AjaxResult getProgressStats() {
Map<String, Object> stats = new HashMap<>(); Map<String, Object> stats = new HashMap<>();
...@@ -84,4 +86,14 @@ public class PortalProgressController extends BaseController { ...@@ -84,4 +86,14 @@ public class PortalProgressController extends BaseController {
SysImplementationProgress progress = implementationProgressService.selectSysImplementationProgress(); SysImplementationProgress progress = implementationProgressService.selectSysImplementationProgress();
return AjaxResult.success(progress); return AjaxResult.success(progress);
} }
/**
* 获取实时动态详情
*/
@Anonymous
@GetMapping("/{id}")
public AjaxResult getProgressDetail(@PathVariable Long id) {
SysRealtimeNews news = realtimeNewsService.selectSysRealtimeNewsById(id);
return AjaxResult.success(news);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment