diff --git a/app/activate/__tests__/page.test.tsx b/app/activate/__tests__/page.test.tsx index d7ae4c4..a27354f 100644 --- a/app/activate/__tests__/page.test.tsx +++ b/app/activate/__tests__/page.test.tsx @@ -28,6 +28,14 @@ const createJsonResponse = (data: any, init?: { ok?: boolean; status?: number; s }); const mockFetchRoutes = (routes: Record) => { + const routesWithDefaults: Record = { + '/api/system-settings': createJsonResponse({ + siteName: 'FLNXI素材库', + loginLogoUrl: '', + }), + ...routes, + }; + (global.fetch as jest.Mock).mockImplementation((input: RequestInfo | URL) => { const url = typeof input === 'string' @@ -36,7 +44,7 @@ const mockFetchRoutes = (routes: Record) => { ? input.toString() : input.url; - const handler = routes[url] ?? routes['*']; + const handler = routesWithDefaults[url] ?? routesWithDefaults['*']; if (handler instanceof Error) { return Promise.reject(handler); } @@ -53,7 +61,7 @@ const renderPage = async () => { await act(async () => { render(); }); - await screen.findByText('FLNXI 素材系统'); + await screen.findByText('FLNXI素材库'); }; describe('ActivatePage', () => { @@ -71,7 +79,7 @@ describe('ActivatePage', () => { it('应该正确渲染页面标题和表单', async () => { await renderPage(); - expect(screen.getByText('FLNXI 素材系统')).toBeInTheDocument(); + expect(screen.getByText('FLNXI素材库')).toBeInTheDocument(); expect(screen.getByText('请输入交付给您的许可证密钥')).toBeInTheDocument(); expect(screen.getByPlaceholderText('XXXX-XXXX-XXXX-XXXX')).toBeInTheDocument(); expect(screen.getByText('激活许可证')).toBeInTheDocument(); diff --git a/app/activate/page.tsx b/app/activate/page.tsx index 74919d4..63fec89 100644 --- a/app/activate/page.tsx +++ b/app/activate/page.tsx @@ -2,6 +2,14 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; +import Image from 'next/image'; + +const DEFAULT_LOGIN_LOGO = process.env.NEXT_PUBLIC_DEFAULT_LOGIN_LOGO_URL || ''; + +type PublicSystemSettings = { + loginLogoUrl?: string | null; + siteName?: string | null; +}; export default function ActivatePage() { const router = useRouter(); @@ -21,6 +29,54 @@ export default function ActivatePage() { const [showFreeTrialModal, setShowFreeTrialModal] = useState(false); const [customerName, setCustomerName] = useState(''); const [contactInfo, setContactInfo] = useState(''); + const [loginLogoUrl, setLoginLogoUrl] = useState(DEFAULT_LOGIN_LOGO); + const [siteName, setSiteName] = useState('FLNXI素材库'); + const [logoError, setLogoError] = useState(false); + + useEffect(() => { + const loadSettings = async () => { + try { + const response = await fetch('/api/system-settings'); + if (response.ok) { + const settings = await response.json() as PublicSystemSettings; + setLoginLogoUrl(settings.loginLogoUrl || DEFAULT_LOGIN_LOGO); + setSiteName(settings.siteName || 'FLNXI素材库'); + setLogoError(false); + } + } catch (error) { + console.error('加载系统设置失败:', error); + } + }; + + loadSettings(); + }, []); + + useEffect(() => { + if (!loginLogoUrl) return; + + if (loginLogoUrl.startsWith('/')) { + setLogoError(false); + return; + } + + try { + const parsed = new URL(loginLogoUrl); + const host = parsed.hostname.toLowerCase(); + const isLocal = host === 'localhost' || host === '127.0.0.1'; + if (isLocal && typeof window !== 'undefined') { + const currentHost = window.location.hostname.toLowerCase(); + if (currentHost !== host) { + console.warn( + `[Activate] Logo 地址 ${loginLogoUrl} 指向本地服务,但当前访问域名为 ${currentHost},自动降级为占位符` + ); + setLogoError(true); + } + } + } catch (error) { + console.warn('[Activate] Logo 地址解析失败,自动降级为占位符:', loginLogoUrl, error); + setLogoError(true); + } + }, [loginLogoUrl]); // 页面加载时检查数据库中是否已有许可证 useEffect(() => { @@ -248,7 +304,24 @@ export default function ActivatePage() {
{/* Logo */}
-

FLNXI 素材系统

+
+ {loginLogoUrl && !logoError ? ( + {siteName} setLogoError(true)} + /> + ) : ( +
+ {siteName.charAt(0) || 'F'} +
+ )} +
+

{siteName}

请输入交付给您的许可证密钥

diff --git a/docs/商业化地基排查问题清单_20260524.md b/docs/商业化地基排查问题清单_20260524.md index de179f1..830bf37 100644 --- a/docs/商业化地基排查问题清单_20260524.md +++ b/docs/商业化地基排查问题清单_20260524.md @@ -1168,14 +1168,14 @@ NAS 私有化系统可能在平板或窄屏浏览器里使用。模板编辑器 - `app/register/page.tsx:88`:注册页已增加默认 Logo 兜底,没有配置图片时显示文字占位,不再显示破图。 - `app/complete-profile/page.tsx:25`:完善资料页已改为和登录页一致的浅灰背景。 - `app/complete-profile/page.tsx:205`:完善资料页已增加默认 Logo 兜底。 -- `app/activate/page.tsx`:激活页已完成第二轮收口,默认只展示许可证密钥激活;“免费试用/购买许可证”入口已改为显式环境开关下的“自助试用”,客户默认安装现场不再露出公网获客口径。 +- `app/activate/page.tsx`:激活页已完成第二轮收口,默认只展示许可证密钥激活,并跟随系统名称和登录 Logo;“免费试用/购买许可证”入口已改为显式环境开关下的“自助试用”,客户默认安装现场不再露出公网获客口径。 - `app/dingtalk-callback/page.tsx`:钉钉回调页已完成第一轮收口,蓝色渐变整屏背景改为浅灰背景,`rounded-2xl shadow-xl` 大卡片改为 `rounded-lg border shadow-sm` 普通状态卡片,加载/成功/失败状态复用同一个中性外壳。 - 代码复扫:`app/dingtalk-callback/page.tsx` 已无 `bg-gradient`、`rounded-2xl`、`shadow-xl`、大尺寸状态图标等命中。 - 验证限制:本轮尝试用 Codex 内置浏览器打开钉钉回调页时返回“没有可用活动面板”,因此该页暂未补截图;已完成 `npm run type-check` 和 `npm run lint` 验证。 建议动作: -- 登录、激活、回调、完善资料页统一成同一套私有化安装/访问风格;登录/注册页已完成第一轮圆角、口径和系统名称/Logo 对齐,邀请注册链路已修复;钉钉回调页和激活页已完成第一轮视觉收口,激活页公网试用/购买口径已进一步隐藏。 +- 登录、激活、回调、完善资料页统一成同一套私有化安装/访问风格;登录/注册/激活页已完成第一轮圆角、口径和系统名称/Logo 对齐,邀请注册链路已修复;钉钉回调页已完成第一轮视觉收口,激活页公网试用/购买口径已进一步隐藏。 - 背景用纯色或轻灰,不用大面积渐变。 - 登录页可以保留品牌感,但减少泛 SaaS 视觉:小 Logo、清晰表单、明确客户系统名称即可。