Selaa lähdekoodia

生成canvas版

htc 17 tuntia sitten
vanhempi
commit
adc465b98c
1 muutettua tiedostoa jossa 132 lisäystä ja 82 poistoa
  1. 132 82
      pagesHome/pdf.vue

+ 132 - 82
pagesHome/pdf.vue

@@ -253,30 +253,30 @@
 			this.$api.get(`/core/report/previewReport/${this.reportId}`).then(({data:res})=>{
 			this.$api.get(`/core/report/previewReport/${this.reportId}`).then(({data:res})=>{
 				if(res.code!==0) return this.$showToast(res.msg)
 				if(res.code!==0) return this.$showToast(res.msg)
 				this.reportData = res.data;
 				this.reportData = res.data;
-				const tempDimensionAnalysis = [
-					{
-						title:'P-宗旨与动机',thbgcolor:'#761E6A',thtextcolor:'#FFFFFF',titlecolor:'#761E6A',dimensionCode:"purpose",bordercolor:'#E4D2E1',
-						desc:`「宗旨与动机」维度,我们旨在探究团队是否有清晰的存在理由和明确的方向,能够激发团队成员的动力并吸引他们的想象力,以及个人与集体的身份认同是否围绕共同的目标,并在实现目标的优先次序上达成一致。`
-					},
-					{
-						title:'E-外部流程、系统与结构',thbgcolor:'#009191',thtextcolor:'#FFFFFF',titlecolor:'#009191',dimensionCode:"external",bordercolor:'#B3DEDE',
-						desc:`「外部流程、系统与结构」维度,我们旨在探究团队如何与各种利益相关者互动,他们与团队的利益相关方各自如何寻求了解对方,以及现有系统和流程的有效性,以帮助管理不同利益相关者的期望和需求。`
-					},
-					{
-						title:'R-人际关系',thbgcolor:'#FFD750',thtextcolor:'#002846',titlecolor:'#002846',dimensionCode:"relationship",bordercolor:'#FFEBA8',
-						desc:`「人际关系」维度,我们旨在探究团队成员相互沟通交流的状态,团队成员的信任程度、尊重和关心的程度、心理安全度,以及团队成员之间的关系如何促进(或破坏)协作。`
-					},
-					{
-						title:'I-内部流程、系统与结构',thbgcolor:'#4EB2B2',thtextcolor:'#FFFFFF',titlecolor:'#33A7A7',dimensionCode:"internal",bordercolor:'#B3DEDE',
-						desc:`「内部流程、系统与结构」维度,我们旨在探究团队在管理工作任务和流程中如何平衡责任与自主权进行协作。主要关注团队应对变化时的敏捷程度、日常沟通方式以及内部决策过程的有效性。`
-					},
-					{
-						title:'L-学习',thbgcolor:'#AFCDF5',thtextcolor:'#002846',titlecolor:'#002846',dimensionCode:"learning",bordercolor:'#C7DCF8',
-						desc:`「学习」维度,我们旨在探究团队如何提高其绩效(完成当下的任务)、能力(提高技能和资源以处理明天的任务)和容量(⻓期的愿景,用更少的资源做更多的事情)以应对当前和未来的任务。同时还关注团队如何管理这些能力和提高效率。`
-					},
-					{
-						title:'L-领导力',thbgcolor:'#002846',thtextcolor:'#FFFFFF',titlecolor:'#002846',dimensionCode:"leadership",bordercolor:'#E6EAED',
-						desc:`「领导力」维度,我们旨在探究团队领导者的素质和行为(包括但不限于正式与非正式的引导、赋能与责任承担方式)如何对团队功能和其他因素产生影响,以及这是积极的还是消极的。`
+				const tempDimensionAnalysis = [
+					{
+						title:'P-宗旨与动机',thbgcolor:'#761E6A',thtextcolor:'#FFFFFF',titlecolor:'#761E6A',dimensionCode:"purpose",bordercolor:'#E4D2E1',
+						desc:`「宗旨与动机」维度,我们旨在探究团队是否有清晰的存在理由和明确的方向,能够激发团队成员的动力并吸引他们的想象力,以及个人与集体的身份认同是否围绕共同的目标,并在实现目标的优先次序上达成一致。`
+					},
+					{
+						title:'E-外部流程、系统与结构',thbgcolor:'#009191',thtextcolor:'#FFFFFF',titlecolor:'#009191',dimensionCode:"external",bordercolor:'#B3DEDE',
+						desc:`「外部流程、系统与结构」维度,我们旨在探究团队如何与各种利益相关者互动,他们与团队的利益相关方各自如何寻求了解对方,以及现有系统和流程的有效性,以帮助管理不同利益相关者的期望和需求。`
+					},
+					{
+						title:'R-人际关系',thbgcolor:'#FFD750',thtextcolor:'#002846',titlecolor:'#002846',dimensionCode:"relationship",bordercolor:'#FFEBA8',
+						desc:`「人际关系」维度,我们旨在探究团队成员相互沟通交流的状态,团队成员的信任程度、尊重和关心的程度、心理安全度,以及团队成员之间的关系如何促进(或破坏)协作。`
+					},
+					{
+						title:'I-内部流程、系统与结构',thbgcolor:'#4EB2B2',thtextcolor:'#FFFFFF',titlecolor:'#33A7A7',dimensionCode:"internal",bordercolor:'#B3DEDE',
+						desc:`「内部流程、系统与结构」维度,我们旨在探究团队在管理工作任务和流程中如何平衡责任与自主权进行协作。主要关注团队应对变化时的敏捷程度、日常沟通方式以及内部决策过程的有效性。`
+					},
+					{
+						title:'L-学习',thbgcolor:'#AFCDF5',thtextcolor:'#002846',titlecolor:'#002846',dimensionCode:"learning",bordercolor:'#C7DCF8',
+						desc:`「学习」维度,我们旨在探究团队如何提高其绩效(完成当下的任务)、能力(提高技能和资源以处理明天的任务)和容量(⻓期的愿景,用更少的资源做更多的事情)以应对当前和未来的任务。同时还关注团队如何管理这些能力和提高效率。`
+					},
+					{
+						title:'L-领导力',thbgcolor:'#002846',thtextcolor:'#FFFFFF',titlecolor:'#002846',dimensionCode:"leadership",bordercolor:'#E6EAED',
+						desc:`「领导力」维度,我们旨在探究团队领导者的素质和行为(包括但不限于正式与非正式的引导、赋能与责任承担方式)如何对团队功能和其他因素产生影响,以及这是积极的还是消极的。`
 					}
 					}
 				]
 				]
 				if(this.reportData&&this.reportData.dimensionAnalysis){
 				if(this.reportData&&this.reportData.dimensionAnalysis){
@@ -436,35 +436,7 @@
 							this.drawTableItem(ctx, item, yPos, leftColWidth, rightColWidth, rowHeight, dimensionData);
 							this.drawTableItem(ctx, item, yPos, leftColWidth, rightColWidth, rowHeight, dimensionData);
 						}
 						}
 
 
-						// --- 7. 绘制右侧底部的刻度 (绘制在最上层) ---
-						// 先绘制白色背景遮挡网格线
-						ctx.fillStyle = '#FFFFFF';
-						const axisHeight = 20; // 底部文字区域高度
-						ctx.fillRect(leftColWidth, totalHeight - axisHeight, rightColWidth, axisHeight);
-						
-						ctx.fillStyle = '#002846';
-						ctx.font = 'bold 10px sans-serif';
-						ctx.textAlign = 'center';
-						ctx.textBaseline = 'bottom';
-
-						const centerX = leftColWidth + rightColWidth / 2;
-						const colWidth = rightColWidth / 12;
-
-						// 刻度 0-5
-						for (let k = 0; k <= 5; k++) {
-							let xR = centerX + k * colWidth;
-							let xL = centerX - k * colWidth;
-							let yTxt = totalHeight - 5; // 距离底部一点距离
-
-							if (k === 0) {
-								ctx.fillText(0, centerX, yTxt);
-							} else {
-								ctx.fillText(k, xR, yTxt);
-								ctx.fillText(k, xL, yTxt);
-							}
-						}
-
-						// --- 8. 绘制最外层边框 ---
+						// --- 7. 绘制最外层边框 ---
 						ctx.strokeStyle = '#E6EAED';
 						ctx.strokeStyle = '#E6EAED';
 						ctx.lineWidth = 1;
 						ctx.lineWidth = 1;
 						ctx.strokeRect(0, 0, canvasWidth, totalHeight);
 						ctx.strokeRect(0, 0, canvasWidth, totalHeight);
@@ -498,17 +470,14 @@
 			})
 			})
 		},
 		},
 		drawTableItem(ctx, item, y, leftW, rightW, h, dimensionData) {
 		drawTableItem(ctx, item, y, leftW, rightW, h, dimensionData) {
-			// 1. 左侧
-			// 边框
-			ctx.strokeStyle = '#E6EAED';
+			// 1. 设置通用边框样式
+			ctx.strokeStyle = dimensionData.bordercolor;
 			ctx.lineWidth = 1;
 			ctx.lineWidth = 1;
-			// 左侧整体边框
-			ctx.strokeRect(0, y, leftW, h);
 
 
-			// 竖线分割 Theme/Question 和 Score
+			// 绘制整行下边框 (贯穿左右)
 			ctx.beginPath();
 			ctx.beginPath();
-			ctx.moveTo(leftW - 54, y);
-			ctx.lineTo(leftW - 54, y + h);
+			ctx.moveTo(0, y + h);
+			ctx.lineTo(leftW + rightW, y + h);
 			ctx.stroke();
 			ctx.stroke();
 
 
 			// --- 计算高度 ---
 			// --- 计算高度 ---
@@ -544,24 +513,6 @@
 
 
 			// 2. 右侧
 			// 2. 右侧
 			const rightX = leftW;
 			const rightX = leftW;
-			
-			// 裁剪右侧区域,防止内容溢出
-			ctx.save();
-			ctx.beginPath();
-			ctx.rect(rightX, y, rightW, h);
-			ctx.clip();
-			
-			// 右侧不需要横向分割线,只绘制竖向网格线
-			// 绘制背景网格线 (竖向,贯穿整个高度)
-			ctx.strokeStyle = '#F0F2F8';
-			ctx.lineWidth = 1;
-			const colWidth = rightW / 12;
-			for (let k = 1; k < 12; k++) {
-				ctx.beginPath();
-				ctx.moveTo(rightX + k * colWidth, y); 
-				ctx.lineTo(rightX + k * colWidth, y + h);
-				ctx.stroke();
-			}
 
 
 			// 绘制中间线
 			// 绘制中间线
 			const centerX = rightX + rightW / 2;
 			const centerX = rightX + rightW / 2;
@@ -581,17 +532,116 @@
 			const agreeW = (avgAgreement / maxVal) * halfW;
 			const agreeW = (avgAgreement / maxVal) * halfW;
 			const vitalW = (avgVital / maxVal) * halfW;
 			const vitalW = (avgVital / maxVal) * halfW;
 
 
+			// --- 绘制背景轨道 (灰色) ---
+			// 模仿 CSS .vbt2r-tb-l-pre-bg: width: calc(100% - 16px); height: 4px;
+			// 左右各留 8px 空隙?或者就是简单的横条
+			const trackHeight = 4;
+			const trackY = y + (h - trackHeight) / 2;
+			ctx.fillStyle = '#E6EAED';
+			
+			// 左侧轨道背景 (从右向左)
+			this.drawRoundedRect(ctx, rightX + 8, trackY, halfW - 8, trackHeight, 2);
+			// 右侧轨道背景 (从左向右)
+			this.drawRoundedRect(ctx, centerX, trackY, halfW - 8, trackHeight, 2);
+
 			// Draw Agreement Bar (grows from center to left)
 			// Draw Agreement Bar (grows from center to left)
 			ctx.fillStyle = '#BA8EB4';
 			ctx.fillStyle = '#BA8EB4';
-			ctx.fillRect(centerX - agreeW, barY, agreeW, barHeight);
+			// 左侧条,圆角在左边 [2, 0, 0, 2]
+			this.drawCustomRoundedRect(ctx, centerX - agreeW, barY, agreeW, barHeight, [2, 0, 0, 2]);
 
 
 			// Draw Vital Bar (grows from center to right)
 			// Draw Vital Bar (grows from center to right)
 			ctx.fillStyle = '#80C8C8';
 			ctx.fillStyle = '#80C8C8';
-			ctx.fillRect(centerX, barY, vitalW, barHeight);
+			// 右侧条,圆角在右边 [0, 2, 2, 0]
+			this.drawCustomRoundedRect(ctx, centerX, barY, vitalW, barHeight, [0, 2, 2, 0]);
+			
+			// --- 绘制数字标签 ---
+			const tagW = 16;
+			const tagH = 20;
+			
+			// 左标签位置 (中心点)
+			// CSS: left: -8px (relative to bar start/end). 
+			// 实际上是在条形图的末端(远离中心的那一端)
+			// 左侧条形图末端 x = centerX - agreeW
+			// 标签中心 x = centerX - agreeW
+			this.drawValueTag(ctx, avgAgreement, centerX - agreeW, y + h/2, '#904A87', 'rgba(131,52,120,0.19)', 'rgba(118,30,106,0.1)');
+			
+			// 右标签位置
+			// 右侧条形图末端 x = centerX + vitalW
+			this.drawValueTag(ctx, avgVital, centerX + vitalW, y + h/2, '#199C9C', 'rgba(51,167,167,0.31)', 'rgba(118,30,106,0.1)');
+		},
+		// 辅助函数:绘制带样式的数值标签
+		drawValueTag(ctx, value, cx, cy, textColor, borderColor, shadowColor) {
+			const width = 16;
+			const height = 20;
+			const x = cx - width / 2;
+			const y = cy - height / 2;
+			const radius = 4;
+			
+			ctx.save();
+			// 阴影
+			ctx.shadowOffsetX = 0;
+			ctx.shadowOffsetY = 4;
+			ctx.shadowBlur = 8;
+			ctx.shadowColor = shadowColor;
+			
+			// 背景
+			ctx.fillStyle = '#FFFFFF';
+			this.drawRoundedRectPath(ctx, x, y, width, height, radius);
+			ctx.fill();
+			
+			// 边框 (取消阴影绘制边框)
+			ctx.shadowColor = 'transparent';
+			ctx.strokeStyle = borderColor;
+			ctx.lineWidth = 1;
+			ctx.stroke();
+			
+			// 文字
+			ctx.fillStyle = textColor;
+			ctx.font = 'bold 12px DIN, sans-serif';
+			ctx.textAlign = 'center';
+			ctx.textBaseline = 'middle';
+			ctx.fillText(value, cx, cy);
 			
 			
-			// 恢复裁剪
 			ctx.restore();
 			ctx.restore();
 		},
 		},
+		// 辅助函数:绘制圆角矩形路径
+		drawRoundedRectPath(ctx, x, y, w, h, r) {
+			ctx.beginPath();
+			ctx.moveTo(x + r, y);
+			ctx.lineTo(x + w - r, y);
+			ctx.arcTo(x + w, y, x + w, y + h, r);
+			ctx.lineTo(x + w, y + h - r);
+			ctx.arcTo(x + w, y + h, x, y + h, r);
+			ctx.lineTo(x + r, y + h);
+			ctx.arcTo(x, y + h, x, y, r);
+			ctx.lineTo(x, y + r);
+			ctx.arcTo(x, y, x + w, y, r);
+			ctx.closePath();
+		},
+		// 辅助函数:绘制圆角矩形 (简单版)
+		drawRoundedRect(ctx, x, y, w, h, r) {
+			this.drawRoundedRectPath(ctx, x, y, w, h, r);
+			ctx.fill();
+		},
+		// 辅助函数:绘制自定义四个角圆角的矩形
+		drawCustomRoundedRect(ctx, x, y, w, h, radii) {
+			// radii: [tl, tr, br, bl]
+			if (!Array.isArray(radii)) radii = [radii, radii, radii, radii];
+			const [tl, tr, br, bl] = radii;
+			
+			ctx.beginPath();
+			ctx.moveTo(x + tl, y);
+			ctx.lineTo(x + w - tr, y);
+			ctx.arcTo(x + w, y, x + w, y + h, tr);
+			ctx.lineTo(x + w, y + h - br);
+			ctx.arcTo(x + w, y + h, x, y + h, br);
+			ctx.lineTo(x + bl, y + h);
+			ctx.arcTo(x, y + h, x, y, bl);
+			ctx.lineTo(x, y + tl);
+			ctx.arcTo(x, y, x + w, y, tl);
+			ctx.closePath();
+			ctx.fill();
+		},
 		// 辅助函数:计算自动换行文字的总高度
 		// 辅助函数:计算自动换行文字的总高度
 		calculateWrappedTextHeight(ctx, text, lineHeight, maxWidth) {
 		calculateWrappedTextHeight(ctx, text, lineHeight, maxWidth) {
 			if (!text) return 0;
 			if (!text) return 0;