Commit b86b7619 authored by liyang's avatar liyang

feat:新增若干修改项

parent 57104fe8
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
v-show="showSearch"
label-width="80px"
>
<el-form-item label="案例名称" prop="title">
<el-input
v-model="queryParams.title"
......@@ -37,8 +44,16 @@
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
......@@ -51,7 +66,8 @@
size="mini"
@click="handleAdd"
v-hasPermi="['sdgsat1:case:add']"
>新增</el-button>
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
......@@ -62,7 +78,8 @@
:disabled="single"
@click="handleUpdate"
v-hasPermi="['sdgsat1:case:edit']"
>修改</el-button>
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
......@@ -73,15 +90,28 @@
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['sdgsat1:case:remove']"
>删除</el-button>
>删除</el-button
>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar
:showSearch.sync="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="caseList" @selection-change="handleSelectionChange">
<el-table
v-loading="loading"
:data="caseList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center" prop="id" width="80" />
<el-table-column label="封面" align="center" prop="coverImage" width="100">
<el-table-column
label="封面"
align="center"
prop="coverImage"
width="100"
>
<template slot-scope="scope">
<el-image
v-if="scope.row.coverImage"
......@@ -107,22 +137,42 @@
:show-overflow-tooltip="true"
min-width="200"
/>
<el-table-column label="应用领域" align="center" prop="applicationField" width="120" />
<el-table-column
label="应用领域"
align="center"
prop="applicationField"
width="120"
/>
<el-table-column label="状态" align="center" prop="status" width="80">
<template slot-scope="scope">
<el-tag :type="scope.row.status === '0' ? 'success' : 'danger'">
{{ scope.row.status === '0' ? '正常' : '停用' }}
{{ scope.row.status === "0" ? "正常" : "停用" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="排序" align="center" prop="sort" width="80" />
<el-table-column label="发布人" align="center" prop="createBy" width="100" />
<el-table-column label="发布时间" align="center" prop="createTime" width="120">
<el-table-column
label="发布人"
align="center"
prop="createBy"
width="100"
/>
<el-table-column
label="发布时间"
align="center"
prop="createTime"
width="120"
>
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
<span>{{ parseTime(scope.row.createTime, "{y}-{m}-{d}") }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
width="180"
>
<template slot-scope="scope">
<el-button
size="mini"
......@@ -130,20 +180,22 @@
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['sdgsat1:case:edit']"
>修改</el-button>
>修改</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['sdgsat1:case:remove']"
>删除</el-button>
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
......@@ -161,12 +213,19 @@
</el-col>
<el-col :span="12">
<el-form-item label="应用领域" prop="applicationField">
<el-input v-model="form.applicationField" placeholder="请输入应用领域" />
<el-input
v-model="form.applicationField"
placeholder="请输入应用领域"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
<el-input-number
v-model="form.sort"
controls-position="right"
:min="0"
/>
</el-form-item>
</el-col>
<el-col :span="12">
......@@ -179,22 +238,32 @@
</el-col>
<el-col :span="24">
<el-form-item label="封面图片">
<image-upload v-model="form.coverImage" :limit="1"/>
<image-upload v-model="form.coverImage" :limit="1" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="概述" prop="summary">
<el-input v-model="form.summary" type="textarea" placeholder="请输入概述" :rows="3" />
<el-input
v-model="form.summary"
type="textarea"
placeholder="请输入概述"
:rows="3"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="内容" prop="content">
<editor v-model="form.content" :min-height="300"/>
<editor v-model="form.content" :min-height="300" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" :rows="2" />
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注"
:rows="2"
/>
</el-form-item>
</el-col>
</el-row>
......@@ -208,7 +277,13 @@
</template>
<script>
import { listCase, getCase, delCase, addCase, updateCase } from "@/api/sdgsat1/case"
import {
listCase,
getCase,
delCase,
addCase,
updateCase,
} from "@/api/sdgsat1/case";
export default {
name: "Case",
......@@ -224,53 +299,49 @@ export default {
title: "",
open: false,
dateRange: [],
baseUrl: '',
baseUrl: "",
queryParams: {
pageNum: 1,
pageSize: 10,
title: undefined,
applicationField: undefined,
createBy: undefined,
status: undefined
status: undefined,
},
form: {},
rules: {
title: [
{ required: true, message: "案例名称不能为空", trigger: "blur" }
{ required: true, message: "案例名称不能为空", trigger: "blur" },
],
applicationField: [
{ required: true, message: "应用领域不能为空", trigger: "blur" }
{ required: true, message: "应用领域不能为空", trigger: "blur" },
],
summary: [
{ required: true, message: "概述不能为空", trigger: "blur" }
],
content: [
{ required: true, message: "内容不能为空", trigger: "blur" }
]
}
}
summary: [{ required: true, message: "概述不能为空", trigger: "blur" }],
content: [{ required: true, message: "内容不能为空", trigger: "blur" }],
},
};
},
created() {
this.baseUrl = process.env.VUE_APP_BASE_API
this.getList()
this.baseUrl = process.env.VUE_APP_BASE_API;
this.getList();
},
methods: {
getList() {
this.loading = true
this.queryParams.params = {}
this.loading = true;
this.queryParams.params = {};
if (this.dateRange && this.dateRange.length === 2) {
this.queryParams.params['beginTime'] = this.dateRange[0]
this.queryParams.params['endTime'] = this.dateRange[1]
this.queryParams.params["beginTime"] = this.dateRange[0];
this.queryParams.params["endTime"] = this.dateRange[1];
}
listCase(this.queryParams).then(response => {
this.caseList = response.rows
this.total = response.total
this.loading = false
})
listCase(this.queryParams).then((response) => {
this.caseList = response.rows;
this.total = response.total;
this.loading = false;
});
},
cancel() {
this.open = false
this.reset()
this.open = false;
this.reset();
},
reset() {
this.form = {
......@@ -282,66 +353,70 @@ export default {
applicationField: undefined,
sort: 0,
status: "0",
remark: undefined
}
this.resetForm("form")
remark: undefined,
};
this.resetForm("form");
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = []
this.resetForm("queryForm")
this.handleQuery()
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!=1
this.multiple = !selection.length
this.ids = selection.map((item) => item.id);
this.single = selection.length != 1;
this.multiple = !selection.length;
},
handleAdd() {
this.reset()
this.open = true
this.title = "添加应用案例"
this.reset();
this.open = true;
this.title = "添加应用案例";
},
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getCase(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改应用案例"
})
this.reset();
const id = row.id || this.ids;
getCase(id).then((response) => {
this.form = response.data;
this.open = true;
this.title = "修改应用案例";
});
},
submitForm: function() {
this.$refs["form"].validate(valid => {
submitForm: function () {
this.$refs["form"].validate((valid) => {
if (valid) {
if (this.form.id != undefined) {
updateCase(this.form).then(() => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addCase(this.form).then(() => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
})
});
},
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除应用案例编号为"' + ids + '"的数据项?').then(function() {
return delCase(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
}
}
}
const ids = row.id || this.ids;
this.$modal
.confirm('是否确认删除应用案例编号为"' + ids + '"的数据项?')
.then(function () {
return delCase(ids);
})
.then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
})
.catch(() => {});
},
},
};
</script>
......@@ -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 @@
<li><router-link to="/portal/science-plan">科学计划与国际合作</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/application">应用与示范</router-link></li>
<li><router-link to="/portal/sdgsat1">SDGSAT-1专区</router-link></li>
<li><router-link to="/portal/notice">通知公告</router-link></li>
</ul>
......
......@@ -59,7 +59,6 @@ export default {
{ path: '/portal/science-plan', title: '科学计划与国际合作' },
{ path: '/portal/progress', title: '计划进展' },
{ path: '/portal/achievement', title: '科研成果' },
{ path: '/portal/application', title: '应用与示范' },
{ path: '/portal/sdgsat1', title: 'SDGSAT-1专区' },
{ path: '/portal/notice', title: '通知公告' }
]
......
......@@ -2,7 +2,7 @@
<div class="banner-carousel">
<el-carousel height="500px" :interval="5000" arrow="hover" v-loading="loading" @change="handleCarouselChange">
<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>
</el-carousel-item>
</el-carousel>
......@@ -61,6 +61,14 @@ export default {
if (this.banners.length > 0) {
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 {
align-items: center;
justify-content: center;
position: relative;
cursor: pointer;
&::before {
content: '';
......@@ -123,6 +132,12 @@ export default {
bottom: 0;
background: rgba(0, 0, 0, 0.2);
}
&:hover {
&::before {
background: rgba(0, 0, 0, 0.3);
}
}
}
.carousel-content {
......
......@@ -35,6 +35,12 @@ export const constantRoutes = [
component: () => import('@/views/portal/Progress'),
meta: { title: '计划进展' }
},
{
path: 'progress/detail',
name: 'PortalProgressDetail',
component: () => import('@/views/portal/ProgressDetail'),
meta: { title: '计划进展详情' }
},
{
path: 'achievement',
name: 'PortalAchievement',
......@@ -47,12 +53,6 @@ export const constantRoutes = [
component: () => import('@/views/portal/AchievementDetail'),
meta: { title: '科研成果详情' }
},
{
path: 'application',
name: 'PortalApplication',
component: () => import('@/views/portal/Application'),
meta: { title: '应用与示范' }
},
{
path: 'sdgsat1',
name: 'PortalSDGSAT1',
......
......@@ -60,6 +60,13 @@
>
科学成果
</div>
<div
class="menu-item"
:class="{ active: currentMenu === 'sdgCases' }"
@click="handleMenuClick('sdgCases')"
>
SDG应用案例
</div>
</div>
</div>
</div>
......@@ -67,7 +74,7 @@
<!-- 右侧内容区域 -->
<div class="right-content">
<div class="content-card" v-loading="loading">
<div class="content-header" v-if="currentMenu === 'overview'">
<div class="content-header">
<span class="title">{{ currentTitle }}</span>
</div>
<div class="content-body">
......@@ -138,6 +145,32 @@
</div>
</section>
</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>
......@@ -150,6 +183,7 @@ import {
getSdgsat1AchievementList,
getAchievementOverview,
} from "@/api/portal/achievement";
import { getApplicationList } from "@/api/portal/application";
export default {
name: "Achievement",
......@@ -167,13 +201,18 @@ export default {
sdgsat1Total: 0,
sdgsat1PageSize: 10,
sdgsat1PageNum: 1,
// SDG应用案例数据
caseList: [],
};
},
computed: {
currentTitle() {
return this.currentMenu === "overview"
? "成果总体描述"
: "科学成果";
const titleMap = {
overview: "成果总体描述",
sdgsat1: "科学成果",
sdgCases: "SDG应用案例",
};
return titleMap[this.currentMenu] || "";
},
},
mounted() {
......@@ -227,8 +266,90 @@ export default {
this.fetchAchievementOverview();
} else if (menu === "sdgsat1") {
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>
......@@ -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 {
.achievements-list {
display: flex;
......
<template>
<div class="achievement-detail-page">
<div class="page-container">
<div class="back-button">
<el-button icon="el-icon-arrow-left" @click="goBack">返回列表</el-button>
<!-- 面包屑导航 -->
<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/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 class="page-container">
<div class="detail-card" v-loading="loading">
<div class="detail-header">
<h1 class="detail-title">{{ achievement.achievementName }}</h1>
......@@ -109,16 +117,33 @@ export default {
width: 100%;
min-height: calc(100vh - 200px);
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;
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 {
margin-bottom: 30px;
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 30px;
}
.detail-card {
......
<template>
<div class="application-container">
<div class="application-content">
<!-- 左侧导航卡片 -->
<div class="left-sidebar">
<div class="sidebar-card">
<div class="sidebar-header">
<span class="title">应用与示范</span>
</div>
<div class="sidebar-decorator">
<div class="decorator-segment" style="background-color: #4B9F37"></div>
<div class="decorator-segment" style="background-color: #C51A2D"></div>
<div class="decorator-segment" style="background-color: #E37221"></div>
<div class="decorator-segment" style="background-color: #0298DB"></div>
<div class="decorator-segment" style="background-color: #F8C72E"></div>
<div class="decorator-segment" style="background-color: #D0103A"></div>
<div class="decorator-segment" style="background-color: #028542"></div>
<div class="decorator-segment" style="background-color: #BF8B2E"></div>
<div class="decorator-segment" style="background-color: #0B97D9"></div>
</div>
<div class="sidebar-menu">
<div
class="menu-item"
:class="{ active: currentMenu === 'sdgCases' }"
@click="handleMenuClick('sdgCases')"
>
SDG应用案例
</div>
<div
class="menu-item"
:class="{ active: currentMenu === 'regionDemos' }"
@click="handleMenuClick('regionDemos')"
>
区域示范
</div>
</div>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="right-content">
<div class="content-card" v-loading="loading">
<div class="content-header">
<span class="title">{{ currentTitle }}</span>
</div>
<div class="content-body">
<!-- 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-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 v-if="currentMenu === 'regionDemos'" class="demos-content">
<div v-if="demoList.length === 0" class="empty-container">
<i class="el-icon-s-flag"></i>
<p>暂无区域示范数据</p>
</div>
<div v-else class="demo-list">
<div class="demo-item" v-for="(item, index) in demoList" :key="item.id || index">
<div class="demo-status">
<el-tag :type="item.statusType">{{ item.status }}</el-tag>
</div>
<div class="demo-content">
<h3>{{ item.title }}</h3>
<p>{{ item.desc }}</p>
<div class="demo-meta">
<span><i class="el-icon-office-building"></i> {{ item.unit }}</span>
<span><i class="el-icon-time"></i> {{ item.period }}</span>
<span><i class="el-icon-map-location"></i> {{ item.scope }}</span>
</div>
</div>
<div class="demo-progress">
<div class="progress-label">完成进度</div>
<el-progress :percentage="item.progress" :color="progressColor"></el-progress>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getApplicationList, getDemoList } from '@/api/portal/application'
export default {
name: 'Application',
data() {
return {
loading: true,
currentMenu: 'sdgCases',
// 应用案例数据(来自管理后台)
caseList: [],
// 区域示范数据
demoList: [],
// 进度条颜色
progressColor: [
{ color: '#f56c6c', percentage: 20 },
{ color: '#e6a23c', percentage: 40 },
{ color: '#5cb87a', percentage: 60 },
{ color: '#1989fa', percentage: 80 },
{ color: '#6f7ad3', percentage: 100 }
]
}
},
computed: {
currentTitle() {
return this.currentMenu === 'sdgCases' ? 'SDG应用案例' : '区域示范'
}
},
created() {
this.fetchData()
},
methods: {
handleMenuClick(menu) {
this.currentMenu = menu
},
// 获取数据
fetchData() {
this.getCaseList()
this.getDemoList()
},
// 获取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()
})
},
// 获取区域示范列表
getDemoList() {
this.loading = true
const query = {
pageNum: 1,
pageSize: 100,
status: '0'
}
getDemoList(query).then(response => {
if (response.code === 200 && response.rows) {
// 转换后端数据为前端展示格式
this.demoList = response.rows.map((item, index) => ({
id: item.id,
title: item.caseName,
desc: item.remark || '暂无描述',
unit: item.applicationUnit || '未知单位',
period: item.publishTime ? this.parseTime(item.publishTime, '{y}-{m}') : '2024-2025',
scope: item.applicationField || '区域',
status: '进行中',
statusType: 'warning',
progress: 30 + (index * 15) % 70 // 模拟进度
}))
}
this.loading = false
}).catch(() => {
this.loading = false
// 如果接口失败,使用默认静态数据
this.demoList = this.getDefaultDemoList()
})
},
// 根据应用领域获取标签类型
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'
}
]
},
// 默认示范数据
getDefaultDemoList() {
return [
{
title: '气候变化监测示范项目',
desc: '在某省建设气候变化监测示范项目,形成可复制推广的经验,为其他地区提供参考。',
unit: '某省生态环境厅',
period: '2023-2025',
scope: '全省',
status: '进行中',
statusType: 'warning',
progress: 65
},
{
title: '生态环境治理示范项目',
desc: '在某市开展生态环境治理示范项目,探索生态治理新模式,取得显著成效。',
unit: '某市生态环境局',
period: '2023-2024',
scope: '全市',
status: '即将完成',
statusType: 'success',
progress: 90
}
]
}
}
}
</script>
<style lang="scss" scoped>
.application-container {
width: 100%;
min-height: 100vh;
background-color: #f5f7fa;
padding: 40px 0;
}
.application-content {
max-width: 1390px;
margin: 0 auto;
padding: 0 30px;
display: grid;
grid-template-columns: 300px 1060px;
gap: 30px;
align-items: start;
}
/* 左侧导航卡片 */
.left-sidebar {
.sidebar-card {
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.sidebar-header {
background: #1EA8C5;
// padding: 30px 24px;
height: 58px;
display: flex;
align-items: center;
// justify-content: center;
padding-left: 19px;
.title {
font-family: Microsoft YaHei, Microsoft YaHei;
font-weight: bold;
font-size: 18px;
color: #FFFFFF;
line-height: 18px;
text-align: left;
font-style: normal;
text-transform: none;
}
}
.sidebar-decorator {
width: 100%;
height: 6px;
display: flex;
overflow: hidden;
.decorator-segment {
width: 80px;
height: 100%;
&:first-child {
margin-left: -40px;
}
&:last-child {
margin-right: -40px;
}
}
}
.sidebar-menu {
padding: 16px 0;
.menu-item {
padding: 16px 24px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 16px;
color: #666666;
border-left: 3px solid transparent;
&:hover {
background-color: #f5f7fa;
color: #1e3c72;
}
&.active {
background-color: #f0f2f5;
color: #1e3c72;
font-weight: 600;
border-left-color: #1e3c72;
}
}
}
}
/* 右侧内容区域 */
.right-content {
.content-card {
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
overflow: hidden;
min-height: 600px;
}
.content-header {
padding: 30px 30px 0;
.title {
font-size: 20px;
font-weight: 600;
color: #1e3c72;
margin: 0;
}
}
.content-body {
padding: 30px;
line-height: 1.8;
color: #333333;
overflow: hidden;
word-wrap: break-word;
overflow-wrap: break-word;
:deep(h1),
:deep(h2),
:deep(h3),
:deep(h4),
:deep(h5),
:deep(h6) {
color: #1e3c72;
margin-top: 24px;
margin-bottom: 16px;
}
:deep(p) {
margin-bottom: 16px;
max-width: 100%;
overflow: hidden;
}
:deep(img) {
max-width: 100% !important;
width: 100% !important;
height: auto !important;
border-radius: 4px;
margin: 16px 0;
display: block;
box-sizing: border-box;
}
:deep(a) {
color: #1e3c72;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
:deep(ul),
:deep(ol) {
margin-left: 24px;
margin-bottom: 16px;
}
:deep(li) {
margin-bottom: 8px;
}
}
}
/* 应用案例样式 */
.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-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;
}
}
}
}
}
}
/* 示范项目样式 */
.demos-content {
.demo-list {
display: flex;
flex-direction: column;
gap: 20px;
.demo-item {
display: flex;
align-items: flex-start;
gap: 20px;
background: #ffffff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
&:hover {
transform: translateX(10px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.demo-status {
flex-shrink: 0;
}
.demo-content {
flex: 1;
h3 {
font-size: 18px;
font-weight: 600;
color: #1e3c72;
margin: 0 0 10px 0;
}
p {
font-size: 14px;
color: #666666;
line-height: 1.6;
margin: 0 0 15px 0;
}
.demo-meta {
display: flex;
gap: 25px;
font-size: 13px;
color: #999999;
span {
display: flex;
align-items: center;
gap: 5px;
i {
font-size: 14px;
}
}
}
}
.demo-progress {
width: 180px;
flex-shrink: 0;
.progress-label {
font-size: 13px;
color: #666666;
margin-bottom: 10px;
}
}
}
}
}
/* 加载和空状态样式 */
.loading-container,
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
color: #999999;
i {
font-size: 48px;
margin-bottom: 16px;
}
p {
font-size: 14px;
}
}
.loading-container {
i {
color: #2a5298;
}
}
/* 响应式设计 */
@media (max-width: 992px) {
.application-content {
grid-template-columns: 1fr;
gap: 20px;
}
.left-sidebar {
order: 2;
.sidebar-menu {
display: flex;
overflow-x: auto;
white-space: nowrap;
.menu-item {
border-left: none;
border-bottom: 3px solid transparent;
display: inline-block;
margin-right: 8px;
border-radius: 4px 4px 0 0;
&.active {
border-left: none;
border-bottom-color: #1e3c72;
}
}
}
}
.right-content {
order: 1;
}
.demo-item {
flex-direction: column;
.demo-progress {
width: 100%;
}
}
}
@media (max-width: 768px) {
.application-content {
padding: 0 20px;
}
.right-content {
.content-card {
min-height: 500px;
}
.content-header {
padding: 20px 20px 0;
}
.content-body {
padding: 20px;
}
}
.left-sidebar {
.sidebar-header {
padding: 20px 20px;
h2 {
font-size: 20px;
}
}
.sidebar-menu {
.menu-item {
padding: 12px 20px;
font-size: 14px;
}
}
}
.case-item {
padding: 20px;
}
.demo-item {
padding: 20px;
}
}
</style>
<style scoped>
/* 确保图片宽度自适应 */
.content-body >>> img {
max-width: 100% !important;
width: 100% !important;
height: auto !important;
border-radius: 4px;
margin: 16px 0;
display: block;
box-sizing: border-box;
}
/* 确保富文本编辑器生成的其他元素也不会溢出 */
.content-body >>> .ql-video {
max-width: 100% !important;
width: 100% !important;
height: auto !important;
}
</style>
<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;
background: #e0e0e0;
img {
width: 100%;
height: 100%;
width: 100% !important;
height: 100% !important;
object-fit: cover;
transition: transform 0.3s ease;
margin: 0 !important;
&:hover {
transform: scale(1.05);
......
<template>
<div class="sdgsat1-case-detail">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<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/sdgsat1">SDGSAT-1专区</a></el-breadcrumb-item>
<el-breadcrumb-item><a href="/portal/sdgsat1?menu=cases">数据开发与应用</a></el-breadcrumb-item>
<el-breadcrumb-item
><a href="/portal/sdgsat1">SDGSAT-1专区</a></el-breadcrumb-item
>
<el-breadcrumb-item
><a href="/portal/sdgsat1?menu=cases"
>数据开发与应用</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="content-container" v-loading="loading">
<!-- 调试信息 -->
<div v-if="debugInfo" class="debug-info">
<h3>调试信息</h3>
<pre>{{ debugInfo }}</pre>
<div class="page-container">
<div class="detail-card" v-loading="loading">
<div class="detail-header">
<h1 class="detail-title">{{ caseDetail.title }}</h1>
</div>
<!-- 标题 -->
<div class="case-title">
<h1>{{ caseDetail.title }}</h1>
<div class="detail-body">
<div class="detail-thumbnail" v-if="caseDetail.thumbnail">
<img :src="caseDetail.thumbnail" :alt="caseDetail.title" />
</div>
<!-- 详情内容 -->
<div class="case-content" v-html="caseDetail.content"></div>
<div class="detail-content">
<div class="content-section" v-if="caseDetail.content">
<div class="content-text" v-html="caseDetail.content"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getCaseDetail } from '@/api/portal/sdgsat1'
import { getCaseDetail } from "@/api/portal/sdgsat1";
export default {
name: 'SDGSAT1CaseDetail',
name: "SDGSAT1CaseDetail",
data() {
return {
loading: true,
debugInfo: '',
debugInfo: "",
caseDetail: {
title: '',
content: ''
}
}
title: "",
content: "",
},
};
},
created() {
this.getDetail()
this.getDetail();
},
methods: {
async getDetail() {
this.loading = true
this.loading = true;
try {
const { id } = this.$route.query
const { id } = this.$route.query;
if (!id) {
this.$message.error('缺少案例ID')
this.debugInfo = '缺少案例ID: ' + JSON.stringify(this.$route.query)
this.loading = false
return
this.$message.error("缺少案例ID");
this.debugInfo = "缺少案例ID: " + JSON.stringify(this.$route.query);
this.loading = false;
return;
}
this.debugInfo = '获取案例详情,ID: ' + id
console.log('获取案例详情,ID:', id)
this.debugInfo = "获取案例详情,ID: " + id;
console.log("获取案例详情,ID:", id);
const response = await getCaseDetail(id)
this.debugInfo += '\nAPI响应: ' + JSON.stringify(response)
console.log('API响应:', response)
const response = await getCaseDetail(id);
this.debugInfo += "\nAPI响应: " + JSON.stringify(response);
console.log("API响应:", response);
if (response.code === 200 && response.data) {
this.caseDetail = response.data
this.debugInfo += '\n案例详情数据: ' + JSON.stringify(this.caseDetail)
console.log('案例详情数据:', this.caseDetail)
this.caseDetail = response.data;
this.debugInfo +=
"\n案例详情数据: " + JSON.stringify(this.caseDetail);
console.log("案例详情数据:", this.caseDetail);
} else {
this.debugInfo += '\nAPI响应格式错误: ' + JSON.stringify(response)
console.error('API响应格式错误:', response)
this.$message.error('获取案例详情失败:响应格式错误')
this.debugInfo += "\nAPI响应格式错误: " + JSON.stringify(response);
console.error("API响应格式错误:", response);
this.$message.error("获取案例详情失败:响应格式错误");
}
} catch (error) {
this.debugInfo += '\n获取案例详情失败: ' + error.message
console.error('获取案例详情失败:', error)
this.$message.error('获取案例详情失败:网络错误')
this.debugInfo += "\n获取案例详情失败: " + error.message;
console.error("获取案例详情失败:", error);
this.$message.error("获取案例详情失败:网络错误");
} finally {
this.loading = false
}
}
this.loading = false;
}
}
},
goBack() {
this.$router.back();
},
},
};
</script>
<style lang="scss" scoped>
.sdgsat1-case-detail {
width: 100%;
min-height: 100vh;
background-color: #f5f7fa;
padding: 40px 0;
min-height: calc(100vh - 200px);
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
/* 面包屑导航 */
.breadcrumb {
max-width: 1390px;
margin: 0 auto 30px;
.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;
}
}
/* 内容容器 */
.content-container {
max-width: 1390px;
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 40px 60px;
padding: 30px;
}
.detail-card {
background: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* 调试信息 */
.debug-info {
background: #f5f7fa;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 20px;
margin-bottom: 30px;
.detail-header {
padding: 40px 40px 30px;
border-bottom: 1px solid #f0f0f0;
h3 {
margin-top: 0;
.detail-title {
font-size: 32px;
font-weight: 700;
color: #1e3c72;
}
pre {
margin: 0;
white-space: pre-wrap;
font-family: monospace;
font-size: 14px;
line-height: 1.5;
color: #333;
line-height: 1.4;
}
}
/* 标题样式 */
.case-title {
.detail-body {
padding: 40px;
}
.detail-thumbnail {
width: 100%;
max-height: 500px;
border-radius: 8px;
overflow: hidden;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 1px solid #e0e0e0;
background: #f5f7fa;
h1 {
font-size: 28px;
font-weight: 700;
color: #1e3c72;
line-height: 1.4;
margin: 0;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
/* 详情内容样式 */
.case-content {
line-height: 1.8;
.detail-content {
.content-section {
margin-bottom: 40px;
&:last-child {
margin-bottom: 0;
}
.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;
}
:deep(h1),
:deep(h2),
:deep(h3),
:deep(h4),
:deep(h5),
:deep(h6) {
h1,
h2,
h3,
h4,
h5,
h6 {
color: #1e3c72;
margin-top: 30px;
margin-bottom: 20px;
}
:deep(h1) {
h1 {
font-size: 24px;
font-weight: 600;
}
:deep(h2) {
h2 {
font-size: 20px;
font-weight: 600;
}
:deep(h3) {
h3 {
font-size: 18px;
font-weight: 500;
}
:deep(p) {
margin-bottom: 20px;
}
:deep(img) {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 20px 0;
display: block;
}
:deep(ul),
:deep(ol) {
ul,
ol {
margin-left: 24px;
margin-bottom: 20px;
}
:deep(li) {
li {
margin-bottom: 10px;
}
:deep(a) {
a {
color: #1e3c72;
text-decoration: none;
......@@ -211,25 +253,26 @@ export default {
text-decoration: underline;
}
}
}
}
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.sdgsat1-case-detail {
padding: 20px 0;
}
.detail-header {
padding: 30px 20px;
.breadcrumb {
margin-bottom: 20px;
padding: 0 20px;
.detail-title {
font-size: 24px;
}
}
.content-container {
padding: 30px 20px;
.detail-body {
padding: 20px;
}
.case-title h1 {
font-size: 24px;
.detail-thumbnail {
max-height: 300px;
}
}
</style>
\ No newline at end of file
......@@ -47,12 +47,14 @@ public class PortalProgressController extends BaseController {
return AjaxResult.success(list);
}
@Anonymous
@GetMapping("/projects")
public AjaxResult getProjectList() {
List<SysProject> list = projectService.selectSysProjectList(new SysProject());
return AjaxResult.success(list);
}
@Anonymous
@GetMapping("/stats")
public AjaxResult getProgressStats() {
Map<String, Object> stats = new HashMap<>();
......@@ -84,4 +86,14 @@ public class PortalProgressController extends BaseController {
SysImplementationProgress progress = implementationProgressService.selectSysImplementationProgress();
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