2ff1064de7
这一提交完成了多项功能升级与优化: 1. 新增全局浮动客服组件,集成腾讯云TRTC聊天能力并添加错误格式化工具 2. 新增图片资源并调整部分静态文件结构 3. 优化案例页面:重构筛选逻辑、新增空状态处理、添加加载更多功能 4. 优化首页布局:修复统计数字动画、调整移动端适配样式、优化轮播逻辑 5. 重构官方内容获取工具:添加缓存机制、优化媒体资源处理 6. 优化API代理配置,移除默认本地代理并新增环境变量配置 7. 新增页面数据恢复刷新逻辑,优化页面交互体验 8. 修复按钮组件冗余代码,调整头部导航移动端样式
190 lines
5.6 KiB
JavaScript
190 lines
5.6 KiB
JavaScript
const ALL_LABEL = '全部'
|
|
const LATEST_LABEL = '最新文章'
|
|
const FALLBACK_IMAGE = '/images/lanhu/slices/home-group95-image21@2x.png'
|
|
|
|
function isUsableUrl(value) {
|
|
const url = String(value || '').trim()
|
|
if (!url || url === '#') return false
|
|
return !/^javascript:/i.test(url)
|
|
}
|
|
|
|
function pickUrl(asset) {
|
|
return [asset?.url, asset?.fileUrl, asset?.filePath].find(isUsableUrl) || ''
|
|
}
|
|
|
|
function buildMediaMap(media = []) {
|
|
return new Map(media.map((asset) => [asset.id, asset]))
|
|
}
|
|
|
|
function noCacheOptions(query = {}) {
|
|
return {
|
|
query: {
|
|
...query,
|
|
_t: Date.now()
|
|
},
|
|
headers: {
|
|
'Cache-Control': 'no-cache'
|
|
}
|
|
}
|
|
}
|
|
|
|
function toTags(value) {
|
|
if (Array.isArray(value)) return value.filter(Boolean)
|
|
return String(value || '')
|
|
.split(/[,,、\s]+/)
|
|
.map((item) => item.trim())
|
|
.filter(Boolean)
|
|
}
|
|
|
|
function bySortAndTime(left, right) {
|
|
const leftSort = Number.isFinite(Number(left.sortOrder)) ? Number(left.sortOrder) : 9999
|
|
const rightSort = Number.isFinite(Number(right.sortOrder)) ? Number(right.sortOrder) : 9999
|
|
if (leftSort !== rightSort) return leftSort - rightSort
|
|
return String(right.updatedAt || right.publishedAt || '').localeCompare(
|
|
String(left.updatedAt || left.publishedAt || '')
|
|
)
|
|
}
|
|
|
|
export function useOfficialContent() {
|
|
const config = useRuntimeConfig()
|
|
const nuxtApp = useNuxtApp()
|
|
const apiBase = config.public.officialApiBase || '/api'
|
|
const siteConfigCache = useState('public-site-config-cache', () => null)
|
|
const siteConfigLoaded = useState('public-site-config-loaded', () => false)
|
|
|
|
function fetchPublicContent(type) {
|
|
return $fetch('/public/content', {
|
|
baseURL: apiBase,
|
|
...noCacheOptions({ type })
|
|
})
|
|
}
|
|
|
|
function fetchPublicCategories() {
|
|
return $fetch('/public/categories', {
|
|
baseURL: apiBase,
|
|
...noCacheOptions()
|
|
})
|
|
}
|
|
|
|
function fetchHomepageSections() {
|
|
return $fetch('/public/homepage/sections', {
|
|
baseURL: apiBase,
|
|
...noCacheOptions()
|
|
})
|
|
}
|
|
|
|
function fetchPublicSiteConfig(options = {}) {
|
|
if (!options.fresh && siteConfigLoaded.value) return siteConfigCache.value || {}
|
|
|
|
nuxtApp._publicSiteConfigPromise ||= $fetch('/public/site-config', {
|
|
baseURL: apiBase,
|
|
...noCacheOptions()
|
|
})
|
|
.then((rows) => {
|
|
siteConfigCache.value = rows || {}
|
|
siteConfigLoaded.value = true
|
|
return siteConfigCache.value
|
|
})
|
|
.finally(() => {
|
|
nuxtApp._publicSiteConfigPromise = null
|
|
})
|
|
|
|
return nuxtApp._publicSiteConfigPromise
|
|
}
|
|
|
|
function fetchPublicMediaAsset(id) {
|
|
if (!id) return null
|
|
return $fetch(`/public/media/${encodeURIComponent(id)}`, {
|
|
baseURL: apiBase,
|
|
...noCacheOptions()
|
|
})
|
|
}
|
|
|
|
function assetUrl(assetId, mediaMap, fallback = FALLBACK_IMAGE) {
|
|
if (!assetId) return fallback
|
|
return pickUrl(mediaMap.get(assetId)) || fallback
|
|
}
|
|
|
|
function assetAlt(assetId, mediaMap, fallback = '') {
|
|
if (!assetId) return fallback
|
|
return mediaMap.get(assetId)?.alt || fallback
|
|
}
|
|
|
|
function normalizeCase(item, mediaMap) {
|
|
const metric = item.metricValue || ''
|
|
const metricLabel = item.metricLabel || ''
|
|
const clientName =
|
|
item.isAnonymous && item.clientName ? `匿名客户:${item.clientName}` : item.clientName
|
|
|
|
return {
|
|
...item,
|
|
industry: item.industry || item.category || '未分类',
|
|
client: clientName || '匿名客户',
|
|
cover: item.coverUrl || assetUrl(item.coverAssetId, mediaMap),
|
|
coverAlt: item.coverAlt || assetAlt(item.coverAssetId, mediaMap, item.title),
|
|
summary: item.summary || item.description || '',
|
|
description: item.description || item.summary || '',
|
|
metric,
|
|
metricLabel,
|
|
metrics: [
|
|
[metric, metricLabel],
|
|
[item.secondary, '辅助成果']
|
|
].filter((row) => row[0]),
|
|
secondary: item.secondary || '',
|
|
contentHtml: item.contentHtml || ''
|
|
}
|
|
}
|
|
|
|
function normalizeArticle(item, mediaMap) {
|
|
return {
|
|
...item,
|
|
category: item.category || '未分类',
|
|
excerpt: item.summary || item.description || '',
|
|
date: item.publishDate || item.publishedAt?.slice?.(0, 10) || '',
|
|
readingTime: item.readingTime || '约 5 分钟',
|
|
views: item.views || '0',
|
|
cover: item.coverUrl || assetUrl(item.coverAssetId, mediaMap),
|
|
coverAlt: item.coverAlt || assetAlt(item.coverAssetId, mediaMap, item.title),
|
|
featured: Boolean(item.isFeatured),
|
|
tags: toTags(item.tags),
|
|
contentHtml: item.contentHtml || ''
|
|
}
|
|
}
|
|
|
|
function normalizeWhitepaper(item, mediaMap) {
|
|
const documentAsset = item.documentAssetId ? mediaMap.get(item.documentAssetId) : null
|
|
|
|
return {
|
|
...item,
|
|
category: item.category || '行业白皮书',
|
|
excerpt: item.summary || item.description || '',
|
|
cta: item.cta || '获取白皮书',
|
|
cover: item.coverUrl || assetUrl(item.coverAssetId, mediaMap),
|
|
documentUrl: isUsableUrl(item.documentUrl) ? item.documentUrl : pickUrl(documentAsset),
|
|
documentName: item.documentName || documentAsset?.name || item.title,
|
|
contentHtml: item.contentHtml || ''
|
|
}
|
|
}
|
|
|
|
function categoryList(items, field = 'category', extras = []) {
|
|
const values = items.map((item) => item[field]).filter(Boolean)
|
|
return [ALL_LABEL, ...extras, ...Array.from(new Set(values))]
|
|
}
|
|
|
|
return {
|
|
ALL_LABEL,
|
|
LATEST_LABEL,
|
|
bySortAndTime,
|
|
buildMediaMap,
|
|
fetchPublicCategories,
|
|
fetchPublicContent,
|
|
fetchHomepageSections,
|
|
fetchPublicMediaAsset,
|
|
fetchPublicSiteConfig,
|
|
normalizeCase,
|
|
normalizeArticle,
|
|
normalizeWhitepaper,
|
|
categoryList
|
|
}
|
|
}
|