feat: add customer service chat, optimize case and homepage, fix api proxy

这一提交完成了多项功能升级与优化:
1.  新增全局浮动客服组件,集成腾讯云TRTC聊天能力并添加错误格式化工具
2.  新增图片资源并调整部分静态文件结构
3.  优化案例页面:重构筛选逻辑、新增空状态处理、添加加载更多功能
4.  优化首页布局:修复统计数字动画、调整移动端适配样式、优化轮播逻辑
5.  重构官方内容获取工具:添加缓存机制、优化媒体资源处理
6.  优化API代理配置,移除默认本地代理并新增环境变量配置
7.  新增页面数据恢复刷新逻辑,优化页面交互体验
8.  修复按钮组件冗余代码,调整头部导航移动端样式
This commit is contained in:
2026-07-24 18:55:51 +08:00
parent 314cd6ccd7
commit 2ff1064de7
25 changed files with 4596 additions and 327 deletions
+84 -10
View File
@@ -1,6 +1,73 @@
<script setup>
const route = useRoute()
const { openConsultation } = useConsultation()
const { fetchPublicMediaAsset, fetchPublicSiteConfig } = useOfficialContent()
const { data: siteConfigRows } = await useAsyncData('public-site-config-footer', () =>
fetchPublicSiteConfig()
)
function parseConfigValue(value, fallback) {
if (value == null || value === '') return fallback
if (typeof value !== 'string') return value
try {
return JSON.parse(value)
} catch {
return fallback
}
}
const siteConfig = computed(() => siteConfigRows.value || {})
const brandConfig = computed(() => parseConfigValue(siteConfig.value.BRAND_PROFILE, {}))
const contactConfig = computed(() => parseConfigValue(siteConfig.value.CONTACT, {}))
const footerConfig = computed(() => parseConfigValue(siteConfig.value.FOOTER, {}))
const companyName = computed(
() => brandConfig.value.companyName || brandConfig.value.name || '成都大马棒传媒有限公司'
)
const companyIntro = computed(
() =>
brandConfig.value.description ||
brandConfig.value.slogan ||
'聚焦 AI 搜索增长,用可信内容、全域信源与转化承接帮助品牌建立长期数字资产。'
)
const contactAddress = computed(
() => contactConfig.value.address || '成都市武侯区芯通科技大厦B座9楼'
)
const contactPhone = computed(() => contactConfig.value.phone || '待补充')
const contactEmail = computed(() => contactConfig.value.email || '待补充')
const copyrightText = computed(
() => footerConfig.value.copyright || `© 2026 ${companyName.value}`
)
const legalText = computed(() => {
const items = [footerConfig.value.icp || '备案信息待补充', footerConfig.value.privacyText || '隐私说明']
return items.filter(Boolean).join(' · ')
})
const footerQrAssetId = computed(() => footerConfig.value.qrAssetId || '')
const isDirectAssetUrl = (value) =>
/^(https?:)?\/\//.test(value) || value.startsWith('/') || value.startsWith('data:image/')
const directQrSrc = computed(() => {
const value = String(footerQrAssetId.value || '').trim()
return value && isDirectAssetUrl(value) ? value : ''
})
const { data: footerQrAsset } = await useAsyncData(
'public-site-config-footer-qr',
() => {
const id = String(footerQrAssetId.value || '').trim()
if (!id || isDirectAssetUrl(id)) return null
return fetchPublicMediaAsset(id).catch(() => null)
},
{ watch: [footerQrAssetId] }
)
const footerQrSrc = computed(
() =>
directQrSrc.value ||
footerQrAsset.value?.url ||
footerQrAsset.value?.fileUrl ||
footerQrAsset.value?.filePath ||
''
)
const footerQrAlt = computed(() => footerQrAsset.value?.alt || footerQrAsset.value?.name || '社媒二维码')
const sourceLabel = computed(() => {
const labels = {
@@ -22,8 +89,8 @@ const sourceLabel = computed(() => {
<footer class="subsite-footer" :class="{ 'is-interior': route.path !== '/' }">
<div class="page-width footer-content">
<div class="footer-company">
<img src="/images/lanhu/slices/home-group114-image33@2x.png" alt="成都大马棒传媒有限公司" />
<p>聚焦 AI 搜索增长用可信内容全域信源与转化承接帮助品牌建立长期数字资产</p>
<img src="/images/lanhu/slices/home-group114-image33@2x.png" :alt="companyName" />
<p>{{ companyIntro }}</p>
<button type="button" @click="openConsultation({ source: sourceLabel })">
<span>预约免费诊断</span>
<AppIcon name="arrow-right" />
@@ -49,23 +116,22 @@ const sourceLabel = computed(() => {
</div>
<div>
<strong>联系我们</strong>
<span class="contact-line"
><AppIcon name="location" />成都市武侯区芯通科技大厦B座9楼</span
>
<span class="contact-line"><AppIcon name="phone" />联系电话待补充</span>
<span class="contact-line"><AppIcon name="message" />商务邮箱待补充</span>
<span class="contact-line"><AppIcon name="location" />{{ contactAddress }}</span>
<span class="contact-line"><AppIcon name="phone" />联系电话{{ contactPhone }}</span>
<span class="contact-line"><AppIcon name="message" />商务邮箱{{ contactEmail }}</span>
</div>
</nav>
<div class="footer-qr" aria-label="专属顾问二维码占位">
<span class="qr-pattern" aria-hidden="true"></span>
<div class="footer-qr" aria-label="社媒二维码">
<img v-if="footerQrSrc" class="qr-image" :src="footerQrSrc" :alt="footerQrAlt" loading="lazy" />
<span v-else class="qr-pattern" aria-hidden="true"></span>
<strong>扫码添加专属顾问</strong>
<small>获取 1 1 GEO 增长建议</small>
</div>
</div>
<div class="footer-legal">
<div class="page-width">
<span>© 2026 成都大马棒传媒有限公司</span><span>备案信息待补充 · 隐私说明</span>
<span>{{ copyrightText }}</span><span>{{ legalText }}</span>
</div>
</div>
</footer>
@@ -177,6 +243,14 @@ const sourceLabel = computed(() => {
justify-items: center;
text-align: center;
}
.qr-image {
width: 86px;
height: 86px;
display: block;
border: 7px solid #fff;
background: #fff;
object-fit: contain;
}
.qr-pattern {
width: 86px;
height: 86px;