job51-gitlab-cr-node-skill-prompt-optimize 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(ls:*)",
5
+ "Bash(pdftotext:*cr-node文件夹下的md和pdf文件,分别是提示词模板和报告结果)"
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,131 @@
1
+ # 代码审查报告转PDF工具使用说明
2
+
3
+ ## 问题分析
4
+
5
+ 您发现当前系统存在以下问题:
6
+ 1. `mr-review-template.md` 是一个用于代码审查的提示词模板
7
+ 2. 当前目录的PDF文件是从GitLab网页导出的页面快照,不是代码审查报告
8
+ 3. 代码审查系统的输出直接发布到GitLab MR中,而不是保存为本地PDF
9
+
10
+ ## 解决方案
11
+
12
+ 我已创建了一个工具脚本 `convert-report-to-pdf.js` 来解决这个问题:
13
+
14
+ ### 功能说明
15
+
16
+ 1. **读取审查报告**:从包含 `<REPORT>` 和 `</REPORT>` 标签的文件中提取审查报告内容
17
+ 2. **格式转换**:将Markdown格式的报告转换为适合打印的HTML格式
18
+ 3. **生成可打印文档**:创建带有适当样式的HTML文档,方便转换为PDF
19
+
20
+ ### 使用方法
21
+
22
+ #### 1. 生成代码审查报告
23
+
24
+ 首先,确保您有代码审查系统的输出文件。如果您还没有实际的审查报告,可以使用以下示例:
25
+
26
+ ```bash
27
+ # 创建示例审查报告文件
28
+ echo '<REPORT>
29
+ ## 🤖 AI代码审查结果
30
+
31
+ **生成时间**: 2026-03-09 09:30:00
32
+ **审查范围**: 2个文件(仅统计有代码变更的文件)
33
+
34
+ ---
35
+
36
+ ### 审查总览
37
+
38
+ 本次审查发现了1个严重问题和2个优化建议,主要涉及安全性、代码可读性和性能方面的问题。
39
+
40
+ ### 🔴 严重问题
41
+
42
+ ①:在UserService.java文件中,数据库查询缺少输入验证,可能存在SQL注入风险
43
+ **文件及行号**:UserService.java:125-130
44
+ **修改建议**:使用参数化查询替换字符串拼接方式,防止SQL注入攻击
45
+ 错误代码:
46
+ ```java
47
+ String sql = "SELECT * FROM users WHERE id = " + userId;
48
+ ```
49
+ 正确示例代码:
50
+ ```java
51
+ String sql = "SELECT * FROM users WHERE id = ?";
52
+ PreparedStatement stmt = connection.prepareStatement(sql);
53
+ stmt.setString(1, userId);
54
+ ```
55
+
56
+ ### 🟢 优化建议
57
+
58
+ **文件及行号**:UserController.java:45-50
59
+ **优化建议**:方法中存在重复代码,建议提取为公共方法提高可维护性
60
+ **示例代码**:
61
+ ```java
62
+ // 原代码
63
+ public void updateUser(Long id, String name) {
64
+ validateUser(id);
65
+ validateName(name);
66
+ // ... update logic
67
+ }
68
+
69
+ public void createUser(String name) {
70
+ validateUser(null); // 重复的验证逻辑
71
+ validateName(name);
72
+ // ... create logic
73
+ }
74
+ ```
75
+
76
+ **文件及行号**:ConfigService.java:78-82
77
+ **优化建议**:配置项可以使用枚举类型提高类型安全
78
+ **示例代码**:
79
+ ```java
80
+ // 原代码
81
+ public static final String DB_TYPE_MYSQL = "mysql";
82
+ public static final String DB_TYPE_POSTGRES = "postgres";
83
+
84
+ // 建议
85
+ public enum DbType {
86
+ MYSQL("mysql"),
87
+ POSTGRES("postgres");
88
+
89
+ private final String value;
90
+ DbType(String value) { this.value = value; }
91
+ public String getValue() { return value; }
92
+ }
93
+ ```
94
+
95
+ ### 🔄 上下文影响分析(需涵盖依赖关系、功能关联性、兼容性风险三个维度)
96
+
97
+ **受影响组件**: 用户管理模块、数据库访问层
98
+ **影响描述**: 如果不修复SQL注入问题,可能导致数据库被恶意查询,造成数据泄露
99
+ **验证建议**: 使用恶意输入测试数据库查询接口,确认是否能阻止非法输入
100
+
101
+ </REPORT>' > sample-review-report.txt
102
+ ```
103
+
104
+ #### 2. 转换为PDF可用的HTML格式
105
+
106
+ ```bash
107
+ node convert-report-to-pdf.js sample-review-report.txt review-report.html
108
+ ```
109
+
110
+ #### 3. 转换为PDF
111
+
112
+ 1. 在浏览器中打开生成的 `review-report.html` 文件
113
+ 2. 按 `Ctrl+P` 打开打印对话框
114
+ 3. 选择 "另存为PDF" 作为打印机
115
+ 4. 设置页面为A4或Letter格式
116
+ 5. 点击保存
117
+
118
+ ### 注意事项
119
+
120
+ - 转换脚本会保留报告的格式和结构
121
+ - HTML文件包含了自动生成PDF的JavaScript代码(在浏览器中)
122
+ - 如果您的审查报告格式与此不同,可以调整 `convert-report-to-pdf.js` 中的解析逻辑
123
+
124
+ ## 完整的工作流程
125
+
126
+ 1. 使用 `index.js` 运行代码审查并获得报告输出
127
+ 2. 将审查结果保存到文件(包含 `<REPORT>` 标签)
128
+ 3. 使用 `convert-report-to-pdf.js` 转换为HTML格式
129
+ 4. 在浏览器中打开HTML文件并打印为PDF
130
+
131
+ 这样您就可以得到一个格式良好的PDF版本的代码审查报告了。
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ /**
7
+ * 将代码审查报告转换为PDF的HTML模板
8
+ */
9
+ function createHtmlTemplate(reportContent) {
10
+ return `
11
+ <!DOCTYPE html>
12
+ <html lang="zh-CN">
13
+ <head>
14
+ <meta charset="UTF-8">
15
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
+ <title>AI代码审查报告</title>
17
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
18
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
19
+ <style>
20
+ body {
21
+ font-family: Arial, sans-serif;
22
+ padding: 20px;
23
+ line-height: 1.6;
24
+ }
25
+ .report-container {
26
+ max-width: 800px;
27
+ margin: 0 auto;
28
+ border: 1px solid #ddd;
29
+ padding: 30px;
30
+ background-color: #fff;
31
+ }
32
+ h1, h2, h3 {
33
+ color: #333;
34
+ }
35
+ h1 {
36
+ border-bottom: 2px solid #4CAF50;
37
+ padding-bottom: 10px;
38
+ }
39
+ h2 {
40
+ color: #2196F3;
41
+ border-left: 4px solid #2196F3;
42
+ padding-left: 10px;
43
+ margin-top: 25px;
44
+ }
45
+ .severity-high {
46
+ border-left: 4px solid #f44336;
47
+ background-color: #ffebee;
48
+ padding: 10px;
49
+ margin: 10px 0;
50
+ }
51
+ .severity-medium {
52
+ border-left: 4px solid #ff9800;
53
+ background-color: #fff3e0;
54
+ padding: 10px;
55
+ margin: 10px 0;
56
+ }
57
+ .severity-low {
58
+ border-left: 4px solid #4CAF50;
59
+ background-color: #e8f5e8;
60
+ padding: 10px;
61
+ margin: 10px 0;
62
+ }
63
+ .code-block {
64
+ background-color: #f5f5f5;
65
+ border: 1px solid #ddd;
66
+ border-radius: 4px;
67
+ padding: 10px;
68
+ overflow-x: auto;
69
+ font-family: monospace;
70
+ white-space: pre-wrap;
71
+ }
72
+ .file-info {
73
+ background-color: #e3f2fd;
74
+ padding: 8px;
75
+ border-radius: 4px;
76
+ margin: 5px 0;
77
+ }
78
+ ul {
79
+ padding-left: 20px;
80
+ }
81
+ li {
82
+ margin: 8px 0;
83
+ }
84
+ </style>
85
+ </head>
86
+ <body>
87
+ <div class="report-container">
88
+ ${reportContent}
89
+ </div>
90
+
91
+ <script>
92
+ // 页面加载完成后自动转换为PDF(可选)
93
+ function convertToPdf() {
94
+ const { jsPDF } = window.jspdf;
95
+ const element = document.querySelector('.report-container');
96
+
97
+ html2canvas(element, {
98
+ scale: 2,
99
+ useCORS: true,
100
+ backgroundColor: '#ffffff'
101
+ }).then(canvas => {
102
+ const imgData = canvas.toDataURL('image/png');
103
+ const pdf = new jsPDF('p', 'mm', 'a4');
104
+ const imgWidth = 210;
105
+ const pageHeight = 295;
106
+ const imgHeight = canvas.height * imgWidth / canvas.width;
107
+
108
+ let heightLeft = imgHeight;
109
+ let position = 0;
110
+
111
+ pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
112
+ heightLeft -= pageHeight;
113
+
114
+ while (heightLeft >= 0) {
115
+ position = heightLeft - imgHeight;
116
+ pdf.addPage();
117
+ pdf.addImage(imgData, 'PNG', 0, position, imgHeight > pageHeight ? imgWidth : imgWidth, imgHeight > pageHeight ? pageHeight : imgHeight);
118
+ heightLeft -= pageHeight;
119
+ }
120
+
121
+ pdf.save('ai-code-review-report.pdf');
122
+ });
123
+ }
124
+
125
+ // 5秒后自动执行转换,或用户手动调用
126
+ // setTimeout(convertToPdf, 5000);
127
+ </script>
128
+ </body>
129
+ </html>`;
130
+ }
131
+
132
+ /**
133
+ * 将Markdown格式的报告转换为HTML
134
+ */
135
+ function mdToHtml(mdContent) {
136
+ // 简单的Markdown到HTML转换
137
+ let html = mdContent;
138
+
139
+ // 转义HTML特殊字符
140
+ html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
141
+
142
+ // 标题
143
+ html = html.replace(/^###\s+(.*)$/gm, '<h3>$1</h3>');
144
+ html = html.replace(/^##\s+(.*)$/gm, '<h2>$1</h2>');
145
+ html = html.replace(/^#\s+(.*)$/gm, '<h1>$1</h1>');
146
+
147
+ // 段落
148
+ html = html.replace(/^\s*(.+)$/gm, '<p>$1</p>');
149
+
150
+ // 特殊标记(严重问题、警告、优化建议)
151
+ html = html.replace(/🔴\s+严重问题/g, '<h2 class="severity-high-title">🔴 严重问题</h2>');
152
+ html = html.replace(/🟡\s+警告/g, '<h2 class="severity-medium-title">🟡 警告</h2>');
153
+ html = html.replace(/🟢\s+优化建议/g, '<h2 class="severity-low-title">🟢 优化建议</h2>');
154
+
155
+ // 回滚之前的替换,只处理行首的标题
156
+ html = html.replace(/<p>##\s+(.*?)<\/p>/g, '<h2>$1</h2>');
157
+ html = html.replace(/<p>###\s+(.*?)<\/p>/g, '<h3>$1</h3>');
158
+
159
+ // 文件和行号信息
160
+ html = html.replace(/\*\*文件及行号\*\*:([^\n<]+)/g, '<div class="file-info"><strong>文件及行号:</strong>$1</div>');
161
+
162
+ // 修改建议和示例代码
163
+ html = html.replace(/\*\*修改建议\*\*:([\s\S]*?)(?=<div class="file-info"|<h2>|<\/div>|$)/g,
164
+ '<div class="severity-high"><strong>修改建议:</strong><br>$1</div>');
165
+ html = html.replace(/\*\*优化建议\*\*:([\s\S]*?)(?=<div class="file-info"|<h2>|<\/div>|$)/g,
166
+ '<div class="severity-low"><strong>优化建议:</strong><br>$1</div>');
167
+
168
+ // 代码块
169
+ html = html.replace(/```([\s\S]*?)```/g, '<div class="code-block">$1</div>');
170
+
171
+ // 换行处理
172
+ html = html.replace(/\n/g, '<br>');
173
+
174
+ // 恢复一些必要的换行和结构
175
+ html = html.replace(/<br><\/div>/g, '</div>');
176
+ html = html.replace(/<br><\/h2>/g, '</h2>');
177
+ html = html.replace(/<br><\/h3>/g, '</h3>');
178
+ html = html.replace(/<br><\/p>/g, '</p>');
179
+ html = html.replace(/<br><\/strong>/g, '</strong>');
180
+
181
+ return html;
182
+ }
183
+
184
+ /**
185
+ * 从文件读取审查报告并转换为PDF可用的HTML
186
+ */
187
+ function convertReviewReportToPdfHtml(inputFilePath, outputHtmlPath) {
188
+ try {
189
+ // 读取输入文件
190
+ const content = fs.readFileSync(inputFilePath, 'utf8');
191
+
192
+ // 如果内容被REPORT标签包围,只提取标签内的内容
193
+ const reportMatch = content.match(/<REPORT>([\s\S]*?)<\/REPORT>/);
194
+ const reportContent = reportMatch ? reportMatch[1] : content;
195
+
196
+ // 转换Markdown为HTML
197
+ const htmlContent = mdToHtml(reportContent);
198
+
199
+ // 创建完整的HTML文档
200
+ const fullHtml = createHtmlTemplate(htmlContent);
201
+
202
+ // 写入输出文件
203
+ fs.writeFileSync(outputHtmlPath, fullHtml);
204
+
205
+ console.log(`✅ 已成功将代码审查报告转换为HTML格式: ${outputHtmlPath}`);
206
+ console.log(`💡 要生成PDF,请在浏览器中打开此HTML文件并使用Ctrl+P打印为PDF`);
207
+
208
+ return true;
209
+ } catch (error) {
210
+ console.error('❌ 转换过程中发生错误:', error.message);
211
+ return false;
212
+ }
213
+ }
214
+
215
+ // 如果直接运行此脚本
216
+ if (require.main === module) {
217
+ const args = process.argv.slice(2);
218
+
219
+ if (args.length < 2) {
220
+ console.log('📝 使用方法:');
221
+ console.log(' node convert-report-to-pdf.js <输入报告文件> <输出HTML文件>');
222
+ console.log('');
223
+ console.log('📖 示例:');
224
+ console.log(' node convert-report-to-pdf.js review-output.txt review-report.html');
225
+ console.log('');
226
+ console.log('💡 提示: 生成的HTML文件可以直接在浏览器中打开,然后使用打印功能保存为PDF');
227
+ process.exit(1);
228
+ }
229
+
230
+ const inputPath = args[0];
231
+ const outputPath = args[1];
232
+
233
+ convertReviewReportToPdfHtml(inputPath, outputPath);
234
+ }
235
+
236
+ module.exports = {
237
+ convertReviewReportToPdfHtml,
238
+ createHtmlTemplate,
239
+ mdToHtml
240
+ };
@@ -104,7 +104,19 @@
104
104
  > - **处理**:不报告此安全问题
105
105
  > - 若该行以 `+` 开头表示新增或保留
106
106
  > - **处理**:报告安全问题,错误代码展示该行(不带 `-` 前缀)
107
-
107
+ 18. **变更内容解析步骤**(分析前必须执行):
108
+ > - **步骤 1:识别变更类型**
109
+ > - 遍历 `<CHANGE_CONTENT>` 中的每一行
110
+ > - 标记每行的类型:`-`(删除)、`+`(新增)、空格(保留)
111
+ > - **步骤 2:构建变更后代码视图**
112
+ > - 仅保留 `+` 开头的行和空格开头的行
113
+ > - **丢弃**所有 `-` 开头的行
114
+ > - **步骤 3:基于变更后代码视图进行审查**
115
+ > - 所有问题识别、行号计算、错误代码选取均基于变更后代码视图
116
+ > - **禁止**引用已丢弃的 `-` 开头行
117
+ > - **步骤 4:验证错误代码示例**
118
+ > - 检查错误代码示例中是否包含 `-` 前缀
119
+ > - 若包含,必须移除 `-` 前缀或重新选取变更后实际存在的代码
108
120
 
109
121
  <REPORT>
110
122
  ## 🤖 AI代码审查结果
@@ -120,42 +132,42 @@
120
132
 
121
133
  ### 🔴 严重问题
122
134
 
123
- [带圈数字序号,如:①,字体格式和后续问题描述一致][具体问题描述]<br/>
135
+ [带圈数字序号,如:①,字体格式和后续问题描述一致][具体问题描述]<br/>
124
136
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
125
137
  **修改建议**:[修复建议,展示错误代码及对应修改后的示例代码,注意:错误代码和正确示例代码是必须存在的部分!!!]
126
138
 
127
- [带圈数字序号,如:②,字体格式和后续问题描述一致][具体问题描述]<br/>
139
+ [带圈数字序号,如:②,字体格式和后续问题描述一致][具体问题描述]<br/>
128
140
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
129
141
  **修改建议**:[修复建议,展示错误代码及对应修改后的示例代码,注意:错误代码和正确示例代码是必须存在的部分!!!]
130
142
 
131
143
  ### 🟡 警告
132
144
 
133
- [带圈数字序号,如:①,字体格式和后续问题描述一致][具体问题描述]<br/>
145
+ [带圈数字序号,如:①,字体格式和后续问题描述一致][具体问题描述]<br/>
134
146
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
135
147
  **修改建议**:[改进建议,展示相应代码及对应修改后的示例代码]
136
148
 
137
- [带圈数字序号,如:②,字体格式和后续问题描述一致][具体问题描述]<br/>
149
+ [带圈数字序号,如:②,字体格式和后续问题描述一致][具体问题描述]<br/>
138
150
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
139
151
  **修改建议**:[改进建议,展示相应代码及对应修改后的示例代码]
140
152
 
141
153
  ### 🟢 优化建议
142
154
 
155
+ [带圈数字序号,如:①,字体格式和后续优化建议一致][优化建议及可以直接使用的修改提示词]<br/>
143
156
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
144
- **优化建议**:[优化建议及可以直接使用的修改提示词]<br/>
145
157
  **示例代码**:[展示相应代码及对应修改优化后的示例代码]
146
158
 
159
+ [带圈数字序号,如:②,字体格式和后续优化建议一致][优化建议及可以直接使用的修改提示词]<br/>
147
160
  **文件及行号**:[问题所在文件相关代码块开始至结束行号,格式类似:ApplyPushServiceImpl.java:176-180]<br/>
148
- **优化建议**:[优化建议及可以直接使用的修改提示词]<br/>
149
161
  **示例代码**:[展示相应代码及对应修改优化后的示例代码]
150
162
 
151
163
  ### 🔄 上下文影响分析(需涵盖依赖关系、功能关联性、兼容性风险三个维度)
152
164
 
165
+ [带圈数字序号,如:①,字体格式和影响描述建议一致][影响描述:功能正确性影响、性能影响、兼容性影响(如API变更导致的下游系统影响)]<br/>
153
166
  **受影响组件**: [直接依赖的类/方法/模块、间接关联的功能模块]<br/>
154
- **影响描述**: [功能正确性影响、性能影响、兼容性影响(如API变更导致的下游系统影响)]<br/>
155
167
  **验证建议**: [如何验证是否存在此问题]
156
168
 
169
+ [带圈数字序号,如:②,字体格式和影响描述建议一致][影响描述:功能正确性影响、性能影响、兼容性影响(如API变更导致的下游系统影响)]<br/>
157
170
  **受影响组件**: [直接依赖的类/方法/模块、间接关联的功能模块]<br/>
158
- **影响描述**: [功能正确性影响、性能影响、兼容性影响(如API变更导致的下游系统影响)]<br/>
159
171
  **验证建议**: [如何验证是否存在此问题]
160
172
 
161
173
  </REPORT>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job51-gitlab-cr-node-skill-prompt-optimize",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "GitLab merge request code review tool with AI-powered analysis",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,167 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>打印Div为PDF示例</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
9
+ <style>
10
+ body {
11
+ font-family: Arial, sans-serif;
12
+ padding: 20px;
13
+ }
14
+
15
+ #contentToPrint {
16
+ border: 2px solid #ccc;
17
+ padding: 20px;
18
+ margin: 20px 0;
19
+ background-color: #f9f9f9;
20
+ min-height: 300px;
21
+ }
22
+
23
+ button {
24
+ background-color: #4CAF50;
25
+ color: white;
26
+ padding: 10px 20px;
27
+ border: none;
28
+ cursor: pointer;
29
+ border-radius: 4px;
30
+ font-size: 16px;
31
+ }
32
+
33
+ button:hover {
34
+ background-color: #45a049;
35
+ }
36
+
37
+ h2 {
38
+ color: #333;
39
+ }
40
+
41
+ p {
42
+ line-height: 1.6;
43
+ }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <h1>打印Div为PDF示例</h1>
48
+
49
+ <button onclick="printDivToPDF()">打印指定区域为PDF</button>
50
+
51
+ <div id="contentToPrint">
52
+ <h2>这是一个要打印到PDF的区域</h2>
53
+ <p>这里是第一段内容。这是一些测试文本,用来展示如何将特定的div区域打印为PDF。</p>
54
+ <p>这里是第二段内容。可以看到我们有丰富的文本内容可以被转换成PDF。</p>
55
+ <ul>
56
+ <li>列表项 1</li>
57
+ <li>列表项 2</li>
58
+ <li>列表项 3</li>
59
+ </ul>
60
+ <p>最后的段落,展示更多内容。</p>
61
+ </div>
62
+
63
+ <div>
64
+ <p>这个区域不会被打印到PDF中,因为它不在目标div内。</p>
65
+ </div>
66
+
67
+ <script>
68
+ // 获取jsPDF实例
69
+ const { jsPDF } = window.jspdf;
70
+
71
+ function printDivToPDF() {
72
+ // 获取要打印的div元素
73
+ const element = document.getElementById('contentToPrint');
74
+
75
+ // 使用html2canvas将div转换为canvas
76
+ html2canvas(element, {
77
+ scale: 2, // 提高清晰度
78
+ useCORS: true, // 允许跨域图像
79
+ backgroundColor: '#ffffff' // 设置背景色
80
+ }).then(canvas => {
81
+ // 将canvas转换为数据URL
82
+ const imgData = canvas.toDataURL('image/png');
83
+
84
+ // 创建PDF
85
+ const pdf = new jsPDF('p', 'mm', 'a4');
86
+
87
+ // 计算图片在PDF中的尺寸以适应页面宽度
88
+ const imgWidth = 210; // A4页面宽度(mm)
89
+ const pageHeight = 295; // A4页面高度(mm)
90
+ const imgHeight = canvas.height * imgWidth / canvas.width;
91
+
92
+ let heightLeft = imgHeight;
93
+ let position = 0;
94
+
95
+ // 第一页添加图像
96
+ pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
97
+ heightLeft -= pageHeight;
98
+
99
+ // 如果内容超过一页,则添加新页
100
+ while (heightLeft >= 0) {
101
+ position = heightLeft - imgHeight;
102
+ pdf.addPage();
103
+ pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
104
+ heightLeft -= pageHeight;
105
+ }
106
+
107
+ // 保存PDF
108
+ pdf.save('div-content.pdf');
109
+ });
110
+ }
111
+
112
+ // 纯函数版本:将div转换为PDF的可重用函数
113
+ function divToPDF(divId, filename = 'output.pdf') {
114
+ return new Promise((resolve, reject) => {
115
+ const element = document.getElementById(divId);
116
+
117
+ if (!element) {
118
+ reject(new Error(`Element with id '${divId}' not found`));
119
+ return;
120
+ }
121
+
122
+ html2canvas(element, {
123
+ scale: 2,
124
+ useCORS: true,
125
+ backgroundColor: '#ffffff'
126
+ }).then(canvas => {
127
+ const imgData = canvas.toDataURL('image/png');
128
+
129
+ const pdf = new jsPDF('p', 'mm', 'a4');
130
+ const imgWidth = 210;
131
+ const pageHeight = 295;
132
+ const imgHeight = canvas.height * imgWidth / canvas.width;
133
+
134
+ let heightLeft = imgHeight;
135
+ let position = 0;
136
+
137
+ pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
138
+ heightLeft -= pageHeight;
139
+
140
+ while (heightLeft >= 0) {
141
+ position = heightLeft - imgHeight;
142
+ pdf.addPage();
143
+ pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
144
+ heightLeft -= pageHeight;
145
+ }
146
+
147
+ pdf.save(filename);
148
+ resolve(pdf);
149
+ }).catch(error => {
150
+ reject(error);
151
+ });
152
+ });
153
+ }
154
+
155
+ // 示例:使用纯函数版本
156
+ function printWithFunction() {
157
+ divToPDF('contentToPrint', 'my-document.pdf')
158
+ .then(() => {
159
+ console.log('PDF已成功生成');
160
+ })
161
+ .catch(error => {
162
+ console.error('生成PDF时出错:', error);
163
+ });
164
+ }
165
+ </script>
166
+ </body>
167
+ </html>