Commit 4e6251ad authored by 王炽's avatar 王炽

Merge branch 'dev' of http://gitlab.dui88.com/fh/20250528_FHQ1 into dev

parents d58b9bf6 a6385b5b
......@@ -110,10 +110,14 @@ const qrcodeImageUrl = ref('') // 二维码图片 URL
const isDownloading = ref(false) // 是否正在下载
// Canvas 尺寸(海报尺寸:542rpx x 956rpx)
// 使用 3 倍像素密度提高分辨率
const pixelRatio = 3
const systemInfo = uni.getSystemInfoSync()
const screenWidth = systemInfo.windowWidth || 375
const canvasWidth = (542 / 750) * screenWidth
const canvasHeight = (956 / 750) * screenWidth
const baseCanvasWidth = (542 / 750) * screenWidth
const baseCanvasHeight = (956 / 750) * screenWidth
const canvasWidth = baseCanvasWidth * pixelRatio
const canvasHeight = baseCanvasHeight * pixelRatio
// 监听 visible 变化,生成二维码
watch(() => props.visible, (newVal) => {
......@@ -135,7 +139,7 @@ const generateQrcodeFunc = async () => {
const result = await generateQRCode({
scene: `id=${props.recordId}`,
page: `pages/XingmaLabDetailPage/XingmaLabDetailPage`,
envVersion: 'develop'
envVersion: 'release'//trial develop
})
if (result && result.ok && result.data && result.data.qrCodeBase64) {
......@@ -221,9 +225,12 @@ const handleDownload = async () => {
// 保存初始状态
ctx.save()
// 海报尺寸(rpx 转 px)
const posterWidth = canvasWidth
const posterHeight = canvasHeight
// 缩放 canvas 上下文以适应高分辨率
ctx.scale(pixelRatio, pixelRatio)
// 海报尺寸(使用基础尺寸,因为已经缩放)
const posterWidth = baseCanvasWidth
const posterHeight = baseCanvasHeight
// 绘制背景
await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterBg.png`, 0, 0, posterWidth, posterHeight)
......@@ -265,7 +272,10 @@ const handleDownload = async () => {
ctx.setTextAlign('left')
const nicknameTextX = nicknameBgX + (props.avatar ? (48 + 20) / 750 * screenWidth : 30 / 750 * screenWidth)
const nicknameTextY = nicknameBgY + nicknameBgH / 2 + (28 / 750) * screenWidth / 3
ctx.fillText(nickname.value || '', nicknameTextX, nicknameTextY)
// 计算昵称文字最大宽度(昵称背景宽度 - 左边距 - 右边距)
const nicknameMaxWidth = nicknameBgW - (props.avatar ? (48 + 20) / 750 * screenWidth : 30 / 750 * screenWidth) - (30 / 750 * screenWidth)
const truncatedNickname = truncateText(ctx, nickname.value || '', nicknameMaxWidth)
ctx.fillText(truncatedNickname, nicknameTextX, nicknameTextY)
// 绘制图片区域背景(居中)
const imageBgW = (494 / 750) * screenWidth
......@@ -275,13 +285,13 @@ const handleDownload = async () => {
await drawImage(ctx, `${$baseUrl}homepage/Q3Res/xingmaLabPosterImgBg.png`, imageBgX, imageBgY, imageBgW, imageBgH)
// 绘制主图片
// 绘制主图片(自适应模式,保持宽高比)
if (props.imageUrl) {
const imageX = imageBgX + (26 / 750) * screenWidth
const imageY = imageBgY + (26 / 750) * screenWidth
const imageW = imageBgW - (52 / 750) * screenWidth
const imageH = (586 / 750) * screenWidth
await drawImage(ctx, props.imageUrl, imageX, imageY, imageW, imageH)
await drawImage(ctx, props.imageUrl, imageX, imageY, imageW, imageH, true)
}
// 绘制文字内容
......@@ -290,7 +300,10 @@ const handleDownload = async () => {
ctx.setTextAlign('left')
const contentX = imageBgX + (22 / 750) * screenWidth
const contentY = imageBgY + imageBgH - (146 / 750) * screenWidth
ctx.fillText(props.content || '', contentX, contentY)
// 计算内容文字最大宽度(图片区域宽度 - 左右边距)
const contentMaxWidth = imageBgW - (44 / 750) * screenWidth
const truncatedContent = truncateText(ctx, props.content || '', contentMaxWidth)
ctx.fillText(truncatedContent, contentX, contentY)
// 绘制唯一藏品编号
ctx.setFillStyle('#D3A458')
......@@ -329,13 +342,13 @@ const handleDownload = async () => {
ctx.draw(false, async () => {
await nextTick()
// 转换为临时文件
// 转换为临时文件(使用高分辨率)
uni.canvasToTempFilePath({
canvasId: 'posterCanvas',
width: posterWidth,
height: posterHeight,
destWidth: posterWidth,
destHeight: posterHeight,
width: canvasWidth,
height: canvasHeight,
destWidth: canvasWidth,
destHeight: canvasHeight,
success: (res) => {
// 保存到相册
uni.saveImageToPhotosAlbum({
......@@ -344,7 +357,7 @@ const handleDownload = async () => {
uni.hideLoading()
uni.showToast({
title: '保存成功',
icon: 'success'
icon: 'none'
})
isDownloading.value = false
},
......@@ -455,9 +468,163 @@ const base64ToTempFilePath = (base64Data) => {
})
}
// 截断文字(canvas 中文字超出宽度时添加省略号)
const truncateText = (ctx, text, maxWidth) => {
if (!text) return ''
// 测量文字宽度
const textWidth = ctx.measureText(text).width
// 如果文字宽度小于等于最大宽度,直接返回
if (textWidth <= maxWidth) {
return text
}
// 如果超出,需要截断并添加省略号
const ellipsis = '...'
const ellipsisWidth = ctx.measureText(ellipsis).width
const availableWidth = maxWidth - ellipsisWidth
// 二分查找合适的截断位置
let left = 0
let right = text.length
let mid = 0
while (left < right) {
mid = Math.floor((left + right) / 2)
const testText = text.substring(0, mid)
const testWidth = ctx.measureText(testText).width
if (testWidth <= availableWidth) {
left = mid + 1
} else {
right = mid
}
}
// 确保至少保留一个字符
const truncatePos = Math.max(1, left - 1)
return text.substring(0, truncatePos) + ellipsis
}
// 获取图片信息(宽高)
const getImageInfo = (src) => {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: (res) => {
resolve({
width: res.width,
height: res.height
})
},
fail: (err) => {
reject(err)
}
})
})
}
// 计算自适应尺寸(保持宽高比,类似 aspectFit)
const calculateFitSize = (imageWidth, imageHeight, containerWidth, containerHeight) => {
const imageRatio = imageWidth / imageHeight
const containerRatio = containerWidth / containerHeight
let drawWidth, drawHeight, drawX, drawY
if (imageRatio > containerRatio) {
// 图片更宽,以宽度为准
drawWidth = containerWidth
drawHeight = containerWidth / imageRatio
drawX = 0
drawY = (containerHeight - drawHeight) / 2
} else {
// 图片更高,以高度为准
drawWidth = containerHeight * imageRatio
drawHeight = containerHeight
drawX = (containerWidth - drawWidth) / 2
drawY = 0
}
return { drawWidth, drawHeight, drawX, drawY }
}
// 绘制图片到 canvas(异步加载图片)
const drawImage = (ctx, src, x, y, w, h) => {
const drawImage = (ctx, src, x, y, w, h, fitMode = false) => {
return new Promise((resolve) => {
// 如果是自适应模式,需要先获取图片尺寸
if (fitMode) {
// 先获取图片路径(Base64 需要转换)
if (src.startsWith('data:image')) {
// Base64 图片:先转换为临时文件,再获取信息
base64ToTempFilePath(src).then((filePath) => {
// 获取图片信息
return getImageInfo(filePath).then((info) => {
const { drawWidth, drawHeight, drawX, drawY } = calculateFitSize(info.width, info.height, w, h)
ctx.drawImage(filePath, x + drawX, y + drawY, drawWidth, drawHeight)
resolve()
}).catch(() => {
// 如果获取图片信息失败,使用原始尺寸
ctx.drawImage(filePath, x, y, w, h)
resolve()
})
}).catch((err) => {
console.error('Base64 图片处理失败:', err)
// 如果转换失败,尝试直接使用(某些平台可能支持)
try {
ctx.drawImage(src, x, y, w, h)
resolve()
} catch (e) {
console.warn('无法绘制 Base64 图片,跳过')
resolve() // 即使失败也继续
}
})
return
} else {
// 普通图片 URL
getImageInfo(src).then((info) => {
const { drawWidth, drawHeight, drawX, drawY } = calculateFitSize(info.width, info.height, w, h)
// 先下载图片
uni.downloadFile({
url: src,
success: (res) => {
if (res.statusCode === 200) {
ctx.drawImage(res.tempFilePath, x + drawX, y + drawY, drawWidth, drawHeight)
resolve()
} else {
console.warn('图片下载失败:', src, 'statusCode:', res.statusCode)
resolve() // 即使失败也继续
}
},
fail: (err) => {
console.warn('图片下载失败:', src, err)
resolve() // 即使失败也继续
}
})
}).catch(() => {
// 如果获取图片信息失败,使用原始逻辑
uni.downloadFile({
url: src,
success: (res) => {
if (res.statusCode === 200) {
ctx.drawImage(res.tempFilePath, x, y, w, h)
resolve()
} else {
console.warn('图片下载失败:', src, 'statusCode:', res.statusCode)
resolve() // 即使失败也继续
}
},
fail: (err) => {
console.warn('图片下载失败:', src, err)
resolve() // 即使失败也继续
}
})
})
return
}
}
// 非自适应模式,使用原始逻辑
// 如果是 Base64 Data URI,先转换为临时文件
if (src.startsWith('data:image')) {
base64ToTempFilePath(src).then((filePath) => {
......@@ -569,16 +736,13 @@ const drawImage = (ctx, src, x, y, w, h) => {
position: absolute;
left: 0;
top: 0;
width: 256rpx;
width: 186rpx;
height: 58rpx;
padding: 0 30rpx;
padding: 5rpx 0 0 70rpx;
font-size: 28rpx;
color: #333333;
color: #000000;
text-align: center;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
......@@ -618,7 +782,8 @@ const drawImage = (ctx, src, x, y, w, h) => {
position: absolute;
bottom: 146rpx;
left: 22rpx;
width: calc(100% - 44rpx);
width: 450rpx;
height: 40rpx;
z-index: 2;
}
......@@ -626,9 +791,12 @@ const drawImage = (ctx, src, x, y, w, h) => {
font-size: 28rpx;
font-weight: 500;
color: #000000;
width: 450rpx;
height: 40rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: absolute;
}
.poster-collection-number {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment