|
|
@@ -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;
|