From 2fa6584e0cfaf2e09de45659ee069b34e49216a9 Mon Sep 17 00:00:00 2001 From: zzc <1761997216@qq.com> Date: Wed, 28 Jan 2026 08:55:59 +0800 Subject: [PATCH] optimize: avatar page share reward --- api/avatar.js | 8 +++ pages/avatar/detail.vue | 45 ++++++------ pages/avatar/index.vue | 147 +++++++++++++++++++++++++++++++++++----- utils/common.js | 7 +- 4 files changed, 165 insertions(+), 42 deletions(-) diff --git a/api/avatar.js b/api/avatar.js index eb51b51..8667a86 100644 --- a/api/avatar.js +++ b/api/avatar.js @@ -21,6 +21,14 @@ export const getAvatarFrameList = async (page = 1) => { }); }; +export const avatarCreateComplete = async (data) => { + return request({ + url: "/api/blessing/avatar/create/complete", + method: "POST", + data, + }); +}; + export const avatarDownloadRecord = async (data) => { return request({ url: "/api/blessing/avatar/download", diff --git a/pages/avatar/detail.vue b/pages/avatar/detail.vue index d40e389..e0bad33 100644 --- a/pages/avatar/detail.vue +++ b/pages/avatar/detail.vue @@ -117,7 +117,8 @@ import { ref, onMounted } from "vue"; import { onLoad } from "@dcloudio/uni-app"; import { getBavBarHeight } from "@/utils/system"; -import { getAvatarFrameList, getPageDetail } from "@/api/avatar.js"; +import { getAvatarFrameList } from "@/api/avatar.js"; +import { getPageDetail } from "@/api/system.js"; const defaultAvatar = "https://file.lihailezzc.com/resource/d9b329082b32f8305101f708593a4882.png"; @@ -234,7 +235,7 @@ const goToWallpaper = () => { diff --git a/pages/avatar/index.vue b/pages/avatar/index.vue index cc343cc..7738884 100644 --- a/pages/avatar/index.vue +++ b/pages/avatar/index.vue @@ -107,6 +107,8 @@ { } }; +const createAvatarId = () => { + const id = generateObjectId(); + avatarCreateComplete({ id }); + return id; +}; + const selectMoreAvatar = (url) => { currentAvatar.value = url; closeMorePopup(); @@ -417,7 +431,109 @@ const goBack = () => { uni.navigateBack(); }; +const saveByCanvas = async (save = true) => { + return new Promise((resolve, reject) => { + const query = uni.createSelectorQuery(); + query + .select("#avatarCanvas") + .fields({ node: true, size: true }) + .exec(async (res) => { + if (!res[0] || !res[0].node) { + reject("Canvas not found"); + return; + } + + const canvas = res[0].node; + const ctx = canvas.getContext("2d"); + + const loadCanvasImage = (url) => { + return new Promise((resolve, reject) => { + const img = canvas.createImage(); + img.onload = () => resolve(img); + img.onerror = (e) => reject(e); + img.src = url; + }); + }; + + // 初始化画布尺寸 + const size = 600; + const avatarPath = await loadCanvasImage(currentAvatar.value); + ctx.clearRect(0, 0, size, size); + ctx.drawImage(avatarPath, 0, 0, size, size); + if (selectedFrame.value) { + const framePath = await loadCanvasImage(selectedFrame.value); + ctx.drawImage(framePath, 0, 0, size, size); + } + if (selectedDecor.value) { + const decorPath = await loadCanvasImage(selectedDecor.value); + ctx.save(); + // 映射 rpx 坐标到 Canvas 坐标 (假设 1rpx = 1 unit for 600x600 canvas logic) + // Canvas size is 600, Preview is 600rpx. Ratio is 1:1 in logical space. + ctx.translate(decorState.value.x, decorState.value.y); + ctx.rotate((decorState.value.rotate * Math.PI) / 180); + const scale = decorState.value.scale; + // 绘制图片,宽高 240 + ctx.drawImage( + decorPath, + -120 * scale, + -120 * scale, + 240 * scale, + 240 * scale, + ); + ctx.restore(); + } + uni.canvasToTempFilePath({ + canvas: canvas, // Canvas 2D 必须传 canvas 实例 + width: 600, + height: 600, + destWidth: 600, + destHeight: 600, + success: (res) => { + if (save) saveImage(res.tempFilePath); + resolve(res.tempFilePath); + }, + fail: (err) => reject(err), + }); + // ctx.draw(false, () => { + // uni.canvasToTempFilePath({ + // canvasId: "avatarCanvas", + // success: (res) => { + // uni.saveImageToPhotosAlbum({ + // filePath: res.tempFilePath, + // success: () => { + // uni.showToast({ title: "已保存到相册", icon: "success" }); + // }, + // }); + // }, + // }); + // }); + }); + }); +}; + +const saveImage = (path) => { + uni.saveImageToPhotosAlbum({ + filePath: path, + success() { + uni.showToast({ title: "已保存到相册" }); + }, + fail() { + uni.showModal({ + title: "提示", + content: "请授权保存到相册", + }); + }, + }); +}; + const saveAndUse = async () => { + saveByCanvas(); + console.log(111111); + return; + if (!isLoggedIn.value) { + loginPopupRef.value.open(); + return; + } const abilityRes = await abilityCheck("avatar_download"); if (!abilityRes.canUse) { if ( @@ -437,9 +553,10 @@ const saveAndUse = async () => { return; } // 调用avatarDownloadRecord API记录下载次数 - await avatarDownloadRecord({ - avatarUrl: currentAvatar.value, - }); + // await avatarDownloadRecord({ + // avatarUrl: currentAvatar.value, + // }); + // saveRecordRequest('', ) const ctx = uni.createCanvasContext("avatarCanvas"); const size = 600; const avatarPath = await loadImage(currentAvatar.value); @@ -487,13 +604,16 @@ const share = () => { }; onShareAppMessage(async () => { - const deviceInfo = getDeviceInfo(); - const shareTokenRes = await createShareToken({ - targetId: "", - scene: "avatar_download", - ...deviceInfo, - }); - getRewardByShare(); + getShareReward({ scene: "avatar_download" }); + if (!isLoggedIn.value) { + const shareTokenRes = await getShareToken("avatar_download_not_login", ""); + return { + title: "新春祝福", + path: `/pages/index/index?shareToken=${shareTokenRes.shareToken}`, + }; + } + const id = createAvatarId(); + const shareTokenRes = await getShareToken("avatar_download", id); return { title: "制作我的新春头像", @@ -503,13 +623,6 @@ onShareAppMessage(async () => { }; }); -const getRewardByShare = async () => { - const res = await getShareReward({ scene: "avatar_download" }); - if (res.success) { - uni.showToast({ title: "分享成功,可下载头像" }); - } -}; - // onShareTimeline(() => { // return { // title: "制作我的新春头像", diff --git a/utils/common.js b/utils/common.js index e2a0783..77ad4d2 100644 --- a/utils/common.js +++ b/utils/common.js @@ -1,5 +1,5 @@ import { getDeviceInfo } from "@/utils/system"; -import { saveRecord, viewRecord } from "@/api/system"; +import { saveRecord, viewRecord, createShareToken } from "@/api/system"; export const generateObjectId = ( m = Math, @@ -87,3 +87,8 @@ export const saveRemoteImageToLocal = (imageUrl) => { }, }); }; + +export const getShareToken = async (scene, targetId) => { + const deviceInfo = getDeviceInfo(); + return await createShareToken({ scene, targetId, ...deviceInfo }); +};