diff --git a/CHANGELOG b/CHANGELOG
index 79b2031..fe95ddf 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
# CHANGELOG
+- [2026-09-25 12:20] FEAT: v2.22 P0-3 打印模板体系。①OrderFormat 模型(blocks 区块 JSON: title/info/image/table/sign)+种子 2 套默认模板; ②/api/order-formats CRUD(区块类型校验+is_default 唯一默认); ③打印中心重构为模板驱动: A4 预览按 blocks 渲染(BOM 块自动拉取 /orders/{id}/bom), 模板管理对话框(区块增删/上下移/title 文本/info 字段多选/table kind 选择); 修复 move(i) v-for 索引 string|number 类型; vue-tsc 0 错+构建通过 (Files: src/huamenchuang/models/extend.py, src/huamenchuang/api/order_format.py, src/huamenchuang/api/router.py, src/huamenchuang/scripts/seed.py, frontend/src/views/PrinterView.vue, doc/API.md)
+
- [2026-09-25 11:40] FEAT: v2.21 P0-2 订单 BOM 计算单+Excel 导出。①quote_service.bom() 从 drawing_data 重放算料输出型材下料清单(calcbomOrder 语义); ②services/excel_service.py 通用 xlsx 工具(标题/带样式表格/列宽)+order_workbook 订单导出(信息+型材清单+玻璃五金汇总+签审行); ③GET /orders/{id}/bom + GET /orders/{id}/export(attachment 下载); ④前端订单详情抽屉加"BOM 单"弹窗(汇总+下料表+合计米数)+导出 Excel(apiDownload blob 带 token); openpyxl 入依赖; vue-tsc 0 错+构建通过 (Files: pyproject.toml, src/huamenchuang/services/excel_service.py, src/huamenchuang/services/quote_service.py, src/huamenchuang/api/orders.py, frontend/src/api/index.ts, frontend/src/views/OrdersView.vue, doc/API.md)
- [2026-09-25 11:00] FEAT: v2.20 P0-1 价格脚本引擎。①services/formula.py 安全公式引擎(递归下降解析, 无 eval; 四则/括号/min/max/round/abs/ceil/floor/sqrt; ALLOWED_VARS 变量白名单+validate_formula 语法校验); ②PriceScript 模型(系列名/target/frame~total 六作用项/formula/enabled)+种子 5 条示例; ③/api/scripts CRUD+preview 试算(product-test 语义); ④offer-price 集成 use_scripts/scripts 参数, 行级 scripted 标记+script_total 直取; ⑤产品管理页拆双 Tab(价格本/脚本 CRUD+试算), 报价弹窗"脚本计价"标记; 前端 vue-tsc 0 错+构建通过 (Files: src/huamenchuang/services/formula.py, src/huamenchuang/models/business.py, src/huamenchuang/api/script.py, src/huamenchuang/api/quote.py, src/huamenchuang/services/quote_service.py, src/huamenchuang/scripts/seed.py, frontend/src/views/ProductsView.vue, frontend/src/components/QuoteDialog.vue, doc/API.md)
diff --git a/doc/API.md b/doc/API.md
index 1162a41..f85bae9 100644
--- a/doc/API.md
+++ b/doc/API.md
@@ -390,3 +390,18 @@ uv run uvicorn huamenchuang.main:app --reload # 启动,http://localhost:8000
|---|---|---|---|---|
| **订单 BOM 单** | GET | `/api/orders/{id}/bom` | 路径 | 对齐目标站 calcbomOrder;返回 `{bars:[{kind,note,length,qty}], glass_area_m2, sash_count, width_mm, height_mm}`(从 drawing_data 重放算料,不含价格) |
| **订单导出 Excel** | GET | `/api/orders/{id}/export` | 路径 | 对齐 orders/export;xlsx(订单信息+BOM 下料清单+玻璃/五金汇总),`Content-Disposition: attachment` |
+
+### 12.17 打印模板 `/api/order-formats`(v2.20 新增,P0-打印模板体系)
+
+对齐目标站 `order_format` 语义:模板=区块列表 `blocks[]`,前端按区块渲染 A4。
+
+区块类型:`title`(text=标题文字)、`info`(fields=订单字段列表:order_no/customer_name/phone/address/count/area/money/created_at/note)、`image`(图纸缩略图)、`table`(kind=process 生产进度 / bom 下料清单)、`sign`(签字行)。
+
+| 接口名称 | 方式 | URL 路径 | 请求参数 | 说明 |
+|---|---|---|---|---|
+| 模板列表 | GET | `/api/order-formats` | — | 按 sort 排序 |
+| 新增模板 | POST | `/api/order-formats` | `{name, order_type, blocks[], is_default?}` | 区块类型校验;is_default=1 时清除其他默认 |
+| 编辑模板 | PUT | `/api/order-formats/{id}` | 同新增 | |
+| 删除模板 | DELETE | `/api/order-formats/{id}` | 路径 | |
+
+种子模板 2 套:标准生产订单(默认)、生产订单+BOM 下料单。
diff --git a/frontend/src/views/PrinterView.vue b/frontend/src/views/PrinterView.vue
index 2a40d01..dc40cd3 100644
--- a/frontend/src/views/PrinterView.vue
+++ b/frontend/src/views/PrinterView.vue
@@ -8,65 +8,234 @@
| {{ p.name }} | -{{ p.status === 2 ? `已完成 ${p.finish_time || ''}` : p.status === 1 ? '进行中' : '未开始' }} | -
客户签字:____________ 日期:____________
+ +| {{ p.name }} | +{{ p.status === 2 ? `已完成 ${p.finish_time || ''}` : p.status === 1 ? '进行中' : '未开始' }} | +
| 用途 | 单件长度(mm) | 数量(件) | 总长(m) |
| {{ row.note || row.kind }} | {{ row.length }} | {{ row.qty }} | +{{ (row.length * row.qty / 1000).toFixed(2) }} | +
| 无下料数据 | |||
客户签字:____________ 日期:____________
+