feat:完善文档编写的技能包和模板文件

This commit is contained in:
fengpu
2026-09-10 11:53:07 +08:00
parent 982cb1c068
commit 9434681a80
8 changed files with 373 additions and 35 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

+132
View File
@@ -0,0 +1,132 @@
---
name: android-capture
description: 通过 Android 真机 + adb 无线调试截取原生 APP 实际页面截图(用于操作手册)。当用户要求「截取 APP 实际页面」「真机调试截图」「生成 XX 端 APP 操作手册截图」时使用。
---
# SkillAndroid 真机 APP 截图
## 触发方式
用户说「截取 XX APP 的实际页面截图」「真机调试截图」时触发。
用于原生 Android / Flutter 混开 APPiOS 同款只做 Android)的实际页面截图。
## 连接方式:无线调试(云桌面场景首选)
**关键背景**:本环境是云桌面 + 云终端盒子,无本地物理机。手机 USB 插在云终端盒子上,但盒子只传画面不传 USB 设备,**云桌面的 Windows 看不到手机**,`adb devices` 永远为空。因此必须走 **adb over WiFi(无线调试)**USB 方式在此环境不可用。
### 无线调试流程(Android 11+
```bash
# 1. 手机:设置 → 开发者选项 → 无线调试 → 打开
# 2. 手机点「使用配对码配对设备」,拿到 6 位配对码 + 配对 IP:端口
# 3. 云桌面执行配对(配对码时效短,拿到后立刻执行)
echo "6位配对码" | adb pair 手机IP:配对端口
# 4. 配对成功后,看手机「无线调试」主页面的「IP 地址和端口」(调试端口,与配对端口不同)
adb connect 手机IP:调试端口
# 5. 验证(显示 device 即成功)
adb devices -l
```
- 配对码有 30 秒~几分钟时效,超时需手机重新点「使用配对码配对设备」
- 配对端口与调试端口是**两个不同端口**,别混用
-`adb pair``protocol fault`,通常是配对码过期,重新获取
## 前置:环境检测(每次先跑)
```bash
adb version # adb 是否可用
adb devices -l # 真机是否连接(显示 device 才算连上)
adb shell wm size # 屏幕分辨率(用于坐标换算)
```
- `adb` 不在 PATH:见「环境准备」装 platform-tools
- `adb devices` 空:无线调试未连上,回到「无线调试流程」重新配对/连接
## 环境准备(首次需要)
### 1. 安装 adbWindowscurl 一键)
```bash
curl -L -o /d/Work/platform-tools.zip https://dl.google.com/android/repository/platform-tools-latest-windows.zip
unzip -q -o /d/Work/platform-tools.zip -d /d/Work/android-sdk/
/d/Work/android-sdk/platform-tools/adb.exe version # 验证
```
- 或手动下载 [Android SDK Platform-Tools](https://developer.android.com/tools/releases/platform-tools) 解压
- 或安装 Android Studio(自带 adb
> 本机已装:`D:\android-sdk\platform-tools\adb.exe`,已加入用户 PATH(注册表已配,但云桌面需**注销重登**才在 cmd 生效;脚本执行时用 `export PATH="$PATH:/d/Work/android-sdk/platform-tools"` 临时生效即可)
### 2. 手机开启无线调试
1. 「设置 → 关于手机 → 版本号」连点 7 次,开启「开发者选项」
2. 「设置 → 开发者选项」打开「无线调试」(需 Android 11+
3. 手机连 WiFi(与云桌面同一网络)
### 3. 确认目标 APP 包名
```bash
adb shell pm list packages | grep -iE "health|yixiong|healthy|jk"
```
本机实际包名:`com.sw.healthyclients`(健康客户端)、`com.sw.healthexpertclient.dev`(健康专家客户端 dev
## 截图流程
### Step 1:启动 APP
```bash
adb shell monkey -p 包名 1 # 无需知道 Activity,直接拉起
sleep 3 # 等待加载
```
### Step 2:截图 + 读界面文字验证(每个页面都做)
```bash
# 截图
adb exec-out screencap -p > 页面名.png
# 读界面文字,确认截到的是哪个页面(用 adb shell cat 直接读,避免 pull 的路径坑)
adb shell "uiautomator dump /sdcard/ui.xml"
adb shell cat /sdcard/ui.xml | grep -oE 'text="[^"]+"' | grep -v 'text=""'
```
> **必须读文字验证画面**:截图前先用 uiautomator 读文字,确认当前页面/弹窗是目标,再截图,避免截错页面。
### Step 3:模拟操作
```bash
adb shell input tap 540 1200 # 点击坐标
adb shell input swipe 540 1600 540 600 300 # 上滑
adb shell input text "admin" # 输入英文/数字(中文需 adbkeyboard 或 broadcast
adb shell input keyevent 4 # 返回
```
### Step 4:元素定位(确定点击坐标)
```bash
adb shell "uiautomator dump /sdcard/ui.xml"
adb shell cat /sdcard/ui.xml | grep -oE 'text="[^"]+"[^>]*bounds="\[[0-9]+,[0-9]+\]\[[0-9]+,[0-9]+\]"'
# 从 bounds="[x1,y1][x2,y2]" 取中心点:tap_x=(x1+x2)/2, tap_y=(y1+y2)/2
```
### Step 5:交给操作手册 skill
截图完成后,交给 `doc-operation-manual` skill 组织成操作手册。
## 输出物
- `交付文档/{子系统名}/操作手册/screenshots/*.png`APP 实际页面截图)
## 注意事项
- **git bash 路径坑**`/sdcard/ui.xml` 会被 MSYS 转义成 Windows 路径,用 `adb shell "uiautomator dump /sdcard/ui.xml"`(加引号)+ `export MSYS_NO_PATHCONV=1` 规避
- **读文字验证画面**:截图前务必用 uiautomator 读 text 确认页面,避免截错
- **配对码时效短**:无线调试配对码几十秒就失效,拿到立刻配对
- **截图命名用中文页面名**,与操作手册章节一一对应
- **点击坐标依分辨率换算**:先 `adb shell wm size` 确认(本机 1264×2800
- **中文输入受限**`adb shell input text` 只支持 ASCII,中文需 adbkeyboard 或 `am broadcast`
- 登录页等需要验证码/密码的场景,输入密码可自动化,验证码需人工识别
- 若 APP 未适配真机(自定义 ROM 兼容问题),先与开发确认
+12 -8
View File
@@ -1,6 +1,6 @@
---
name: doc-brochure
description: 生成指定子系统的《产品宣传册》(HTML 静态页,A4 分页排版,可直接转 PDF)。当用户要求「生成 XX 系统的宣传册/产品手册」时使用。
description: 生成指定子系统的《产品宣传册》(横版 A4HTML 生成后自动转 PDF 交付)。当用户要求「生成 XX 系统的宣传册/产品手册」时使用。
---
# Skill:生成产品宣传册
@@ -37,6 +37,7 @@ description: 生成指定子系统的《产品宣传册》(HTML 静态页,A4
- **核心功能**:选 4~6 个最有代表性的功能模块,每个配 1 个 emoji 图标 + 简介 + 2 个亮点
- **特色优势**:6 条优势(01~06 编号卡片)
- **终端覆盖**:列出该子系统覆盖的终端(每个配 emoji + 一句说明)
- **界面展示**:放 5 张 APP 竖版截图(手机边框 130×282)+ 3 张 Web 端截图(浏览器边框 290×181),分两行(上方 5 张 APP、下方 3 张 Web),从各端 `doc/screenshots/` 选核心页面(长截图用 object-fit 顶部裁剪)
### Step 3:填充 HTML 模板
@@ -45,18 +46,21 @@ description: 生成指定子系统的《产品宣传册》(HTML 静态页,A4
3. 保持内联 CSS 与设计令牌不变,仅替换文字与 emoji
4. 确保所有 `{...}` 占位符都被替换,不留占位符残留
### Step 4:写入输出文件
### Step 4:写入 HTML 源文件并检查
输出`交付文档/{子系统名}/{子系统名}产品宣传册.html`
1. 输出 HTML `交付文档/{子系统名}/{子系统名}产品宣传册.html`
2. 用浏览器打开检查:内容、截图是否加载、分页、无 `{占位符}` 残留
### Step 5 PDF 检查(可选
### Step 5生成 PDF(必做
- 宣传册模板已内置 `@page A4 landscape`(横版)与 `.page` 分页
- 可用 headless 浏览器打印转 PDF,或直接交付 HTML(甲方以 HTML 浏览 / 转 PDF 均可)
1. 检查 HTML 无误后,运行 `node _templates/docs/pdf-export.js <html文件>` 转出同名 PDF
2. PDF 为最终交付物,HTML 作为源文件保留
3. 转 PDF 参数已内置:横版 A4(landscape+ 保留背景色(printBackground
## 输出物
- `交付文档/{子系统名}/{子系统名}产品宣传册.html`
- `交付文档/{子系统名}/{子系统名}产品宣传册.pdf`(交付物)
- `交付文档/{子系统名}/{子系统名}产品宣传册.html`(源文件)
## 注意事项
@@ -65,4 +69,4 @@ description: 生成指定子系统的《产品宣传册》(HTML 静态页,A4
- **配色统一**:沿用模板品牌蓝 `#1890FF` 设计令牌,不另造配色
- **内容通用**:宣传册必须通用,不得体现客户信息(如「油田」、客户公司名、委托方名称等)
- emoji 图标用于视觉占位,正式交付如需替换为专业图标,告知用户
- 文档名称「{子系统名}产品宣传册.html」为中文,符合甲方要求
- 文档名称「{子系统名}产品宣传册.html / .pdf」为中文,符合甲方要求
+55 -21
View File
@@ -1,6 +1,6 @@
---
name: doc-operation-manual
description: 生成指定子系统、指定终端的《操作手册》。当用户要求「生成 XX 系统 XX 端操作手册」「编写 XX 端使用手册/操作说明」时使用。操作手册按「子系统 + 终端」分册。
description: 生成指定子系统、指定终端的《操作手册》。当用户要求「生成 XX 系统 XX 端操作手册」「编写 XX 端使用手册/操作说明」时使用。操作手册按「子系统 + 终端」分册,截图必须用实际系统截图(不用原型图)
---
# Skill:生成操作手册
@@ -12,44 +12,75 @@ description: 生成指定子系统、指定终端的《操作手册》。当用
## 输入参数
- **子系统名**(必须,中文):如 `体重管理系统`
- **终端名**(必须,中文):如 `Web管理端` / `员工端APP` / `体检医生工作台` / `医疗点PAD`
- **子系统名**(必须,中文):如 `健康监测系统`
- **终端名**(必须,中文):如 `Web管理端` / `员工端APP` / `医疗点PAD`
- **适用对象**(可选):用户角色,缺省按终端推断
## 核心原则:截图必须用实际系统截图
**操作手册严禁用原型图**。原型和实际交付页面有差异,必须用测试环境真实页面截图。截图来源按终端区分:
| 终端 | 截图方式 | 对应 skill |
|------|---------|-----------|
| Web 管理端 | 浏览器自动化截图 | `web-capture` |
| 员工端 APP(原生 Android/Flutter | 真机 adb 截图 | `android-capture`(待编写) |
| 医疗点 PAD(横屏 Web) | 浏览器横屏视口截图 | `web-capture` |
| 微信小程序 | 浏览器模拟手机视口(若为 H5) | `web-capture` |
## 模板与素材
- 模板:`_templates/docs/operation-manual-template.md`
- 公共章节素材:`_templates/docs/common-sections-template.md`
- 素材:该终端 `doc/PRD.md` 的「逐页说明」(功能说明 + 交互说明)与截图
- **文字素材**:该终端 `doc/PRD.md` 的「逐页说明」(功能说明 + 交互说明 + 业务规则),用于提炼操作步骤与注意事项
- **截图素材**:由截图 skill(`web-capture` / `android-capture`)生成的实际系统截图,存放在 `交付文档/{子系统名}/操作手册/screenshots/`
## 执行流程
### Step 1:定位目标终端目录
### Step 0:先截图(前置,必须)
1. **Web 管理端**:调用 `web-capture` skill,登录测试环境 → 导航到目标模块 → 逐页面截图
2. **员工端 APP**:调用 `android-capture` skill,真机 adb 逐页面截图
3. 截图存放 `交付文档/{子系统名}/操作手册/screenshots/`,命名用中文页面名
4. 若截图 skill 尚未就绪或模块未上线,**暂停并告知用户**,不退回用原型图
### Step 1:定位模块文字素材
1. 读根目录 `index.html`grep `SITE_MAP``name:'{子系统名}'` 的条目
2. 根据「终端名」匹配所属端SITE_MAP 顶层 `id`/`label`,如「Web管理端」→ `id:'web-admin'`),定位到唯一模块目录,如 `web-admin/weight-management`
3.该终端 `prd` 字段指向的 `doc/PRD.md`(缺失则 Glob 补查),获取页面清单逐页说明
2. 根据「终端名」匹配所属端,定位模块目录 `doc/PRD.md`
3. PRD 的「模块概述」「页面清单」「逐页说明」,获取**菜单结构 + 功能说明 + 交互说明 + 业务规则**(用于文字,不用其截图)
### Step 2:整理运行环境与适用对象
- 运行环境引用 `项目背景.md` 2.x 视觉规范对应终端尺寸:
- Web 管理端:浏览器,最小宽度 1280px,侧边栏 240px
- Web 管理端:浏览器,最小宽度 1280px
- 员工端 APP390×844iPhone 14
- 医疗点 PAD1280×800 横屏
- 微信小程序:375×812
- 适用对象按终端推断(平台管理员/能源员工/体检医生/医疗点医护人员等)
- 适用对象按终端推断(平台管理员/能源员工/医疗点医护人员等)
### Step 3将 PRD 改写为操作手册
### Step 3按业务线路组织操作手册(核心)
`operation-manual-template.md` 生成,核心是把 PRD 的「交互说明」从**设计视角改写为「用户操作视角」的分步操作**
**先梳理业务线路**(不是按页面/菜单顺序罗列)
1. 从 PRD 的「模块概述」「页面导航结构」提炼**业务主线**——即用户完成某个业务目标的完整操作流程
2. 每条业务线路对应手册一个章节(通常对应一个一级菜单/业务域,但不是简单按菜单排)
3. 示例(健康监测):① 档案查询 → ② 健康现状查看 → ③ 健康排查 → ④ 穿戴监测 → ⑤ 环境监测 → ⑥ 异常处理
**选择性覆盖原则**(与 PRD 前后呼应,但绝不完全一样):
- 只覆盖**核心操作**(高频、关键业务操作),不罗列所有小交互
- 只收录**主要弹窗**和**核心二级页面**(详情页等),不列所有弹窗
- **页面状态选择性说明**:只说明用户常见的关键状态,不穷举所有状态(如只讲"进行中"和"已结束",不讲所有中间态)
`operation-manual-template.md` 生成:
- **1. 概述**:系统简介 + 适用对象 + 运行环境 + 登录说明(步骤化)
- **2. 界面总览**:布局说明 + 首页/工作台截图 + 导航结构
- **3. 功能操作指南**:按「一级菜单/业务线分组,每个功能点写
- **操作步骤**(编号列表,从「进入某页面」到「完成操作」)
- **界面截图**沿用 PRD 截图)
- **注意事项**从 PRD 业务规则提炼用户须知的要点
- **4. 常见问题与故障处理**提炼常见操作疑问与处理方法(表格)
- **2. 界面总览**:布局说明 + 首页/工作台截图 + 导航结构(含业务线路导读)
- **3. 功能操作指南**:按业务线分组,每条线路写核心操作
- **操作步骤**(编号列表,从「进入某页面」到「完成核心操作」)
- **界面截图**核心页面 + 主要弹窗,引用实际系统截图)
- **注意事项**关键业务规则用户须知)
- **4. 常见问题与故障处理**核心操作的高频问题(表格)
- **5. 附录**:术语表 + 快捷操作
### Step 4:并入公共操作章节
@@ -58,8 +89,9 @@ description: 生成指定子系统、指定终端的《操作手册》。当用
### Step 5:截图引用
- 沿用该终端 `doc/screenshots/` 现有截图
- 路径`交付文档/{子系统名}/操作手册/` 指回:`../../../{端目录}/doc/screenshots/{页面名}.jpg`
- 引用 `交付文档/{子系统名}/操作手册/screenshots/` 下的实际系统截图
- 路径相对操作手册文件:`screenshots/{页面名}.png`
- 若某页面截图缺失,标注「待补」,不退回用原型图
### Step 6:写入输出文件
@@ -68,10 +100,12 @@ description: 生成指定子系统、指定终端的《操作手册》。当用
## 输出物
- `交付文档/{子系统名}/操作手册/{子系统名}{终端名}操作手册.md`
- `交付文档/{子系统名}/操作手册/screenshots/*.png`(实际系统截图)
## 注意事项
- 操作手册面向**终端用户**,语言要通俗、步骤化,避免出现实现细节(如字段名、接口名)
- 截图沿用现有,不重新截图
- **截图必须实际系统截图**,严禁原型图;模块未上线或截图 skill 未就绪时,暂停并告知用户
- **与 PRD 前后呼应**:手册章节对应 PRD 功能模块,但只覆盖核心操作、主要弹窗、核心二级页面,不逐页罗列所有弹窗/小交互/页面状态
- 操作手册面向**终端用户**,语言通俗、步骤化,避免实现细节(字段名、接口名)
- 每个子系统的每个终端单独一份手册,公共操作(登录/消息等)在各手册中重复体现(甲方要求)
- 文档全部使用简体中文
+92
View File
@@ -0,0 +1,92 @@
---
name: web-capture
description: 截取 Web 管理端测试环境的实际系统页面截图(用于操作手册)。当用户要求「截取 XX 模块的实际页面」「生成 XX 模块操作手册截图」「登录测试环境截图」时使用。
---
# Skill:Web 管理端实际系统截图
## 触发方式
用户说「截取 XX 模块的实际页面截图」「登录测试环境截图」「生成 XX 模块操作手册」时触发。
## 输入参数
- **测试环境地址**(必须):如 `https://dev.yixiong-tech.com:8086/login`
- **账号 / 密码**(必须):如 `admin` / `Aa987654321!`
- **目标模块**(必须,中文):如 `健康监测``营养管理`
## 关键约束(本系统特有,务必遵守)
1. **验证码需人工识别**:登录页有 4 位图片验证码,Claude 无法自动识别图片。流程是——截图验证码存成文件 → 请用户看图报出验证码 → 填入登录。
2. **微前端子应用**:各业务模块是 Qiankun 子应用,URL 为 `/subXxx/...`,不是简单的一级路径。
3. **纯 JS 路由,无 `<a href>`**:页面用 Vue Router,菜单是 Ant Design 组件,**不能用 `navigate_page` 直接跳菜单**,也不能找 `<a href>`
4. **popover 菜单项 click 不跳转**:首页模块卡片 hover 弹出的 `ant-popover` 菜单项,直接 click 不生效。**可靠导航方式是 `router.push()`**(见下)。
## 模块 → 子应用 URL 映射
| 模块名 | 子应用路由前缀 |
|--------|--------------|
| 系统管理 | `/subSystemManagement/` |
| 健康监测 | `/subHealthMonitoring/` |
| 健康评估 | `/subHealthAssessment/` |
| 专家咨询 | `/subHealthExpertConsult/` |
| 健康助手 | `/subHealthHelper/` |
| 应急就医 | `/subEmergencyMedical/` |
| 营养管理 | `/subNutritionManagement/` |
| 体重管理 | `/subWeightManagement/` |
## 执行流程
### Step 1:打开登录页并截图验证码
1. 用 chrome-devtools `new_page` 打开测试环境登录页
2. `fill_form` 填账号、密码
3. `take_screenshot` 截图(含验证码),保存到临时文件(如 `_login_captcha.png`
4. **请用户看图报出 4 位验证码**
### Step 2:登录
1. 用户报验证码后,`fill` 验证码输入框 + `click` 登录按钮
2.`evaluate_script` 检查 `location.href` 是否从 `/login` 跳到 `/home`
3. 若失败(验证码错误/过期),重新截图验证码请用户再报
### Step 3:导航到目标模块
`evaluate_script` 调用 Vue Router 的 `push`
```js
() => {
const router = document.querySelector('#app').__vue_app__.config.globalProperties.$router;
router.push('/subHealthMonitoring/archiveModule/personalEmergencyData');
return '已导航';
}
```
- 子应用内部具体路径需先进入模块后,从浏览器地址栏读取实际 URL
- 导航后页面跳转到子应用,`location.href` 会变成 `/subXxx/...`
### Step 4:摸清模块菜单结构
1. `take_snapshot` 获取侧边栏菜单:一级菜单(`menuitem`+ 二级菜单
2. 记录菜单层级,规划截图清单(每个叶子页面截一张)
### Step 5:逐页面截图
1. 对每个叶子页面:`router.push` 到对应 URL(或点击侧边栏菜单项)
2. `take_screenshot` 保存到 `交付文档/{子系统名}/操作手册/screenshots/{页面名}.png`
3. 截图命名用中文页面名(如 `个人监测数据.png`),与操作手册章节对应
### Step 6:交给操作手册 skill
截图完成后,交给 `doc-operation-manual` skill,用真实截图组织成操作手册。
## 输出物
- `交付文档/{子系统名}/操作手册/screenshots/*.png`(实际系统截图)
## 注意事项
- **验证码是每次登录的障碍**:优先建议让开发在测试环境关闭验证码;未关闭则每次登录请用户人工报验证码
- **模块可能未上线**:首页模块卡片标「开发中,敬请期待」的模块不可截图,先和用户确认目标模块是否可用
- **截图以实际系统为准**:操作手册严禁用原型图,必须是测试环境真实页面
- 截图文件名用中文页面名,与操作手册章节一一对应
+39 -6
View File
@@ -54,12 +54,12 @@
/* ===== 封面 ===== */
.cover{ background:linear-gradient(135deg,#0a2540 0%,#0a5cb8 55%,#1890FF 100%);color:#fff; }
.cover .inner{ justify-content:space-between;position:relative; }
.cover__top{ display:flex;justify-content:space-between;align-items:center;font-size:13px;opacity:.92; }
.cover__top{ display:flex;justify-content:space-between;align-items:center;font-size:14px;color:rgba(255,255,255,.96); }
.cover__logo{ font-weight:800;font-size:16px;letter-spacing:1px;display:flex;align-items:center;gap:8px; }
.cover__logo .dot{ width:10px;height:10px;border-radius:50%;background:linear-gradient(135deg,#40c9ff,#1890FF); }
.cover__mid{ display:flex;align-items:center;justify-content:space-between;gap:40px; }
.cover__title{ font-size:44px;font-weight:900;line-height:1.2;margin-bottom:14px;letter-spacing:2px; }
.cover__slogan{ font-size:16px;opacity:.92;line-height:1.9; }
.cover__slogan{ font-size:16px;color:rgba(255,255,255,.96);line-height:1.9; }
.cover__tags{ margin-top:22px;display:flex;gap:10px;flex-wrap:wrap; }
.cover__tag{ padding:7px 16px;border:1px solid rgba(255,255,255,.5);border-radius:20px;font-size:13px;background:rgba(255,255,255,.08); }
.cover__deco{ width:280px;height:280px;position:relative;flex-shrink:0; }
@@ -67,7 +67,7 @@
.r1{ width:280px;height:280px;border:2px solid rgba(255,255,255,.18); }
.r2{ width:210px;height:210px;border:2px dashed rgba(255,255,255,.22);left:35px;top:35px; }
.r3{ width:140px;height:140px;background:radial-gradient(circle,rgba(255,255,255,.28),transparent 70%);left:70px;top:70px; }
.cover__bottom{ display:flex;justify-content:space-between;font-size:12px;opacity:.82; }
.cover__bottom{ display:flex;justify-content:space-between;font-size:14px;color:rgba(255,255,255,.96); }
/* ===== 概述 ===== */
.stat-row{ display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:18px 0; }
@@ -100,11 +100,21 @@
.term span{ font-size:11.5px;color:var(--ink-2);line-height:1.6; }
/* ===== 封底 ===== */
.shots{ display:flex;flex-direction:column;gap:20px;flex:1;justify-content:center; }
.shots__row{ display:flex;gap:26px;justify-content:center;align-items:flex-end; }
.shot{ display:flex;flex-direction:column;align-items:center;gap:8px; }
.shot__frame{ overflow:hidden;box-shadow:0 6px 18px rgba(15,60,120,.14);background:#f5f7fa; }
.shot__frame--phone{ border:4px solid #2d3748;border-radius:16px; }
.shot__frame--phone img{ width:130px;height:282px;object-fit:cover;object-position:top;display:block; }
.shot__frame--web{ border:1px solid #d9e1ec;border-radius:10px; }
.shot__frame--web img{ width:290px;height:181px;object-fit:cover;object-position:top;display:block; }
.shot__label{ font-size:11px;color:var(--ink-2); }
.back{ background:linear-gradient(135deg,#0a2540,#0a5cb8);color:#fff; }
.back .inner{ align-items:center;justify-content:center;text-align:center; }
.back__title{ font-size:32px;font-weight:900;margin-bottom:12px; }
.back__line{ font-size:14px;opacity:.9;line-height:2; }
.back__foot{ margin-top:30px;font-size:12px;opacity:.7; }
.back__line{ font-size:15px;color:rgba(255,255,255,.96);line-height:2; }
.back__foot{ margin-top:30px;font-size:14px;color:rgba(255,255,255,.92); }
</style>
</head>
<body>
@@ -184,7 +194,30 @@
</div>
</section>
<!-- 6. 封底 -->
<!-- 6. 产品界面展示 -->
<section class="page">
<div class="inner">
<div class="head"><span class="kicker">界面展示</span><h2 class="sec">产品界面预览</h2><div class="secbar"></div></div>
<div class="shots">
<div class="shots__row">
<div class="shot">
<div class="shot__frame shot__frame--phone"><img src="{截图相对路径}" alt="{页面名}"></div>
<span class="shot__label">{页面名}</span>
</div>
<!-- 复制 .shot,第一行放 5 张 APP 竖版截图(phone -->
</div>
<div class="shots__row">
<div class="shot">
<div class="shot__frame shot__frame--web"><img src="{截图相对路径}" alt="{页面名}"></div>
<span class="shot__label">{页面名}</span>
</div>
<!-- 复制 .shot,第二行放 3 张 Web 端截图(web) -->
</div>
</div>
</div>
</section>
<!-- 7. 封底 -->
<section class="page back">
<div class="inner">
<div class="back__title">{子系统名}</div>
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env node
/**
* HTML 宣传册转 PDF(横版 A4,保留背景)
* 用法:node pdf-export.js <html文件路径>
* 输出:与 HTML 同目录同名 .pdf
* 依赖:playwrightchromium),无则用系统 Edge 兜底
*/
const { chromium } = require('playwright');
const { pathToFileURL } = require('url');
const path = require('path');
const htmlFile = process.argv[2];
if (!htmlFile) {
console.error('用法:node pdf-export.js <html文件路径>');
process.exit(1);
}
(async () => {
const abs = path.resolve(htmlFile);
const url = pathToFileURL(abs).href;
const pdfFile = process.argv[3] || abs.replace(/\.html$/i, '.pdf');
let browser;
try {
browser = await chromium.launch();
} catch (e) {
// playwright 自带 chromium 不可用时,退回系统 Edge
browser = await chromium.launch({ channel: 'msedge' });
}
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.pdf({
path: pdfFile,
format: 'A4',
landscape: true,
printBackground: true,
margin: { top: 0, bottom: 0, left: 0, right: 0 },
displayHeaderFooter: false,
});
await browser.close();
console.log('PDF 已生成:' + pdfFile);
})();