Files
hjc-web/nuxt.config.ts
T
weicw-dshsa 0f1a6144c0 feat(web): 接入品牌 Favicon,并修好一直 404 的图标链接
nuxt.config.ts 早已声明 /favicon.ico,但 public/ 下并无该文件,
该请求一直 404(配置写了、文件没放)。

- public/ 新增 favicon.svg(内嵌 256px PNG,高分屏清晰)、favicon.ico
  (16/32/48/64 四尺寸打包)、apple-touch-icon.png、icon-192.png
- link 里把 SVG 排到 .ico 之前:浏览器取第一个能用的,SVG 更清晰,.ico 兜底旧版
- .gitignore 忽略 .codegraph/(本地索引)与 hjc-web.zip
  (项目快照包,内含被排除的 .env,入库会把配置写进历史)
2026-10-09 10:20:11 +08:00

173 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function getOrigin(url: string) {
try {
return new URL(url).origin
} catch {
return ''
}
}
/**
* 解析布尔型环境变量:'true'→true,'false'→false,其余→null(交由后台配置决定)。
*/
function toBool(v?: string): boolean | null {
if (v === 'true') return true
if (v === 'false') return false
return null
}
const tenantId = process.env.NUXT_PUBLIC_TENANT_ID || ''
// appId 是 app_product.product_id 数字主键(如 42),不是 productCode('website')。
// 留空时下游 getSubscriptionStatus 等按空值降级,绝不回退到编码值,
// 否则 /api/app/product/detail/{productId} 会因 Integer 转换失败报错。
const appId = process.env.NUXT_PUBLIC_APP_ID || ''
const templateId = process.env.NUXT_PUBLIC_TEMPLATE_ID || 'template-07'
// 强制模板 ID:一旦设置则拥有最高优先级,覆盖应用/站点绑定(主要用于本地调试)
const forceTemplateId = process.env.NUXT_PUBLIC_FORCE_TEMPLATE_ID || ''
// 站点名称覆盖:设置后优先于后端 AppProduct / CMS 返回值(多用于本地调试或强制品牌名)
const siteName = process.env.NUXT_PUBLIC_SITE_NAME || ''
const modulesApiBase =
process.env.NUXT_PUBLIC_MODULES_API_BASE ||
'https://api.gxhuijicai.com/api'
const appApiBase =
process.env.NUXT_PUBLIC_APP_API_BASE ||
'https://api.gxhuijicai.com'
const fileServerBase =
process.env.NUXT_PUBLIC_FILE_SERVER_BASE ||
getOrigin(appApiBase) ||
'https://api.gxhuijicai.com'
const baseDomain =
process.env.NUXT_PUBLIC_BASE_DOMAIN || 'gxhuijicai.com'
// 多主域清单:子域名租户解析支持的主域(逗号分隔)。
// 默认 gxhuijicai.com(官网主域)居首,shoplnk.cn/sitelink.cn/wsdns.cn 为平台遗留主域共存保留;
// baseDomain 仅作兼容保留(取第一个)。
const baseDomains = (
process.env.NUXT_PUBLIC_BASE_DOMAINS ||
'gxhuijicai.com,shoplnk.cn,sitelink.cn,wsdns.cn'
).split(',').map(s => s.trim()).filter(Boolean)
// 子域名前缀白名单:仅这些前缀 + 数字租户号 + 主域 会被识别为平台子域名租户
const subdomainPrefixes = (
process.env.NUXT_PUBLIC_SUBDOMAIN_PREFIXES ||
'site,shop,store,mp,app,oa'
).split(',').map(s => s.trim()).filter(Boolean)
// 联系电话覆盖:当后端 phone 被脱敏时,作为兜底显示
const phone = process.env.NUXT_PUBLIC_PHONE || ''
const WATCH_IGNORED = [
'**/.git/**',
'**/node_modules/**',
'**/.nuxt/**',
'**/.output/**',
'**/dist/**',
'**/coverage/**',
'**/.cache/**',
'**/.workbuddy/**'
]
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss', '@ant-design-vue/nuxt'],
app: {
head: {
htmlAttrs: { lang: 'zh-CN' },
titleTemplate: '%s',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'robots', content: 'index,follow' }
],
link: [
// 图标优先级:SVG 在前,浏览器取第一个能用的;.ico 兜底旧版浏览器
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'apple-touch-icon', href: '/apple-touch-icon.png' },
// 提前与接口/图片源站建立连接,降低首字节与图片加载延迟
{ rel: 'preconnect', href: 'https://api.gxhuijicai.com', crossorigin: '' },
{ rel: 'dns-prefetch', href: 'https://api.gxhuijicai.com' },
{ rel: 'preconnect', href: 'https://file.wsdns.cn', crossorigin: '' },
{ rel: 'dns-prefetch', href: 'https://file.wsdns.cn' },
// 思源黑体(Noto Sans SC,SIL 开源许可,版权无忧)网页字体,全站统一字体
{ rel: 'preconnect', href: 'https://fonts.googleapis.cn' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.cn', crossorigin: '' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.cn/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap' }
],
// 首屏绘制前注入 .js 类,配合 animations.css 的 `.js [data-reveal]`
// 规则实现「无 JS 时内容可见、有 JS 时先隐藏再由滚动揭示」,杜绝 SSR 闪烁
script: [
{
innerHTML:
"document.documentElement.classList.add('js')",
tagPosition: 'head'
}
]
}
},
tailwindcss: {
cssPath: '~/assets/css/tailwind.css',
configPath: 'tailwind.config.cjs'
},
experimental: { appManifest: false },
// 全局 SSR,保证 SEO
routeRules: {
'/renewal': { ssr: true }
},
runtimeConfig: {
// 仅服务端可见
subscriptionCacheTtl: Number(process.env.NUXT_SUBSCRIPTION_CACHE_TTL || 60),
public: {
tenantId,
appId,
templateId,
forceTemplateId,
siteName,
modulesApiBase,
appApiBase,
fileServerBase,
baseDomain,
baseDomains,
subdomainPrefixes,
phone,
// 微信二维码覆盖:设置后优先于 CMS 返回的 wxQrcode(用于本地调试或强制指定二维码)
// ⚠️ 不要写硬编码默认图!必须保持空字符串,让 Footer 从 siteInfo.config.wxQrcode / siteInfo.qrCode 读取后台上传
wxQrcode: process.env.NUXT_PUBLIC_WX_QRCODE || '',
// 在线留言滑块验证码开关覆盖(可选):
// 默认 null → 由后台「网站设置」setting.requireCaptcha 决定;
// 设 true/false 可在不改后台的情况下强制开/关(仅部署级覆盖,后台显式配置优先)。
contactCaptcha: toBool(process.env.NUXT_PUBLIC_CONTACT_CAPTCHA)
}
},
css: ['~/assets/css/tailwind.css', '~/assets/css/animations.css'],
vite: {
server: {
// 本地开发允许用自定义域名(如 cz-hro.shoplnk.cn)访问,
// 否则 Vite 会在请求进入中间件前直接 403(Blocked request. This host is not allowed)
// 以 "." 开头的条目表示允许该域名及其所有子域。
// 注意:这里只是放宽 Vite 的 host 校验,真正「是否解析为合法租户」仍由
// tenant 中间件按白名单前缀 + 主域清单判断;非白名单子域不会命中租户。
allowedHosts: ['.gxhuijicai.com', '.shoplnk.cn', '.sitelink.cn', '.wsdns.cn', 'localhost', '127.0.0.1'],
watch: {
ignored: WATCH_IGNORED
}
},
optimizeDeps: {
include: ['dayjs']
}
},
nitro: {
watchOptions: {
ignored: WATCH_IGNORED
}
},
watchers: {
chokidar: {
ignored: WATCH_IGNORED
}
}
})