optimize: avatar page share reward
This commit is contained in:
@@ -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 = () => {
|
||||
<style lang="scss" scoped>
|
||||
.avatar-detail-page {
|
||||
min-height: 100vh;
|
||||
background: #fff0f5; /* Light Pink Background */
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -245,7 +246,7 @@ const goToWallpaper = () => {
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
box-sizing: border-box;
|
||||
background: #fff0f5;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
@@ -271,9 +272,6 @@ const goToWallpaper = () => {
|
||||
margin-right: 50rpx; /* Balance back button */
|
||||
}
|
||||
|
||||
.content-scroll {
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
padding: 30rpx 40rpx 60rpx;
|
||||
}
|
||||
@@ -326,16 +324,17 @@ const goToWallpaper = () => {
|
||||
|
||||
/* Main Card */
|
||||
.main-card {
|
||||
background: linear-gradient(180deg, #ffffff 0%, #fff5f5 100%);
|
||||
background: #ffffff;
|
||||
border-radius: 40rpx;
|
||||
padding: 24rpx;
|
||||
box-shadow: 0 20rpx 60rpx rgba(255, 59, 48, 0.15);
|
||||
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.06);
|
||||
margin-bottom: 60rpx;
|
||||
border: 2rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.card-inner {
|
||||
position: relative;
|
||||
background: #fffaf0;
|
||||
background: #fff9f9;
|
||||
border-radius: 30rpx;
|
||||
padding: 60rpx;
|
||||
display: flex;
|
||||
@@ -348,7 +347,7 @@ const goToWallpaper = () => {
|
||||
height: 400rpx;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
|
||||
border: 8rpx solid #d63333;
|
||||
border: 8rpx solid #ff3b30;
|
||||
}
|
||||
|
||||
.loading-box {
|
||||
@@ -358,7 +357,7 @@ const goToWallpaper = () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
background: #eee;
|
||||
background: #f0f0f0;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
@@ -381,14 +380,14 @@ const goToWallpaper = () => {
|
||||
.card-footer-text {
|
||||
margin-top: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #d63333;
|
||||
color: #ff3b30;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
padding: 10rpx 30rpx;
|
||||
border-radius: 99rpx;
|
||||
box-shadow: 0 4rpx 10rpx rgba(214, 51, 51, 0.1);
|
||||
box-shadow: 0 4rpx 10rpx rgba(255, 59, 48, 0.1);
|
||||
}
|
||||
|
||||
.card-footer-text .icon {
|
||||
@@ -423,8 +422,8 @@ const goToWallpaper = () => {
|
||||
}
|
||||
|
||||
.secondary-btn {
|
||||
background: #eaeaea;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn .icon {
|
||||
@@ -478,10 +477,9 @@ const goToWallpaper = () => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
background: #f8f8f8;
|
||||
border-radius: 24rpx;
|
||||
padding: 20rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.frame-img-box {
|
||||
@@ -490,7 +488,7 @@ const goToWallpaper = () => {
|
||||
margin-bottom: 16rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: #f9f9f9;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.frame-img {
|
||||
@@ -506,19 +504,18 @@ const goToWallpaper = () => {
|
||||
|
||||
/* Wallpaper Banner */
|
||||
.wallpaper-banner {
|
||||
background: #fff;
|
||||
background: #f8f8f8;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 60rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: #fff0f5;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -569,18 +566,18 @@ const goToWallpaper = () => {
|
||||
.footer-line .line {
|
||||
width: 60rpx;
|
||||
height: 2rpx;
|
||||
background: #ccc;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.footer-line .text {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
color: #ccc;
|
||||
margin: 0 20rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.footer-sub {
|
||||
font-size: 20rpx;
|
||||
color: #ccc;
|
||||
color: #ddd;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
</view>
|
||||
|
||||
<canvas
|
||||
type="2d"
|
||||
id="avatarCanvas"
|
||||
canvas-id="avatarCanvas"
|
||||
class="hidden-canvas"
|
||||
style="width: 600px; height: 600px"
|
||||
@@ -162,7 +164,13 @@ import {
|
||||
getAvatarSystemList,
|
||||
getAvatarFrameList,
|
||||
getAvatarDecorList,
|
||||
avatarCreateComplete,
|
||||
} from "@/api/avatar.js";
|
||||
import {
|
||||
saveRecordRequest,
|
||||
getShareToken,
|
||||
generateObjectId,
|
||||
} from "@/utils/common.js";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const loginPopupRef = ref(null);
|
||||
@@ -313,6 +321,12 @@ const loadMoreAvatars = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
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: "制作我的新春头像",
|
||||
|
||||
Reference in New Issue
Block a user