Initial commit: Receipt Printer design docs and examples
This commit is contained in:
commit
1c4187cb29
125
PROJECT.md
Normal file
125
PROJECT.md
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# Receipt Printer 项目
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
一个基于 WiFi ESC/POS 协议的 80mm 小票打印系统,支持:
|
||||||
|
- 可视化模板配置(YAML + 实时预览)
|
||||||
|
- REST API 调用打印
|
||||||
|
- 自动从模板提取数据 Schema
|
||||||
|
- 多种排版元素(文本、表格、图片、条码等)
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
- **Runtime**: Bun
|
||||||
|
- **Web 框架**: Hono
|
||||||
|
- **模板引擎**: Mustache
|
||||||
|
- **配置格式**: YAML
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
```
|
||||||
|
receipt-printer/
|
||||||
|
├── README.md # 项目说明
|
||||||
|
├── package.json # 依赖配置
|
||||||
|
├── docs/
|
||||||
|
│ ├── design.md # 系统设计文档
|
||||||
|
│ └── api.md # API 规范
|
||||||
|
├── templates/examples/ # 示例模板
|
||||||
|
│ ├── daily-todo.yaml # 每日待办
|
||||||
|
│ ├── food-order-simple.yaml # 餐饮订单
|
||||||
|
│ ├── fancy-receipt.yaml # 精致账单
|
||||||
|
│ ├── ticket-list.yaml # Ticket 列表
|
||||||
|
│ └── long-text.yaml # 长文阅读
|
||||||
|
└── src/ # 源码目录(待开发)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心设计决策
|
||||||
|
|
||||||
|
### 1. 模板配置格式
|
||||||
|
使用 YAML 而非 JSON:
|
||||||
|
- 支持注释
|
||||||
|
- 多行字符串更友好
|
||||||
|
- 编辑器支持语法高亮
|
||||||
|
|
||||||
|
### 2. Schema 自动生成
|
||||||
|
- 从模板中的 `{{变量}}` 自动提取
|
||||||
|
- 无需手动维护数据结构
|
||||||
|
- API 可 introspect 获取数据要求
|
||||||
|
|
||||||
|
### 3. 块类型系统
|
||||||
|
| 类型 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| text | 普通文本(支持样式) |
|
||||||
|
| row | 多列行布局 |
|
||||||
|
| table | 多列表格 |
|
||||||
|
| list | 循环渲染数组 |
|
||||||
|
| divider | 分隔线 |
|
||||||
|
| image | 图片 |
|
||||||
|
| barcode | 条码/二维码 |
|
||||||
|
| space | 空行 |
|
||||||
|
|
||||||
|
### 4. 样式支持
|
||||||
|
- 对齐:left/center/right
|
||||||
|
- 字体大小:small/normal/large/xlarge
|
||||||
|
- 文本样式:bold/italic/underline
|
||||||
|
- 间距:lineHeight/marginTop/marginBottom
|
||||||
|
|
||||||
|
## API 设计概览
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/print/:templateId # 打印
|
||||||
|
GET /api/templates # 列出模板
|
||||||
|
GET /api/templates/:id # 获取模板
|
||||||
|
POST /api/templates # 创建模板
|
||||||
|
PUT /api/templates/:id # 更新模板
|
||||||
|
DELETE /api/templates/:id # 删除模板
|
||||||
|
GET /api/templates/:id/schema # 获取数据 Schema
|
||||||
|
GET /api/jobs/:id # 查询任务状态
|
||||||
|
GET /api/printer/status # 打印机状态
|
||||||
|
POST /api/printer/test # 打印测试页
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下一步开发计划
|
||||||
|
|
||||||
|
1. **核心引擎**
|
||||||
|
- YAML 模板解析
|
||||||
|
- Mustache 数据绑定
|
||||||
|
- ESC/POS 指令生成
|
||||||
|
|
||||||
|
2. **打印驱动**
|
||||||
|
- WiFi 打印机连接
|
||||||
|
- 指令发送队列
|
||||||
|
- 状态轮询
|
||||||
|
|
||||||
|
3. **Web 服务**
|
||||||
|
- Hono HTTP 服务
|
||||||
|
- REST API 实现
|
||||||
|
- 静态文件服务
|
||||||
|
|
||||||
|
4. **配置界面**
|
||||||
|
- YAML 编辑器(CodeMirror)
|
||||||
|
- 实时预览(HTML 模拟)
|
||||||
|
- 数据模拟器
|
||||||
|
|
||||||
|
5. **示例与文档**
|
||||||
|
- 更多模板示例
|
||||||
|
- API 使用示例
|
||||||
|
- 部署指南
|
||||||
|
|
||||||
|
## 示例模板使用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 打印每日待办
|
||||||
|
curl -X POST http://localhost:3000/api/print/daily-todo \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"data": {
|
||||||
|
"date": "2025-02-12",
|
||||||
|
"tasks": [
|
||||||
|
{"status": "☐", "title": "修复 Bug #123"},
|
||||||
|
{"status": "☑", "title": "代码审查"}
|
||||||
|
],
|
||||||
|
"completedCount": 1
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
|
||||||
|
# 获取模板 Schema
|
||||||
|
curl http://localhost:3000/api/print/daily-todo/schema
|
||||||
|
```
|
||||||
67
README.md
Normal file
67
README.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Receipt Printer - 80mm 小票打印系统
|
||||||
|
|
||||||
|
基于 WiFi ESC/POS 的 80mm 小票打印系统,支持可视化模板配置和 REST API 调用。
|
||||||
|
|
||||||
|
## 核心特性
|
||||||
|
|
||||||
|
- 🎨 **可视化模板配置** - Web 界面实时编辑和预览
|
||||||
|
- 📝 **YAML/JSON 模板** - 声明式排版配置
|
||||||
|
- 🔌 **REST API** - 通过 HTTP 调用打印
|
||||||
|
- 📐 **自动 Schema 提取** - 从模板变量自动生成数据契约
|
||||||
|
- 🖨️ **WiFi ESC/POS** - 支持主流 80mm 热敏打印机
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- **后端**: Bun + Hono (轻量高性能)
|
||||||
|
- **模板引擎**: 自研 YAML → ESC/POS 转换器
|
||||||
|
- **前端**: 原生 HTML/JS (轻量化)
|
||||||
|
- **打印机协议**: ESC/POS over TCP
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
├── docs/ # 设计文档
|
||||||
|
├── templates/ # 模板文件
|
||||||
|
├── src/ # 源码
|
||||||
|
│ ├── server.ts # HTTP 服务
|
||||||
|
│ ├── printer.ts # 打印机驱动
|
||||||
|
│ ├── template.ts # 模板引擎
|
||||||
|
│ └── static/ # Web 界面
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
bun install
|
||||||
|
|
||||||
|
# 配置打印机
|
||||||
|
bun run config
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
bun run start
|
||||||
|
|
||||||
|
# 打开配置页面
|
||||||
|
open http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 打印模板
|
||||||
|
curl -X POST http://localhost:3000/api/print/daily-todo \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"data": {
|
||||||
|
"date": "2025-02-12",
|
||||||
|
"tasks": [
|
||||||
|
{"status": "☐", "title": "修复 Bug #123"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
MIT
|
||||||
425
docs/api.md
Normal file
425
docs/api.md
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
# API 规范
|
||||||
|
|
||||||
|
## 基础信息
|
||||||
|
|
||||||
|
```
|
||||||
|
Base URL: http://localhost:3000/api
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 端点列表
|
||||||
|
|
||||||
|
### 1. 打印
|
||||||
|
|
||||||
|
#### 执行打印
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /print/:templateId
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求体:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"date": "2025-02-12",
|
||||||
|
"tasks": [
|
||||||
|
{"status": "☐", "title": "修复 Bug #123"},
|
||||||
|
{"status": "☑", "title": "代码审查"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"copies": 1,
|
||||||
|
"cutPaper": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**参数说明:**
|
||||||
|
|
||||||
|
| 字段 | 类型 | 必填 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| `data` | object | 是 | 模板数据,结构由模板 Schema 定义 |
|
||||||
|
| `options.copies` | number | 否 | 打印份数,默认 1 |
|
||||||
|
| `options.cutPaper` | boolean | 否 | 是否切纸,默认 true |
|
||||||
|
|
||||||
|
**成功响应 (200):**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"jobId": "job_abc123",
|
||||||
|
"status": "queued",
|
||||||
|
"estimatedTime": "5s"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误响应 (400/404/500):**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": {
|
||||||
|
"code": "VALIDATION_ERROR",
|
||||||
|
"message": "数据验证失败",
|
||||||
|
"details": [
|
||||||
|
"tasks: 必填字段缺失"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**错误码说明:**
|
||||||
|
|
||||||
|
| 错误码 | HTTP 状态 | 说明 |
|
||||||
|
|--------|-----------|------|
|
||||||
|
| `TEMPLATE_NOT_FOUND` | 404 | 模板不存在 |
|
||||||
|
| `VALIDATION_ERROR` | 400 | 数据验证失败 |
|
||||||
|
| `PRINTER_OFFLINE` | 503 | 打印机离线 |
|
||||||
|
| `TEMPLATE_RENDER_ERROR` | 500 | 模板渲染失败 |
|
||||||
|
| `PRINT_FAILED` | 500 | 打印失败 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 模板管理
|
||||||
|
|
||||||
|
#### 列出所有模板
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /templates
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"id": "daily-todo",
|
||||||
|
"name": "每日待办",
|
||||||
|
"description": "打印今日待办事项列表",
|
||||||
|
"updatedAt": "2025-02-12T07:00:00Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 获取单个模板
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /templates/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"template": {
|
||||||
|
"id": "daily-todo",
|
||||||
|
"name": "每日待办",
|
||||||
|
"config": { /* 完整模板配置 */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 创建模板
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /templates
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求体:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "food-order",
|
||||||
|
"name": "餐饮订单",
|
||||||
|
"description": "打印餐饮订单小票",
|
||||||
|
"config": {
|
||||||
|
"name": "餐饮订单",
|
||||||
|
"width": "80mm",
|
||||||
|
"blocks": [ /* 块定义 */ ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"template": {
|
||||||
|
"id": "food-order",
|
||||||
|
"name": "餐饮订单",
|
||||||
|
"createdAt": "2025-02-12T07:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 更新模板
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT /templates/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求体:**(同创建,可部分更新)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "新的名称",
|
||||||
|
"config": { /* 新配置 */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 删除模板
|
||||||
|
|
||||||
|
```
|
||||||
|
DELETE /templates/:id
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "模板已删除"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 模板 Schema
|
||||||
|
|
||||||
|
#### 获取模板数据 Schema
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /templates/:id/schema
|
||||||
|
```
|
||||||
|
|
||||||
|
用于获取模板所需的数据结构和示例。
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"templateId": "food-order",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["orderId", "items"],
|
||||||
|
"properties": {
|
||||||
|
"orderType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "订单类型"
|
||||||
|
},
|
||||||
|
"orderId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "订单编号"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "商品列表",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"quantity": { "type": "number" },
|
||||||
|
"price": { "type": "number" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"orderType": "外带",
|
||||||
|
"orderId": "35205-1",
|
||||||
|
"items": [
|
||||||
|
{ "name": "双层吉士堡", "quantity": 1, "price": 22 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 打印任务
|
||||||
|
|
||||||
|
#### 查询任务状态
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /jobs/:jobId
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"job": {
|
||||||
|
"id": "job_abc123",
|
||||||
|
"templateId": "daily-todo",
|
||||||
|
"status": "completed",
|
||||||
|
"createdAt": "2025-02-12T07:00:00Z",
|
||||||
|
"startedAt": "2025-02-12T07:00:01Z",
|
||||||
|
"completedAt": "2025-02-12T07:00:05Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**状态值:**
|
||||||
|
- `queued` - 排队中
|
||||||
|
- `printing` - 打印中
|
||||||
|
- `completed` - 完成
|
||||||
|
- `failed` - 失败
|
||||||
|
- `cancelled` - 已取消
|
||||||
|
|
||||||
|
#### 列出最近任务
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /jobs?limit=10&status=completed
|
||||||
|
```
|
||||||
|
|
||||||
|
**查询参数:**
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `limit` | number | 返回数量,默认 10 |
|
||||||
|
| `status` | string | 按状态筛选 |
|
||||||
|
| `templateId` | string | 按模板筛选 |
|
||||||
|
|
||||||
|
#### 取消任务
|
||||||
|
|
||||||
|
```
|
||||||
|
DELETE /jobs/:jobId
|
||||||
|
```
|
||||||
|
|
||||||
|
仅可取消 `queued` 状态的任务。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. 打印机管理
|
||||||
|
|
||||||
|
#### 获取打印机状态
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /printer/status
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"printer": {
|
||||||
|
"name": "XP-80C",
|
||||||
|
"ip": "192.168.1.100",
|
||||||
|
"port": 9100,
|
||||||
|
"status": "online",
|
||||||
|
"paperStatus": "ok",
|
||||||
|
"coverStatus": "closed",
|
||||||
|
"queueLength": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**状态值:**
|
||||||
|
- `online` - 在线
|
||||||
|
- `offline` - 离线
|
||||||
|
- `error` - 错误(查看 `errorCode`)
|
||||||
|
|
||||||
|
#### 打印测试页
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /printer/test
|
||||||
|
```
|
||||||
|
|
||||||
|
打印一张测试页,用于检查连接和打印质量。
|
||||||
|
|
||||||
|
**响应:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"jobId": "job_test_123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 更新打印机配置
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /printer/config
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求体:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ip": "192.168.1.100",
|
||||||
|
"port": 9100,
|
||||||
|
"width": 80
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 认证(可选)
|
||||||
|
|
||||||
|
如需简单保护,在配置文件中启用 API Key:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
security:
|
||||||
|
enabled: true
|
||||||
|
apiKey: "your-secret-key"
|
||||||
|
```
|
||||||
|
|
||||||
|
启用后,所有请求需携带 Header:
|
||||||
|
|
||||||
|
```
|
||||||
|
X-API-Key: your-secret-key
|
||||||
|
```
|
||||||
|
|
||||||
|
未提供或错误的 Key 返回:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": {
|
||||||
|
"code": "UNAUTHORIZED",
|
||||||
|
"message": "无效的 API Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WebSocket 实时状态(可选)
|
||||||
|
|
||||||
|
如需实时推送打印状态,可连接 WebSocket:
|
||||||
|
|
||||||
|
```
|
||||||
|
ws://localhost:3000/ws
|
||||||
|
```
|
||||||
|
|
||||||
|
**事件类型:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
// 任务状态更新
|
||||||
|
{
|
||||||
|
"type": "jobUpdate",
|
||||||
|
"data": {
|
||||||
|
"jobId": "job_abc123",
|
||||||
|
"status": "printing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印机状态更新
|
||||||
|
{
|
||||||
|
"type": "printerUpdate",
|
||||||
|
"data": {
|
||||||
|
"status": "online",
|
||||||
|
"paperStatus": "low"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
389
docs/design.md
Normal file
389
docs/design.md
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
# 系统设计文档
|
||||||
|
|
||||||
|
## 1. 系统架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ 小票打印系统 (本地服务) │
|
||||||
|
├─────────────────────────────────────────────────────────┤
|
||||||
|
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
|
||||||
|
│ │ Web 配置页 │◄──►│ 模板引擎 │◄──►│ 打印机 │ │
|
||||||
|
│ │ (Vue/React) │ │ (渲染/ESC-POS)│ │ (WiFi ESC)│ │
|
||||||
|
│ └──────────────┘ └──────────────┘ └───────────┘ │
|
||||||
|
│ ▲ │
|
||||||
|
│ │ REST API │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
│ │ 外部调用端 │ ← 脚本、IFTTT、快捷指令等 │
|
||||||
|
│ │ (curl/HTTP) │ │
|
||||||
|
│ └──────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据流
|
||||||
|
|
||||||
|
1. **配置阶段**: Web 页创建/编辑模板 → 保存为 YAML 配置
|
||||||
|
2. **调用阶段**: POST 数据到 `/api/print/:templateId` → 模板引擎渲染 → 生成 ESC/POS 指令 → 发送到打印机
|
||||||
|
|
||||||
|
## 2. 模板系统
|
||||||
|
|
||||||
|
### 2.1 模板结构
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 模板元信息
|
||||||
|
name: "每日待办"
|
||||||
|
id: "daily-todo"
|
||||||
|
width: 80mm
|
||||||
|
description: "打印今日待办事项列表"
|
||||||
|
|
||||||
|
# 页面设置
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
# 默认样式
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.0
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 内容块
|
||||||
|
blocks:
|
||||||
|
# 文本块
|
||||||
|
- type: text
|
||||||
|
content: "📋 {{date}} 待办"
|
||||||
|
align: center
|
||||||
|
fontSize: large
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 列表循环
|
||||||
|
- type: list
|
||||||
|
data: "{{tasks}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: "[{{status}}] {{title}}"
|
||||||
|
fontSize: normal
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 多列行
|
||||||
|
- type: row
|
||||||
|
marginBottom: 1
|
||||||
|
columns:
|
||||||
|
- content: "{{leftText}}"
|
||||||
|
align: left
|
||||||
|
width: 50%
|
||||||
|
bold: true
|
||||||
|
- content: "{{rightText}}"
|
||||||
|
align: right
|
||||||
|
width: 50%
|
||||||
|
|
||||||
|
# 表格
|
||||||
|
- type: table
|
||||||
|
marginBottom: 1
|
||||||
|
columns:
|
||||||
|
- header: "商品"
|
||||||
|
align: left
|
||||||
|
width: 50%
|
||||||
|
- header: "数量"
|
||||||
|
align: center
|
||||||
|
width: 25%
|
||||||
|
- header: "金额"
|
||||||
|
align: right
|
||||||
|
width: 25%
|
||||||
|
data: "{{items}}"
|
||||||
|
|
||||||
|
# 图片
|
||||||
|
- type: image
|
||||||
|
src: "{{logoUrl}}"
|
||||||
|
align: center
|
||||||
|
maxWidth: 200
|
||||||
|
|
||||||
|
# 条码
|
||||||
|
- type: barcode
|
||||||
|
format: "CODE128" # CODE128 | QR | EAN13
|
||||||
|
data: "{{orderId}}"
|
||||||
|
align: center
|
||||||
|
height: 64
|
||||||
|
|
||||||
|
# 空行
|
||||||
|
- type: space
|
||||||
|
lines: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 支持的块类型
|
||||||
|
|
||||||
|
| 类型 | 用途 | 关键属性 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `text` | 普通文本 | `content`, `align`, `fontSize`, `bold`, `italic`, `underline`, `lineHeight`, `marginTop`, `marginBottom` |
|
||||||
|
| `list` | 循环渲染数组 | `data`, `itemTemplate` |
|
||||||
|
| `table` | 多列表格 | `columns`, `data` |
|
||||||
|
| `row` | 多列行布局 | `columns` |
|
||||||
|
| `divider` | 分隔线 | `char` |
|
||||||
|
| `image` | 图片/Logo | `src`, `align`, `maxWidth` |
|
||||||
|
| `barcode` | 条码/二维码 | `format`, `data`, `height` |
|
||||||
|
| `space` | 空行 | `lines` |
|
||||||
|
|
||||||
|
### 2.3 样式属性
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 对齐
|
||||||
|
align: left | center | right
|
||||||
|
|
||||||
|
# 字体大小
|
||||||
|
fontSize: small | normal | large | xlarge
|
||||||
|
|
||||||
|
# 文本样式
|
||||||
|
bold: true | false
|
||||||
|
italic: true | false # 打印机支持时
|
||||||
|
underline: true | false
|
||||||
|
|
||||||
|
# 间距
|
||||||
|
lineHeight: 1.0 # 行高倍数
|
||||||
|
marginTop: 0 # 上方间距(行数)
|
||||||
|
marginBottom: 0 # 下方间距(行数)
|
||||||
|
|
||||||
|
# 列宽(仅 row/table)
|
||||||
|
width: 50% | 20 # 百分比或字符数
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 数据绑定
|
||||||
|
|
||||||
|
使用 Mustache 语法 `{{variable}}`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
content: "订单编号: {{orderId}}"
|
||||||
|
data: "{{items}}" # 列表数据源
|
||||||
|
```
|
||||||
|
|
||||||
|
支持嵌套访问:
|
||||||
|
```yaml
|
||||||
|
content: "{{customer.name}} - {{customer.phone}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 Schema 自动生成
|
||||||
|
|
||||||
|
系统从模板的 `{{变量}}` 自动提取数据结构:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 模板示例
|
||||||
|
blocks:
|
||||||
|
- content: "{{orderType}} - {{orderId}}"
|
||||||
|
- data: "{{items}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
自动生成:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"orderType": { "type": "string" },
|
||||||
|
"orderId": { "type": "string" },
|
||||||
|
"items": { "type": "array" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
通过 `GET /api/templates/:id/schema` 获取 Schema 和示例数据。
|
||||||
|
|
||||||
|
## 3. Web 配置页
|
||||||
|
|
||||||
|
### 3.1 核心功能
|
||||||
|
|
||||||
|
1. **模板编辑器**
|
||||||
|
- 左侧:YAML 代码编辑(语法高亮 + 错误提示)
|
||||||
|
- 右侧:实时预览(HTML 模拟小票效果)
|
||||||
|
|
||||||
|
2. **块类型选择器**
|
||||||
|
- 快捷插入按钮(文本、表格、图片、分隔线等)
|
||||||
|
- 点击后插入模板代码片段
|
||||||
|
|
||||||
|
3. **数据模拟器**
|
||||||
|
- 基于 Schema 生成表单
|
||||||
|
- 输入模拟数据测试渲染
|
||||||
|
- 保存测试数据集
|
||||||
|
|
||||||
|
4. **打印机设置**
|
||||||
|
- IP 地址配置
|
||||||
|
- 打印测试页
|
||||||
|
- 打印队列状态
|
||||||
|
|
||||||
|
### 3.2 界面布局
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ [Logo] 小票打印机 [打印机状态 ●] │
|
||||||
|
├────────────┬────────────────────────┬───────────────────┤
|
||||||
|
│ │ │ │
|
||||||
|
│ 模板列表 │ YAML 编辑器 │ 实时预览 │
|
||||||
|
│ ─────── │ │ │
|
||||||
|
│ daily-todo│ │ ┌─────────┐ │
|
||||||
|
│ food-order│ - type: text │ │ 📋 待办 │ │
|
||||||
|
│ ticket │ content: "..." │ │ ======= │ │
|
||||||
|
│ │ │ │ [☐] ... │ │
|
||||||
|
│ [+ 新建] │ │ └─────────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
├────────────┴────────────────────────┴───────────────────┤
|
||||||
|
│ 块类型: [文本] [表格] [图片] [分隔线] [条码] [空行] │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 设计参考
|
||||||
|
|
||||||
|
### 4.1 餐饮小票(麦当劳风格)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
blocks:
|
||||||
|
# 顶部双栏
|
||||||
|
- type: row
|
||||||
|
columns:
|
||||||
|
- content: "外带"
|
||||||
|
align: left
|
||||||
|
- content: "订单编号: {{orderId}}"
|
||||||
|
align: right
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
|
||||||
|
# 商品列表(带缩进备注)
|
||||||
|
- type: list
|
||||||
|
data: "{{items}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: "{{quantity}} {{name}}"
|
||||||
|
- type: list
|
||||||
|
data: "{{notes}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: " {{.}}"
|
||||||
|
fontSize: small
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
|
||||||
|
# 取餐号(超大)
|
||||||
|
- type: text
|
||||||
|
content: "取餐柜取餐"
|
||||||
|
align: center
|
||||||
|
marginBottom: 0
|
||||||
|
- type: text
|
||||||
|
content: "{{pickupNumber}}"
|
||||||
|
align: center
|
||||||
|
fontSize: xlarge
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 条码
|
||||||
|
- type: barcode
|
||||||
|
format: "CODE128"
|
||||||
|
data: "{{pickupNumber}}"
|
||||||
|
align: center
|
||||||
|
|
||||||
|
# 时间戳
|
||||||
|
- type: row
|
||||||
|
columns:
|
||||||
|
- content: "MOBILE"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
- content: "{{timestamp}}"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 精致小票(铁板烧风格)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
blocks:
|
||||||
|
# Logo 图片
|
||||||
|
- type: image
|
||||||
|
src: "{{logoUrl}}"
|
||||||
|
align: center
|
||||||
|
maxWidth: 150
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 英文小字
|
||||||
|
- type: text
|
||||||
|
content: "THE SOURCE OF THIS DESIGN MATERIAL IS TEPPANYAKI DESIGN"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
|
||||||
|
# 中文标题
|
||||||
|
- type: text
|
||||||
|
content: "铁板烧设计"
|
||||||
|
align: center
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 装饰分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 表头
|
||||||
|
- type: row
|
||||||
|
columns:
|
||||||
|
- content: "商品名称"
|
||||||
|
align: left
|
||||||
|
bold: true
|
||||||
|
width: 50%
|
||||||
|
- content: "数量"
|
||||||
|
align: center
|
||||||
|
bold: true
|
||||||
|
width: 25%
|
||||||
|
- content: "金额"
|
||||||
|
align: right
|
||||||
|
bold: true
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
# 表格数据
|
||||||
|
- type: table
|
||||||
|
columns:
|
||||||
|
- align: left
|
||||||
|
width: 50%
|
||||||
|
- align: center
|
||||||
|
width: 25%
|
||||||
|
- align: right
|
||||||
|
width: 25%
|
||||||
|
data: "{{items}}"
|
||||||
|
|
||||||
|
# 汇总
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
- type: row
|
||||||
|
columns:
|
||||||
|
- content: "小计"
|
||||||
|
align: left
|
||||||
|
- content: "¥{{subtotal}}"
|
||||||
|
align: right
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 技术决策
|
||||||
|
|
||||||
|
### 5.1 为什么选择 YAML?
|
||||||
|
|
||||||
|
- 比 JSON 更易手写(支持注释、多行字符串)
|
||||||
|
- 比纯代码更声明式(专注"是什么"而非"怎么做")
|
||||||
|
- 主流编辑器支持语法高亮和验证
|
||||||
|
|
||||||
|
### 5.2 为什么自动生成 Schema?
|
||||||
|
|
||||||
|
- 降低用户负担(无需学习 JSON Schema)
|
||||||
|
- 保持数据和模板同步(一处修改,处处生效)
|
||||||
|
- 自动生成文档和表单
|
||||||
|
|
||||||
|
### 5.3 为什么用 HTML 预览?
|
||||||
|
|
||||||
|
- 无需连接真实打印机即可调试
|
||||||
|
- 跨平台、易实现
|
||||||
|
- 样式可精确还原(CSS)
|
||||||
|
|
||||||
|
## 6. 待决策事项
|
||||||
|
|
||||||
|
- [ ] 是否支持模板继承/复用?
|
||||||
|
- [ ] 是否支持条件渲染(if/else)?
|
||||||
|
- [ ] 是否支持自定义字体?
|
||||||
|
- [ ] 是否支持打印历史记录?
|
||||||
|
- [ ] 是否支持多打印机管理?
|
||||||
30
package.json
Normal file
30
package.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "receipt-printer",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "80mm 小票打印系统 - 支持可视化模板配置和 REST API",
|
||||||
|
"main": "src/server.ts",
|
||||||
|
"scripts": {
|
||||||
|
"start": "bun run src/server.ts",
|
||||||
|
"dev": "bun run --watch src/server.ts",
|
||||||
|
"build": "bun build src/server.ts --outdir dist",
|
||||||
|
"test": "bun test"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.0.0",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"mustache": "^4.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/mustache": "^4.2.5",
|
||||||
|
"bun-types": "latest"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"receipt",
|
||||||
|
"printer",
|
||||||
|
"esc-pos",
|
||||||
|
"thermal-printer",
|
||||||
|
"80mm"
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
58
templates/examples/daily-todo.yaml
Normal file
58
templates/examples/daily-todo.yaml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# 每日待办模板
|
||||||
|
|
||||||
|
name: "每日待办"
|
||||||
|
id: "daily-todo"
|
||||||
|
width: 80mm
|
||||||
|
description: "打印今日待办事项列表"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.0
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
# 标题
|
||||||
|
- type: text
|
||||||
|
content: "📋 {{date}} 待办"
|
||||||
|
align: center
|
||||||
|
fontSize: large
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 待办列表
|
||||||
|
- type: list
|
||||||
|
data: "{{tasks}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: "[{{status}}] {{title}}"
|
||||||
|
fontSize: normal
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 空行
|
||||||
|
- type: space
|
||||||
|
lines: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 统计
|
||||||
|
- type: text
|
||||||
|
content: "共 {{tasks.length}} 项待办"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
|
|
||||||
|
- type: text
|
||||||
|
content: "已完成 {{completedCount}} / {{tasks.length}}"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
190
templates/examples/fancy-receipt.yaml
Normal file
190
templates/examples/fancy-receipt.yaml
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
# 精致小票模板(铁板烧风格)
|
||||||
|
|
||||||
|
name: "精致账单"
|
||||||
|
id: "fancy-receipt"
|
||||||
|
width: 80mm
|
||||||
|
description: "带有 Logo 和详细商品信息的精致账单"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.0
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
# Logo 图片
|
||||||
|
- type: image
|
||||||
|
src: "{{logoUrl}}"
|
||||||
|
align: center
|
||||||
|
maxWidth: 150
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 英文标语
|
||||||
|
- type: text
|
||||||
|
content: "{{taglineEn}}"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 中文店名
|
||||||
|
- type: text
|
||||||
|
content: "{{shopName}}"
|
||||||
|
align: center
|
||||||
|
fontSize: large
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 装饰分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 菜单标题
|
||||||
|
- type: text
|
||||||
|
content: "{{menuTitle}}"
|
||||||
|
align: center
|
||||||
|
fontSize: normal
|
||||||
|
bold: true
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 时间
|
||||||
|
- type: text
|
||||||
|
content: "{{datetime}}"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 表头
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "商品名称"
|
||||||
|
align: left
|
||||||
|
bold: true
|
||||||
|
width: 50%
|
||||||
|
- content: "数量"
|
||||||
|
align: center
|
||||||
|
bold: true
|
||||||
|
width: 25%
|
||||||
|
- content: "金额"
|
||||||
|
align: right
|
||||||
|
bold: true
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 商品表格
|
||||||
|
- type: table
|
||||||
|
data: "{{items}}"
|
||||||
|
columns:
|
||||||
|
- align: left
|
||||||
|
width: 50%
|
||||||
|
- align: center
|
||||||
|
width: 25%
|
||||||
|
- align: right
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 汇总行
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "小计"
|
||||||
|
align: left
|
||||||
|
width: 50%
|
||||||
|
- content: "{{totalQuantity}}"
|
||||||
|
align: center
|
||||||
|
width: 25%
|
||||||
|
- content: "¥{{subtotal}}"
|
||||||
|
align: right
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "税收({{taxRate}})"
|
||||||
|
align: left
|
||||||
|
width: 50%
|
||||||
|
- content: ""
|
||||||
|
align: center
|
||||||
|
width: 25%
|
||||||
|
- content: "¥{{tax}}"
|
||||||
|
align: right
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "账单总额"
|
||||||
|
align: left
|
||||||
|
bold: true
|
||||||
|
width: 50%
|
||||||
|
- content: ""
|
||||||
|
align: center
|
||||||
|
width: 25%
|
||||||
|
- content: "¥{{total}}"
|
||||||
|
align: right
|
||||||
|
bold: true
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "折扣"
|
||||||
|
align: left
|
||||||
|
width: 50%
|
||||||
|
- content: ""
|
||||||
|
align: center
|
||||||
|
width: 25%
|
||||||
|
- content: "{{discount}}"
|
||||||
|
align: right
|
||||||
|
width: 25%
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 签名区
|
||||||
|
- type: text
|
||||||
|
content: "签名"
|
||||||
|
align: left
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 装饰
|
||||||
|
- type: text
|
||||||
|
content: "{{signature}}"
|
||||||
|
align: center
|
||||||
|
fontSize: large
|
||||||
|
italic: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 底部分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 版权信息
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "★"
|
||||||
|
align: left
|
||||||
|
width: 10%
|
||||||
|
- content: "{{copyright}}"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
width: 80%
|
||||||
|
- content: "★"
|
||||||
|
align: right
|
||||||
|
width: 10%
|
||||||
88
templates/examples/food-order-simple.yaml
Normal file
88
templates/examples/food-order-simple.yaml
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 餐饮订单模板(麦当劳风格)
|
||||||
|
|
||||||
|
name: "餐饮订单"
|
||||||
|
id: "food-order-simple"
|
||||||
|
width: 80mm
|
||||||
|
description: "外带餐饮订单小票"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.0
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
# 顶部信息
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "{{orderType}}"
|
||||||
|
align: left
|
||||||
|
fontSize: normal
|
||||||
|
- content: "订单编号: {{orderId}}"
|
||||||
|
align: right
|
||||||
|
fontSize: normal
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 商品列表(带备注)
|
||||||
|
- type: list
|
||||||
|
data: "{{items}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: "{{quantity}} {{name}}"
|
||||||
|
marginBottom: 0
|
||||||
|
- type: list
|
||||||
|
data: "{{notes}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: " 不要 {{.}}"
|
||||||
|
fontSize: small
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 取餐信息
|
||||||
|
- type: text
|
||||||
|
content: "取餐柜取餐"
|
||||||
|
align: center
|
||||||
|
fontSize: normal
|
||||||
|
marginTop: 1
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
- type: text
|
||||||
|
content: "{{pickupNumber}}"
|
||||||
|
align: center
|
||||||
|
fontSize: xlarge
|
||||||
|
bold: true
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 条码
|
||||||
|
- type: barcode
|
||||||
|
format: "CODE128"
|
||||||
|
data: "{{pickupNumber}}"
|
||||||
|
align: center
|
||||||
|
height: 64
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 底部信息
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "MOBILE"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
- content: "{{timestamp}}"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
75
templates/examples/long-text.yaml
Normal file
75
templates/examples/long-text.yaml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# 长文阅读模板
|
||||||
|
|
||||||
|
name: "长文阅读"
|
||||||
|
id: "long-text"
|
||||||
|
width: 80mm
|
||||||
|
description: "打印长篇文章或阅读材料"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.2
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
# 标题
|
||||||
|
- type: text
|
||||||
|
content: "{{title}}"
|
||||||
|
align: center
|
||||||
|
fontSize: xlarge
|
||||||
|
bold: true
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 作者
|
||||||
|
- type: text
|
||||||
|
content: "作者: {{author}}"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 日期
|
||||||
|
- type: text
|
||||||
|
content: "{{publishDate}}"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 正文段落
|
||||||
|
- type: list
|
||||||
|
data: "{{paragraphs}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: "{{content}}"
|
||||||
|
align: left
|
||||||
|
lineHeight: 1.3
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 空行
|
||||||
|
- type: space
|
||||||
|
lines: 2
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 阅读统计
|
||||||
|
- type: text
|
||||||
|
content: "共 {{wordCount}} 字 | {{paragraphs.length}} 段落"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
- type: text
|
||||||
|
content: "打印于 {{printDate}}"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
107
templates/examples/ticket-list.yaml
Normal file
107
templates/examples/ticket-list.yaml
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
# Ticket/Issue 列表模板
|
||||||
|
|
||||||
|
name: "Ticket 列表"
|
||||||
|
id: "ticket-list"
|
||||||
|
width: 80mm
|
||||||
|
description: "打印 GitHub/Jira 等 Ticket 或 Issue 列表"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
fontSize: normal
|
||||||
|
lineHeight: 1.0
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
page:
|
||||||
|
marginTop: 2
|
||||||
|
marginBottom: 3
|
||||||
|
|
||||||
|
blocks:
|
||||||
|
# 标题
|
||||||
|
- type: text
|
||||||
|
content: "🎫 {{project}} Tickets"
|
||||||
|
align: center
|
||||||
|
fontSize: large
|
||||||
|
bold: true
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 副标题
|
||||||
|
- type: text
|
||||||
|
content: "{{filter}} - 共 {{tickets.length}} 条"
|
||||||
|
align: center
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# Ticket 列表
|
||||||
|
- type: list
|
||||||
|
data: "{{tickets}}"
|
||||||
|
itemTemplate:
|
||||||
|
# Ticket 编号和状态
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "#{{id}}"
|
||||||
|
align: left
|
||||||
|
bold: true
|
||||||
|
width: 30%
|
||||||
|
- content: "[{{status}}]"
|
||||||
|
align: center
|
||||||
|
width: 35%
|
||||||
|
- content: "{{priority}}"
|
||||||
|
align: right
|
||||||
|
width: 35%
|
||||||
|
|
||||||
|
# 标题
|
||||||
|
- type: text
|
||||||
|
content: "{{title}}"
|
||||||
|
align: left
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 标签
|
||||||
|
- type: text
|
||||||
|
content: "标签: {{labels.join ', '}}"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
# 负责人和时间
|
||||||
|
- type: row
|
||||||
|
marginBottom: 0
|
||||||
|
columns:
|
||||||
|
- content: "👤 {{assignee}}"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
width: 50%
|
||||||
|
- content: "📅 {{dueDate}}"
|
||||||
|
align: right
|
||||||
|
fontSize: small
|
||||||
|
width: 50%
|
||||||
|
|
||||||
|
# 分隔线
|
||||||
|
- type: divider
|
||||||
|
char: "-"
|
||||||
|
marginBottom: 1
|
||||||
|
|
||||||
|
# 底部统计
|
||||||
|
- type: divider
|
||||||
|
char: "="
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
- type: text
|
||||||
|
content: "按状态统计:"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
bold: true
|
||||||
|
marginBottom: 0
|
||||||
|
|
||||||
|
- type: list
|
||||||
|
data: "{{statusStats}}"
|
||||||
|
itemTemplate:
|
||||||
|
- type: text
|
||||||
|
content: " {{status}}: {{count}}"
|
||||||
|
align: left
|
||||||
|
fontSize: small
|
||||||
|
marginBottom: 0
|
||||||
Loading…
x
Reference in New Issue
Block a user