Files
spring-festival-greetings/pages/wallpaper/index.vue
2026-01-26 18:37:32 +08:00

351 lines
7.7 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>
<view class="action-btn share" @tap.stop="shareWallpaper(item)">
<text class="icon"></text>
</view>
</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>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { getBavBarHeight } from "@/utils/system";
import { getWallpaperList, getWallpaperCategoryList } from "@/api/wallpaper.js";
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);
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 onRefresh = () => {
isRefreshing.value = true;
loadWallpapers(true);
};
const previewImage = (index) => {
const urls = wallpapers.value.map((item) => item.url);
uni.previewImage({
urls,
current: index,
});
};
const downloadWallpaper = (item) => {
uni.showLoading({ title: "下载中..." });
uni.downloadFile({
url: item.url,
success: (res) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading();
uni.showToast({ title: "保存成功", icon: "success" });
},
fail: (err) => {
uni.hideLoading();
console.error(err);
uni.showToast({ title: "保存失败", icon: "none" });
},
});
} else {
uni.hideLoading();
uni.showToast({ title: "下载失败", icon: "none" });
}
},
fail: () => {
uni.hideLoading();
uni.showToast({ title: "下载失败", icon: "none" });
},
});
};
const shareWallpaper = (item) => {
// uni.share is for App, specific provider.
// For general sharing, we might just preview it or use button open-type="share" if it was a button component.
// Since this is a custom UI, we can guide user to preview and long press, or just preview.
// Or if on MP-Weixin, show share menu.
uni.previewImage({
urls: [item.url],
});
// uni.showToast({ title: '长按图片发送给朋友', icon: 'none' });
};
</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>