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

View File

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

View File

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

View File

@@ -131,6 +131,18 @@
>
<image :src="item.imageUrl" class="grid-img" mode="aspectFill" />
<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 v-if="frameLoading" class="loading-more">加载中...</view>
@@ -150,6 +162,18 @@
>
<image :src="item.imageUrl" class="grid-img" mode="aspectFill" />
<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 v-if="decorLoading" class="loading-more">加载中...</view>
@@ -171,6 +195,7 @@
@logind="handleLogind"
:share-token="shareToken"
/>
<RewardAd ref="rewardAdRef" @onReward="handleAdReward" />
<!-- More Avatar Popup -->
<!-- <uni-popup ref="morePopup" type="bottom" background-color="#fff">
@@ -213,14 +238,14 @@ import {
onReachBottom,
} from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import { checkAbilityAndHandle } from "@/utils/ability.js";
import { checkAbilityAndHandle, getUnlockLabel } from "@/utils/ability.js";
import {
getAvatarSystemList,
getAvatarFrameList,
getAvatarDecorList,
avatarCreateComplete,
} from "@/api/avatar.js";
import { getShareReward } from "@/api/system.js";
import { getShareReward, watchAdReward } from "@/api/system.js";
import {
saveRecordRequest,
getShareToken,
@@ -231,9 +256,12 @@ import {
import { trackRecord } from "@/utils/common.js";
import NavBar from "@/components/NavBar/NavBar.vue";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
import RewardAd from "@/components/RewardAd/RewardAd.vue";
const userStore = useUserStore();
const loginPopupRef = ref(null);
const rewardAdRef = ref(null);
const currentUnlockItem = ref(null);
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
const userPoints = computed(() => userStore.userInfo.points || 0);
@@ -316,6 +344,7 @@ const loadFrames = async () => {
if (list.length > 0) {
frames.value.push(
...list.map((item) => ({
...item,
id: item.id,
imageUrl: item.imageUrl,
})),
@@ -343,6 +372,7 @@ const loadDecors = async () => {
if (list.length > 0) {
decors.value.push(
...list.map((item) => ({
...item,
id: item.id,
imageUrl: item.imageUrl,
})),
@@ -423,6 +453,10 @@ const toggleAvatar = (avatar) => {
};
const toggleFrame = (frame) => {
if (frame.unlockType && !frame.isUnlock) {
handleUnlock(frame);
return;
}
trackRecord({
eventName: "avatar_frame_click",
eventType: `select`,
@@ -436,6 +470,10 @@ const toggleFrame = (frame) => {
};
const toggleDecor = (decor) => {
if (decor.unlockType && !decor.isUnlock) {
handleUnlock(decor);
return;
}
trackRecord({
eventName: "avatar_decor_click",
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({
x: 300, // 初始中心 X (rpx)
@@ -775,7 +900,7 @@ onShareAppMessage(async (options) => {
getShareReward({ scene: "avatar_download" });
uni.hideLoading();
return {
title: "3 秒生成新春专属头像,真的好看😆",
title: "3 秒生成专属头像,真的好看😆",
path: `/pages/avatar/detail?shareToken=${shareToken}`,
imageUrl:
imageUrl +
@@ -785,7 +910,7 @@ onShareAppMessage(async (options) => {
const shareToken = await getShareToken("avatar_download_not_login", "");
getShareReward({ scene: "avatar_index" });
return {
title: "3 秒生成新春专属头像,真的好看😆",
title: "3 秒生成专属头像,真的好看😆",
path: `/pages/avatar/index?shareToken=${shareToken}`,
imageUrl:
"https://file.lihailezzc.com/resource/8dd026d76ef7a63d123b7fd698fb989b.png",
@@ -1148,6 +1273,69 @@ onShareTimeline(async () => {
font-size: 20rpx;
padding: 4rpx 8rpx;
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 {

View File

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

View File

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