# 健康装修科普可点击全文 实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 让 landing「健康装修科普」的 4 篇卡片可点击进入看原创全文,并提供 `/articles` 列表页与 `/article/:id` 详情页。 **Architecture:** 纯前端静态。新建单一内容模块 `src/common/articles.ts`(4 篇原创全文 + `getArticle`),landing 轮播、列表页、详情页三处共用。新增两条 hash 公开路由,无后端/数据库改动。 **Tech Stack:** UmiJS 4 + React 18 + Antd 5 + TypeScript。无单测框架 —— 用 `npx tsc --noEmit -p tsconfig.json` 做类型验证(既有仅 2 条 eslint 类型根告警属预存,新增文件须零错误),交互留浏览器实测。 ## Global Constraints - 工作目录:`C:/code/空气质量预测/源码/用户端/iapip-web`。所有相对路径以此为根。 - 页面为公开页,路由 `layout: false`,风格与 `src/pages/landing`、`src/pages/forum` 一致,内联样式即可,**不新增全局 CSS**。 - hash 路由:文章 `id` 必须为 URL 友好的 ASCII kebab-case slug。 - 文章 4 篇,slug 固定:`gb-standards` / `formaldehyde-release` / `summer-exceed` / `predict-before-cma`。 - 每篇正文结尾附免责声明段落:「本文为科普整理,预测结果仅供参考,以 CMA 检测为准。」 - 提交在仓库根 `C:/code`(`git add` 用含中文的完整路径),每任务末尾提交一次。 - 修改 `.umirc.ts` 后需重启前端 dev(配置变更不热更新);前端 dev 端口 8001。 --- ## 文件结构 - `src/common/articles.ts` — **新建**,文章类型 + 4 篇内容 + `getArticle`(Task 1) - `src/pages/article/index.tsx` — **新建**,详情页 `/article/:id`(Task 2) - `src/pages/articles/index.tsx` — **新建**,列表页 `/articles`(Task 3) - `.umirc.ts` — 新增 2 条路由(Task 2 加 `/article/:id`,Task 3 加 `/articles`) - `src/pages/landing/index.tsx` — news 派生自 articles、卡片与「全部文章」接线(Task 4) --- ## Task 1: 文章内容模块 `src/common/articles.ts` **Files:** - Create: `src/common/articles.ts` **Interfaces:** - Produces: `interface ArticleSection { heading?: string; paragraphs: string[] }`;`interface Article { id: string; tag: string; date: string; title: string; summary: string; body: ArticleSection[]; sources?: string[] }`;`const articles: Article[]`(4 篇);`function getArticle(id: string): Article | undefined`。 - [ ] **Step 1: 创建内容模块(完整内容如下,勿留占位)** 创建 `src/common/articles.ts`: ```ts export interface ArticleSection { heading?: string paragraphs: string[] } export interface Article { id: string tag: string date: string title: string summary: string body: ArticleSection[] sources?: string[] } const DISCLAIMER = '本文为科普整理,预测结果仅供参考,以 CMA 检测为准。' export const articles: Article[] = [ { id: 'gb-standards', tag: '政策解读', date: '专栏 · 2026.05', title: '两大国标怎么读?GB/T 18883 与 GB 50325 的差别', summary: '一个是“住进去后”的室内空气质量标准,一个是“交工验收时”的工程控制标准——限值与采样条件并不相同,看懂它们才能判断房子到底达不达标。', body: [ { heading: '两个标准,管的是两件不同的事', paragraphs: [ 'GB/T 18883《室内空气质量标准》管的是“人住进去之后”的日常居住环境,属于推荐性标准(编号带 GB/T),衡量的是房间在正常生活状态下的空气质量。', 'GB 50325《民用建筑工程室内环境污染控制标准》管的是“工程交付验收时”的室内环境,属于强制性工程标准,是新建、扩建民用建筑竣工验收必须满足的底线。' ] }, { heading: '采样条件不同,数字不能直接比', paragraphs: [ '两个标准最容易被忽略的差别是采样前的“关闭门窗时间”。工程验收类标准通常要求关闭门窗较短时间后采样,模拟的是交工状态;居住类标准要求关闭门窗较长时间后采样,模拟的是日常关窗睡觉的真实场景。', '关闭时间越长,累积的污染物浓度往往越高。因此同一套房子,用两套标准、两种采样条件测出来的数字并不能直接比较,更不能混用限值。具体限值与采样时长以两部标准原文为准。' ] }, { heading: '该看哪一个', paragraphs: [ '装修完工、准备验收,看 GB 50325;已经入住、想给居住环境做“体检”,看 GB/T 18883。', '本系统在预测时同时对照两部国标的限值,帮助你在选材和验收两个阶段都心里有数。', DISCLAIMER ] } ], sources: ['GB/T 18883 室内空气质量标准', 'GB 50325 民用建筑工程室内环境污染控制标准'] }, { id: 'formaldehyde-release', tag: '科普', date: '专栏 · 2026.04', title: '新装住宅的甲醛,为什么能持续释放 3–15 年?', summary: '人造板里的脲醛树脂胶会缓慢分解释放甲醛,释放周期长、受温湿度影响大。短期通风只能降一时浓度,真正的关键在源头材料的选择。', body: [ { heading: '甲醛藏在哪里', paragraphs: [ '室内甲醛的主要来源是人造板材——刨花板、密度板、胶合板等。它们用脲醛树脂胶把木屑、木片粘合成板,而脲醛树脂胶里既含有未完全反应的游离甲醛,也含有遇水遇热会缓慢分解的化学结构。' ] }, { heading: '为什么能释放这么久', paragraphs: [ '脲醛树脂在温度和湿度作用下会发生缓慢水解,不断把结合态的甲醛释放为游离甲醛。这个过程非常缓慢却持续,板材类污染源的释放周期可长达数年,通常说的 3–15 年正来源于此。', '相比之下,油漆、涂料等表面材料的挥发性污染物释放周期短得多(数周到数月),这也是为什么装修后长期超标的“主角”往往是板材而非油漆。' ] }, { heading: '通风为什么治标不治本', paragraphs: [ '通风只是把已经释放到空气中的甲醛换走,降低当下的空气浓度,并不改变材料内部的释放源。一旦停止通风、关闭门窗,浓度又会回升,这就是常见的“反弹”。', '真正有效的做法是从源头入手:优先选用低释放等级(E0/E1)的板材、控制板材总用量,再配合长期通风与温湿度管理。', DISCLAIMER ] } ], sources: ['人造板脲醛树脂释放机理(通用建材科学知识)'] }, { id: 'summer-exceed', tag: '指南', date: '专栏 · 2026.03', title: '夏天为什么更容易超标?温度与释放速率', summary: '温度升高,材料的甲醛释放速率会明显增大。这也是“冬天测达标、夏天又超标”的原因。预测时把环境温湿度纳入计算,结果才靠谱。', body: [ { heading: '温度:释放速率的放大器', paragraphs: [ '温度越高,材料内部甲醛分子的扩散和释放越活跃,单位时间释放到空气中的量随之增大。夏季室温明显高于冬季,因此同一套房子在夏天测得的浓度往往更高。' ] }, { heading: '湿度也在帮倒忙', paragraphs: [ '较高的空气湿度会加速脲醛树脂的水解,进一步促进甲醛释放。高温加上高湿的梅雨、盛夏季节,是甲醛浓度最容易冲高的时段。' ] }, { heading: '“冬测达标”不等于安全', paragraphs: [ '冬季低温下测得达标,并不代表夏季高温高湿时依然达标。判断房子是否安全,必须结合采样时的温湿度条件来看。', '本系统在预测中引入了温湿度修正因子,把房间实际的温度、湿度纳入计算,让预测结果更贴近你真正居住的季节。', DISCLAIMER ] } ], sources: ['温湿度对材料释放速率的影响(通用建材科学知识)'] }, { id: 'predict-before-cma', tag: '方法', date: '专栏 · 2026.02', title: '先预测,再决定要不要做 CMA 检测', summary: '上门检测有成本。用本系统先做一次免费预测、定位高风险房间与主要污染材料,再有针对性地安排第三方 CMA 检测,省钱也更有的放矢。', body: [ { heading: 'CMA 检测是什么', paragraphs: [ 'CMA(中国计量认证)检测是具有法律效力的第三方检测,结果权威、可作为依据。但它需要在装修完成后上门采样,存在时间与费用成本,点位越多越贵。' ] }, { heading: '先预测的价值', paragraphs: [ '在选材和装修阶段,用本系统先做一次免费预测:输入房间、环境与所用材料,估算各房间的污染物浓度,定位出高风险房间和贡献最大的污染材料。', '这样你就能把有限的检测预算花在刀刃上——重点检测预测中接近或超过限值的房间,而不是每个房间都盲目下点位。' ] }, { heading: '怎么配合使用', paragraphs: [ '预测显示某房间接近或超过限值,就针对该区域安排 CMA 检测复核;预测整体余量较大,则可适当减少检测点位。', '预测基于材料释放模型与国标限值,是决策辅助手段;房子最终是否达标,仍以 CMA 检测结果为准。', DISCLAIMER ] } ], sources: ['CMA 计量认证检测流程(通用知识)'] } ] export function getArticle(id: string): Article | undefined { return articles.find(a => a.id === id) } ``` - [ ] **Step 2: 类型检查** Run: ```bash cd "C:/code/空气质量预测/源码/用户端/iapip-web" && npx tsc --noEmit -p tsconfig.json 2>&1 | grep -E "error TS" | grep -v "eslint" | grep -iE "articles" || echo "articles.ts 无类型错误" ``` Expected: `articles.ts 无类型错误` - [ ] **Step 3: 确认 4 篇 slug 齐全** Run: ```bash grep -oE "id: '[a-z-]+'" "C:/code/空气质量预测/源码/用户端/iapip-web/src/common/articles.ts" ``` Expected: 4 行 —— `id: 'gb-standards'`、`id: 'formaldehyde-release'`、`id: 'summer-exceed'`、`id: 'predict-before-cma'`。 - [ ] **Step 4: Commit** ```bash cd "C:/code" && git add "空气质量预测/源码/用户端/iapip-web/src/common/articles.ts" && git commit -m "feat(web): add health-science articles content module" ``` --- ## Task 2: 文章详情页 `/article/:id` **Files:** - Create: `src/pages/article/index.tsx` - Modify: `.umirc.ts`(新增 `/article/:id` 路由) **Interfaces:** - Consumes: `getArticle`、`Article` from `@/common/articles`。 - Produces: 路由 `/article/:id`(组件 `./article`)。 - [ ] **Step 1: 创建详情页组件** 创建 `src/pages/article/index.tsx`: ```tsx import { history, useParams } from '@umijs/max' import { getArticle } from '@/common/articles' export default function ArticleDetail() { const { id } = useParams<{ id: string }>() const article = id ? getArticle(id) : undefined if (!article) { return (

文章不存在或已下线。

history.push('/landing')} style={{ color: '#B43533' }}>← 返回首页
) } return (
history.push('/landing')} style={{ color: '#888' }}>← 返回首页
{article.tag} · {article.date}

{article.title}

{article.summary}

{article.body.map((sec, i) => (
{sec.heading &&

{sec.heading}

} {sec.paragraphs.map((p, j) => (

{p}

))}
))} {article.sources && article.sources.length > 0 && (
参考来源
)}
) } ``` - [ ] **Step 2: 新增路由** `.umirc.ts` 的 `routes` 数组中,`帖子详情`(`/forum/thread/:id`)路由项之后追加: ```ts { name: '文章详情', path: '/article/:id', component: './article', layout: false }, ``` - [ ] **Step 3: 类型检查** Run: ```bash cd "C:/code/空气质量预测/源码/用户端/iapip-web" && npx tsc --noEmit -p tsconfig.json 2>&1 | grep -E "error TS" | grep -ivE "eslint" || echo "无新增类型错误" ``` Expected: `无新增类型错误`(仅可能残留既有的 2 条 eslint 告警,已被过滤)。 - [ ] **Step 4: 重启前端并浏览器验证** 重启前端 dev(配置变更需重启):停止当前 dev → 释放 8001 → `cd 用户端/iapip-web && PORT=8001 pnpm dev`。 浏览器打开 `http://localhost:8001/iapip-web/#/article/gb-standards`。 Expected: 显示该篇标题、`标签 · 日期`、摘要、分节正文(含小标题)、底部“参考来源”。访问 `#/article/nope` 显示“文章不存在”与返回链接。 - [ ] **Step 5: Commit** ```bash cd "C:/code" && git add "空气质量预测/源码/用户端/iapip-web/src/pages/article/index.tsx" "空气质量预测/源码/用户端/iapip-web/.umirc.ts" && git commit -m "feat(web): article detail page and route" ``` --- ## Task 3: 文章列表页 `/articles` **Files:** - Create: `src/pages/articles/index.tsx` - Modify: `.umirc.ts`(新增 `/articles` 路由) **Interfaces:** - Consumes: `articles` from `@/common/articles`。 - Produces: 路由 `/articles`(组件 `./articles`)。 - [ ] **Step 1: 创建列表页组件** 创建 `src/pages/articles/index.tsx`: ```tsx import { history } from '@umijs/max' import { articles } from '@/common/articles' export default function ArticleList() { return (
history.push('/landing')} style={{ color: '#888' }}>← 返回首页

健康装修科普

{articles.map(a => (
history.push('/article/' + a.id)} style={{ cursor: 'pointer', padding: 20, border: '1px solid #eee', borderRadius: 8, transition: 'box-shadow .2s' }} >
{a.tag} · {a.date}

{a.title}

{a.summary}

))}
) } ``` - [ ] **Step 2: 新增路由** `.umirc.ts` 的 `routes` 数组中,Task 2 新增的 `文章详情` 路由项**之前**追加(`/articles` 与 `/article/:id` 路径前缀不同,顺序不影响匹配,但列表在前更清晰): ```ts { name: '科普文章', path: '/articles', component: './articles', layout: false }, ``` - [ ] **Step 3: 类型检查** Run: ```bash cd "C:/code/空气质量预测/源码/用户端/iapip-web" && npx tsc --noEmit -p tsconfig.json 2>&1 | grep -E "error TS" | grep -ivE "eslint" || echo "无新增类型错误" ``` Expected: `无新增类型错误` - [ ] **Step 4: 重启前端并浏览器验证** 重启前端 dev。浏览器打开 `http://localhost:8001/iapip-web/#/articles`。 Expected: 列出 4 篇(标签/日期/标题/摘要),点击任一进入对应 `/article/:id` 详情。 - [ ] **Step 5: Commit** ```bash cd "C:/code" && git add "空气质量预测/源码/用户端/iapip-web/src/pages/articles/index.tsx" "空气质量预测/源码/用户端/iapip-web/.umirc.ts" && git commit -m "feat(web): articles list page and route" ``` --- ## Task 4: landing 接线(卡片可点、全部文章跳列表) **Files:** - Modify: `src/pages/landing/index.tsx` **Interfaces:** - Consumes: `articles` from `@/common/articles`(复用其 `id/tag/date/title/summary`)。 - [ ] **Step 1: 用 articles 替换本地 news 常量** 在 `landing/index.tsx` 顶部 import 段追加: ```ts import { articles } from '@/common/articles' ``` 删除文件中现有的本地 `const news = [ ... ]`(4 条写死资讯的整段数组定义),并在其原位置替换为: ```ts const news = articles ``` > 说明:`articles` 每项含 `tag/date/title/summary`,取代原 `news` 的 `tag/date/title/desc`。渲染处会把 `desc` 改为 `summary`(下一步)。 - [ ] **Step 2: 轮播卡片改用 summary 并加点击跳转** 在 `#news` 区块的轮播渲染中,找到 `news.map((n, i) => ( ... ))` 生成的 `
`。做两处改动: 其一,给整张卡加点击跳转——将 `
` 改为: ```tsx
history.push('/article/' + n.id)}> ``` 其二,把卡片正文里的摘要字段 `n.desc` 改为 `n.summary`: ```tsx

{n.summary}

``` (`阅读全文` 的 `.cslide-more` 已在整卡点击范围内,无需单独绑定。) - [ ] **Step 3: 「全部文章」按钮跳列表页** 在 `#news` 区块头部,将: ```tsx 全部文章 ``` 改为: ```tsx history.push('/articles')}>全部文章 ``` - [ ] **Step 4: 类型检查** Run: ```bash cd "C:/code/空气质量预测/源码/用户端/iapip-web" && npx tsc --noEmit -p tsconfig.json 2>&1 | grep -E "error TS" | grep -ivE "eslint" || echo "无新增类型错误" ``` Expected: `无新增类型错误` - [ ] **Step 5: 浏览器验证(landing 为 tsx,热更新即可,无需重启)** 浏览器打开 `http://localhost:8001/iapip-web/#/landing`。 Expected: - 「健康装修科普」轮播卡片可点击 → 进入对应 `/article/:id` 全文。 - 顶部「全部文章」→ `/articles` 列表页。 - 轮播自动播放、左右切换、圆点交互不受影响;卡片摘要文字正常显示(非空白)。 - [ ] **Step 6: Commit** ```bash cd "C:/code" && git add "空气质量预测/源码/用户端/iapip-web/src/pages/landing/index.tsx" && git commit -m "feat(web): wire landing news cards to article pages" ``` --- ## Self-Review 记录 - **Spec 覆盖**:内容模块→Task 1;详情页 `/article/:id`→Task 2;列表页 `/articles`→Task 3;landing 接线(卡片可点 + 全部文章跳转 + news 派生自 articles)→Task 4;路由→Task 2/3。全覆盖。 - **类型一致**:`Article`/`ArticleSection` 字段(`id/tag/date/title/summary/body/sources`、`heading/paragraphs`)在 Task 1 定义,Task 2/3/4 消费一致;`getArticle(id)`、`articles` 命名一致;landing 渲染由 `n.desc` 改为 `n.summary` 与新数据源匹配。 - **占位符**:无 TODO/TBD;4 篇正文全文已写入 Task 1;各步含具体命令与预期。 - **风险**:landing 删除本地 `news` 数组时须整段删除、勿残留;`.umirc.ts` 改动后 Task 2/3 需重启前端,Task 4 仅 tsx 热更新。