This commit is contained in:
123 committed 2026-05-08 09:13:27 +08:00
1 parent fb10cafe77
commit 4ee6e9ae83
800 files changed
+114519 -75

No files matched your search

+33 -2
View File
@@ -480,6 +480,31 @@ function repairMojibakeText(input) {
}
}
/**
* 等待外部样式表应用完成,避免 pageReady 过早解除后整页 FOUC(导航竖排、大图撑破布局等)。
*/
function waitForStylesheetLink(link) {
return new Promise((resolve) => {
if (!link || link.rel !== 'stylesheet') {
resolve()
return
}
let done = false
const finish = () => {
if (done) return
done = true
resolve()
}
const tid = window.setTimeout(finish, 12000)
const onDone = () => {
window.clearTimeout(tid)
finish()
}
link.addEventListener('load', onDone, { once: true })
link.addEventListener('error', onDone, { once: true })
})
}
/** 必须先拉取再注入:若直接 link.href=服务器 CSS,浏览器会先解析远程 url(...) 资源,异步 patch 会来不及拦截 */
async function addHeadLink(href) {
if (!href) return
@@ -511,6 +536,8 @@ async function addHeadLink(href) {
const needsBlob = hadArchivedCdn || fixed !== raw
const link = document.createElement('link')
link.rel = 'stylesheet'
link.dataset.haiho = '1'
const loadPromise = waitForStylesheetLink(link)
if (needsBlob) {
const blobUrl = URL.createObjectURL(new Blob([fixed], { type: 'text/css;charset=utf-8' }))
cdnPatchBlobUrls.push(blobUrl)
@@ -518,10 +545,10 @@ async function addHeadLink(href) {
} else {
link.href = canonical
}
link.dataset.haiho = '1'
document.head.appendChild(link)
appendedNodes.push(link)
injectedHaihoStylesheetUrls.add(canonical)
await loadPromise
return
}
} catch {
@@ -532,11 +559,13 @@ async function addHeadLink(href) {
if (document.querySelector(`link[data-haiho="1"][href="${finalHref}"]`)) return
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = /^https?:\/\//i.test(finalHref) ? finalHref : canonical
link.dataset.haiho = '1'
const loadPromise = waitForStylesheetLink(link)
link.href = /^https?:\/\//i.test(finalHref) ? finalHref : canonical
document.head.appendChild(link)
appendedNodes.push(link)
injectedHaihoStylesheetUrls.add(canonical)
await loadPromise
}
function appendInlineScript(content) {
@@ -1191,7 +1220,9 @@ async function patchLinkedStylesheetsArchiverCdn() {
const blobUrl = URL.createObjectURL(new Blob([fixed], { type: 'text/css;charset=utf-8' }))
cdnPatchBlobUrls.push(blobUrl)
link.dataset.haihoCdnPatch = '1'
const loadPromise = waitForStylesheetLink(link)
link.href = blobUrl
await loadPromise
}
}
+74 -12
View File
@@ -114,15 +114,9 @@ function applyPrivacySkeletonFallback() {
}
})
// 新闻区:后端不可用时用中性提示替换列表项,维持列表节奏
const newsContainer = document.querySelector('.home-news-container .p_list, .e_loop-6 .p_list, .e_loop-16 .p_list')
if (newsContainer) {
newsContainer.innerHTML = `
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 92%"></div></div>
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 88%"></div></div>
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 84%"></div></div>
`
}
// 新闻区:不在此处改写 DOM。原逻辑用骨架覆盖 .p_loopitem 会破坏建站器结构,
// 导致后续即使 /site/news/homeRecommend 有数据,克隆模板也无法写入标题/封面(仍显示「内容建设中」)。
// 无数据时的占位在 injectHomeContent 末尾统一处理。
// 七道鲜矩阵(section4)由 fixedSection 独立注入,fallback 阶段不要改写其图片 DOM,
// 否则后续 clone 模板时会继承骨架占位,导致图片无法恢复。
@@ -173,6 +167,63 @@ export async function fetchHomeDataPublic() {
}
}
/** 首页推荐新闻(与 /site/home/public 中 news 同源;用于聚合接口失败或 news 为空时兜底) */
export async function fetchHomeRecommendNewsPublic(limit = 3) {
try {
const base = API_BASE.replace(/\/$/, '')
const q = new URLSearchParams({ limit: String(limit) })
const res = await fetch(`${base}/news/homeRecommend?${q.toString()}`, {
credentials: 'omit',
cache: 'no-store',
})
if (!res.ok) return []
const json = await res.json()
if (json.code !== 200) return []
const payload = json.data
if (Array.isArray(payload)) return payload
if (payload && typeof payload === 'object' && Array.isArray(payload.rows)) return payload.rows
return []
} catch (e) {
console.warn('[fetchHomeRecommendNewsPublic] failed:', e)
return []
}
}
function buildHomeNewsArchiveLoopItemHtml(news) {
const nid = Number(news?.newsId)
const href = Number.isFinite(nid) && nid > 0 ? `?page=news&newsId=${nid}` : '?page=news'
const title = escapeHtml(news?.newsTitle || '')
const rawCover = news?.newsCover ? String(news.newsCover).trim() : ''
const cover = rawCover ? resolveImageUrl(rawCover) : '/images/no-news.png'
let dateStr = ''
if (news?.publishTime) {
const d = new Date(news.publishTime)
if (!Number.isNaN(d.getTime())) {
dateStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
}
const safeCover = escapeHtml(cover)
const safeHref = escapeHtml(href)
const safeDate = escapeHtml(dateStr)
return `<div class="cbox-6 p_loopitem"><div class="e_container-7 s_layout">
<div class="cbox-7-0 p_item"><div class="e_image-8 s_img">
<img src="${safeCover}" alt="${title}" loading="lazy" />
</div><h3 class="e_h3-9 s_summary">
<a href="${safeHref}" target="_self">${title}</a>
</h3><p class="e_text-10 s_title">
<a href="${safeHref}" target="_self"></a>
</p><p class="e_timeFormat-11 s_title">${safeDate}</p></div>
</div></div>`
}
function homeNewsPrivacySkeletonInnerHtml() {
return `
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 92%"></div></div>
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 88%"></div></div>
<div class="p_loopitem"><p class="hy-privacy-mask-text">内容建设中</p><div class="hy-privacy-skeleton-line" style="width: 84%"></div></div>
`
}
function buildHomeProductCardHtml(product, iconChat, iconLink, contactHref) {
const imgUrl = firstProductImageUrl(product.productImages)
const name = escapeHtml(product.productName)
@@ -439,13 +490,17 @@ export async function injectHomeContent() {
}
// 4. 新闻注入 — 仅显示「首页推荐」新闻;首页模板为 e_loop-6(非 e_loop-16),需同时匹配
const newsList = (data?.news || []).slice(0, 3)
let newsList = (data?.news || []).slice(0, 3)
if (newsList.length === 0) {
newsList = await fetchHomeRecommendNewsPublic(3)
}
const newsContainer = document.querySelector(
'.home-news-container .p_list, .e_loop-6 .p_list, .e_loop-16 .p_list'
)
if (newsContainer && newsList.length > 0) {
const template = newsContainer.querySelector('.p_loopitem')
if (template) {
const templateOk = !!(template && template.querySelector('h3.e_h3-9, .e_h3-9'))
if (templateOk) {
const itemTemplate = template.cloneNode(true)
newsContainer.innerHTML = ''
newsList.forEach((news) => {
@@ -465,10 +520,17 @@ export async function injectHomeContent() {
const imgEl = item.querySelector('img')
if (imgEl) imgEl.src = resolveImageUrl(news.newsCover)
const aTags = item.querySelectorAll('a')
aTags.forEach(a => a.href = `?page=news&newsId=${news.newsId}`)
aTags.forEach((a) => {
a.href = `?page=news&newsId=${news.newsId}`
})
newsContainer.appendChild(item)
})
} else {
newsContainer.innerHTML = newsList.map((n) => buildHomeNewsArchiveLoopItemHtml(n)).join('')
}
} else if (newsContainer && newsList.length === 0) {
ensureHomePrivacySkeletonStyle()
newsContainer.innerHTML = homeNewsPrivacySkeletonInnerHtml()
}
// 5. 首页其他静态按钮动态化
+12 -4
View File
@@ -80,6 +80,9 @@ export async function t(langCode, key) {
* @param {string} langCode
*/
export function setCurrentLang(langCode) {
if (langCode === 'ko_kr') {
langCode = 'zh_cn'
}
currentLang.value = langCode
// 保存到 localStorage
localStorage.setItem('huayang_lang', langCode)
@@ -92,7 +95,12 @@ export function setCurrentLang(langCode) {
export function initLanguage() {
const saved = localStorage.getItem('huayang_lang')
if (saved) {
currentLang.value = saved
if (saved === 'ko_kr') {
localStorage.setItem('huayang_lang', 'zh_cn')
currentLang.value = 'zh_cn'
} else {
currentLang.value = saved
}
}
}
@@ -100,13 +108,14 @@ export function initLanguage() {
* 注入多语言切换器到页面头部
*/
export async function injectLanguageSwitcher() {
const languages = await fetchLanguageList()
let languages = await fetchLanguageList()
languages = languages.filter((l) => l.langCode !== 'ko_kr')
if (!languages || languages.length <= 1) return
const container = document.querySelector('.lang')
if (!container) return
// 动态生成“中 | 英 | 日 | 韩”结构
// 动态生成“中 | 英 | …”结构(韩语已下线)
container.innerHTML = ''
languages.forEach((lang, index) => {
const span = document.createElement('span')
@@ -172,7 +181,6 @@ function bindNativeLanguageSwitcher(languages) {
if (t.includes('简体') || t.includes('中文') || t === '中') return 'zh_cn'
if (t.includes('english') || t.includes('en') || t === '英') return 'en_us'
if (t.includes('日本') || t.includes('ja') || t === '日') return 'ja_jp'
if (t.includes('한국') || t.includes('한') || t.includes('ko') || t === '韩') return 'ko_kr'
return ''
}
-1
View File
@@ -59,7 +59,6 @@ export const templateModules = [
{ reqCode: 'REQ-024', title: '中文包', desc: '简体中文版本', type: 'i18n' },
{ reqCode: 'REQ-025', title: '英文包', desc: '英文版本', type: 'i18n' },
{ reqCode: 'REQ-026', title: '日文包', desc: '日文版本', type: 'i18n' },
{ reqCode: 'REQ-027', title: '韩文包', desc: '韩文版本', type: 'i18n' },
{ reqCode: 'REQ-028', title: '语言切换器', desc: '在页面顶部提供语言切换按钮入口', type: 'i18n-switch' }
]
},
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
+2 -3
View File
@@ -224,9 +224,8 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">
@@ -83,9 +83,6 @@
<a href="javascript:;" target="_blank">
| 日
</a>
<a href="javascript:;" target="_blank">
| 韩
</a>
</div>
</div>
<ul class="p_level1Box">