Files
spring-festival-greetings/pages/wallpaper/index.vue
2026-01-28 21:13:27 +08:00

386 lines
8.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
class="wallpaper-page"
:style="{ paddingTop: getBavBarHeight() + 'px' }"
>
<!-- Custom Navbar -->
<view class="nav-bar">
<view class="back" @tap="goBack"></view>
<text class="nav-title">新春精美壁纸</text>
</view>
<!-- Category Tabs -->
<view class="category-tabs">
<scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
<view class="tabs-content">
<view
v-for="(item, index) in categories"
:key="index"
class="tab-item"
:class="{ active: currentCategoryId === item.id }"
@tap="switchCategory(item.id)"
>
{{ item.name }}
</view>
</view>
</scroll-view>
</view>
<!-- Wallpaper Grid -->
<scroll-view
scroll-y
class="wallpaper-scroll"
@scrolltolower="loadMore"
refresher-enabled
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresh"
>
<view class="grid-container">
<view
class="grid-item"
v-for="(item, index) in wallpapers"
:key="index"
>
<image
:src="item.imageUrl"
mode="aspectFill"
class="wallpaper-img"
@tap="previewImage(index)"
/>
<view class="action-overlay">
<view
class="action-btn download"
@tap.stop="downloadWallpaper(item)"
>
<text class="icon"></text>
</view>
<button
class="action-btn share"
open-type="share"
:data-item="item"
@tap.stop="shareWallpaper(item)"
>
<text class="icon"></text>
</button>
</view>
</view>
</view>
<!-- Loading State -->
<view class="loading-state" v-if="loading">
<text>加载中...</text>
</view>
<view class="empty-state" v-if="!loading && wallpapers.length === 0">
<text>暂无壁纸</text>
</view>
<view
class="no-more"
v-if="!loading && !hasMore && wallpapers.length > 0"
>
<text>没有更多了</text>
</view>
</scroll-view>
<LoginPopup ref="loginPopupRef" @logind="handleLogind" />
</view>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { getBavBarHeight } from "@/utils/system";
import { getWallpaperList, getWallpaperCategoryList } from "@/api/wallpaper.js";
import {
saveRemoteImageToLocal,
saveRecordRequest,
getShareToken,
} from "@/utils/common.js";
import { onShareAppMessage } from "@dcloudio/uni-app";
import { getShareReward, abilityCheck } from "@/api/system.js";
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
const loginPopupRef = ref(null);
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
const categories = ref([]);
const currentCategoryId = ref(null);
const wallpapers = ref([]);
const page = ref(1);
const loading = ref(false);
const hasMore = ref(true);
const isRefreshing = ref(false);
onShareAppMessage(async (options) => {
getShareReward({ scene: "wallpaper_download" });
if (options.from === "button") {
const shareTokenRes = await getShareToken(
"wallpaper_download",
options?.target?.dataset?.item?.id,
);
return {
title: "分享精美壁纸",
path: `/pages/wallpaper/detail?shareToken=${shareTokenRes.shareToken}`,
};
} else {
const shareTokenRes = await getShareToken("wallpaper_download", "");
return {
title: "新春祝福",
path: `/pages/index/index?shareToken=${shareTokenRes.shareToken}`,
};
}
});
onMounted(async () => {
await fetchCategories();
});
const goBack = () => {
uni.navigateBack();
};
const fetchCategories = async () => {
try {
const res = await getWallpaperCategoryList();
const list = Array.isArray(res) ? res : res?.list || [];
if (list.length > 0) {
categories.value = list;
currentCategoryId.value = list[0].id;
loadWallpapers(true);
}
} catch (e) {
console.error("Failed to fetch categories", e);
uni.showToast({ title: "获取分类失败", icon: "none" });
}
};
const switchCategory = (id) => {
if (currentCategoryId.value === id) return;
currentCategoryId.value = id;
loadWallpapers(true);
};
const loadWallpapers = async (reset = false) => {
if (loading.value) return;
if (reset) {
page.value = 1;
hasMore.value = true;
wallpapers.value = [];
}
if (!hasMore.value) return;
loading.value = true;
try {
const res = await getWallpaperList(currentCategoryId.value, page.value);
const list = res?.list || [];
hasMore.value = !!res?.hasNext;
if (reset) {
wallpapers.value = list;
} else {
wallpapers.value = [...wallpapers.value, ...list];
}
if (hasMore.value) {
page.value++;
}
} catch (e) {
console.error("Failed to fetch wallpapers", e);
uni.showToast({ title: "获取壁纸失败", icon: "none" });
} finally {
loading.value = false;
isRefreshing.value = false;
}
};
const loadMore = () => {
loadWallpapers();
};
const handleLogind = async () => {
// Logic after successful login if needed
};
const onRefresh = () => {
isRefreshing.value = true;
loadWallpapers(true);
};
const previewImage = (index) => {
const urls = wallpapers.value.map((item) => item.url);
uni.previewImage({
urls,
current: index,
});
};
const downloadWallpaper = async (item) => {
if (!isLoggedIn.value) {
loginPopupRef.value.open();
return;
}
const abilityRes = await abilityCheck("wallpaper_download");
if (!abilityRes.canUse) {
if (
abilityRes?.blockType === "need_share" &&
abilityRes?.message === "分享可继续"
) {
uni.showToast({
title: "分享给好友即可下载",
icon: "none",
});
return;
}
uni.showToast({
title: "您今日壁纸下载次数已用完,明日再试",
icon: "none",
});
return;
}
uni.showLoading({ title: "下载中..." });
await saveRemoteImageToLocal(item.imageUrl);
saveRecordRequest("", item.id, "wallpaper_download", item.imageUrl);
};
const shareWallpaper = (item) => {};
</script>
<style lang="scss" scoped>
.wallpaper-page {
height: 100vh;
background-color: #7a0909; /* Dark Red Background */
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.nav-bar {
display: flex;
align-items: center;
padding: 16rpx 24rpx;
/* background: #7A0909; */
position: sticky;
top: 0;
z-index: 100;
}
.back {
font-size: 50rpx;
margin-right: 24rpx;
line-height: 1;
color: #ffd700; /* Gold */
}
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #ffd700; /* Gold */
}
.category-tabs {
padding: 20rpx 0;
/* background-color: #7A0909; */
}
.tabs-scroll {
white-space: nowrap;
width: 100%;
}
.tabs-content {
display: inline-flex;
padding: 0 24rpx;
gap: 20rpx;
}
.tab-item {
padding: 12rpx 32rpx;
border-radius: 999rpx;
font-size: 28rpx;
color: #ffd700;
background: rgba(0, 0, 0, 0.3);
border: 2rpx solid transparent;
transition: all 0.3s;
}
.tab-item.active {
background: linear-gradient(90deg, #ff3b30 0%, #ff9500 100%);
color: #fff;
border-color: #ffd700;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
}
.wallpaper-scroll {
flex: 1;
overflow: hidden;
/* padding: 24rpx; */
box-sizing: border-box;
}
.grid-container {
display: flex;
flex-wrap: wrap;
padding: 24rpx;
justify-content: space-between;
}
.grid-item {
width: 340rpx;
height: 600rpx;
border-radius: 24rpx;
overflow: hidden;
margin-bottom: 24rpx;
position: relative;
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.3);
background: #333;
}
.wallpaper-img {
width: 100%;
height: 100%;
}
.action-overlay {
position: absolute;
bottom: 20rpx;
right: 20rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.action-btn {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
border: 1rpx solid rgba(255, 255, 255, 0.3);
}
.action-btn .icon {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.action-btn.share .icon {
font-size: 28rpx;
}
.loading-state,
.empty-state,
.no-more {
text-align: center;
padding: 40rpx;
color: rgba(255, 255, 255, 0.6);
font-size: 24rpx;
}
</style>