Sfoglia il codice sorgente

添加一键生成按钮

htc 4 giorni fa
parent
commit
db9f146d52
1 ha cambiato i file con 125 aggiunte e 95 eliminazioni
  1. 125 95
      pagesHome/pdf.vue

+ 125 - 95
pagesHome/pdf.vue

@@ -95,8 +95,6 @@
 								</div>
 							</div>
 						</div>
-						<!-- (可选)添加一个生成图片的按钮 -->
-						<button @click="generateScoreImage(item, 0)" style="margin-top: 20px;">生成“目的与动机”评分图片</button>
 					</div>
 					<div class="v2-data" :style="{'border':'1px solid '+item.bcolor}">
 						<div class="vd-title vt3" :class="{'black':item.title==='目的与动机'}" :style="{'background-image':'url(https://gitee.com/hw_0302/chuang-heng-wechat-images/raw/master/versionTwo/'+typeDict[item.title]+'_title_bg1.png)'}">诊断结果</div>
@@ -116,6 +114,7 @@
 				</div>
 			</template>
 		</view>
+		<view class="pdf_btn" @click="createPdf">生成PDF</view>
    </view>
 </template>
 
@@ -224,7 +223,8 @@
 				  color: '#0096D8'
 				}
             ],
-        };
+			pdfImages:[],
+		};
     },
     mounted() {
         // reportData.value = props.reportData;
@@ -257,81 +257,95 @@
 		});
     },
     methods: {
+		async createPdf(){
+			uni.showLoading({
+				title:'正在生成PDF所需的图片...'
+			})
+			await this.downloadZtzdfxImg();
+			this.reportData.dimensionAnalysis.forEach(async d=>{
+				await this.generateScoreImage(d)
+			})
+		},
 		// 绘制主函数
-		async generateScoreImage(dimensionData, index) {
-		    console.log('开始生成图片...');
-		    uni.showLoading({ title: '图片生成中...' });
-		
-		    // --- 1. 定义尺寸和样式 ---
-		    const canvasWidth = 588; // .v2-box 的宽度大约是 630 - 20*2(padding) - 1*2(border) = 588
-		    const itemHeight = 70; // 每个评估项的高度
-		    const totalHeight = itemHeight * this.scoreData.length;
-		    // 调整为整数,避免边框模糊
-		    const canvasHeight = totalHeight;
-		
-		    // --- 2. 获取 Canvas 节点 ---
-		    // 使用 ID 选择器更精确
-		    const query = uni.createSelectorQuery().in(this);
-		    query.select('#score-canvas')
-		        .fields({ node: true, size: true })
-		        .exec(async (res) => {
-		            // 【重要】增加健壮性检查
-		            if (!res || !res[0] || !res[0].node) {
-		                uni.hideLoading();
-		                console.error('获取 Canvas 节点失败,请检查 canvas-id 和 type="2d" 是否正确设置。');
-		                uni.showToast({ title: '组件初始化失败', icon: 'none' });
-		                return;
-		            }
-		
-		            const canvasNode = res[0].node;
-		            const context = canvasNode.getContext('2d');
-		            const dpr = uni.getSystemInfoSync().pixelRatio;
-		
-		            // --- 3. 设置画布尺寸和缩放以适应高分屏 ---
-		            canvasNode.width = canvasWidth * dpr;
-		            canvasNode.height = canvasHeight * dpr;
-		            context.scale(dpr, dpr);
-		
-		            // --- 4. 开始绘制 ---
-		            // 绘制大背景
-		            context.fillStyle = '#FFFFFF';
-		            context.fillRect(0, 0, canvasWidth, canvasHeight);
-		
-		            // --- 5. 循环绘制每一项 ---
-		            for (let i = 0; i < this.scoreData.length; i++) {
-		                const item = this.scoreData[i];
-		                const yPos = i * itemHeight;
-		                // 注意:这里不再需要 await,因为 canvas 2d 绘图是同步的
-		                this.drawScoreItem(context, item, yPos, canvasWidth, itemHeight, dimensionData);
-		            }
-		            
-		            // 【补充】绘制最外层的上下边框,避免被循环内的矩形覆盖
-		            context.strokeStyle = dimensionData.bcolor;
-		            context.lineWidth = 1;
-		            context.strokeRect(0, 0, canvasWidth, canvasHeight);
-		
-		
-		            // --- 6. 生成图片 ---
-		            uni.hideLoading();
-		            uni.canvasToTempFilePath({
-		                canvas: canvasNode,
-		                x: 0,
-		                y: 0,
-		                width: canvasWidth,
-		                height: canvasHeight,
-		                destWidth: canvasWidth * dpr,
-		                destHeight: canvasHeight * dpr,
-		                success: async (result) => {
-		                    console.log('图片生成成功!', result.tempFilePath);
-		                    const fileurl = await this.uploadFilePromise(result.tempFilePath);
-		                    console.log(fileurl, 'fileurl');
-		                },
-		                fail: (err) => {
-		                    console.error('图片生成失败', err);
-		                    uni.showToast({ title: '图片生成失败', icon: 'none' });
-		                }
-		            }, this);
-		    });
+		async generateScoreImage(dimensionData) {
+			return new Promise(resolve=>{
+				console.log('开始生成图片...');
+				// --- 1. 定义尺寸和样式 ---
+				const canvasWidth = 588; // .v2-box 的宽度大约是 630 - 20*2(padding) - 1*2(border) = 588
+				const itemHeight = 70; // 每个评估项的高度
+				const totalHeight = itemHeight * this.scoreData.length;
+				// 调整为整数,避免边框模糊
+				const canvasHeight = totalHeight;
+						
+				// --- 2. 获取 Canvas 节点 ---
+				// 使用 ID 选择器更精确
+				const query = uni.createSelectorQuery().in(this);
+				query.select('#score-canvas')
+				    .fields({ node: true, size: true })
+				    .exec(async (res) => {
+				        // 【重要】增加健壮性检查
+				        if (!res || !res[0] || !res[0].node) {
+				            console.error('获取 Canvas 节点失败,请检查 canvas-id 和 type="2d" 是否正确设置。');
+				            uni.showToast({ title: '组件初始化失败', icon: 'none' });
+				            return;
+				        }
+						
+				        const canvasNode = res[0].node;
+				        const context = canvasNode.getContext('2d');
+				        const dpr = uni.getSystemInfoSync().pixelRatio;
+						
+				        // --- 3. 设置画布尺寸和缩放以适应高分屏 ---
+				        canvasNode.width = canvasWidth * dpr;
+				        canvasNode.height = canvasHeight * dpr;
+				        context.scale(dpr, dpr);
+						
+				        // --- 4. 开始绘制 ---
+				        // 绘制大背景
+				        context.fillStyle = '#FFFFFF';
+				        context.fillRect(0, 0, canvasWidth, canvasHeight);
+						
+				        // --- 5. 循环绘制每一项 ---
+				        for (let i = 0; i < this.scoreData.length; i++) {
+				            const item = this.scoreData[i];
+				            const yPos = i * itemHeight;
+				            // 注意:这里不再需要 await,因为 canvas 2d 绘图是同步的
+				            this.drawScoreItem(context, item, yPos, canvasWidth, itemHeight, dimensionData);
+				        }
+				        
+				        // 【补充】绘制最外层的上下边框,避免被循环内的矩形覆盖
+				        context.strokeStyle = dimensionData.bcolor;
+				        context.lineWidth = 1;
+				        context.strokeRect(0, 0, canvasWidth, canvasHeight);
+						
+						
+				        // --- 6. 生成图片 ---
+				        uni.hideLoading();
+				        uni.canvasToTempFilePath({
+				            canvas: canvasNode,
+				            x: 0,
+				            y: 0,
+				            width: canvasWidth,
+				            height: canvasHeight,
+				            destWidth: canvasWidth * dpr,
+				            destHeight: canvasHeight * dpr,
+				            success: async (result) => {
+				                console.log('图片生成成功!', result.tempFilePath);
+				                const fileurl = await this.uploadFilePromise(result.tempFilePath);
+				                console.log(fileurl, 'fileurl');
+								this.pdfImages.push(fileurl);
+								if(this.pdfImages.length===7){
+									uni.hideLoading();
+									console.log(this.pdfImages,'pdfImages');
+								}
+								resolve()
+				            },
+				            fail: (err) => {
+				                console.error('图片生成失败', err);
+				                uni.showToast({ title: '图片生成失败', icon: 'none' });
+				            }
+				        }, this);
+				});
+			})
 		},
 		// 辅助函数:绘制单个评估项
 		drawScoreItem(ctx, scoreItem, y, width, height, dimensionData) {
@@ -346,7 +360,7 @@
 			
 			// 绘制左侧标题文字
 			ctx.fillStyle = dimensionData.title === '目的与动机' ? '#000000' : '#FFFFFF';
-			ctx.font = '13px sans-serif';
+			ctx.font = '10px sans-serif';
 			ctx.textAlign = 'center';
 			ctx.textBaseline = 'middle';
 			// 实现文字自动换行
@@ -358,8 +372,8 @@
 			ctx.strokeRect(rightBoxX, y, rightBoxWidth, height);
 	
 			// 绘制右侧描述文字
-			ctx.fillStyle = '#333333';
-			ctx.font = '12px sans-serif';
+			ctx.fillStyle = '#193D59';
+			ctx.font = '9px sans-serif';
 			ctx.textAlign = 'left';
 			ctx.textBaseline = 'top';
 			this.drawWrappedText(ctx, scoreItem.desc, rightBoxX + 10, y + 12, 16, rightBoxWidth - 20);
@@ -392,6 +406,7 @@
 			// 气泡背景和边框
 			ctx.fillStyle = '#FFFFFF';
 			ctx.strokeStyle = dimensionData.bcolor;
+			ctx.borderRadius= 4;
 			ctx.lineWidth = 1;
 			ctx.beginPath();
 			ctx.arc(bubbleX + bubbleSize / 2, bubbleY + bubbleSize / 2, bubbleSize / 2, 0, Math.PI * 2);
@@ -439,7 +454,6 @@
 			return colorStops;
 		},
 		
-		
 		calculateScaleAndPosition() {
 		    uni.getSystemInfo({
 				success: (res) => {
@@ -464,20 +478,24 @@
 			}).exec();
 		},
 		downloadZtzdfxImg(){
-			if (!this.isChartReady) return console.log('图表尚未准备好');
-			
-			const chartRef = this.$refs.ztzdfxRef;
-			if (!chartRef) return console.log('无法找到图表组件');
-			
-			chartRef.canvasToTempFilePath({
-				success: async (res) => {
-					const imgUrl = await this.uploadFilePromise(res.tempFilePath);
-					console.log(imgUrl,'imgUrl');
-				},
-				fail: (err) => {
-					console.log('生成图片失败:', err);
-				}
-			});
+			return new Promise(resolve=>{
+				if (!this.isChartReady) return console.log('图表尚未准备好');
+				
+				const chartRef = this.$refs.ztzdfxRef;
+				if (!chartRef) return console.log('无法找到图表组件');
+				
+				chartRef.canvasToTempFilePath({
+					success: async (res) => {
+						const imgUrl = await this.uploadFilePromise(res.tempFilePath);
+						console.log(imgUrl,'imgUrl');
+						this.pdfImages.push(imgUrl);
+						resolve()
+					},
+					fail: (err) => {
+						console.log('生成图片失败:', err);
+					}
+				});
+			})
 		},
 		uploadFilePromise(url) {
 		  return new Promise((resolve, reject) => {
@@ -567,6 +585,18 @@
 		left: -9999px;
 	}
 	
+	.pdf_btn{
+		padding: 15rpx 20rpx;
+		border-radius: 20rpx;
+		font-size: 28rpx;
+		color: #FFFFFF;
+		background: #189B9B;
+		position: fixed;
+		right: 30rpx;
+		bottom: 100rpx;
+		z-index: 1000;
+	}
+	
 	.page-wrappe{
 		width: 100%;
 		background: #FFFFFF;