فهرست منبع

样式最终版(和设计图一致)

htc 4 روز پیش
والد
کامیت
727f7f1eba
1فایلهای تغییر یافته به همراه110 افزوده شده و 39 حذف شده
  1. 110 39
      pagesHome/pdf.vue

+ 110 - 39
pagesHome/pdf.vue

@@ -272,7 +272,7 @@
 				console.log('开始生成图片...');
 				// --- 1. 定义尺寸和样式 ---
 				const canvasWidth = 588; // .v2-box 的宽度大约是 630 - 20*2(padding) - 1*2(border) = 588
-				const itemHeight = 70; // 每个评估项的高度
+				const itemHeight = 49; // 每个评估项的高度
 				const totalHeight = itemHeight * this.scoreData.length;
 				// 调整为整数,避免边框模糊
 				const canvasHeight = totalHeight;
@@ -347,80 +347,151 @@
 				});
 			})
 		},
+		// 辅助函数:计算自动换行文字的总高度
+		calculateWrappedTextHeight(ctx, text, lineHeight, maxWidth) {
+		    let words = text.split('');
+		    let line = '';
+		    let height = lineHeight; // 至少有一行
+		    for (let n = 0; n < words.length; n++) {
+		        let testLine = line + words[n];
+		        let metrics = ctx.measureText(testLine);
+		        let testWidth = metrics.width;
+		        if (testWidth > maxWidth && n > 0) {
+		            line = words[n];
+		            height += lineHeight;
+		        } else {
+		            line = testLine;
+		        }
+		    }
+		    return height;
+		},
 		// 辅助函数:绘制单个评估项
 		drawScoreItem(ctx, scoreItem, y, width, height, dimensionData) {
-			const leftBoxWidth = 110;
-			const padding = 10;
+		    const leftBoxWidth = 110;
 			const rightBoxX = leftBoxWidth;
 			const rightBoxWidth = width - leftBoxWidth;
-	
-			// 绘制左侧标题背景
+			const rightPadding = 10; // 右侧内容的通用内边距
+		
+			// 1. --- 绘制左侧部分 ---
 			ctx.fillStyle = dimensionData.titlecolor;
 			ctx.fillRect(0, y, leftBoxWidth, height);
 			
-			// 绘制左侧标题文字
-			ctx.fillStyle = dimensionData.title === '目的与动机' ? '#000000' : '#FFFFFF';
+			// 绘制白色下边框
+			if (y + height < ctx.canvas.height / (uni.getSystemInfoSync().pixelRatio)) {
+				ctx.strokeStyle = '#FFFFFF';
+				ctx.lineWidth = 1;
+				ctx.beginPath();
+				ctx.moveTo(0, y + height - 1);
+				ctx.lineTo(leftBoxWidth, y + height - 1);
+				ctx.stroke();
+			}
+			
+			// 绘制左侧标题文字 (要求 1)
+			ctx.fillStyle = '#002846';
 			ctx.font = '10px sans-serif';
 			ctx.textAlign = 'center';
 			ctx.textBaseline = 'middle';
-			// 实现文字自动换行
-			this.drawWrappedText(ctx, scoreItem.title, leftBoxWidth / 2, y + height / 2, 20, leftBoxWidth - 10);
-	
-			// 绘制右侧边框
+			this.drawWrappedText(ctx, scoreItem.title, leftBoxWidth / 2, y + height / 2, 20, leftBoxWidth - 20); // 左右留10px边距
+		
+			// 2. --- 绘制右侧部分 ---
+			// 绘制右侧边框
 			ctx.strokeStyle = dimensionData.bcolor;
 			ctx.lineWidth = 1;
 			ctx.strokeRect(rightBoxX, y, rightBoxWidth, height);
-	
-			// 绘制右侧描述文字
+		
+			// --- 计算右侧内容垂直居中需要的参数 (要求 2) ---
+			const descLineHeight = 16;
+			const descMaxWidth = rightBoxWidth - rightPadding * 2;
+			ctx.font = '9px sans-serif'; // 设置好字体用于计算
+			const descHeight = this.calculateWrappedTextHeight(ctx, scoreItem.desc, descLineHeight, descMaxWidth);
+			
+			const spacing = 7; // 文字与进度条间距 (要求 4)
+			const progressBarHeight = 6; // 进度条高度 (要求 3)
+			
+			// 计算右侧所有内容的总高度
+			const totalContentHeight = descHeight + spacing + progressBarHeight;
+			// 计算内容块的起始 Y 坐标,使其在右侧框内垂直居中
+			const contentStartY = y + (height - totalContentHeight) / 2;
+		
+			// --- 开始绘制右侧内容 ---
+			// 绘制右侧描述文字 (要求 2)
 			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);
-	
-			// 绘制进度条
-			const progressBarY = y + 45;
-			const progressBarWidth = rightBoxWidth - 20;
+			ctx.textBaseline = 'top'; // 基线设为 top 方便计算
+			this.drawWrappedText(ctx, scoreItem.desc, rightBoxX + rightPadding, contentStartY, descLineHeight, descMaxWidth);
+		
+			// 绘制进度条 (要求 3)
+			const progressBarY = contentStartY + descHeight + spacing;
+			const progressBarWidth = rightBoxWidth - rightPadding * 2;
 			const scoreWidth = (scoreItem.score / 5) * progressBarWidth;
-	
-			// 创建渐变
-			const gradient = ctx.createLinearGradient(rightBoxX + 10, 0, rightBoxX + 10 + progressBarWidth, 0);
-			// 解析渐变色
+			const barRadius = 3;
+		
+			// 绘制灰色背景
+			ctx.fillStyle = '#F0F2F8';
+			ctx.beginPath();
+			ctx.moveTo(rightBoxX + rightPadding + barRadius, progressBarY);
+			ctx.arcTo(rightBoxX + rightPadding + progressBarWidth, progressBarY, rightBoxX + rightPadding + progressBarWidth, progressBarY + progressBarHeight, barRadius);
+			ctx.arcTo(rightBoxX + rightPadding + progressBarWidth, progressBarY + progressBarHeight, rightBoxX + rightPadding, progressBarY + progressBarHeight, barRadius);
+			ctx.arcTo(rightBoxX + rightPadding, progressBarY + progressBarHeight, rightBoxX + rightPadding, progressBarY, barRadius);
+			ctx.arcTo(rightBoxX + rightPadding, progressBarY, rightBoxX + rightPadding + progressBarWidth, progressBarY, barRadius);
+			ctx.closePath();
+			ctx.fill();
+		
+			// 绘制实际得分的渐变色进度条
+			const gradient = ctx.createLinearGradient(rightBoxX + rightPadding, 0, rightBoxX + rightPadding + progressBarWidth, 0);
 			const gradientColors = this.parseGradient(dimensionData.pfztfb);
 			gradientColors.forEach(c => gradient.addColorStop(c.stop, c.color));
 			ctx.fillStyle = gradient;
-			ctx.fillRect(rightBoxX + 10, progressBarY, scoreWidth, 6);
-	
-			// 绘制分数气泡
-			const bubbleSize = 24;
-			const bubbleX = rightBoxX + 10 + scoreWidth - (bubbleSize / 2);
-			const bubbleY = progressBarY - (bubbleSize / 2) + 3; // +3 微调居中
+			ctx.save();
+			ctx.clip(); // 使用上面的圆角矩形路径进行裁剪
+			ctx.fillRect(rightBoxX + rightPadding, progressBarY, scoreWidth, progressBarHeight);
+			ctx.restore();
+		
+			// 绘制分数框 (要求 4)
+			const scoreFontSize = 12;
+			const scorePaddingY = 4; // 上下内边距
+			const scorePaddingX = 7; // 左右内边距
+			const scoreBoxRadius = 4;
 			
-			// 气泡阴影
+			ctx.font = `bold ${scoreFontSize}px sans-serif`;
+			const scoreTextMetrics = ctx.measureText(scoreItem.score);
+			const scoreBoxWidth = scoreTextMetrics.width + scorePaddingX * 2;
+			const scoreBoxHeight = scoreFontSize + scorePaddingY * 2;
+		
+			// 计算分数框的位置,使其右端对齐在进度条的末端
+			const scoreBoxX = rightBoxX + rightPadding + scoreWidth - scoreBoxWidth;
+			// 垂直居中于进度条
+			const scoreBoxY = progressBarY + (progressBarHeight / 2) - (scoreBoxHeight / 2);
+		
+			// 绘制阴影
 			ctx.save();
 			ctx.shadowColor = dimensionData.bcolor;
 			ctx.shadowBlur = 6;
 			ctx.shadowOffsetX = 0;
 			ctx.shadowOffsetY = 2;
 			
-			// 气泡背景和边框
+			// 绘制分数框背景和边框
 			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);
+			ctx.moveTo(scoreBoxX + scoreBoxRadius, scoreBoxY);
+			ctx.arcTo(scoreBoxX + scoreBoxWidth, scoreBoxY, scoreBoxX + scoreBoxWidth, scoreBoxY + scoreBoxHeight, scoreBoxRadius);
+			ctx.arcTo(scoreBoxX + scoreBoxWidth, scoreBoxY + scoreBoxHeight, scoreBoxX, scoreBoxY + scoreBoxHeight, scoreBoxRadius);
+			ctx.arcTo(scoreBoxX, scoreBoxY + scoreBoxHeight, scoreBoxX, scoreBoxY, scoreBoxRadius);
+			ctx.arcTo(scoreBoxX, scoreBoxY, scoreBoxX + scoreBoxWidth, scoreBoxY, scoreBoxRadius);
+			ctx.closePath();
 			ctx.fill();
 			ctx.stroke();
-			ctx.closePath();
 			ctx.restore();
-	
-			// 气泡内分数文字
-			ctx.fillStyle = '#333333';
-			ctx.font = 'bold 12px sans-serif';
+		
+			// 绘制分数文字
+			ctx.fillStyle = '#002846';
+			ctx.font = `bold ${scoreFontSize}px sans-serif`;
 			ctx.textAlign = 'center';
 			ctx.textBaseline = 'middle';
-			ctx.fillText(scoreItem.score, bubbleX + bubbleSize / 2, bubbleY + bubbleSize / 2);
+			ctx.fillText(scoreItem.score, scoreBoxX + scoreBoxWidth / 2, scoreBoxY + scoreBoxHeight / 2);
 		},
 		// 辅助函数:绘制自动换行的文字
 		drawWrappedText(ctx, text, x, y, lineHeight, maxWidth) {