feat: 后台管理系统完整重构 + 前后台数据联通
- 后台全面引入 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>
这个提交包含在:
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<section class="section section--warm">
|
||||
<div class="container">
|
||||
<div class="story-layout">
|
||||
<!-- 左侧:配图 -->
|
||||
<div class="story-photo">
|
||||
<div class="story-photo-card">
|
||||
<img
|
||||
:src="images.brandStory.photo"
|
||||
alt="呼籁旅行创始人在呼伦贝尔草原"
|
||||
width="600"
|
||||
height="800"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:文字 -->
|
||||
<div class="story-text">
|
||||
<h2 class="story-title">{{ story.title }}</h2>
|
||||
<div class="story-title-line" />
|
||||
<div class="story-content">
|
||||
<template v-if="story.paragraphs">
|
||||
<p v-for="(para, i) in story.paragraphs" :key="i" class="story-para">{{ para }}</p>
|
||||
</template>
|
||||
<p v-else>{{ story.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import images from '~/data/images.json'
|
||||
defineProps({ story: { type: Object, required: true } })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.story-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: @space-2xl;
|
||||
|
||||
.respond-lg({
|
||||
grid-template-columns: 5fr 7fr;
|
||||
gap: @space-3xl;
|
||||
align-items: start;
|
||||
});
|
||||
}
|
||||
|
||||
// 配图
|
||||
.story-photo {
|
||||
.respond-lg({
|
||||
position: sticky;
|
||||
top: 100px;
|
||||
});
|
||||
}
|
||||
|
||||
.story-photo-card {
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
box-shadow: @shadow-lg;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
aspect-ratio: 3 / 4;
|
||||
object-fit: cover;
|
||||
object-position: center 30%;
|
||||
|
||||
.respond-lg({
|
||||
aspect-ratio: 4 / 5;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 文字
|
||||
.story-text {
|
||||
padding-top: @space-md;
|
||||
}
|
||||
|
||||
.story-title {
|
||||
font-size: @font-size-2xl;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0 0 @space-md;
|
||||
line-height: @line-height-tight;
|
||||
|
||||
.respond-md({
|
||||
font-size: @font-size-3xl;
|
||||
});
|
||||
}
|
||||
|
||||
.story-title-line {
|
||||
width: 48px;
|
||||
height: 3px;
|
||||
background: @gradient-accent;
|
||||
border-radius: 3px;
|
||||
margin-bottom: @space-xl;
|
||||
}
|
||||
|
||||
.story-content {
|
||||
p {
|
||||
font-size: @font-size-base;
|
||||
line-height: 2;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
|
||||
.story-para {
|
||||
text-indent: 2em;
|
||||
margin-bottom: @space-lg;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<CommonSectionTitle title="知识产权" subtitle="商标全品类注册 + 4项国家版权登记" />
|
||||
<p class="copyright-intro">呼籁集团重视品牌知识产权保护。「呼籁」商标已完成全品类注册,核心产品名称、品牌标识和IP形象均已在中国版权保护中心完成作品登记。</p>
|
||||
|
||||
<!-- 商标 -->
|
||||
<div v-if="trademark" class="trademark-card">
|
||||
<div class="trademark-header">
|
||||
<span class="trademark-badge">® 商标</span>
|
||||
<h3 class="trademark-name">「{{ trademark.name }}」{{ trademark.scope }}</h3>
|
||||
</div>
|
||||
<p class="trademark-desc">{{ trademark.description }}</p>
|
||||
<p class="trademark-holder">持有人:{{ trademark.holder }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 版权 -->
|
||||
<div class="copyright-grid">
|
||||
<div v-for="c in copyrights" :key="c.regNo" class="copyright-card">
|
||||
<div class="copyright-header">
|
||||
<h3 class="copyright-name">{{ c.name }}</h3>
|
||||
<span class="copyright-category">{{ c.category }}</span>
|
||||
</div>
|
||||
<p class="copyright-desc">{{ c.description }}</p>
|
||||
<div class="copyright-meta">
|
||||
<div class="copyright-meta-item">
|
||||
<span class="meta-label">登记号</span>
|
||||
<span class="meta-value">{{ c.regNo }}</span>
|
||||
</div>
|
||||
<div class="copyright-meta-item">
|
||||
<span class="meta-label">登记日期</span>
|
||||
<span class="meta-value">{{ c.date }}</span>
|
||||
</div>
|
||||
<div class="copyright-meta-item">
|
||||
<span class="meta-label">著作权人</span>
|
||||
<span class="meta-value">{{ c.holder }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="copyright-note">以上作品均经中国版权保护中心审核,依据《作品自愿登记试行办法》予以登记。</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
copyrights: { type: Array, required: true },
|
||||
trademark: { type: Object, default: null }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.copyright-intro {
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto @space-xl;
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
.trademark-card {
|
||||
max-width: 900px;
|
||||
margin: 0 auto @space-xl;
|
||||
padding: @space-xl;
|
||||
background: @color-primary-bg;
|
||||
border: 1px solid fade(@color-primary, 20%);
|
||||
border-radius: @border-radius-md;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trademark-header {
|
||||
margin-bottom: @space-sm;
|
||||
}
|
||||
|
||||
.trademark-badge {
|
||||
display: inline-block;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-bg-white;
|
||||
background: @color-primary;
|
||||
padding: 2px 10px;
|
||||
border-radius: 20px;
|
||||
margin-bottom: @space-sm;
|
||||
}
|
||||
|
||||
.trademark-name {
|
||||
font-size: @font-size-md;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: @space-sm 0 0;
|
||||
}
|
||||
|
||||
.trademark-desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-loose;
|
||||
max-width: 700px;
|
||||
margin: @space-sm auto @space-sm;
|
||||
}
|
||||
|
||||
.trademark-holder {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.copyright-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: @space-md;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
|
||||
.respond-md({
|
||||
grid-template-columns: 1fr 1fr;
|
||||
});
|
||||
}
|
||||
|
||||
.copyright-card {
|
||||
background: @color-bg-white;
|
||||
border: 1px solid @color-border;
|
||||
border-radius: @border-radius-md;
|
||||
padding: @space-lg;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
.copyright-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: @space-sm;
|
||||
}
|
||||
|
||||
.copyright-name {
|
||||
font-size: @font-size-md;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.copyright-category {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-primary;
|
||||
background: @color-primary-bg;
|
||||
padding: 2px 8px;
|
||||
border-radius: @border-radius-sm;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.copyright-desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
margin-bottom: @space-md;
|
||||
}
|
||||
|
||||
.copyright-meta {
|
||||
border-top: 1px solid @color-bg-gray;
|
||||
padding-top: @space-sm;
|
||||
}
|
||||
|
||||
.copyright-meta-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
padding: 3px 0;
|
||||
font-size: @font-size-xs;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
color: @color-text-muted;
|
||||
flex-shrink: 0;
|
||||
margin-right: @space-sm;
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
color: @color-text-secondary;
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.copyright-note {
|
||||
text-align: center;
|
||||
margin-top: @space-xl;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<CommonSectionTitle title="企业文化" />
|
||||
<p v-if="culture.core" class="culture-core">{{ culture.core }}</p>
|
||||
<div class="values-grid">
|
||||
<div v-for="v in culture.values" :key="v.name" class="value-item">
|
||||
<h3 class="value-name">{{ v.name }}</h3>
|
||||
<p class="value-expr">{{ v.expression }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({ culture: { type: Object, required: true } })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.culture-core {
|
||||
text-align: center; font-size: @font-size-xl; font-weight: 600; color: @color-primary;
|
||||
letter-spacing: 0.1em; margin-bottom: @space-2xl;
|
||||
}
|
||||
.culture-core {
|
||||
text-align: center; font-size: @font-size-xl; font-weight: 600; color: @color-primary;
|
||||
letter-spacing: 0.1em; margin-bottom: @space-2xl;
|
||||
}
|
||||
.values-grid {
|
||||
display: grid; grid-template-columns: 1fr; gap: @space-lg;
|
||||
.respond-md({ grid-template-columns: repeat(3, 1fr); });
|
||||
}
|
||||
.value-item { text-align: center; padding: @space-xl @space-lg; }
|
||||
.value-name { font-size: @font-size-xl; color: @color-primary; margin-bottom: @space-sm; }
|
||||
.value-expr { font-size: @font-size-base; color: @color-text-secondary; }
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<section class="section section--warm">
|
||||
<div class="container">
|
||||
<CommonSectionTitle title="品牌IP · 籁小咩" subtitle="LAI XIAO MIE" />
|
||||
|
||||
<div class="mascot-hero">
|
||||
<div class="mascot-image">
|
||||
<img
|
||||
:src="images.mascot.front"
|
||||
alt="籁小咩 - 呼籁旅行品牌IP吉祥物,一只可爱的小羊形象"
|
||||
width="280"
|
||||
height="380"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="mascot-info">
|
||||
<h3 class="mascot-name">籁小咩</h3>
|
||||
<p class="mascot-intro">呼籁旅行的品牌IP吉祥物,灵感来自呼伦贝尔草原上自由奔跑的小羊。圆润的羊毛卷发、温暖的大眼睛、招手的小手势——籁小咩代表着呼籁对每一个家庭的热情欢迎。</p>
|
||||
<div class="mascot-tags">
|
||||
<span class="mascot-tag">原创IP形象</span>
|
||||
<span class="mascot-tag">国家版权登记</span>
|
||||
<span class="mascot-tag">表情包</span>
|
||||
</div>
|
||||
<p class="mascot-copyright">国家版权局作品登记:国作登字-2026-F-00020268</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 草原场景 -->
|
||||
<div class="mascot-banner">
|
||||
<img
|
||||
:src="images.mascot.banner"
|
||||
alt="籁小咩在呼伦贝尔草原上向你招手"
|
||||
width="800"
|
||||
height="300"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 表情包二维码 -->
|
||||
<div class="mascot-sticker">
|
||||
<div class="mascot-sticker-qr">
|
||||
<img
|
||||
:src="images.mascot.stickerQr"
|
||||
alt="籁小咩微信表情包二维码"
|
||||
width="160"
|
||||
height="160"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="mascot-sticker-text">
|
||||
<p class="mascot-sticker-title">把籁小咩带进你的聊天</p>
|
||||
<p class="mascot-sticker-desc">微信扫一扫,免费领取「籁小咩」表情包。旅行前发给家人朋友,快乐先行一步。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import images from '~/data/images.json'
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.mascot-hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: @space-xl;
|
||||
max-width: 800px;
|
||||
margin: 0 auto @space-2xl;
|
||||
|
||||
.respond-md({
|
||||
flex-direction: row;
|
||||
text-align: left;
|
||||
gap: @space-2xl;
|
||||
});
|
||||
}
|
||||
|
||||
.mascot-image {
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 200px;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
}
|
||||
|
||||
.mascot-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mascot-name {
|
||||
font-size: 28px;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin: 0 0 @space-md;
|
||||
}
|
||||
|
||||
.mascot-intro {
|
||||
font-size: @font-size-base;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-loose;
|
||||
margin-bottom: @space-lg;
|
||||
}
|
||||
|
||||
.mascot-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: @space-sm;
|
||||
margin-bottom: @space-md;
|
||||
justify-content: center;
|
||||
|
||||
.respond-md({
|
||||
justify-content: flex-start;
|
||||
});
|
||||
}
|
||||
|
||||
.mascot-tag {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-primary;
|
||||
background: @color-primary-bg;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.mascot-copyright {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mascot-banner {
|
||||
max-width: 800px;
|
||||
margin: 0 auto @space-2xl;
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mascot-sticker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
gap: @space-lg;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
padding: @space-xl;
|
||||
background: @color-bg-white;
|
||||
border-radius: @border-radius-lg;
|
||||
border: 1px solid @color-border;
|
||||
|
||||
.respond-md({
|
||||
flex-direction: row;
|
||||
text-align: left;
|
||||
});
|
||||
}
|
||||
|
||||
.mascot-sticker-qr {
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: @border-radius-md;
|
||||
}
|
||||
}
|
||||
|
||||
.mascot-sticker-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mascot-sticker-title {
|
||||
font-size: @font-size-md;
|
||||
font-weight: @font-weight-medium;
|
||||
color: @color-text-primary;
|
||||
margin-bottom: @space-sm;
|
||||
}
|
||||
|
||||
.mascot-sticker-desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<!-- 资质认证 -->
|
||||
<CommonSectionTitle title="资质认证" />
|
||||
<div class="qual-list">
|
||||
<div v-for="c in certifications" :key="c.title" class="qual-item qual-item--cert">
|
||||
<div class="qual-icon">🏆</div>
|
||||
<div class="qual-body">
|
||||
<h3 class="qual-title">{{ c.title }}</h3>
|
||||
<p class="qual-detail">{{ c.detail }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 小红书WILL大会获奖照片 -->
|
||||
<div class="award-gallery">
|
||||
<div class="award-gallery-grid">
|
||||
<img src="/images/awards/will-award-person.jpg" alt="呼籁旅行创始人在2026小红书WILL商业大会领取年度成长型品牌奖" class="award-img award-img--main" loading="lazy" />
|
||||
<img src="/images/awards/will-award-trophy.jpg" alt="2026小红书WILL商业大会年度成长型品牌奖杯" class="award-img" loading="lazy" />
|
||||
<img src="/images/awards/will-award-stage.jpg" alt="2026小红书WILL商业大会年度成长型品牌颁奖典礼" class="award-img" loading="lazy" />
|
||||
</div>
|
||||
<p class="award-caption">2026小红书WILL商业大会 · 年度成长型品牌 · 文旅行业仅6家获此殊荣</p>
|
||||
</div>
|
||||
|
||||
<!-- 服务保障 -->
|
||||
<CommonSectionTitle title="服务保障" style="margin-top: 48px;" />
|
||||
<div class="qual-list qual-list--grid">
|
||||
<div v-for="g in guarantees" :key="g.title" class="qual-item qual-item--guarantee">
|
||||
<div class="qual-icon">✅</div>
|
||||
<div class="qual-body">
|
||||
<h3 class="qual-title">{{ g.title }}</h3>
|
||||
<p class="qual-detail">{{ g.detail }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
certifications: { type: Array, required: true },
|
||||
guarantees: { type: Array, required: true }
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.qual-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-md;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.qual-list--grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: @space-md;
|
||||
.respond-md({
|
||||
grid-template-columns: 1fr;
|
||||
});
|
||||
}
|
||||
.qual-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: @space-sm;
|
||||
padding: @space-lg;
|
||||
border-radius: @border-radius-md;
|
||||
transition: transform 0.2s ease;
|
||||
&:hover { transform: translateY(-2px); }
|
||||
}
|
||||
.qual-item--cert {
|
||||
background: @color-primary-bg;
|
||||
border-left: 3px solid @color-primary;
|
||||
}
|
||||
.qual-item--guarantee {
|
||||
background: #f8faf5;
|
||||
border-left: 3px solid @color-primary;
|
||||
}
|
||||
.qual-icon {
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
.qual-body { flex: 1; }
|
||||
.qual-title {
|
||||
font-size: @font-size-md;
|
||||
color: @color-text-primary;
|
||||
margin-bottom: @space-xs;
|
||||
font-weight: @font-weight-bold;
|
||||
}
|
||||
.qual-detail {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
|
||||
.award-gallery {
|
||||
max-width: 800px;
|
||||
margin: @space-xl auto 0;
|
||||
}
|
||||
|
||||
.award-gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: @space-sm;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.award-img {
|
||||
width: 100%;
|
||||
border-radius: @border-radius-md;
|
||||
object-fit: cover;
|
||||
aspect-ratio: 3 / 4;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
.award-caption {
|
||||
text-align: center;
|
||||
margin-top: @space-md;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<section class="section section--gray">
|
||||
<div class="container">
|
||||
<CommonSectionTitle :title="`${subsidiaries.length}家集团企业`" subtitle="各司其职,共同为您的旅行保驾护航" />
|
||||
|
||||
<!-- 集团母公司 -->
|
||||
<div v-if="parent" class="sub-parent">
|
||||
<span class="sub-parent-tag">集团母公司</span>
|
||||
<h3 class="sub-parent-name">{{ parent.name }}</h3>
|
||||
<p class="sub-parent-role">{{ parent.role }}</p>
|
||||
<p v-if="parent.established" class="sub-parent-date">成立于{{ formatDate(parent.established) }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 子公司网格 -->
|
||||
<div class="sub-grid">
|
||||
<div v-for="sub in children" :key="sub.name" class="sub-item">
|
||||
<h3 class="sub-name">{{ sub.name }}</h3>
|
||||
<p class="sub-role">{{ sub.role }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({ subsidiaries: { type: Array, required: true } })
|
||||
|
||||
const parent = props.subsidiaries.find(s => s.role.includes('集团母公司') || s.role.includes('母公司'))
|
||||
const children = props.subsidiaries.filter(s => s !== parent)
|
||||
|
||||
function formatDate(dateStr) {
|
||||
const [year, month] = dateStr.split('-')
|
||||
return `${year}年${parseInt(month)}月`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sub-parent {
|
||||
max-width: 900px;
|
||||
margin: 0 auto @space-xl;
|
||||
padding: @space-xl @space-2xl;
|
||||
background: @color-bg-white;
|
||||
border: 1px solid @color-border;
|
||||
border-radius: @border-radius-lg;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sub-parent-tag {
|
||||
display: inline-block;
|
||||
font-size: @font-size-xs;
|
||||
color: @color-bg-white;
|
||||
background: @color-primary;
|
||||
padding: 2px 12px;
|
||||
border-radius: 20px;
|
||||
margin-bottom: @space-sm;
|
||||
}
|
||||
|
||||
.sub-parent-name {
|
||||
font-size: @font-size-lg;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
margin-bottom: @space-xs;
|
||||
}
|
||||
|
||||
.sub-parent-role {
|
||||
font-size: @font-size-base;
|
||||
color: @color-text-secondary;
|
||||
margin-bottom: @space-xs;
|
||||
}
|
||||
|
||||
.sub-parent-date {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sub-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: @space-md;
|
||||
|
||||
.respond-md({ grid-template-columns: repeat(2, 1fr); });
|
||||
.respond-lg({ grid-template-columns: repeat(2, 1fr); max-width: 900px; margin-left: auto; margin-right: auto; });
|
||||
}
|
||||
|
||||
.sub-item {
|
||||
padding: @space-lg;
|
||||
background: @color-bg-white;
|
||||
border-radius: @border-radius-md;
|
||||
border: 1px solid @color-border;
|
||||
}
|
||||
|
||||
.sub-name {
|
||||
font-size: @font-size-base;
|
||||
color: @color-primary;
|
||||
margin-bottom: @space-xs;
|
||||
}
|
||||
|
||||
.sub-role {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<section class="section section--warm">
|
||||
<div class="container">
|
||||
<CommonSectionTitle title="我们的团队" />
|
||||
<p class="team-summary">{{ team.summary }}</p>
|
||||
|
||||
<!-- 团队照片墙 -->
|
||||
<div class="team-gallery">
|
||||
<!-- 上排:两年踩线对比,体现年年坚持 -->
|
||||
<div class="team-gallery-row team-gallery-row--survey">
|
||||
<div class="team-photo-card">
|
||||
<div class="team-photo-wrap team-photo-wrap--survey">
|
||||
<img
|
||||
:src="images.team.guideGroup"
|
||||
alt="2024呼籁文旅车队踩线全体领队合影"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="team-photo-caption">
|
||||
<span class="caption-tag">2024踩线</span>
|
||||
<span class="caption-text">「额吉的故乡」全体车队师傅集体踩线</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="team-photo-card">
|
||||
<div class="team-photo-wrap team-photo-wrap--survey">
|
||||
<img
|
||||
:src="images.team.routeSurvey"
|
||||
alt="2025秋呼籁文旅游牧的森林车队踩线全体领队合影"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="team-photo-caption">
|
||||
<span class="caption-tag">2025踩线</span>
|
||||
<span class="caption-text">「游牧的森林」新品路线全员实地考察</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下排:培训 + 牧场 -->
|
||||
<div class="team-gallery-row team-gallery-row--misc">
|
||||
<div class="team-photo-card">
|
||||
<div class="team-photo-wrap team-photo-wrap--misc">
|
||||
<img
|
||||
:src="images.team.guideTraining"
|
||||
alt="呼籁文旅2026年司导线下培训"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="team-photo-caption">
|
||||
<span class="caption-tag">专业培训</span>
|
||||
<span class="caption-text">每年冬季集中培训,统一服务标准</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="team-photo-card">
|
||||
<div class="team-photo-wrap team-photo-wrap--misc">
|
||||
<img
|
||||
:src="images.team.ranchStore"
|
||||
alt="呼籁牧场线下店开业团队合影"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="team-photo-caption">
|
||||
<span class="caption-tag">实体布局</span>
|
||||
<span class="caption-text">呼籁牧场线下体验店正式落成</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import images from '~/data/images.json'
|
||||
defineProps({ team: { type: Object, required: true } })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.team-summary {
|
||||
max-width: 800px;
|
||||
margin: 0 auto @space-2xl;
|
||||
text-align: center;
|
||||
font-size: @font-size-md;
|
||||
line-height: @line-height-base;
|
||||
color: @color-text-secondary;
|
||||
}
|
||||
|
||||
.team-gallery {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: @space-lg;
|
||||
|
||||
.respond-md({
|
||||
gap: @space-xl;
|
||||
});
|
||||
}
|
||||
|
||||
.team-gallery-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: @space-lg;
|
||||
|
||||
.respond-md({
|
||||
gap: @space-xl;
|
||||
});
|
||||
}
|
||||
|
||||
.team-gallery-row--survey {
|
||||
.respond-md({
|
||||
grid-template-columns: 1fr 1fr;
|
||||
});
|
||||
}
|
||||
|
||||
.team-gallery-row--misc {
|
||||
.respond-md({
|
||||
grid-template-columns: 1fr 1fr;
|
||||
});
|
||||
}
|
||||
|
||||
.team-photo-card {
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
background: @color-bg-white;
|
||||
box-shadow: @shadow-md;
|
||||
transition: transform @transition-base, box-shadow @transition-base;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: @shadow-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.team-photo-wrap {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.team-photo-wrap--survey {
|
||||
img {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
}
|
||||
|
||||
.team-photo-wrap--misc {
|
||||
img {
|
||||
aspect-ratio: 16 / 10;
|
||||
}
|
||||
}
|
||||
|
||||
.team-photo-caption {
|
||||
padding: @space-md @space-lg;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: @space-md;
|
||||
}
|
||||
|
||||
.caption-tag {
|
||||
flex-shrink: 0;
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
background: @color-primary-bg;
|
||||
color: @color-primary;
|
||||
font-size: @font-size-sm;
|
||||
font-weight: @font-weight-medium;
|
||||
border-radius: @border-radius-sm;
|
||||
}
|
||||
|
||||
.caption-text {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<CommonSectionTitle title="小红书上的呼籁" subtitle="XIAOHONGSHU" />
|
||||
|
||||
<div class="xhs-card">
|
||||
<!-- 顶部:品牌主页截图作为横幅 -->
|
||||
<div class="xhs-banner">
|
||||
<img
|
||||
:src="images.xiaohongshu.storefront"
|
||||
alt="呼籁旅行小红书品牌主页 - Mr.小雷-呼籁旅行 50万家庭青睐的草原旅行品牌"
|
||||
width="800"
|
||||
height="300"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 底部:数据 + 信息 -->
|
||||
<div class="xhs-body">
|
||||
<!-- 账号信息 -->
|
||||
<div class="xhs-header">
|
||||
<div class="xhs-account">
|
||||
<span class="xhs-account-name">{{ xhs.account }}</span>
|
||||
<span class="xhs-badge">{{ xhs.verifiedType }}</span>
|
||||
</div>
|
||||
<p class="xhs-tagline">{{ xhs.tagline }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 数据指标 -->
|
||||
<div class="xhs-stats">
|
||||
<div class="xhs-stat">
|
||||
<span class="xhs-stat-value">{{ xhs.followers }}</span>
|
||||
<span class="xhs-stat-label">粉丝</span>
|
||||
</div>
|
||||
<div class="xhs-stat-divider" />
|
||||
<div class="xhs-stat">
|
||||
<span class="xhs-stat-value">{{ xhs.likes }}</span>
|
||||
<span class="xhs-stat-label">获赞与收藏</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 荣誉 + 描述 -->
|
||||
<div class="xhs-awards">
|
||||
<span v-for="award in xhs.awards" :key="award" class="xhs-award">{{ award }}</span>
|
||||
</div>
|
||||
|
||||
<p class="xhs-desc">{{ xhs.description }}</p>
|
||||
|
||||
<div class="xhs-tags">
|
||||
<span v-for="tag in xhs.tags" :key="tag" class="xhs-tag">{{ tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import images from '~/data/images.json'
|
||||
defineProps({ xhs: { type: Object, required: true } })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.xhs-card {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
border-radius: @border-radius-lg;
|
||||
overflow: hidden;
|
||||
background: @color-bg-white;
|
||||
box-shadow: @shadow-md;
|
||||
}
|
||||
|
||||
.xhs-banner {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.xhs-body {
|
||||
padding: @space-xl;
|
||||
|
||||
.respond-md({
|
||||
padding: @space-xl @space-2xl;
|
||||
});
|
||||
}
|
||||
|
||||
.xhs-header {
|
||||
margin-bottom: @space-lg;
|
||||
}
|
||||
|
||||
.xhs-account {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: @space-sm;
|
||||
margin-bottom: @space-xs;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.xhs-account-name {
|
||||
font-size: @font-size-lg;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
}
|
||||
|
||||
.xhs-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 10px;
|
||||
background: #ff2442;
|
||||
color: #fff;
|
||||
font-size: @font-size-xs;
|
||||
border-radius: 20px;
|
||||
font-weight: @font-weight-medium;
|
||||
}
|
||||
|
||||
.xhs-tagline {
|
||||
font-size: @font-size-base;
|
||||
color: @color-primary;
|
||||
font-weight: @font-weight-medium;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.xhs-stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: @space-lg;
|
||||
padding: @space-lg 0;
|
||||
margin-bottom: @space-lg;
|
||||
border-top: 1px solid @color-border;
|
||||
border-bottom: 1px solid @color-border;
|
||||
}
|
||||
|
||||
.xhs-stat {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.xhs-stat-divider {
|
||||
width: 1px;
|
||||
height: 32px;
|
||||
background: @color-border;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.xhs-stat-value {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @color-text-primary;
|
||||
line-height: 1.2;
|
||||
|
||||
.respond-md({
|
||||
font-size: 28px;
|
||||
});
|
||||
}
|
||||
|
||||
.xhs-stat-label {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-muted;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xhs-awards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: @space-sm;
|
||||
margin-bottom: @space-md;
|
||||
}
|
||||
|
||||
.xhs-award {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 5px 12px;
|
||||
background: linear-gradient(135deg, #FFF8E1, #FFF3CD);
|
||||
color: #B8860B;
|
||||
font-size: @font-size-sm;
|
||||
font-weight: @font-weight-medium;
|
||||
border-radius: @border-radius-sm;
|
||||
border: 1px solid #F0D58C;
|
||||
|
||||
&::before {
|
||||
content: "🏆";
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.xhs-desc {
|
||||
font-size: @font-size-sm;
|
||||
color: @color-text-secondary;
|
||||
line-height: @line-height-loose;
|
||||
margin-bottom: @space-md;
|
||||
}
|
||||
|
||||
.xhs-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: @space-sm;
|
||||
}
|
||||
|
||||
.xhs-tag {
|
||||
font-size: @font-size-xs;
|
||||
color: @color-text-secondary;
|
||||
background: @color-bg-gray;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
</style>
|
||||
在新工单中引用
屏蔽一个用户