Files
hldrCenter/web/src/views/BaseOverview.vue
2025-10-27 17:22:07 +08:00

455 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="base-overview-container">
<!-- 顶部全屏封面图 -->
<div class="page-cover">
<img
:src="pageImageUrl || defaultCover"
alt="基地概况封面图"
class="cover-image"
@error="handleImageError"
>
<!-- 叠加的标题文字 -->
<div class="cover-title">
<h1 class="main-title">基地概况</h1>
<p class="english-title">BASE OVERVIEW</p>
</div>
</div>
<!-- 内容容器 -->
<div class="content-wrapper">
<!-- 导航标签区域 -->
<el-tabs
v-model="activeTab"
class="content-tabs"
:border="false"
>
<el-tab-pane label="基地简介" name="introduction">
<div class="tab-content">
<div class="content-layout">
<div class="content-left">
<h2 class="section-title">基地简介</h2>
<p class="section-english">BASE INTRODUCTION</p>
</div>
<div class="content-right">
<div class="text-content">
<!-- 按处理后的换行符渲染多段落 -->
<p v-for="(paragraph, index) in introductionParagraphs" :key="index">
{{ paragraph }}
</p>
<!-- 若无数据显示提示 -->
<p v-if="!baseInfo.introduction || introductionParagraphs.length === 0">暂无基地简介信息</p>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="基地成员" name="members">
<div class="tab-content">
<div class="content-layout">
<div class="content-left">
<h2 class="section-title">基地成员</h2>
<p class="section-english">BASE MEMBERS</p>
</div>
<div class="content-right">
<div class="text-content">
<p><strong>主任</strong> {{ baseInfo.director || '暂无信息' }}</p>
<p><strong>副主任</strong> {{ baseInfo.deputy_director || '暂无信息' }}</p>
<p><strong>研究人员</strong> {{ baseInfo.researchers || '暂无信息' }}</p>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="规章制度" name="regulations">
<div class="tab-content">
<div class="content-layout">
<div class="content-left">
<h2 class="section-title">规章制度</h2>
<p class="section-english">REGULATIONS</p>
</div>
<div class="content-right">
<div class="text-content">
<!-- 按换行分割渲染规章制度 -->
<p v-for="(item, index) in regulationsItems" :key="index">
{{ item }}
</p>
<!-- 若无数据显示提示 -->
<p v-if="!baseInfo.regulations || regulationsItems.length === 0">暂无规章制度信息</p>
</div>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="联系我们" name="contact">
<div class="tab-content">
<div class="content-layout">
<div class="content-left">
<h2 class="section-title">联系我们</h2>
<p class="section-english">CONTACT US</p>
</div>
<div class="content-right">
<div class="text-content">
<p><strong>地址</strong> {{ baseInfo.address || '暂无信息' }}</p>
<p><strong>电话</strong> {{ baseInfo.phone || '暂无信息' }}</p>
<p><strong>邮箱</strong> {{ baseInfo.email || '暂无信息' }}</p>
<p><strong>网站</strong>
<!-- 修正使用处理后的完整URL -->
<a
v-if="formattedWebsite"
:href="formattedWebsite"
target="_blank"
>
{{ baseInfo.website }}
</a>
<span v-else>暂无信息</span>
</p>
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
<!-- 底部二维码区域 -->
<div class="qrcode-container">
<div class="qrcode-item">
<img src="https://picsum.photos/100/100?random=1" alt="微信公众号" class="qrcode-img">
</div>
<div class="qrcode-item">
<img src="https://picsum.photos/100/100?random=2" alt="官方网站" class="qrcode-img">
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, computed, watch } from 'vue';
import axios from 'axios';
import { ElMessage } from 'element-plus';
// 控制当前激活的标签页
const activeTab = ref('introduction');
// 封面图地址(优先使用管理端上传的图片)
const pageImageUrl = ref('');
// 默认封面图
const defaultCover = 'https://p11-flow-imagex-download-sign.byteimg.com/tos-cn-i-a9rns2rl98/6cc03dbbc19040e888533c9c1fd65b06.png~tplv-a9rns2rl98-resize-jpeg-v1.png?rcl=20251027155649B3056BCBCEFDAC17A006&rk3s=8e244e95&rrcfp=8a172a1a&x-expires=1762156610&x-signature=moWHZ%2FOpffhAFvZmA42l0w22M%2B8%3D';
// 存储基地信息的响应式对象(与接口返回字段对应)
const baseInfo = reactive({
id: 0,
introduction: '',
regulations: '',
address: '',
phone: '',
email: '',
website: '', // 网站地址可能缺少http/https协议
director: '',
deputy_director: '',
researchers: ''
});
// 页面挂载时加载数据
onMounted(() => {
fetchCoverImage();
fetchBaseInfo();
});
// 从后端获取封面图
const fetchCoverImage = async () => {
try {
const response = await axios.post('http://localhost:8080/api/page-image/get', { page: 'BaseOverview' });
if (response.data.message === '查询成功') {
pageImageUrl.value = response.data.images[0].image_url;
}
console.log("获取封面成功:", response.data);
} catch (error) {
console.log('封面图加载失败,使用默认图', error);
}
};
// 从后端获取基地概况信息并绑定到页面
const fetchBaseInfo = async () => {
try {
const response = await axios.get('http://localhost:8080/api/base-overview');
if (response.data.success) {
Object.assign(baseInfo, response.data.data);
} else {
ElMessage.warning('获取基地信息失败:' + (response.data.message || '未知错误'));
}
} catch (error) {
console.error('获取基地概况信息失败:', error);
ElMessage.error('获取基地信息失败,请刷新页面重试');
}
};
// 图片加载失败时使用默认图
const handleImageError = () => {
pageImageUrl.value = defaultCover;
};
// 处理简介换行
const introductionParagraphs = computed(() => {
if (!baseInfo.introduction) return [];
const unescapedText = baseInfo.introduction.replace(/\\n/g, '\n');
return unescapedText.split(/\r?\n/)
.map(paragraph => paragraph.trim())
.filter(paragraph => paragraph);
});
// 处理规章制度换行
const regulationsItems = computed(() => {
if (!baseInfo.regulations) return [];
const unescapedText = baseInfo.regulations.replace(/\\n/g, '\n');
return unescapedText.split(/\r?\n/)
.map(item => item.trim())
.filter(item => item);
});
// 核心修正确保网站链接为完整URL自动补全http/https协议
const formattedWebsite = computed(() => {
if (!baseInfo.website) return '';
// 检查是否已包含协议头http:// 或 https://
const hasProtocol = /^https?:\/\//.test(baseInfo.website);
if (hasProtocol) {
return baseInfo.website; // 已有协议,直接返回
} else {
// 无协议时默认添加https://(更安全)
return `https://${baseInfo.website}`;
}
});
</script>
<style scoped>
/* 样式保持不变 */
.base-overview-container {
width: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
.page-cover {
position: relative;
width: 100vw;
height: 400px;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
margin: 0 0 40px 0;
}
.cover-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.cover-title {
position: absolute;
left: 100px;
top: 50%;
transform: translateY(-50%);
color: white;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
z-index: 2;
}
.main-title {
font-size: 48px;
font-weight: 600;
margin: 0 0 10px 0;
line-height: 1.2;
}
.english-title {
font-size: 24px;
text-transform: uppercase;
margin: 0;
letter-spacing: 2px;
}
.content-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box;
}
.content-tabs {
margin-bottom: 40px;
border-bottom: 1px solid #e0e0e0;
}
:deep(.el-tabs__nav) {
margin: 0 auto;
display: flex;
justify-content: center;
}
:deep(.el-tabs__item) {
font-size: 16px;
color: #666;
padding: 0 20px;
margin-right: 10px;
}
:deep(.el-tabs__item.is-active) {
color: #b50009;
font-weight: 500;
}
:deep(.el-tabs__active-bar) {
background-color: #b50009;
}
.tab-content {
padding: 30px 0;
}
.content-layout {
display: grid;
grid-template-columns: 280px 1fr;
gap: 30px;
align-items: start;
}
.content-left {
padding-right: 20px;
border-right: 1px solid #e0e0e0;
}
.section-title {
font-size: 24px;
color: #333;
margin: 0 0 10px 0;
font-weight: 500;
}
.section-english {
font-size: 16px;
color: #999;
margin: 0 0 20px 0;
text-transform: uppercase;
}
.text-content {
line-height: 1.8;
color: #555;
font-size: 16px;
}
.text-content p {
margin: 0 0 24px 0;
text-align: justify;
text-indent: 2em;
}
.text-content p:last-child {
margin-bottom: 0;
}
.text-content a {
color: #b50009;
text-decoration: none;
}
.text-content a:hover {
text-decoration: underline;
}
.qrcode-container {
display: flex;
justify-content: flex-end;
gap: 30px;
margin-top: 50px;
padding-top: 20px;
border-top: 1px solid #e0e0e0;
}
.qrcode-item {
width: 100px;
height: 100px;
}
.qrcode-img {
width: 100%;
height: 100%;
object-fit: cover;
border: 1px solid #eee;
padding: 4px;
}
@media (max-width: 1200px) {
.cover-title {
left: 80px;
}
}
@media (max-width: 1024px) {
.page-cover {
height: 300px;
}
.main-title {
font-size: 36px;
}
.english-title {
font-size: 20px;
}
.cover-title {
left: 60px;
}
}
@media (max-width: 768px) {
.page-cover {
height: 200px;
margin-bottom: 20px;
}
.cover-title {
left: 30px;
}
.main-title {
font-size: 28px;
}
.english-title {
font-size: 16px;
}
.content-layout {
grid-template-columns: 1fr;
}
.content-left {
border-right: none;
border-bottom: 1px solid #e0e0e0;
padding-bottom: 15px;
margin-bottom: 20px;
}
.text-content p {
text-indent: 0;
margin-bottom: 16px;
}
.qrcode-container {
justify-content: center;
}
}
@media (max-width: 480px) {
.cover-title {
left: 20px;
}
.main-title {
font-size: 24px;
}
.english-title {
font-size: 14px;
}
}
</style>