diff --git a/.gitignore b/.gitignore index baf5e79..77434a0 100644 --- a/.gitignore +++ b/.gitignore @@ -89,12 +89,16 @@ e2e-tests/reports/ *.crt .auth/ +# WorkBuddy 工作记忆目录(工具生成,不入库) +.workbuddy/ + # ============================================================ # Database # ============================================================ *.db *.db-journal data.db +data/ # ============================================================ # Uploads & Storage diff --git a/CONTEXT.md b/CONTEXT.md index cd80a19..dcbd8e9 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -166,6 +166,7 @@ Novalon 的 6 个核心产品,互为互补关系,常以组合形式出现在 | 品牌主口号 | **智连未来 · 成长伙伴 —— 您的数字化转型同行者**(整句全站统一采用)。品牌叙事内核(定位声明 / 承诺 / L0 价值主张 / L1 三支柱 / 语气语调)见 `docs/brand-narrative-core.md`,作为全站文案统一唯一锚点 | ✅ 2026-08-19 确认 | | 品牌叙事 CMS 化 | 品牌叙事内核全部下沉到 CMS `site-config` 模型(`slogan` / `valueProposition` / `positioningStatement` / `brandPromise` / `toneOfVoice` / `pillars`),后台可编辑;代码侧 `COMPANY_INFO` 与 `BRAND_NARRATIVE`(`src/lib/constants/company.ts`)作为未配置 CMS 时的兜底唯一真源,`layout.tsx` 读取后经 `SiteConfigProvider` 注入全站 | ✅ 2026-08-19 确认 | | 结构性文案 CMS 化 | 新增 `page-copy` 内容模型承载各业务页面章节标题/眉标/描述/CTA/空状态,覆盖首页 + 服务/方案/产品/案例/新闻列表页,seed 写入 6 条(home/services/solutions/products/cases/news)。实现为「CMS 优先 + 硬编码兜底」:CMS 未配置时回退 `TRUST_SIGNALS`/`EARLY_ACCESS`/`NARRATIVE_ACTS` 等常量,保证任何情况不白屏。数据层新增 `getPageCopy(pageCode)` / `getHomePageCopy()`,各 `page.tsx` 读取后以 `pageCopy` props 传入组件。详情页小节标题、页面内嵌业务常量(服务流程/合作模式/产品组合)、Header/Footer 等 UI 外壳按 UI 职责保留代码。数据实体(服务/产品/方案/案例/新闻/指标/Hero)仍由 `data-server` 从 Prisma 读取 | ✅ 2026-08-19 确认 | +| 首页对标埃森哲重构(scene#16) | **视觉基调:局部深色 Hero(推荐项)**——保持浅色咨询风为底,仅 Hero 升级为全出血深色画布(近黑底 `bg-ink` + 白色 Logo `/logo-white.svg` + 品牌红 #C41E3A 单电压 ≤10%),制造埃森哲式戏剧感;**实施范围:Phase A+B 全量**——①内容支柱补齐:新增 Insights 行业洞察(2×2 gap-px 卡片网格 + 方法论/观点/共创类型标签)、FounderQuote 创始人观点(深红区块 + 白色点阵 + 大引言)、News 新闻动态(最近 3 条 + 查看全部)三个对标埃森哲思想领导力支柱的区块;②Hero 深色化;③CTA 签名符号:全站 Button 组件 `rounded-md` → `rounded-full`(Pill 胶囊化)+ ArrowRight 签名箭头。零编造内容原则:公司 2026-01-15 成立,Insights 兜底 4 条均为可验证方法论内容(不虚构研究报告/人名),FounderQuote 署名兜底「Novalon 创始团队」不虚构具体人名。实现:`home-content-v15.tsx` 替换 v14(9 区块顺序 Hero→Trust→Narrative→Insights→Services→FounderQuote→Cases→News→CTA),CMS 优先 + FALLBACK_INSIGHTS/NEWS 常量兜底;seed page-copy 新增 insights*/founderQuote*/news* 字段(upsert 幂等)。验证:tsc 0 errors / eslint 0 errors / Jest 128 套件 1621 通过 / Playwright 视觉回归 22 passed(macOS 11 限制下用 Chromium 117 兼容配置生成快照) | ✅ 2026-08-31 确认 | ## 页面类型与四层映射 diff --git a/IMPECCABLE_AUDIT.md b/IMPECCABLE_AUDIT.md new file mode 100644 index 0000000..9a5c230 --- /dev/null +++ b/IMPECCABLE_AUDIT.md @@ -0,0 +1,655 @@ +# Impeccable 复审报告(代码更新后) + +复审日期:2026-08-29 +第一轮:`/audit` 6/20(Poor) +第二轮(代码更新后):**14/20(Good)** +第三轮(执行修复后):**17/20(Good)** + +> 代码库已发生重大重构:字体体系重建、设计令牌重写、`effects/` 目录整体删除、 +> 深色模式移除、页面改由 CMS 驱动(新增 `components/content`、`components/detail`、`lib/cms`)。 +> 第二轮为**全量重新审计**;第三轮为**按建议执行修复**后的复核。 + +--- + +## 第三轮 · 修复记录 + +见文末「附录:修复记录与审计更正」。 + +--- + +## 评分对比 + +| 维度 | 上轮 | 本轮 | 变化 | 关键 | +|---|:---:|:---:|:---:|---| +| Accessibility | 2 | **3** | +1 | 3 处对比度违规修好 2 处,剩 `text-hint` | +| Performance | 1 | **3** | **+2** | 4.4MB 字体删除、强制重排归零、profiling 移除 | +| Responsive | 2 | **2** | — | 两项建议未采纳 | +| Theming | 1 | **3** | **+2** | 令牌体系落地,硬编码从普遍降到 29 处 | +| Anti-Patterns | 0 | **3** | **+3** | 渐变文字/spring/bounce/特效库全部清除 | +| **合计** | **6** | **14** | **+8** | **Poor → Good** | + +--- + +## Anti-Patterns 判定:由「未通过」转为「通过」 + +上一轮点名的 7 项生效中的套路,本轮逐一复查: + +| 特征 | 上轮 | 本轮 | 验证 | +|---|---|---|---| +| 渐变文字 | 生效中 | **0 处** | `grep bg-clip-text` = 0 | +| Spring 回弹缓动 | 43 处 | **0 处** | `grep type:'spring'` = 0 | +| Bounce 缓动 | 生效中 | **0 处使用** | `CounterWithEffect` 组件仍在 `lib/animations.tsx:852`,但引用数为 0(死代码) | +| 特效组件过载 | 20+ 个 | **目录已删除** | `components/effects/` = 0 文件 | +| Hero 指标网格 | 4 格统计 | **已拆解** | Hero 改为「克制白底 + 品牌红点缀」;仅 `why-us-section` 保留 1 项指标 | +| 动 width 布局动画 | 2 处关键帧 | **0** | `expandWidth`/`typewriter` 已移除 | +| 毛玻璃 | header 全宽 | **收敛为 14 处局部** | 主要是对话框遮罩 `bg-ink/60 backdrop-blur-sm`,属标准模态用法 | + +Hero 区注释直接写明意图:`// ===== Hero 区:克制白底 + 品牌红点缀 =====` +——这是有意识的设计决策,不是碰巧。 + +**结论**:上一轮判定的"AI 套路层"已被系统性剥离,底下真实的品牌观点(水墨 + 朱砂红)现在看得见了。 + +--- + +## 已修复(按重要性) + +### ✅ 4.4MB 字体阻塞 → 删除(上轮 P0-1) +`public/fonts/AoyagiReisho.ttf`(4,412,684 B)已移除。 +新增 `src/app/fonts/` 仅 140KB(geist-sans + geist-mono woff2)。 +书法字体改为系统楷体栈,不再下载: + +```css +--font-brand: "STKaiti", "KaiTi", "楷体", "SimKai", serif; +``` + +**这是本轮最大的一笔收益。** + +### ✅ 中文字体栈补齐(上轮 P1-5,我上轮的判断被推翻并修好) +```css +--font-sans: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", + "Noto Sans SC", -apple-system, BlinkMacSystemFont, ...; +``` +CJK 字体栈完整,跨平台字形可控。 + +### ✅ 滚动强制重排 → 归零(上轮 P1-1) +`header.tsx` 中的 `offsetTop`/`offsetHeight` 循环读取已完全移除, +不再有每滚动事件 12 次强制同步布局。 + +### ✅ 深色模式 → 彻底移除(上轮 P0-3) +采纳了我给的选项二。验证结果: +- `layout.tsx` 无 `.dark` 注入脚本 +- 全站 `dark:` 变体 **0 处** +- `ThemeProvider` 已删除 +- `themeColor` 仅保留 `#FFFFFF` + +**干净利落,没有留下中间态。** + +### ✅ 生产 profiling 移除(上轮 P1-2) +### ✅ 6 个悬空 CSS 变量 → 0(上轮 P1-8,通过删除死代码) +### ✅ 对比度违规修好 2/3 +| 上轮 FAIL | 本轮 | +|---|---| +| Hero 描述 `#718096` 4.02:1 | → slate 令牌 **4.76+ PASS** | +| 页脚 `#718096` 3.68:1 | → **PASS** | +| 统计占位 `#CBD5E0` 1.49:1 | → **仍 FAIL**(见下) | + +### ✅ 图片缺失 20 张 → 8 张 + +--- + +## 仍然存在的问题 + +### ⚠️ [P1] `text-hint` 1.48:1,且用在有实际含义的内容上 +- **位置**:`globals.css:66` `--color-text-hint: #CBD5E1` +- **使用处**: + - `home-content-v14.tsx:401` — `text-4xl sm:text-5xl font-black` 章节序号(01/02/03) + - `select.tsx:25,28` / `textarea.tsx:17` — `placeholder:text-text-hint` +- **实测**:**1.48:1**(AA 要求 4.5:1,大字也要求 3:1)→ **双重不达标** +- **影响**:章节序号几乎不可见;表单 placeholder 同样。 +- **注意**:这个 token 比上轮的 `#CBD5E0` 只差最后一位,本质上同一个问题换了个名字。 +- **标准**:WCAG 2.1 AA 1.4.3 +- **建议**:若序号是装饰性的 → 加 `aria-hidden` 并淡化得更彻底;若承载信息 → 改 `--color-text-muted`(#64748B,4.76:1)。placeholder 直接用 `text-muted`。 + +### ⚠️ [P1] 图片优化仍关闭,且改为无条件 +- **位置**:`next.config.mjs:8` `images.unoptimized: true` +- **变化**:上轮是 `!isDev`(开发环境还优化),现在**生产环境也完全不优化**。 + 同文件 `formats: ['image/avif','image/webp']` 依然是死配置。 +- **说明**:如果是有意走 CDN 优化(`assetPrefix` + `CDN_DOMAIN` 的存在支持这个猜测), + 那这是合理选择——但 `formats` 配置项应当一并删除,否则会误导后来者。 +- **建议**:确认意图。若走 CDN → 删掉 `formats` 并加注释说明;否则改 `false`。 + +### ⚠️ [P1] 8 张图片仍缺失(裂图) +``` +/images/cases/manufacturing.jpg /images/erp.jpg +/images/insights/trends.jpg /images/news/cms.webp +/images/news/fallback.webp /images/news/test.webp +/images/product.png /images/products/erp.jpg +``` +`news/fallback.webp` 缺失尤其危险——兜底图本身不存在,意味着兜底机制是失效的。 + +### ⚠️ [P2] 移动端全局 44px 规则未改 +- **位置**:`globals.css:1122-1126` +- ```css + a:not(nav[aria-label="breadcrumb"] a), + button { min-height: 44px; min-width: 44px; } + ``` +- 仅排除了面包屑,正文行内链接仍会被撑成 44px 方块。 + +### ⚠️ [P2] 根字号 16/17/18px 缩放未改 +- **位置**:`globals.css:343-359` +- 所有 rem(即整个 Tailwind 尺寸体系)在断点处放大 12.5%。 + +### ⚠️ [P2] 新增越界色,色板又开始漂移 +| 颜色 | 位置 | 对比度 | +|---|---|---| +| `#d97706` amber | `detail/brand-elements.tsx:28,30` | **3.19 FAIL** | +| `#2563eb` blue | `detail/brand-elements.tsx:34,36` | 5.17 PASS | +| `#3b82f6` / `#eff6ff` | `detail-cross-recommend.tsx:16,17` | — | +| `#1e3a5f` navy | `erp-upgrade-v3/page.tsx:26`、`lib/constants/hero-themes.ts:29` | — | + +上一轮清理掉冷蓝灰后,**蓝色和琥珀色又从 detail 组件里长了出来**。 +`#d97706` 同时还是个对比度 FAIL。 + +--- + +## 新发现(上轮未覆盖) + +### 🆕 [P1] 生产代码 70 个 TypeScript 错误 +``` +src/app/(marketing)/cases/[slug]/page.tsx(16,21) TS7006 隐式 any +src/app/(marketing)/news/[slug]/page.tsx(13,21) TS7006 +src/app/(marketing)/products/[id]/page.tsx(13,21) TS7006 +src/app/(marketing)/services/[id]/page.tsx(13,21) TS7006 +src/app/(marketing)/solutions/[id]/page.tsx(14,21) TS7006 +src/app/api/admin/models/route.ts(17,32) TS7006 +...共 70 处 +``` +另有测试文件大量 `TS2305: '@testing-library/react' has no exported member 'screen'`—— +说明 `@testing-library/react` 依赖缺失或版本不匹配,**测试套件可能跑不起来**。 + +### 🆕 [P2] 140KB 字体文件无引用 +`src/app/fonts/geist-sans.woff2`(69KB)+ `geist-mono.woff2`(71KB)**全站 0 引用**—— +`@font-face` 未声明,也没有 `next/font/local` 导入。是孤立资源。 + +### 🆕 [P2] 安全与缓存头回退 +上轮配置里的 `Strict-Transport-Security`(HSTS,max-age=63072000)与 +静态资源 `Cache-Control: immutable` 规则**在新配置中消失**。 +新配置新增了 CSP(含 `unsafe-inline`/`unsafe-eval`,Next.js 水合所需,可接受)。 + +--- + +## 正面发现(本轮新增,值得肯定) + +1. **敢删东西**——`effects/` 整个目录、4.4MB 字体、深色模式半实现,全部删除而非修修补补。这比加功能更需要判断力。 +2. **深色模式选择了"彻底移除"而非"凑合补全"**,不留中间态,符合工程洁癖。 +3. **令牌体系真正落地**——71 个语义化令牌 + Tailwind config 映射(`font-sans`/`text-ink`/`bg-brand`),硬编码从"普遍"降到 29 处 / 13 文件。 +4. **`optimizePackageImports` 新增 `framer-motion`**。 +5. **收紧了图像配置**——`dangerouslyAllowSVG`、`remotePatterns: '**'` 均已移除。 +6. **Hero 有明确的克制意图**(代码注释直接写明),不是被动简化。 +7. **叙事化章节序号**(01/02/03 + hairline grid)替代了 4 格指标堆砌,信息架构更成熟——只是序号的对比度需要修。 + +--- + +## 建议下一步(按性价比排序) + +| 优先级 | 动作 | 理由 | +|---|---|---| +| **1** | 修 `text-hint`(改用 `text-muted` 或加 `aria-hidden`) | 1 行改动,消除唯一 AA 违规 | +| **2** | 补齐 8 张图,优先 `news/fallback.webp` | 兜底图缺失 = 兜底失效 | +| **3** | 确认 `unoptimized: true` 是否刻意为 CDN 让路 | 若刻意,删掉 `formats` 死配置以免误导 | +| **4** | 清掉 3 处越界色(`#d97706` 尤其,它 FAIL) | 防止色板二次漂移 | +| **5** | 修 70 个 TS 错误 + 测试库依赖 | 类型安全是后续重构的地基 | +| **6** | 删除 140KB 无用字体,或补 `next/font/local` 接入 | 要么用,要么删 | +| **7** | 恢复 HSTS 与静态资源缓存头 | 安全与性能的低成本回归 | +| **8** | 移动端 44px 改为精确选择器、根字号固定 16px | 上轮建议,仍未采纳 | + +> 前 4 项预计 2 小时内可完成,能直接把分数推到 16-17 区间。 +> 修复后重跑 `/audit` 复核。 + +--- + +## 一句话总结 + +从 6 分到 14 分,主要靠**做减法**——删掉 4.4MB 字体、20+ 特效组件、整层 AI 套路和半吊子深色模式。 +剩下的失分集中在三类:一个换名未换实的对比度 token、尚未补完的图片资源、以及新长出来的色板漂移。 +这些都是小修,地基已经稳了。 + +--- + +# 附录:修复记录与审计更正 + +## 一、第三轮评分:14 → 17 + +| 维度 | 二轮 | 三轮 | 变化 | +|---|:---:|:---:|---| +| Accessibility | 3 | **4** | 全部对比度违规清零(含新发现的状态色问题) | +| Performance | 3 | **3** | 图片项为误判;补 `sizes` 后维持 | +| Responsive | 2 | **2** | 未改动(两项建议待定) | +| Theming | 3 | **4** | 越界色全部令牌化,新增状态色文字变体 | +| Anti-Patterns | 3 | **3** | 维持 | +| **合计** | **14** | **17** | | + +## 二、已执行的修复 + +### 1. `text-hint` 对比度(1.48:1 → 合规) +根因不是配色,是**用错了令牌**——系统里早有 `--color-text-placeholder`(4.76:1), +组件却拿 `text-hint` 当占位符用。修正如下: + +| 文件 | 改动 | +|---|---| +| `globals.css:66-68` | `--color-text-hint` 由 `#CBD5E1`(1.48:1) 改为 `#7C8CA5`(3.41:1),并加注释限定「仅 ≥24px 大号文本」 | +| `ui/input.tsx:18` | `placeholder:text-text-hint` → `text-text-placeholder` | +| `ui/select.tsx:25,28` | 同上(含 `data-[placeholder]:`) | +| `ui/textarea.tsx:17` | 同上 | +| `sections/why-us-section.tsx:177` | 10px 小字 → `text-muted` | +| `layout/mobile-menu.tsx:104` | 12px 小字 → `text-muted` | +| `home-content-v14.tsx:401` | 装饰性章节序号加 `aria-hidden="true"`,保留 `text-hint` | + +> 后三处是我二轮漏检的:`text-hint` 还被用在 10px/12px 的小字上, +> 即便按新值 3.41:1 也对正文不达标。 + +### 2. 图片项:确认是误判,改为修健壮性 +**更正**:所谓「8 张缺失图片」全部只出现在 `.test.tsx` 测试夹具里。 +严格排除测试文件后,生产代码仅引用 3 张图(`beian-icon.png`、`qrcode.webp`、`wechat-business-qr.webp`), +**且这 3 张都存在**。测试断言的是 `src` 字符串,不需要真实文件——不构成生产问题。 + +但底层担忧(CMS 图片失效会裂图)成立,因此改为修健壮性: + +| 文件 | 改动 | +|---|---| +| `sections/insight-card.tsx` | `` → CSS `background-image`,404 时静默降级,加 `aria-hidden` | +| `sections/case-card.tsx` | 同上 | +| `detail/product-card.tsx` | 补 `sizes`(上轮标记的 P2-8) | + +### 3. 越界色清理 +**更正**:二轮把 `#3b82f6` 判为越界是错的——它正是设计系统的 `--color-accent-blue`。 +真正越界的只有 `BrandSeal` 里的 gold/blue 变体,而该组件**零引用**。 + +| 文件 | 改动 | +|---|---| +| `detail/brand-elements.tsx` | 移除 gold(`#d97706`, 3.19:1 FAIL) 与 blue(`#2563eb`) 变体,仅保留品牌红;硬编码改令牌 | +| `detail/brand-elements.tsx:63` | 修 `${color.border}10` 拼接——改令牌后会产生 `var(...)10` 无效值,改用 `rgba(var(--color-brand-rgb), 0.06)` | +| `detail/detail-cross-recommend.tsx` | 三变体全部改令牌:`accent-blue` / `brand` / `accent-purple` | + +`hero-themes.ts` 的 navy(`#1e3a5f`/`#1e40af`)**保留**——经查该文件被 +`detail-hero.tsx`、`detail-cta-section.tsx`、`standalone/[id]/client.tsx` 使用, +属有意的产品主题变体,不是漂移。 + +### 4. `next.config.mjs`:确认非 bug +**更正**:`CLAUDE.md:189` 明确记载 +「Images are unoptimized (static export limitation)」,且存在 `docs/CDN_CONFIGURATION.md`、 +`docs/CDN_QUICK_START.md`。这是 Nginx 静态托管 + CDN 分发的架构决定,**不是缺陷**。 + +仅补注释说明意图,并标注 `formats` 在 `unoptimized: true` 下不生效,避免后人误改。 + +## 三、修复中新发现的问题(已一并修复) + +### 状态色整体不满足文字对比度 +审计时发现一个系统性盲区:**所有状态色都是按图形用途调的,却被当作文字色使用**。 + +| 令牌 | 色值 | 白底对比度 | 判定 | +|---|---|---|---| +| `--color-success` | `#16A34A` | 3.30:1 | FAIL | +| `--color-warning` | `#D97706` | 3.19:1 | FAIL | +| `--color-info` | `#3B82F6` | 3.68:1 | FAIL | +| `--color-error` | `#C41E3A` | 5.84:1 | PASS | + +影响面:`ui/alert.tsx`、`ui/badge.tsx`(小到 10px)、`ui/product-card.tsx` 状态徽章、`ui/sonner.tsx`。 + +**修法**:新增三个「文字专用」令牌,图形用途保持原色不变。 + +| 新令牌 | 色值 | 白底 | 于对应浅底 | +|---|---|---|---| +| `--color-success-text` | `#15803D` | 5.02:1 | 4.79:1 | +| `--color-warning-text` | `#B45309` | 5.02:1 | 4.84:1 | +| `--color-info-text` | `#1D4ED8` | 6.70:1 | 6.16:1 | + +已在 `tailwind.config.js` 注册为 `success.text` / `warning.text` / `info.text`, +并应用于 alert、badge、product-card、sonner。 + +### 11 个悬空 CSS 变量引用(导致边框/网格/悬停态不渲染) +全站 47 个 `var(--color-*)` 引用中有 **11 个从未定义**。这类变量不会报错, +但对应样式静默失效——是"看起来没坏、实际没生效"的典型。 + +| 悬空变量 | 影响位置 | 实际后果 | 修法 | +|---|---|---|---| +| `--color-border` | `privacy/page.tsx:139,142-144,147` | **Cookie 表格边框全部不渲染** | → `--color-border-primary` | +| `--color-border-primary-rgb` | `detail/list-page-hero.tsx:66` | 网格背景线不渲染 | 新增令牌 `226, 232, 240` | +| `--color-brand-lighter` | `mobile-menu.tsx:49`、`CookieConsent.tsx:163` | 悬停背景失效 | → `--color-brand-bg` | +| `--color-challenge-isolation-hover` | `not-found-content.tsx:50,60,70,80` | 悬停背景失效 | → `--color-brand-soft` | +| `--color-hero-dark-end` | `privacy:251`、`terms:196` | CTA 渐变末端失效 | → `--color-brand-section` | +| `--color-flip-card-bg` | `ui/flip-clock.tsx`(5 处) | 翻页钟卡片背景失效 | → `--color-bg-secondary` | +| `--color-flip-card-border` | 同上 | 边框失效 | → `--color-border-primary` | +| `--color-flip-card-divider` | 同上 | 分隔线失效 | → `--color-border-primary` | +| `--color-flip-card-divider-subtle` | 同上 | 次级分隔线失效 | → `--color-border-light` | +| `--color-accent-cyan` / `-rgb` | `ui/product-card.tsx:21`、`brand-visuals.tsx:58`、`hero-section-v2.tsx:177` | 第 4 个强调色不渲染 | 新增令牌 `#06B6D4` + rgb | + +修复后复检:**悬空引用 0 / 39 个唯一引用**。 + +> 检测方法(可复用): +> ```bash +> grep -rhoE "var\(--color-[a-z0-9-]+\)" src/ --include='*.tsx' --include='*.css' \ +> | sed 's/var(--//;s/)//' | sort -u > /tmp/refs.txt +> while read v; do grep -q -- "--$v:" src/app/globals.css || echo "悬空: --$v"; done < /tmp/refs.txt +> ``` + +## 四、最终对比度全景 + +| 令牌 | 色值 | 对比度 | 要求 | 判定 | +|---|---|---|:---:|---| +| text-primary | `#0A0E14` | 19.34:1 | 4.5 | PASS | +| text-secondary | `#334155` | 10.35:1 | 4.5 | PASS | +| text-tertiary | `#475569` | 7.58:1 | 4.5 | PASS | +| text-muted / subtle / placeholder | `#64748B` | 4.76:1 | 4.5 | PASS | +| text-hint(大字专用) | `#7C8CA5` | 3.41:1 | 3.0 | PASS | +| brand | `#C41E3A` | 5.84:1 | 4.5 | PASS | +| success-text | `#15803D` | 5.02:1 | 4.5 | PASS | +| warning-text | `#B45309` | 5.02:1 | 4.5 | PASS | +| info-text | `#1D4ED8` | 6.70:1 | 4.5 | PASS | +| error | `#C41E3A` | 5.84:1 | 4.5 | PASS | + +**全站 WCAG AA 对比度违规:0。** + +## 五、收尾修复记录(三轮评审后的最终批次) + +原"遗留事项"清单已全部处置完毕,逐项结论如下: + +| # | 原遗留项 | 处置结果 | +|---|---|---| +| 1 | [P1] 70 个 TS 错误 | **已解决(误判根因)**。错误源于 npm 依赖缺失破坏类型推断,而非代码缺陷。补齐依赖后 `tsc` 0 错误,构建 TS 检查 32.9s 通过 | +| 2 | [P2] 测试库缺导出 | **已解决**。同一依赖问题;`screen`/`waitFor`/`fireEvent` 已验证存在 | +| 3 | [P2] Geist 字体 0 引用 | **已解决**。`layout.tsx` 经 `next/font/local` 挂载 `--font-geist-sans/mono`,globals.css 字体栈已接 `var(--font-geist-sans, ...)` | +| 4 | [P2] 移动端全局 44px + 根字号缩放 | **已修复**。44px 规则收窄至 `@media (max-width:768px)` 下的 nav/header/footer/menu 交互元素,不再撑大正文行内链接;根字号删除 640px→17px、1024px→18px 两档缩放,固定 16px,恢复 rem 缩放对用户浏览器字号设置的响应 | +| 5 | [P3] Tailwind v3 透明度修饰符失效 | **已修复**。`tailwind.config.js` 全部颜色令牌改写为 `rgb(var(--color-*-rgb) / )` 形式(含 `border.*` 六项——代码中 `border-brand/30` 等 54 处实际用法的兜底),globals.css 补齐对应 `-rgb` 通道令牌;`prefers-contrast: more` 无障碍覆写同步补写 `--color-border-primary-rgb: 0,0,0`。已用 Tailwind CLI 探针验证:`bg-brand/10 → rgb(var(--color-brand-rgb) / 0.1)`、`border-brand/30` 等均正确编译 | +| 6 | [P3] 死代码 | **已删除**。`detail/index.ts` barrel 中 `CrossRecommendGrid`、`BrandSeal/CalligraphyText/SectionHeader` 三组 0 消费者(生产引用为 0)的重新导出已移除;组件文件 `brand-elements.tsx`、`detail-cross-recommend.tsx` 于 2026-08-31 删除;`detail.test.tsx` 中仅针对该组件的 5 个测试用例与专用 mock 一并删除(tsc 全量 0 错误复验)。barrel 与测试文件均附留档注释。`erp-upgrade-v3` 经核实是真实路由(`/products/erp-upgrade-v3`,构建产物 ○ Static 可直达 URL),属信息架构/SEO 问题而非死代码:**保留(产品决策项,可选:补充内链入口)** | + +### 最终构建验证 + +- **✅ 完整生产构建通过(`/tmp/nb4`,`npm run build` exit=0)**:`Compiled successfully` + TypeScript 通过 + 全部页面数据收集成功 +- 此前 page-data 收集失败的根因链(已定案并修复): + 1. `.env.local` 的 `DATABASE_URL=file:./data/novalon.db` 为相对路径,构建期解析落空 → `Unable to open the database file` + 2. 改绝对路径后数据库可打开,但本地 `data/novalon.db`(2026-04 旧库)是旧 schema:只有 `content`/`site_config`/`users`/`audit_logs`/`content_versions` 表,无 Prisma schema 的 `ContentItem` 等 10 张表 → `P2021: table main.ContentItem does not exist` + 3. **修复**:`prisma migrate reset --force` 重建 schema(旧库备份为 `data/novalon.db.bak-20260830`)→ seed 填充(16 模型 / 38 条目 / 6 区域 / admin)→ 构建全绿 +- **注意**:本机 `tsx`/`ts-node` 均不可用(esbuild 0.28.1 需 macOS 12+,本机 11.7)。seed 改用已装的 esbuild 0.18.20 打包 `prisma/seed.ts` 为 ESM(`--packages=external`,import.meta 兼容)后以 node 运行;如再遇 `db:seed` 失败可复用此方案(脚本 `.tmp-seed/seed.mjs` 已清理) +- middleware 弃用警告:Next 16.3 已将其识别为 "ƒ Proxy (Middleware)",构建无警告,无需迁移 +- Tailwind 透明度转换已产物级验证(Tailwind CLI 编译探针,非仅构建通过) +- 测试套件:**1616 通过 / 0 失败 / 2 跳过**(128 套件全绿;死代码清理移除了 5 个 CrossRecommendGrid 用例)。 + insight-card、case-card 两处断言原查找 ``,已随组件改为 CSS 背景图同步更新断言 + (`[style*="background-image"]` + `toHaveStyle`);删除后 `tsc --noEmit` 全量复验 0 错误 +- 注意事项:`NEXT_DIST_DIR` 传绝对路径会被 Next.js 规范化为**项目内相对路径**(如传 `/tmp/nb4` 实际写入 `./tmp/nb4`), + 且会向 `tsconfig.json` 的 `include` 追加(绝对化后的)临时目录条目。构建后需:`git checkout tsconfig.json` 还原 + 删除项目内 `tmp/` 产物 + (本次已多次触发并清理,最多时 449MB)。不传 `NEXT_DIST_DIR` 时产物落在默认 `dist/`,同样注意 tsconfig 污染 + +### 全站路由 × sitemap × 站内入口对照(泛化检查) + +| 路由 | sitemap | 站内入口 | 结论 | +|---|---|---|:---:|---| +| `/`、`/about`、`/services`、`/products`、`/solutions`、`/cases`、`/news`、`/team`、`/methodology`、`/contact` | ✅ | ✅ | OK | +| `/[id]` 系列(services/products/solutions/cases/news) | ✅ CMS slug | ✅ 列表页 | OK | +| `/about/brand`(品牌故事) | ❌→**✅ 已加** | ❌ | **修复**:正规内容页此前站内零入口且不进 sitemap;入口可经 CMS 配置(about 页 `hero*CtaHref` 字段) | +| `/products/erp-upgrade`(ERP升级专题) | ❌→**✅ 已加** | ❌ | **修复**:孤儿营销页(详见上);站内入口待产品决策 | +| `/products/erp-upgrade-v3` | ❌ 有意 | ❌ | 内部迭代/dogfood 页,保持现状 | +| `/products/standalone/[id]` | ❌ 有意 | ❌ | externalUrl 外链机制(成熟产品跳独立子域名站),设计使然 | +| `/privacy`、`/terms` | ❌ 惯例 | ✅ footer(StaticLink) | 法律页惯例不进 sitemap,footer 有入口,OK | +| `/test-error-tracking` | ❌ | ❌ | **修复**:QA 测试页此前可被搜索引擎收录(robots 未屏蔽),已重构为 server wrapper + `metadata.robots: noindex` | +| `/admin/*` | ❌ | ❌ | robots.txt disallow,OK | +| `/api/*` | ❌ | ❌ | robots.txt disallow,OK | + +**泛化检查结论**:全站非 admin 路由逐一对照完毕,孤儿/SEO 问题共 3 处(erp-upgrade、about/brand、test-error-tracking),已全部修复;其余有意排除项均经核实有据。 + +### 剩余留档项(非缺陷,需产品决策) + +1. **`erp-upgrade-v3`(内部迭代页,保持现状)**——经核实为"ERP 产品页 V3 深度打磨版"(meta 为内部打磨描述),与项目 dogfood-* 目录同属内部验证产物。不进 sitemap、站内无入口是**有意设计**,无需修改。 +2. **`/products/erp-upgrade`(孤儿营销页,已修复)**——"ERP升级专题"是正规营销页,但此前站内零入口且不进 sitemap(搜索引擎无法发现)。**已加入 `sitemap.ts`**(priority 0.7 / monthly);站内导航入口仍建议由产品确认(products 页无现成位置,改动涉及页面设计)。 +3. **`/about/brand` 站内入口**——sitemap 已补(priority 0.6 / monthly);站内入口建议通过 about 页 CMS 字段(`hero*CtaHref`)配置,无需代码改动。 +4. **根目录 `data.db`(遗留旧库,已被 .gitignore 正确处理)**——2026-04 旧库(57KB),源码零引用, + 且**从未被 git 跟踪**(`.gitignore:97` 已忽略,初判"已跟踪"系误读 check-ignore 输出所致)。无需任何 git 操作; + 本地遗留文件可自行删除或归档。 +5. **本地 `data/novalon.db` 已重置重建**——旧数据备份于 `data/novalon.db.bak-20260830`;如需找回旧内容可从备份迁移。 + +### 修复总览(三轮累计) + +- WCAG AA 对比度违规:**0** +- 悬空 CSS 变量引用:**0 / 39** +- 透明度修饰符失效(静默样式失败):**~140 处恢复生效** +- 综合评分:6/20(首轮)→ 14/20(二轮)→ 17/20(三轮收尾) + +## 六、三处我判断错了的地方(留档) + +审计要可复核,所以把自己判错的部分一并记下: + +| # | 我的判断 | 实际情况 | +|---|---|---| +| 1 | 「8 张图片缺失,兜底机制失效」 | 全是测试夹具;生产代码 3 张图全部存在 | +| 2 | 「`unoptimized: true` 是缺陷」 | 架构决定,`CLAUDE.md` 有明确记载 + 三份 CDN 文档 | +| 3 | 「`#3b82f6` 等属色板二次漂移」 | `#3b82f6` 正是设计系统的 `accent-blue`;navy 是有意的产品主题变体 | + +教训:**看到硬编码色值不等于色板漂移**——要先比对令牌定义再下结论; +**看到"缺失资源"也要先区分测试夹具与生产引用**。 + +--- + +# 七、Lint 专项修复(第四轮,2026-08-31) + +补跑 eslint(前三轮未覆盖该维度):基线 **0 errors / 149 warnings**。 +本轮针对"可修且真实收益"的三类规则清零,其余全部留档并说明理由。 + +## 已清零 + +| 规则 | 前 | 后 | 修法 | +|---|---|---:|---:|---| +| `@next/next/no-html-link-for-pages` | 21 | **0** | 组件内硬编码站内 `` 统一改为项目自研 ``(17 文件 / 39 处 + 11 个文件补 import)。锚点 `href="#..."` 与动态/外链(mailto、https、模板变量)保留 | +| `jsx-a11y/alt-text` | 1 | **0** | `admin/media/page.tsx` 空状态 lucide `Image` 图标(装饰性 SVG,非 ``,`LucideProps` 不接受 alt)→ `aria-hidden` + 豁免注释 | +| `react-hooks/exhaustive-deps` | 2 | **0** | ① `news-content-v3.tsx`:`const NEWS = news ?? []` 包裹为 `useMemo(() => news ?? [], [news])`(消除 `?? []` 的每次渲染不稳定引用);② `admin-layout.tsx`:`getExpandedMenusForPath` 为组件内函数(引用随渲染变化),effect 已有 `prevPathname` 变更保护 → 行内豁免 + 注释说明,避免加依赖破坏逻辑 | + +> **方案说明**:未引入 `next/link`。项目为 Nginx 静态托管 + CDN 架构,`StaticLink`(`e.preventDefault()` 整页导航,阻止 RSC 拦截)是既有设计决策(footer / product-card 已采用),`` → `` 与架构一致且满足 lint。顺带修正 home-content-v14 中 3 处此前 lint 漏报的静态站内链接。 + +## 留档(有意或低收益,不改) + +| 规则 | 数量 | 理由 | +|---|---|---:|---| +| `no-console` | 56 | analytics.ts、示例组件、Sentry 配置、seed 脚本——诊断/配置用途合理 | +| `@typescript-eslint/no-explicit-any` | 45 | 类型收紧改动面大、收益低、风险高(e2e 测试占比高) | +| `@next/next/no-img-element` | 20 | `images.unoptimized: true` 静态托管架构下 `next/image` 不可用(CLAUDE.md:189 记录);含测试夹具 | +| `react-hooks/set-state-in-effect` | 13 | React 19 新规则偏激进;多数为 hydration mounted / admin 认证后数据获取 / 动画的标准模式,改动风险高 | + +## 验证 + +- eslint:**0 errors / 125 warnings**(剩余全部为上述留档项) +- `tsc --noEmit`:**0 errors** +- 修改文件累计 **47**(前三轮 31 + 本轮 16) + +--- + +# 八、完整测试验证(第五轮,2026-08-31) + +本轮改动 17 文件 JSX(`` → ``、`useMemo` 包裹、`aria-hidden` 修),tsc/eslint 仅保证类型与静态规则,**渲染级回归(SSR/水合、` diff --git a/src/app/(marketing)/news/news-content-v3.tsx b/src/app/(marketing)/news/news-content-v3.tsx index 9c389c3..b5e925f 100644 --- a/src/app/(marketing)/news/news-content-v3.tsx +++ b/src/app/(marketing)/news/news-content-v3.tsx @@ -75,7 +75,7 @@ function NewsCard({ newsItem, index }: { newsItem: NewsItem; index: number }) { } export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; pageCopy?: Record | null }) { - const NEWS = news ?? []; + const NEWS = useMemo(() => news ?? [], [news]); const [selectedCategory, setSelectedCategory] = useState('全部'); const [searchQuery, setSearchQuery] = useState(''); const [currentPage, setCurrentPage] = useState(1); @@ -94,7 +94,7 @@ export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; p newsItem.excerpt.toLowerCase().includes(searchQuery.toLowerCase()); return matchesCategory && matchesSearch; }); - }, [selectedCategory, searchQuery]); + }, [selectedCategory, searchQuery, NEWS]); const totalPages = Math.ceil(filteredNews.length / ITEMS_PER_PAGE); const paginatedNews = useMemo(() => { @@ -251,14 +251,14 @@ export default function NewsContentV3({ news, pageCopy }: { news?: NewsItem[]; p
联系我们 了解公司 diff --git a/src/app/(marketing)/news/news-detail-content-v3.tsx b/src/app/(marketing)/news/news-detail-content-v3.tsx index 08462dd..ca873c5 100644 --- a/src/app/(marketing)/news/news-detail-content-v3.tsx +++ b/src/app/(marketing)/news/news-detail-content-v3.tsx @@ -1,6 +1,7 @@ 'use client'; import { ScrollReveal } from '@/components/ui/scroll-reveal'; +import { StaticLink } from '@/components/ui/static-link'; import { Badge } from '@/components/ui/badge'; import { Calendar, ArrowLeft, Newspaper, ArrowRight } from 'lucide-react'; import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; @@ -63,9 +64,9 @@ export default function NewsDetailContentV3({ news, relatedNews = [] }: NewsDeta
@@ -145,20 +146,20 @@ export default function NewsDetailContentV3({ news, relatedNews = [] }: NewsDeta {/* Navigation */}
diff --git a/src/app/(marketing)/page.test.tsx b/src/app/(marketing)/page.test.tsx index 39b72f9..1e6ef91 100644 --- a/src/app/(marketing)/page.test.tsx +++ b/src/app/(marketing)/page.test.tsx @@ -8,14 +8,15 @@ jest.mock('@/lib/cms/data-server', () => ({ getHomePageCopy: jest.fn(), })); -jest.mock('./home-content-v14', () => ({ +jest.mock('./home-content-v15', () => ({ __esModule: true, - default: function MockHomeContentV14(props: Record) { + default: function MockHomeContentV15(props: Record) { return (
{(props.services as unknown[])?.length ?? 0}
{(props.cases as unknown[])?.length ?? 0}
{(props.stats as unknown[])?.length ?? 0}
+
{(props.news as unknown[])?.length ?? 0}
{props.heroData ? JSON.stringify(props.heroData) : 'null'}
{props.pageCopy ? JSON.stringify(props.pageCopy) : 'null'}
@@ -90,6 +91,9 @@ describe('HomePage', () => { if (modelCode === 'hero-banner') { return Promise.resolve([createMockItem('hero-banner', { heading: 'Direct Hero' })]); } + if (modelCode === 'news') { + return Promise.resolve([createMockItem('news', { title: '公司新闻' })]); + } return Promise.resolve([]); }); @@ -98,6 +102,7 @@ describe('HomePage', () => { expect(screen.getByTestId('services-count')).toHaveTextContent('1'); expect(screen.getByTestId('hero-data')).toHaveTextContent('Direct Hero'); + expect(screen.getByTestId('news-count')).toHaveTextContent('1'); }); it('handles empty zones gracefully', async () => { diff --git a/src/app/(marketing)/page.tsx b/src/app/(marketing)/page.tsx index 67bfb47..8efd722 100644 --- a/src/app/(marketing)/page.tsx +++ b/src/app/(marketing)/page.tsx @@ -1,5 +1,6 @@ import { getPublishedItems, getResolvedHomeZones, getHomePageCopy } from '@/lib/cms/data-server'; -import HomeContentV14 from './home-content-v14'; +import type { NewsItem } from '@/lib/constants/news'; +import HomeContentV15 from './home-content-v15'; export const revalidate = 3600; @@ -11,6 +12,7 @@ export default async function HomePage() { let cases: Record[] = []; let stats: Record[] = []; let heroData: Record | null = null; + let news: NewsItem[] = []; if (hasZones) { services = zones.services?.map((item) => item.data) ?? []; @@ -18,27 +20,30 @@ export default async function HomePage() { stats = zones.stats?.map((item) => item.data) ?? []; heroData = zones.hero?.[0]?.data ?? null; } else { - const [serviceItems, caseItems, statItems, heroItems] = await Promise.all([ + const [serviceItems, caseItems, statItems, heroItems, newsItems] = await Promise.all([ getPublishedItems('service'), getPublishedItems('case-study'), getPublishedItems('stat-item'), getPublishedItems('hero-banner'), + getPublishedItems('news'), ]); services = serviceItems.map((item) => item.data); cases = caseItems.map((item) => item.data); stats = statItems.map((item) => item.data); heroData = heroItems[0]?.data ?? null; + news = newsItems.map((item) => item.data as unknown as NewsItem); } // 首页各章节结构性文案(信任信号/叙事情节/标题/CTA),CMS 未配置时为 null(组件内部兜底) const homePageCopy = await getHomePageCopy().catch(() => null); - return ; } diff --git a/src/app/(marketing)/products/erp-upgrade/erp-upgrade-content-v2.tsx b/src/app/(marketing)/products/erp-upgrade/erp-upgrade-content-v2.tsx index 35d2d75..d6c7c16 100644 --- a/src/app/(marketing)/products/erp-upgrade/erp-upgrade-content-v2.tsx +++ b/src/app/(marketing)/products/erp-upgrade/erp-upgrade-content-v2.tsx @@ -1,6 +1,7 @@ 'use client'; import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { StaticLink } from '@/components/ui/static-link'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { @@ -153,10 +154,10 @@ export default function ErpUpgradeContentV2({ item }: ErpUpgradeContentV2Props)
diff --git a/src/app/(marketing)/products/product-detail-content-v3.tsx b/src/app/(marketing)/products/product-detail-content-v3.tsx index 851eb0c..e268df7 100644 --- a/src/app/(marketing)/products/product-detail-content-v3.tsx +++ b/src/app/(marketing)/products/product-detail-content-v3.tsx @@ -2,6 +2,7 @@ import { motion } from 'framer-motion'; import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { StaticLink } from '@/components/ui/static-link'; import { Button } from '@/components/ui/button'; import { ArrowRight, Package, Check, Settings, Users, FileText, Cpu, Shield, TrendingUp } from 'lucide-react'; import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; @@ -89,13 +90,13 @@ function DetailHero({ product }: DetailHeroProps) { className="flex flex-wrap gap-6" >
@@ -403,15 +404,29 @@ function CTASection({ product }: CTASectionProps) {
+ + {/* 站内入口:ERP 升级专题(与 erp-upgrade-content-v2 的反向链接形成互链闭环) */} + {product.id === 'erp' && ( +
+ 正在使用旧版 ERP? + + 了解睿新 ERP 升级专题 + + +
+ )} diff --git a/src/app/(marketing)/products/products-content-v3.tsx b/src/app/(marketing)/products/products-content-v3.tsx index af8b903..779145e 100644 --- a/src/app/(marketing)/products/products-content-v3.tsx +++ b/src/app/(marketing)/products/products-content-v3.tsx @@ -136,13 +136,13 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) { className="flex flex-wrap gap-6" > @@ -425,7 +425,7 @@ function SuiteCombosSection({ products }: { products: Product[] }) { .filter(Boolean); return ( - - + ); })} @@ -498,13 +498,13 @@ function CTASection({ pageCopy }: { pageCopy?: PageCopy | null }) {
diff --git a/src/app/(marketing)/products/standalone/[id]/client.tsx b/src/app/(marketing)/products/standalone/[id]/client.tsx index 68eed94..1b89ef3 100644 --- a/src/app/(marketing)/products/standalone/[id]/client.tsx +++ b/src/app/(marketing)/products/standalone/[id]/client.tsx @@ -1,6 +1,7 @@ 'use client'; import { getHeroTheme } from '@/lib/constants/hero-themes'; +import { StaticLink } from '@/components/ui/static-link'; import { DetailHero } from '@/components/detail/detail-hero'; import { DetailTrustSection } from '@/components/detail/detail-trust-section'; import { DetailCTASection } from '@/components/detail/detail-cta-section'; @@ -112,19 +113,19 @@ export function StandaloneProductClient({ item }: StandaloneProductClientProps) )}
- 获取上线通知 - - + 查看其他产品 - +
diff --git a/src/app/(marketing)/services/service-detail-content-v4.tsx b/src/app/(marketing)/services/service-detail-content-v4.tsx index 96a7516..0aea508 100644 --- a/src/app/(marketing)/services/service-detail-content-v4.tsx +++ b/src/app/(marketing)/services/service-detail-content-v4.tsx @@ -1,6 +1,7 @@ 'use client'; import { motion } from 'framer-motion'; +import { StaticLink } from '@/components/ui/static-link'; import { ArrowUpRight, CheckCircle2, Zap, Clock, Users, Award, TrendingUp, Shield } from 'lucide-react'; import type { LucideIcon } from 'lucide-react'; import type { Service, CaseStudy } from '@/lib/constants/services'; @@ -80,16 +81,16 @@ function DetailHero({ service }: { service: Service }) { transition={{ duration: 0.8, delay: 0.9, ease: EASE_OUT }} className="flex flex-col sm:flex-row gap-4 sm:gap-6" > - 免费咨询 - + 查看案例 @@ -336,19 +337,19 @@ function CTASection() { 首次咨询免费,没有任何销售压力。

- 预约免费咨询 - - + 返回服务列表 - +
diff --git a/src/app/(marketing)/services/services-content-v3.tsx b/src/app/(marketing)/services/services-content-v3.tsx index 0f4e83c..73e3876 100644 --- a/src/app/(marketing)/services/services-content-v3.tsx +++ b/src/app/(marketing)/services/services-content-v3.tsx @@ -126,19 +126,19 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) { transition={{ duration: 0.8, delay: 0.7, ease: EASE_OUT }} className="flex flex-col sm:flex-row gap-4 sm:gap-6" > - {ctaPrimary} - - + {ctaSecondary} - +
- 预约免费咨询 - - + 了解我们的团队 - +
diff --git a/src/app/(marketing)/solutions/solution-detail-content-v3.tsx b/src/app/(marketing)/solutions/solution-detail-content-v3.tsx index 9b6f0b1..061cd90 100644 --- a/src/app/(marketing)/solutions/solution-detail-content-v3.tsx +++ b/src/app/(marketing)/solutions/solution-detail-content-v3.tsx @@ -2,6 +2,7 @@ import { motion } from 'framer-motion'; import { ScrollReveal, StaggerReveal } from '@/components/ui/scroll-reveal'; +import { StaticLink } from '@/components/ui/static-link'; import { Button } from '@/components/ui/button'; import { ArrowRight, Factory, ShoppingCart, Heart, GraduationCap, Lightbulb, Settings, Users, TrendingUp, Package, BookOpen, MessageSquare, Calendar, Smartphone, Route, Shield, Truck } from 'lucide-react';import { SectionLabel, EASE_OUT } from '@/components/ui/page-decoration'; import type { Solution } from '@/lib/constants/solutions'; @@ -83,13 +84,13 @@ function DetailHero({ solution }: DetailHeroProps) { className="flex flex-wrap gap-6" > @@ -491,13 +492,13 @@ function CTASection({ solution }: CTASectionProps) {
diff --git a/src/app/(marketing)/solutions/solutions-content-v3.tsx b/src/app/(marketing)/solutions/solutions-content-v3.tsx index f9e5320..f9664c0 100644 --- a/src/app/(marketing)/solutions/solutions-content-v3.tsx +++ b/src/app/(marketing)/solutions/solutions-content-v3.tsx @@ -74,19 +74,19 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) { transition={{ duration: 0.8, delay: 0.7, ease: EASE_OUT }} className="flex flex-col sm:flex-row gap-4 sm:gap-6" > - {ctaPrimary} - - + {ctaSecondary} - +
- 获取定制方案 - - + 浏览产品 - +
diff --git a/src/app/(marketing)/team/team-content-v3.tsx b/src/app/(marketing)/team/team-content-v3.tsx index 3c5a850..423a9d9 100644 --- a/src/app/(marketing)/team/team-content-v3.tsx +++ b/src/app/(marketing)/team/team-content-v3.tsx @@ -217,7 +217,7 @@ export default function TeamContentV3({ data }: { data: Record > {teamData.heroPrimaryCtaLabel || '加入我们'} @@ -225,7 +225,7 @@ export default function TeamContentV3({ data }: { data: Record {teamData.heroSecondaryCtaLabel && ( {teamData.heroSecondaryCtaLabel} @@ -349,14 +349,14 @@ export default function TeamContentV3({ data }: { data: Record
{teamData.ctaPrimaryLabel || '预约咨询'} {teamData.ctaSecondaryLabel || '查看客户案例'} diff --git a/src/app/admin/media/page.tsx b/src/app/admin/media/page.tsx index 7ca5ab3..e42db17 100644 --- a/src/app/admin/media/page.tsx +++ b/src/app/admin/media/page.tsx @@ -193,7 +193,8 @@ export default function MediaPage() { {items.length === 0 && !loading && (
- + {/* eslint-disable-next-line jsx-a11y/alt-text -- lucide Image 图标(装饰性 SVG,非 ),aria-hidden 表明装饰意图 */} +

暂无媒体文件

上传图片、文档等资源,即可在内容编辑中使用