You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
674 lines
15 KiB
674 lines
15 KiB
<script setup>
|
|
import { ref, onMounted, onUnmounted, nextTick, computed } from 'vue'
|
|
import { getHomeConfig } from '../services/api'
|
|
import { showToast } from 'vant'
|
|
import SubmitAchievementForm from '../components/SubmitAchievementForm.vue'
|
|
import SubmitDemandForm from '../components/SubmitDemandForm.vue'
|
|
import AchievementList from '../components/AchievementList.vue'
|
|
import DemandList from '../components/DemandList.vue'
|
|
|
|
// 定义数据
|
|
const config = ref({
|
|
title: '',
|
|
introduction: '',
|
|
serviceHotline: [],
|
|
emails: [],
|
|
officialSite: []
|
|
})
|
|
|
|
const loading = ref(true)
|
|
|
|
// 将简介文本按换行符分割成段落
|
|
const introductionParagraphs = computed(() => {
|
|
if (!config.value.introduction) return []
|
|
return config.value.introduction.split(/\r\n|\r|\n/).filter(paragraph => paragraph.trim())
|
|
})
|
|
// 切换标签数据<van-icon name="balance-list" /><van-icon name="add-square" />
|
|
const tabs = [
|
|
{ key: 'achievement', name: '成果清单', icon: 'balance-list', color: '#2196f3', activeColor: '#1976d2' },
|
|
{ key: 'demand', name: '需求清单', icon: 'award', color: '#9c27b0', activeColor: '#7b1fa2' },
|
|
{ key: 'submit-achievement', name: '成果填报', icon: 'add', color: '#4caf50', activeColor: '#388e3c' },
|
|
{ key: 'submit-demand', name: '需求填报', icon: 'add-square', color: '#f44336', activeColor: '#d32f2f' }
|
|
]
|
|
|
|
// 当前激活的标签
|
|
const activeTab = ref('achievement')
|
|
|
|
// 处理标签点击
|
|
const handleTabClick = (tab) => {
|
|
// 更新激活状态
|
|
activeTab.value = tab.key
|
|
}
|
|
|
|
// 是否固定标签栏
|
|
const isTabsFixed = ref(false)
|
|
|
|
// 标签栏DOM引用
|
|
const tabsContainer = ref(null)
|
|
|
|
// 标签栏初始top值
|
|
const tabsInitialTop = ref(0)
|
|
|
|
// 滚动监听处理函数
|
|
const handleScroll = () => {
|
|
if (tabsInitialTop.value === 0) return
|
|
|
|
const scrollTop = window.scrollY
|
|
|
|
isTabsFixed.value = scrollTop >= tabsInitialTop.value
|
|
}
|
|
|
|
// 十六进制颜色转RGB值函数
|
|
const hexToRgb = (hex) => {
|
|
// 移除#号
|
|
hex = hex.replace(/^#/, '')
|
|
|
|
// 解析十六进制值
|
|
let r = 0, g = 0, b = 0
|
|
|
|
if (hex.length === 3) {
|
|
// 简写形式,如 #RGB
|
|
r = parseInt(hex[0] + hex[0], 16)
|
|
g = parseInt(hex[1] + hex[1], 16)
|
|
b = parseInt(hex[2] + hex[2], 16)
|
|
} else if (hex.length === 6) {
|
|
// 完整形式,如 #RRGGBB
|
|
r = parseInt(hex.substring(0, 2), 16)
|
|
g = parseInt(hex.substring(2, 4), 16)
|
|
b = parseInt(hex.substring(4, 6), 16)
|
|
}
|
|
|
|
return `${r}, ${g}, ${b}`
|
|
}
|
|
|
|
// 页面挂载时获取数据和添加滚动监听
|
|
onMounted(async () => {
|
|
// 获取数据
|
|
try {
|
|
loading.value = true
|
|
const response = await getHomeConfig()
|
|
if (response.code === 1) {
|
|
config.value = response.data
|
|
// 设置浏览器标题
|
|
if (response.data?.siteTitle) {
|
|
document.title = response.data.siteTitle
|
|
}
|
|
} else {
|
|
showToast('获取数据失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('请求失败:', error)
|
|
showToast('网络错误,请稍后重试')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
|
|
// 等待DOM更新后获取初始位置
|
|
nextTick(() => {
|
|
if (tabsContainer.value) {
|
|
tabsInitialTop.value = tabsContainer.value.offsetTop
|
|
}
|
|
})
|
|
|
|
// 添加滚动监听
|
|
window.addEventListener('scroll', handleScroll)
|
|
})
|
|
|
|
// 页面卸载时移除滚动监听
|
|
onUnmounted(() => {
|
|
window.removeEventListener('scroll', handleScroll)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="home">
|
|
<!-- 顶部标题区域 -->
|
|
<div class="header">
|
|
<h1 class="title">{{ config.title }}</h1>
|
|
<p class="subtitle">{{ config.subTitle }}</p>
|
|
</div>
|
|
|
|
<!-- 内容区 -->
|
|
<div class="content">
|
|
<!-- 简介区域 -->
|
|
<div class="card intro-card">
|
|
<!-- <h2 class="section-title">平台简介</h2> -->
|
|
<div v-if="loading" class="loading-container">
|
|
<van-skeleton title :row="3" animated />
|
|
</div>
|
|
<div v-else class="intro-content">
|
|
<p v-for="(paragraph, index) in introductionParagraphs" :key="index" class="intro-paragraph">
|
|
{{ paragraph }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 切换按钮区域 -->
|
|
<div class="tabs-container" ref="tabsContainer" :class="{ 'tabs-fixed': isTabsFixed }">
|
|
<div class="tabs-wrapper">
|
|
<div
|
|
v-for="tab in tabs"
|
|
:key="tab.key"
|
|
class="tab-item"
|
|
:style="{
|
|
'--tab-color': activeTab === tab.key ? tab.activeColor : tab.color,
|
|
'--tab-color-rgb': hexToRgb(activeTab === tab.key ? tab.activeColor : tab.color)
|
|
}"
|
|
:class="{ active: activeTab === tab.key }"
|
|
@click="handleTabClick(tab)"
|
|
>
|
|
<van-icon :name="tab.icon" size="18" />
|
|
<span class="tab-text">{{ tab.name }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 切换内容区域 -->
|
|
<div class="tab-content">
|
|
<!-- 成果清单 -->
|
|
<div v-if="activeTab === 'achievement'" class="tab-panel">
|
|
<AchievementList :config="config" />
|
|
</div>
|
|
|
|
<!-- 需求清单 -->
|
|
<div v-else-if="activeTab === 'demand'" class="tab-panel">
|
|
<DemandList :config="config" />
|
|
</div>
|
|
|
|
<!-- 成果填报 -->
|
|
<div v-else-if="activeTab === 'submit-achievement'" class="tab-panel">
|
|
<SubmitAchievementForm />
|
|
</div>
|
|
|
|
<!-- 需求填报 -->
|
|
<div v-else-if="activeTab === 'submit-demand'" class="tab-panel">
|
|
<SubmitDemandForm />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 底部咨询对接区域 -->
|
|
<div class="contact-section">
|
|
<div class="contact-card">
|
|
<div class="contact-title-container">
|
|
<van-icon name="chat-o" size="20" color="#4caf50" />
|
|
<h2 class="contact-title">咨询对接</h2>
|
|
</div>
|
|
|
|
<!-- 服务热线 -->
|
|
<div class="contact-item-card phone-card">
|
|
<div class="contact-item-header">
|
|
<van-icon name="service-o" size="18" color="#2196f3" />
|
|
<span class="contact-item-label">服务热线</span>
|
|
</div>
|
|
<div class="contact-item-content">
|
|
<div v-for="(item, index) in config.serviceHotline" :key="index" class="contact-person">
|
|
<span class="person-name">{{ item.name }}</span>
|
|
<a :href="`tel:${item.phone}`" class="person-phone">
|
|
<van-icon name="phone" size="14" color="#2196f3" />
|
|
<span>{{ item.phone }}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 邮箱咨询 -->
|
|
<div class="contact-item-card email-card">
|
|
<div class="contact-item-header">
|
|
<van-icon name="envelop-o" size="18" color="#9c27b0" />
|
|
<span class="contact-item-label">邮箱咨询</span>
|
|
</div>
|
|
<div class="contact-item-content">
|
|
<a v-for="(email, index) in config.emails" :key="index" :href="`mailto:${email}`" class="contact-email">
|
|
{{ email }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 协会官网 -->
|
|
<div class="contact-item-card official-site-card">
|
|
<div class="contact-item-header">
|
|
<van-icon name="link-o" size="18" color="#4caf50" />
|
|
<span class="contact-item-label">协会官网</span>
|
|
</div>
|
|
<div class="contact-item-content">
|
|
<a v-for="(site, index) in config.officialSite" :key="index" :href="site" class="contact-official-site" target="_blank">
|
|
{{ site }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* 全局样式重置和基础设置 */
|
|
.home {
|
|
min-height: 100vh;
|
|
background-color: #f5f5f5;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
}
|
|
|
|
/* 顶部标题区域 */
|
|
.header {
|
|
background: linear-gradient(135deg, #2563eb 0%, #7e22ce 100%);
|
|
color: white;
|
|
padding: 30px 20px;
|
|
text-align: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.title {
|
|
font-size: 22px;
|
|
font-weight: 700;
|
|
margin: 0;
|
|
line-height: 1.4;
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* 副标题样式 */
|
|
.subtitle {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
margin: 8px 0 0 0;
|
|
line-height: 1.3;
|
|
word-break: break-word;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
/* 内容区 */
|
|
.content {
|
|
padding: 15px;
|
|
}
|
|
|
|
/* 卡片样式 */
|
|
.card {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
margin-bottom: 15px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.card:active {
|
|
transform: translateY(1px);
|
|
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
/* 标题样式 */
|
|
.section-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin: 0 0 15px 0;
|
|
padding-bottom: 8px;
|
|
border-bottom: 2px solid #1e88e5;
|
|
}
|
|
|
|
/* 加载状态 */
|
|
.loading-container {
|
|
padding: 10px 0;
|
|
}
|
|
|
|
/* 简介内容容器 */
|
|
.intro-content {
|
|
display: block;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* 简介段落 */
|
|
.intro-paragraph {
|
|
font-size: 14px;
|
|
line-height: 1.7;
|
|
color: #555;
|
|
text-align: justify;
|
|
text-indent: 2em;
|
|
margin: 0 0 12px 0;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.intro-paragraph:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* 正文占位符 */
|
|
.content-placeholder {
|
|
text-align: center;
|
|
padding: 30px 15px;
|
|
color: #999;
|
|
font-size: 15px;
|
|
background-color: #fafafa;
|
|
border-radius: 8px;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
/* 底部咨询对接区域 */
|
|
.contact-section {
|
|
background-color: #f5f5f5;
|
|
padding: 20px 15px;
|
|
margin-top: auto;
|
|
}
|
|
|
|
/* 整体咨询卡片 */
|
|
.contact-card {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 咨询标题容器 */
|
|
.contact-title-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
/* 咨询标题 */
|
|
.contact-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin: 0;
|
|
}
|
|
|
|
/* 单个咨询项卡片 */
|
|
.contact-item-card {
|
|
border-radius: 10px;
|
|
padding: 15px;
|
|
margin-bottom: 15px;
|
|
background-color: #fafafa;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.contact-item-card:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* 服务热线卡片样式 */
|
|
.phone-card {
|
|
background-color: rgba(33, 150, 243, 0.05);
|
|
border-left: 4px solid #2196f3;
|
|
}
|
|
|
|
.phone-card .contact-item-label {
|
|
color: #2196f3;
|
|
}
|
|
|
|
.phone-card .person-phone {
|
|
color: #2196f3;
|
|
}
|
|
|
|
/* 邮箱咨询卡片样式 */
|
|
.email-card {
|
|
background-color: rgba(156, 39, 176, 0.05);
|
|
border-left: 4px solid #9c27b0;
|
|
}
|
|
|
|
.email-card .contact-item-label {
|
|
color: #9c27b0;
|
|
}
|
|
|
|
.email-card .contact-email {
|
|
color: #9c27b0;
|
|
}
|
|
|
|
/* 协会官网卡片样式 */
|
|
.official-site-card {
|
|
background-color: rgba(76, 175, 80, 0.05);
|
|
border-left: 4px solid #4caf50;
|
|
}
|
|
|
|
.official-site-card .contact-item-label {
|
|
color: #4caf50;
|
|
}
|
|
|
|
.official-site-card .contact-official-site {
|
|
color: #4caf50;
|
|
}
|
|
|
|
/* 咨询项头部 */
|
|
.contact-item-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 12px;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* 咨询项标签 */
|
|
.contact-item-label {
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* 咨询项内容 */
|
|
.contact-item-content {
|
|
padding-left: 26px;
|
|
}
|
|
|
|
/* 联系人信息 */
|
|
.contact-person {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-bottom: 12px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.contact-person:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* 联系人姓名 */
|
|
.person-name {
|
|
color: #666;
|
|
font-weight: 500;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
/* 联系人电话 */
|
|
.person-phone {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* 联系邮箱 */
|
|
.contact-email {
|
|
display: block;
|
|
font-size: 14px;
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* 协会官网链接 */
|
|
.contact-official-site {
|
|
display: block;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 1.6;
|
|
text-decoration: none;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.contact-official-site:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* 切换按钮区域 */
|
|
.tabs-container {
|
|
margin: 0;
|
|
background-color: white;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
z-index: 10;
|
|
transition: all 0.3s ease;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* 固定状态 */
|
|
.tabs-fixed {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* 切换按钮容器 */
|
|
.tabs-wrapper {
|
|
display: flex;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
/* 确保所有状态下按钮容器的宽度正确 */
|
|
.tabs-container {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* 固定状态下的按钮容器 */
|
|
.tabs-fixed .tabs-wrapper {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
/* 确保固定状态下按钮容器不超出视口 */
|
|
.tabs-fixed {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* 切换按钮项 */
|
|
.tab-item {
|
|
flex: 1 1 25%;
|
|
width: 25%;
|
|
padding: 12px 0;
|
|
text-align: center;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
color: var(--tab-color);
|
|
cursor: pointer;
|
|
position: relative;
|
|
transition: all 0.2s ease;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
border-left: 1px solid #e5e5e5;
|
|
background-color: white;
|
|
box-sizing: border-box;
|
|
min-width: 0;
|
|
}
|
|
|
|
.tab-item:first-child {
|
|
border-left: none;
|
|
}
|
|
|
|
.tab-item:active {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
/* 激活状态 */
|
|
.tab-item.active {
|
|
font-weight: 600;
|
|
/* 使用对应颜色的深色背景作为选中效果 */
|
|
background-color: var(--tab-color);
|
|
}
|
|
|
|
/* 激活状态的图标 */
|
|
.tab-item.active .van-icon {
|
|
color: white;
|
|
}
|
|
|
|
/* 激活状态的文字 */
|
|
.tab-item.active .tab-text {
|
|
color: white;
|
|
}
|
|
|
|
/* 非激活状态的图标 */
|
|
.tab-item:not(.active) .van-icon {
|
|
color: var(--tab-color);
|
|
font-size: 18px;
|
|
}
|
|
|
|
/* 非激活状态的文字 */
|
|
.tab-item:not(.active) .tab-text {
|
|
color: var(--tab-color);
|
|
font-size: 13px;
|
|
}
|
|
|
|
/* 切换内容区域 */
|
|
.tab-content {
|
|
margin-top: 15px;
|
|
}
|
|
|
|
/* 切换面板 */
|
|
.tab-panel {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
min-height: 400px;
|
|
}
|
|
|
|
/* 成果填报表单容器 */
|
|
.tab-panel :deep(.submit-achievement-form) {
|
|
padding: 0;
|
|
}
|
|
|
|
/* 适配小屏幕 */
|
|
@media (max-width: 375px) {
|
|
.tab-panel {
|
|
padding: 12px;
|
|
}
|
|
}
|
|
|
|
/* 适配小屏幕 */
|
|
@media (max-width: 375px) {
|
|
.header {
|
|
padding: 25px 15px;
|
|
}
|
|
|
|
.title {
|
|
font-size: 20px;
|
|
}
|
|
|
|
.content {
|
|
padding: 12px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 17px;
|
|
}
|
|
|
|
.contact-section {
|
|
padding: 15px 12px;
|
|
}
|
|
|
|
/* 小屏幕切换按钮 */
|
|
.tab-item {
|
|
font-size: 14px;
|
|
padding: 12px 0;
|
|
}
|
|
|
|
.tab-panel {
|
|
padding: 15px;
|
|
min-height: 300px;
|
|
}
|
|
}
|
|
</style> |