fix: lock resousers

This commit is contained in:
zzc
2026-03-12 10:56:03 +08:00
parent f653e6659a
commit ff8d9836d5
6 changed files with 211 additions and 23 deletions

View File

@@ -3,7 +3,7 @@
{ {
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "新春祝福", "navigationBarTitleText": "祝福 壁纸 头像",
"enablePullDownRefresh": true, "enablePullDownRefresh": true,
"navigationStyle": "custom", "navigationStyle": "custom",
"backgroundColor": "#FFFFFF" "backgroundColor": "#FFFFFF"
@@ -36,7 +36,7 @@
{ {
"path": "pages/mine/greeting", "path": "pages/mine/greeting",
"style": { "style": {
"navigationBarTitleText": "我的新春祝福", "navigationBarTitleText": "我的祝福",
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh": true "enablePullDownRefresh": true
} }
@@ -44,7 +44,7 @@
{ {
"path": "pages/mine/wallpaper", "path": "pages/mine/wallpaper",
"style": { "style": {
"navigationBarTitleText": "我的新春壁纸", "navigationBarTitleText": "我的壁纸",
"navigationStyle": "custom", "navigationStyle": "custom",
"enablePullDownRefresh": true "enablePullDownRefresh": true
} }
@@ -92,7 +92,7 @@
{ {
"path": "pages/avatar/index", "path": "pages/avatar/index",
"style": { "style": {
"navigationBarTitleText": "新春头像挂饰", "navigationBarTitleText": "头像挂饰",
"enablePullDownRefresh": false, "enablePullDownRefresh": false,
"navigationStyle": "custom" "navigationStyle": "custom"
} }
@@ -124,7 +124,7 @@
{ {
"path": "pages/detail/index", "path": "pages/detail/index",
"style": { "style": {
"navigationBarTitleText": "新春祝福详情", "navigationBarTitleText": "祝福详情",
"enablePullDownRefresh": false, "enablePullDownRefresh": false,
"navigationStyle": "custom" "navigationStyle": "custom"
} }

View File

@@ -17,7 +17,7 @@
}}</text> }}</text>
<view class="tag">马年专属</view> <view class="tag">马年专属</view>
</view> </view>
<text class="action-text">换上了新春头像</text> <text class="action-text">换上了专属头像</text>
</view> </view>
</view> </view>
@@ -53,7 +53,7 @@
<view class="section-header"> <view class="section-header">
<view class="left"> <view class="left">
<view class="bar"></view> <view class="bar"></view>
<text class="title">热门新春头像</text> <text class="title">热门头像</text>
</view> </view>
<text class="more" @tap="goToMake">查看全部</text> <text class="more" @tap="goToMake">查看全部</text>
</view> </view>
@@ -69,10 +69,10 @@
</view> </view>
<text class="frame-name">{{ <text class="frame-name">{{
item.type === "decor" item.type === "decor"
? "新春饰品" ? "精美饰品"
: item.type === "avatar" : item.type === "avatar"
? "新春头像" ? "爆款头像"
: "新春相框" : "热门相框"
}}</text> }}</text>
</view> </view>
</view> </view>
@@ -114,10 +114,10 @@
<view class="page-footer"> <view class="page-footer">
<view class="footer-line"> <view class="footer-line">
<text class="line"></text> <text class="line"></text>
<text class="text">2026 HAPPY NEW YEAR</text> <text class="text">LUCKY EVERY DAY</text>
<text class="line"></text> <text class="line"></text>
</view> </view>
<text class="footer-sub">新春祝福 · 传递温情</text> <text class="footer-sub">专属头像</text>
</view> </view>
</view> </view>
</view> </view>

View File

@@ -156,7 +156,7 @@ onShareAppMessage(async (options) => {
options?.target?.dataset?.item?.id, options?.target?.dataset?.item?.id,
); );
return { return {
title: "快来挑选喜欢的新春头像吧", title: "快来挑选喜欢的专属头像吧",
path: `/pages/avatar/download?shareToken=${shareToken}`, path: `/pages/avatar/download?shareToken=${shareToken}`,
}; };
} else { } else {

View File

@@ -131,6 +131,18 @@
> >
<image :src="item.imageUrl" class="grid-img" mode="aspectFill" /> <image :src="item.imageUrl" class="grid-img" mode="aspectFill" />
<view v-if="selectedFrame?.id === item.id" class="check"></view> <view v-if="selectedFrame?.id === item.id" class="check"></view>
<!-- Lock Overlay -->
<view v-if="!item.isUnlock && item.unlockType" class="lock-overlay">
<!-- Badge -->
<view class="unlock-badge" :class="item.unlockType">
{{ getUnlockLabel(item.unlockType) }}
</view>
<!-- Center Lock -->
<view class="center-lock">
<uni-icons type="locked-filled" size="18" color="#fff" />
</view>
</view>
</view> </view>
</view> </view>
<view v-if="frameLoading" class="loading-more">加载中...</view> <view v-if="frameLoading" class="loading-more">加载中...</view>
@@ -150,6 +162,18 @@
> >
<image :src="item.imageUrl" class="grid-img" mode="aspectFill" /> <image :src="item.imageUrl" class="grid-img" mode="aspectFill" />
<view v-if="selectedDecor?.id === item.id" class="check"></view> <view v-if="selectedDecor?.id === item.id" class="check"></view>
<!-- Lock Overlay -->
<view v-if="!item.isUnlock && item.unlockType" class="lock-overlay">
<!-- Badge -->
<view class="unlock-badge" :class="item.unlockType">
{{ getUnlockLabel(item.unlockType) }}
</view>
<!-- Center Lock -->
<view class="center-lock">
<uni-icons type="locked-filled" size="18" color="#fff" />
</view>
</view>
</view> </view>
</view> </view>
<view v-if="decorLoading" class="loading-more">加载中...</view> <view v-if="decorLoading" class="loading-more">加载中...</view>
@@ -171,6 +195,7 @@
@logind="handleLogind" @logind="handleLogind"
:share-token="shareToken" :share-token="shareToken"
/> />
<RewardAd ref="rewardAdRef" @onReward="handleAdReward" />
<!-- More Avatar Popup --> <!-- More Avatar Popup -->
<!-- <uni-popup ref="morePopup" type="bottom" background-color="#fff"> <!-- <uni-popup ref="morePopup" type="bottom" background-color="#fff">
@@ -213,14 +238,14 @@ import {
onReachBottom, onReachBottom,
} from "@dcloudio/uni-app"; } from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import { checkAbilityAndHandle } from "@/utils/ability.js"; import { checkAbilityAndHandle, getUnlockLabel } from "@/utils/ability.js";
import { import {
getAvatarSystemList, getAvatarSystemList,
getAvatarFrameList, getAvatarFrameList,
getAvatarDecorList, getAvatarDecorList,
avatarCreateComplete, avatarCreateComplete,
} from "@/api/avatar.js"; } from "@/api/avatar.js";
import { getShareReward } from "@/api/system.js"; import { getShareReward, watchAdReward } from "@/api/system.js";
import { import {
saveRecordRequest, saveRecordRequest,
getShareToken, getShareToken,
@@ -231,9 +256,12 @@ import {
import { trackRecord } from "@/utils/common.js"; import { trackRecord } from "@/utils/common.js";
import NavBar from "@/components/NavBar/NavBar.vue"; import NavBar from "@/components/NavBar/NavBar.vue";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue"; import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
import RewardAd from "@/components/RewardAd/RewardAd.vue";
const userStore = useUserStore(); const userStore = useUserStore();
const loginPopupRef = ref(null); const loginPopupRef = ref(null);
const rewardAdRef = ref(null);
const currentUnlockItem = ref(null);
const isLoggedIn = computed(() => !!userStore.userInfo.nickName); const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
const userPoints = computed(() => userStore.userInfo.points || 0); const userPoints = computed(() => userStore.userInfo.points || 0);
@@ -316,6 +344,7 @@ const loadFrames = async () => {
if (list.length > 0) { if (list.length > 0) {
frames.value.push( frames.value.push(
...list.map((item) => ({ ...list.map((item) => ({
...item,
id: item.id, id: item.id,
imageUrl: item.imageUrl, imageUrl: item.imageUrl,
})), })),
@@ -343,6 +372,7 @@ const loadDecors = async () => {
if (list.length > 0) { if (list.length > 0) {
decors.value.push( decors.value.push(
...list.map((item) => ({ ...list.map((item) => ({
...item,
id: item.id, id: item.id,
imageUrl: item.imageUrl, imageUrl: item.imageUrl,
})), })),
@@ -423,6 +453,10 @@ const toggleAvatar = (avatar) => {
}; };
const toggleFrame = (frame) => { const toggleFrame = (frame) => {
if (frame.unlockType && !frame.isUnlock) {
handleUnlock(frame);
return;
}
trackRecord({ trackRecord({
eventName: "avatar_frame_click", eventName: "avatar_frame_click",
eventType: `select`, eventType: `select`,
@@ -436,6 +470,10 @@ const toggleFrame = (frame) => {
}; };
const toggleDecor = (decor) => { const toggleDecor = (decor) => {
if (decor.unlockType && !decor.isUnlock) {
handleUnlock(decor);
return;
}
trackRecord({ trackRecord({
eventName: "avatar_decor_click", eventName: "avatar_decor_click",
eventType: `select`, eventType: `select`,
@@ -448,6 +486,93 @@ const toggleDecor = (decor) => {
} }
}; };
const handleUnlock = (item) => {
switch (item.unlockType) {
case "vip":
uni.navigateTo({
url: "/pages/mine/vip",
});
break;
case "ad":
currentUnlockItem.value = item;
rewardAdRef.value.show();
break;
case "sing1":
uni.showToast({
title: "需要连续签到1天解锁",
icon: "none",
});
break;
case "sing3":
uni.showToast({
title: "需要连续签到3天解锁",
icon: "none",
});
break;
case "sing5":
uni.showToast({
title: "需要连续签到5天解锁",
icon: "none",
});
break;
case "sing7":
uni.showToast({
title: "需要连续签到7天解锁",
icon: "none",
});
break;
default:
uni.showToast({
title: "未满足解锁条件",
icon: "none",
});
}
};
const handleAdReward = async (token) => {
try {
const res = await watchAdReward(
token,
"unlock",
activeTool.value === "frame" ? "avatar_frame" : "avatar_decor",
currentUnlockItem.value.id,
);
if (res) {
uni.showToast({
title: "解锁成功",
icon: "success",
});
// 解锁成功后,更新本地状态,允许使用
if (currentUnlockItem.value) {
currentUnlockItem.value.isUnlock = true;
if (activeTool.value === "frame") {
const index = frames.value.findIndex(
(t) => t.id === currentUnlockItem.value.id,
);
if (index !== -1) {
frames.value[index].isUnlock = true;
}
toggleFrame(currentUnlockItem.value);
} else if (activeTool.value === "decor") {
const index = decors.value.findIndex(
(t) => t.id === currentUnlockItem.value.id,
);
if (index !== -1) {
decors.value[index].isUnlock = true;
}
toggleDecor(currentUnlockItem.value);
}
currentUnlockItem.value = null;
}
}
} catch (e) {
console.error("Reward claim failed", e);
uni.showToast({ title: "奖励发放失败", icon: "none" });
}
};
// 挂饰状态 // 挂饰状态
const decorState = ref({ const decorState = ref({
x: 300, // 初始中心 X (rpx) x: 300, // 初始中心 X (rpx)
@@ -775,7 +900,7 @@ onShareAppMessage(async (options) => {
getShareReward({ scene: "avatar_download" }); getShareReward({ scene: "avatar_download" });
uni.hideLoading(); uni.hideLoading();
return { return {
title: "3 秒生成新春专属头像,真的好看😆", title: "3 秒生成专属头像,真的好看😆",
path: `/pages/avatar/detail?shareToken=${shareToken}`, path: `/pages/avatar/detail?shareToken=${shareToken}`,
imageUrl: imageUrl:
imageUrl + imageUrl +
@@ -785,7 +910,7 @@ onShareAppMessage(async (options) => {
const shareToken = await getShareToken("avatar_download_not_login", ""); const shareToken = await getShareToken("avatar_download_not_login", "");
getShareReward({ scene: "avatar_index" }); getShareReward({ scene: "avatar_index" });
return { return {
title: "3 秒生成新春专属头像,真的好看😆", title: "3 秒生成专属头像,真的好看😆",
path: `/pages/avatar/index?shareToken=${shareToken}`, path: `/pages/avatar/index?shareToken=${shareToken}`,
imageUrl: imageUrl:
"https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png", "https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
@@ -1148,6 +1273,69 @@ onShareTimeline(async () => {
font-size: 20rpx; font-size: 20rpx;
padding: 4rpx 8rpx; padding: 4rpx 8rpx;
border-radius: 0 0 0 16rpx; border-radius: 0 0 0 16rpx;
z-index: 2;
}
/* 锁定遮罩样式 */
.lock-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none; /* 让点击事件穿透到 grid-item */
}
.unlock-badge {
position: absolute;
top: 0;
right: 0;
background: linear-gradient(
135deg,
#ff9a9e 0%,
#fecfef 99%,
#fecfef 100%
);
color: #fff;
font-size: 16rpx;
padding: 4rpx 10rpx;
border-radius: 0 0 0 12rpx;
font-weight: bold;
z-index: 20;
&.vip {
background: linear-gradient(135deg, #ffd700 0%, #ffa500 100%);
}
&.ad {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
&.sing1,
&.sing3,
&.sing5,
&.sing7 {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
}
.center-lock {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 48rpx;
height: 48rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(4px);
} }
&.upload-card { &.upload-card {

View File

@@ -16,7 +16,7 @@
/> />
<view class="user-meta"> <view class="user-meta">
<view class="user-name">{{ cardDetail?.blessingFrom }}</view> <view class="user-name">{{ cardDetail?.blessingFrom }}</view>
<view class="user-msg">给你发来了一条新春祝福</view> <view class="user-msg">给你发来了一条专属祝福</view>
<view class="year-tag"> <view class="year-tag">
<text class="fire-icon">🎉</text> {{ cardDetail?.year || 2026 }} <text class="fire-icon">🎉</text> {{ cardDetail?.year || 2026 }}
{{ cardDetail?.festival || "丙午马年" }} {{ cardDetail?.festival || "丙午马年" }}
@@ -113,10 +113,10 @@
<view class="page-footer"> <view class="page-footer">
<view class="footer-line"> <view class="footer-line">
<text class="line"></text> <text class="line"></text>
<text class="text">2026 HAPPY NEW YEAR</text> <text class="text">HAPPY EVERY DAY</text>
<text class="line"></text> <text class="line"></text>
</view> </view>
<view class="footer-sub">新春祝福 · 传递温情</view> <view class="footer-sub">专属祝福 · 传递温情</view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@@ -196,7 +196,7 @@ onShareAppMessage(async () => {
const token = await getShareToken("card_generate", cardDetail.value?.id); const token = await getShareToken("card_generate", cardDetail.value?.id);
getShareReward({ scene: "card_generate" }); getShareReward({ scene: "card_generate" });
return { return {
title: "送你一张精美的新春祝福卡片 🎊", title: "送你一张精美的专属祝福卡片 🎊",
path: `/pages/detail/index?shareToken=${token || ""}`, path: `/pages/detail/index?shareToken=${token || ""}`,
imageUrl: imageUrl:
cardDetail.value?.imageUrl || cardDetail.value?.imageUrl ||
@@ -207,7 +207,7 @@ onShareAppMessage(async () => {
onShareTimeline(async () => { onShareTimeline(async () => {
const token = await getShareToken("card_generate", cardDetail.value?.id); const token = await getShareToken("card_generate", cardDetail.value?.id);
return { return {
title: "送你一张精美的新春祝福卡片 🎊", title: "送你一张精美的专属祝福卡片 🎊",
query: `shareToken=${token}`, query: `shareToken=${token}`,
imageUrl: imageUrl:
cardDetail.value?.imageUrl || cardDetail.value?.imageUrl ||

View File

@@ -195,7 +195,7 @@ onShareAppMessage(async () => {
onShareTimeline(async () => { onShareTimeline(async () => {
const shareToken = await getShareToken("fortune_timeline"); const shareToken = await getShareToken("fortune_timeline");
return { return {
title: "新春到,抽灵签!快来测测你的年运势 🏮", title: "好运到,抽灵签!快来测测你的年运势 🏮",
query: `shareToken=${shareToken}`, query: `shareToken=${shareToken}`,
imageUrl: imageUrl:
"https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png", "https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",