docs: 更新生成原型技能,明确前端渲染方案

- 将 Step 5 和 Step 6 更新为前端渲染方案
- 使用 markdown-it 在浏览器中渲染 Markdown
- 文档点击后直接在页面中展开,不使用模态框
- 明确多路径兼容和渐进加载策略

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yourname
2026-03-29 03:41:31 +00:00
parent 7388e79b09
commit 88e569dda5

View File

@@ -319,105 +319,219 @@ prototype/
- 快速链接导航
- 项目概览信息
## Step 5: 生成文档系统
## Step 5: 生成文档系统(前端渲染方案)
生成 `docs-index.html` 文档首页,自动读取和展示 `docs/` 目录下的 Markdown 文件。
生成 `docs-index.html` 文档首页,使用前端 JavaScript 渲染 Markdown 文件。
### 功能特性
- 自动扫描 `docs/` 目录
- 实时 Markdown 转 HTML
- 文档搜索功能
- 卡片/内容双视图切换
- 自动扫描 `docs/``planning-artifacts/` 目录
- **前端 Markdown 渲染**:使用 markdown-it 库在浏览器中渲染
- 文档卡片展示,点击后在页面中展开显示内容
- 响应式设计,支持桌面端和移动端
### 技术实现
**1. 引入 markdown-it 库**
```html
<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.0.0/dist/markdown-it.min.js"></script>
```
**2. 文档卡片结构**
```html
<!-- 文档卡片 -->
<div class="doc-card">
<div class="doc-header">
<h3>产品需求文档 (PRD)</h3>
<span class="doc-badge">核心文档</span>
</div>
<div class="doc-meta">
<span>1,082 行</span>
<span>40 KB</span>
</div>
<div class="doc-actions">
<button onclick="toggleDoc('prd.md')" class="btn-view">
<i class="fas fa-eye"></i> 查看文档
</button>
</div>
<!-- 文档内容容器(默认隐藏) -->
<div id="doc-prd.md" class="doc-content hidden"></div>
</div>
```
**3. JavaScript 渲染逻辑**
```javascript
// 初始化 markdown-it
const md = window.markdownit({
html: true,
linkify: true,
typographer: true,
breaks: true
});
// 切换文档显示
function toggleDoc(docFile) {
const container = document.getElementById('doc-' + docFile);
const isHidden = container.classList.contains('hidden');
if (isHidden) {
// 加载并渲染文档
loadDoc(docFile, container);
} else {
// 隐藏文档
container.classList.add('hidden');
}
}
// 加载文档
async function loadDoc(docFile, container) {
// 尝试多个路径
const paths = ['/planning-artifacts/' + docFile, '/docs/' + docFile, '/' + docFile];
for (const path of paths) {
try {
const response = await fetch(path);
if (response.ok) {
const markdown = await response.text();
const html = md.render(markdown);
container.innerHTML = html;
container.classList.remove('hidden');
return;
}
} catch (err) {
continue;
}
}
// 所有路径都失败
container.innerHTML = '<p class="error">无法加载文档: ' + docFile + '</p>';
container.classList.remove('hidden');
}
```
**4. Markdown 内容样式**
```css
.markdown-body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 16px;
line-height: 1.6;
color: #24292f;
}
.markdown-body h1, .markdown-body h2 {
border-bottom: 1px solid #e5e7eb;
padding-bottom: 0.5em;
margin-bottom: 1em;
}
.markdown-body h1 { font-size: 2em; }
.markdown-body h2 { font-size: 1.5em; }
.markdown-body h3 { font-size: 1.25em; }
.markdown-body pre {
background: #1f2937;
color: #e5e7eb;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
}
.markdown-body code {
background: #f3f4f6;
padding: 0.2em 0.4em;
border-radius: 0.25rem;
font-size: 0.9em;
}
.markdown-body table {
border-collapse: collapse;
width: 100%;
}
.markdown-body table th, .markdown-body table td {
border: 1px solid #e5e7eb;
padding: 0.5rem;
}
.markdown-body table tr:nth-child(even) {
background: #f9fafb;
}
.markdown-body blockquote {
border-left: 4px solid #3b82f6;
padding-left: 1rem;
color: #6b7280;
}
.markdown-body a {
color: #2563eb;
}
.markdown-body img {
max-width: 100%;
}
```
### 文档链接格式
`index.html` 和其他页面中,文档链接使用以下格式:
```html
<!-- 点击展开文档 -->
<button onclick="toggleDoc('prd.md')">
<i class="fas fa-eye"></i> 查看 PRD
</button>
<!-- 或者使用链接 -->
<a href="#" onclick="toggleDoc('prd.md'); return false;">
<i class="fas fa-file-alt"></i> PRD 文档
</a>
```
### ⚠️ 关键设计原则
1. **前端渲染优先**:使用 markdown-it 在浏览器中渲染,不依赖后端 API
2. **直接展示**:点击文档链接后,直接在页面中展开显示内容,不使用弹窗/模态框
3. **渐进加载**:文档内容在需要时才加载,提升页面加载速度
4. **多路径兼容**尝试多个可能的文档路径planning-artifacts、docs、根目录
## Step 6: 生成本地预览服务器
生成 `server.js` 文件,提供静态文件服务和文档渲染 API
生成 `server.js` 文件,提供静态文件服务。
### 服务器功能
- 静态文件服务
- Markdown 转 HTML
- 文档列表 API: `GET /api/docs`
- 文档渲染 API: `GET /api/doc/:name`
- **中文文件名支持**: URL 解码处理 `decodeURIComponent()`
- 静态文件服务HTML、CSS、JS
- 支持 `.md` 文件的直接访问(返回原始 Markdown
- 支持父目录文件访问(用于 planning-artifacts 目录)
- CORS 支持(如果需要)
### ⚠️ 中文文件名处理
**关键修复**: 文档渲染 API 必须处理 URL 编码的中文文件名
### 服务器配置
```javascript
// server.js 中文档渲染端点
app.get('/api/doc/:name', (req, res) => {
const docName = decodeURIComponent(req.params.name); // 必须解码!
// 如果文件名已包含 .md 后缀,不要再加
const docPath = docName.endsWith('.md') ? docName : docName + '.md';
const filePath = path.join(DOCS_DIR, docPath);
// ...
});
const PORT = 8080;
const PROTOTYPE_DIR = __dirname;
const PARENT_DIR = path.dirname(__dirname); // 支持父目录访问
// MIME 类型
const MIME_TYPES = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.md': 'text/markdown; charset=utf-8',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.svg': 'image/svg+xml',
};
// 构建文件路径(支持父目录)
const filePath = urlPath.startsWith("/planning-artifacts/")
? path.join(PARENT_DIR, urlPath)
: path.join(PROTOTYPE_DIR, urlPath);
```
**问题原因**: 前端请求中文文件名时会自动 URL 编码(如 `%E5%8E%9F%E5%A7%8B...`),后端必须解码才能找到文件。
### ⚠️ 代码块样式处理
**关键修复**: Markdown 转 HTML 时必须保护代码块内容,避免换行被错误替换
```javascript
// 使用占位符保护代码块
const codeBlocks = [];
let html = markdown
// 先提取代码块,用占位符替换
.replace(/```(\w*)\n?([\s\S]*?)```/g, (match, lang, code) => {
const placeholder = `__CODE_BLOCK_${codeBlocks.length}__`;
const isAsciiArt = /┌|│|└|├|─|┐|┘|┤/.test(code);
const bgClass = isAsciiArt ? 'bg-gray-800 text-gray-100' : 'bg-gray-100 text-gray-900';
codeBlocks.push(`<pre class="${bgClass} p-4 rounded-lg overflow-x-auto my-4 font-mono text-sm whitespace-pre"><code>${code}</code></pre>`);
return placeholder;
})
// ... 其他处理 ...
// 最后恢复代码块
.replace(/__CODE_BLOCK_(\d+)__/g, (match, index) => codeBlocks[index]);
```
**问题原因**: 直接用 `.replace(/\n/g, '<br>')` 会把代码块中的换行也替换成 `<br>`,导致每行都有背景。
**样式规范**:
- ASCII图表代码块`bg-gray-800 text-gray-100`(深色背景+浅色文字)
- 普通代码块:`bg-gray-100 text-gray-900`(浅色背景+深色文字)
- 内联代码:`bg-gray-200 text-gray-800`(浅灰背景+深色文字)
### ⚠️ Markdown 代码块样式
**关键修复**: 代码块必须正确处理,避免白字白底无法阅读
```javascript
// 代码块正则(支持带或不带换行符的格式)
.replace(/```(\w*)\n?([\s\S]*?)```/g, (match, lang, code) => {
// 判断是否为ASCII图表包含框线字符
const isAsciiArt = /┌|│|└|├|─|┐|┘|┤/.test(code);
// 如果是ASCII图表使用深色背景+浅色文字;否则使用灰色背景
const bgClass = isAsciiArt ? 'bg-gray-800 text-gray-100' : 'bg-gray-100 text-gray-800';
return `<pre class="${bgClass} p-4 rounded-lg overflow-x-auto my-4 font-mono text-sm whitespace-pre"><code>${code}</code></pre>`;
})
```
**问题原因**: Markdown 代码块可能使用 ``` 后面直接跟内容(无换行符),正则必须兼容这种格式。同时需要为 ASCII 图表使用深色背景以提升可读性。
### 使用方法
```bash
cd prototype/
node server.js
# 访问: http://localhost:7357
```
### 端口说明
- **默认端口**: 7357 (偏门端口)
- **统一入口**: http://localhost:7357/index.html
- **原型系统**: http://localhost:7357/admin-index.html
- **文档系统**: http://localhost:7357/docs-index.html
---
### ⚠️ 中文文件名支持
前端使用 fetch 请求时,浏览器会自动处理 URL 编码,无需特殊处理。
## Step 7: 自动验证与测试
使用 **Playwright MCP****图片 MCP** 进行自动化验证。