- 后台全面引入 Naive UI 组件库,统一 UI 规范 - 前台 usePublicApi 切换到 API 调用(GET /api/content/[key]) - 前后台数据源统一(site_content 表) - 产品线统一管理(tour/camp/course 三套编辑器) - 产品数据归一化(itinerary/faq/pricing 格式统一) - 表单提交 API 改写入 submissions 表 - 新增 submissions 标记已读 API - site-content PUT 支持 upsert - 修复前台 bug(stories 路由冲突、占位符警告、selector breadcrumb) - 消除 PageHero 类型警告(useSEO reactive 修复) - 25 个前台页面全部 HTTP 200,展示效果不变 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
205 行
4.6 KiB
Vue
205 行
4.6 KiB
Vue
<template>
|
||
<footer class="app-footer">
|
||
<div class="footer-container">
|
||
<div v-if="brand && images" class="footer-grid">
|
||
<!-- 品牌信息 -->
|
||
<div class="footer-brand">
|
||
<div class="footer-logo">
|
||
<img v-if="images?.logo?.main" :src="images.logo.main" alt="呼籁旅行" class="footer-logo-img" width="44" height="44" />
|
||
<div class="footer-logo-text">
|
||
<span class="logo-text">呼籁旅行</span>
|
||
<span class="logo-sub">HULAI TRAVEL</span>
|
||
</div>
|
||
</div>
|
||
<p class="footer-desc">{{ brand?.slogan?.functional }}</p>
|
||
<div class="footer-qr-list">
|
||
<CommonQRCodeBlock
|
||
v-for="ch in qrChannels"
|
||
:key="ch.type"
|
||
:image="ch.qrImage"
|
||
:label="ch.label"
|
||
size="small"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 导航链接 -->
|
||
<div v-for="group in footerNav" :key="group.title" class="footer-nav-group">
|
||
<h4 class="footer-nav-title">{{ group.title }}</h4>
|
||
<ul class="footer-nav-list">
|
||
<li v-for="link in group.links" :key="link.to">
|
||
<NuxtLink :to="link.to" class="footer-nav-link">{{ link.text }}</NuxtLink>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部信息 -->
|
||
<div class="footer-bottom">
|
||
<p class="footer-company">旅行服务:内蒙古呼籁国际旅行社有限公司</p>
|
||
<p class="footer-legal">
|
||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" class="footer-icp-link">{{ brand?.icp }}</a>
|
||
<span class="footer-divider">|</span>
|
||
{{ brand?.icpEntity }}
|
||
</p>
|
||
<p class="footer-copyright">
|
||
© {{ new Date().getFullYear() }} {{ brand?.fullName }} 版权所有
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</template>
|
||
|
||
<script setup>
|
||
const { data: brand } = await usePublicApi('brand')
|
||
const { data: images } = await usePublicApi('images')
|
||
const { data: navData } = await usePublicApi('navigation')
|
||
const { data: contact } = await usePublicApi('contact')
|
||
|
||
const footerNav = computed(() => navData.value?.footer || [])
|
||
const qrChannels = computed(() => (contact.value?.channels || []).filter(c => c.qrImage && !c.hidden))
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.app-footer {
|
||
background: linear-gradient(180deg, @color-primary-dark 0%, #0F2419 100%);
|
||
color: rgba(255, 255, 255, 0.7);
|
||
padding: @space-2xl 0 @space-lg;
|
||
position: relative;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 3px;
|
||
background: linear-gradient(90deg, @color-primary, @color-accent, @color-primary);
|
||
}
|
||
}
|
||
|
||
.footer-container {
|
||
.container();
|
||
}
|
||
|
||
.footer-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: @space-xl;
|
||
|
||
.respond-md({
|
||
grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
|
||
gap: @space-xl;
|
||
});
|
||
}
|
||
|
||
.footer-brand {
|
||
margin-bottom: @space-lg;
|
||
}
|
||
|
||
.footer-logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: @space-sm;
|
||
margin-bottom: @space-md;
|
||
}
|
||
|
||
.footer-logo-img {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 6px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.footer-logo-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.logo-text {
|
||
font-size: @font-size-lg;
|
||
font-weight: @font-weight-bold;
|
||
color: #fff;
|
||
}
|
||
|
||
.logo-sub {
|
||
font-size: @font-size-xs;
|
||
color: rgba(255, 255, 255, 0.4);
|
||
letter-spacing: 2px;
|
||
}
|
||
}
|
||
|
||
.footer-desc {
|
||
font-size: @font-size-sm;
|
||
margin-bottom: @space-lg;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.footer-qr-list {
|
||
display: flex;
|
||
gap: @space-lg;
|
||
}
|
||
|
||
.footer-nav-title {
|
||
font-size: @font-size-base;
|
||
font-weight: @font-weight-medium;
|
||
color: #fff;
|
||
margin-bottom: @space-md;
|
||
}
|
||
|
||
.footer-nav-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: @space-sm;
|
||
}
|
||
|
||
.footer-nav-link {
|
||
font-size: @font-size-sm;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
text-decoration: none;
|
||
transition: color @transition-fast;
|
||
|
||
&:hover {
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.footer-bottom {
|
||
margin-top: @space-2xl;
|
||
padding-top: @space-lg;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||
text-align: center;
|
||
}
|
||
|
||
.footer-company {
|
||
font-size: @font-size-sm;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
margin-bottom: @space-xs;
|
||
}
|
||
|
||
.footer-legal {
|
||
font-size: @font-size-xs;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
margin-bottom: @space-xs;
|
||
}
|
||
|
||
.footer-divider {
|
||
margin: 0 @space-sm;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.footer-copyright {
|
||
font-size: @font-size-xs;
|
||
color: rgba(255, 255, 255, 0.45);
|
||
}
|
||
|
||
.footer-icp-link {
|
||
color: rgba(255, 255, 255, 0.5);
|
||
text-decoration: none;
|
||
transition: color @transition-fast;
|
||
|
||
&:hover {
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
}
|
||
</style>
|