Files
spring-festival-greetings/pages/mine/wallpaper.vue
2026-01-28 23:08:34 +08:00

317 lines
6.5 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: navBarTop + 'px' }">
<!-- Navbar -->
<view
class="nav-bar"
:style="{ height: navBarHeight + 'px', paddingTop: navBarTop + 'px' }"
>
<view class="nav-content">
<view class="back-btn" @tap="goBack">
<text class="back-arrow"></text>
</view>
<text class="title">我的新春壁纸</text>
</view>
</view>
<!-- Header Stats -->
<view class="header-stats">
<view class="stats-card">
<view class="stats-left">
<text class="label">我的收藏</text>
<view class="value-wrap">
<text class="prefix">已保存</text>
<text class="value">{{ totalCount }}</text>
<text class="suffix">张壁纸</text>
</view>
</view>
<view class="stats-right">
<view class="icon-circle">
<text>🖼</text>
</view>
</view>
</view>
</view>
<!-- List Section -->
<view class="list-section">
<view class="list-container">
<view v-for="item in list" :key="item.id" class="grid-item" @tap="onPreview(item)">
<image :src="item.imageUrl" mode="aspectFill" class="wallpaper-img" />
<view class="date-badge">
<text>{{ formatDate(item.createdAt) }}</text>
</view>
</view>
</view>
<!-- Loading State -->
<view class="loading-state" v-if="loading">
<text>加载中...</text>
</view>
<view class="empty-state" v-if="!loading && list.length === 0">
<text>暂无壁纸记录</text>
</view>
<view class="no-more" v-if="!loading && !hasMore && list.length > 0">
<text>没有更多了</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
import { getMyWallpaper } from "@/api/mine.js";
const navBarTop = ref(0);
const navBarHeight = ref(44);
const list = ref([]);
const page = ref(1);
const loading = ref(false);
const hasMore = ref(true);
const isRefreshing = ref(false);
const totalCount = ref(0);
onMounted(() => {
const sysInfo = uni.getSystemInfoSync();
navBarTop.value = sysInfo.statusBarHeight;
fetchList(true);
});
onPullDownRefresh(() => {
onRefresh();
});
onReachBottom(() => {
loadMore();
});
const fetchList = async (reset = false) => {
if (loading.value) return;
if (reset) {
page.value = 1;
hasMore.value = true;
}
if (!hasMore.value) return;
loading.value = true;
try {
const res = await getMyWallpaper(page.value);
const dataList = res?.list || [];
totalCount.value = res?.totalCount || 0;
if (reset) {
list.value = dataList;
} else {
list.value = [...list.value, ...dataList];
}
hasMore.value = res.hasNext;
if (hasMore.value) {
page.value++;
}
} catch (e) {
console.error("Failed to fetch wallpaper list", e);
uni.showToast({ title: "加载失败", icon: "none" });
} finally {
loading.value = false;
isRefreshing.value = false;
uni.stopPullDownRefresh();
}
};
const loadMore = () => {
fetchList();
};
const onRefresh = () => {
isRefreshing.value = true;
fetchList(true);
};
const goBack = () => {
uni.navigateBack();
};
const formatDate = (dateStr) => {
if (!dateStr) return "";
const date = new Date(dateStr);
const m = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
return `${m}-${d}`;
};
const onPreview = (item) => {
uni.previewImage({
urls: [item.imageUrl],
current: item.imageUrl
});
};
</script>
<style lang="scss" scoped>
.wallpaper-page {
min-height: 100vh;
background: #f9f9f9;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.nav-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
background-color: #f9f9f9;
.nav-content {
height: 44px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.back-btn {
position: absolute;
left: 16px;
height: 100%;
display: flex;
align-items: center;
padding: 0 10px;
.back-arrow {
font-size: 32px;
color: #333;
font-weight: 300;
}
}
.title {
font-size: 18px;
font-weight: 600;
color: #333;
}
}
}
.header-stats {
padding: 20px;
background: #f9f9f9;
margin-top: 44px; // Initial offset for fixed nav
.stats-card {
background: #fff;
border-radius: 20px;
padding: 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
.stats-left {
display: flex;
flex-direction: column;
.label {
font-size: 12px;
color: #999;
margin-bottom: 8px;
}
.value-wrap {
display: flex;
align-items: baseline;
.prefix {
font-size: 16px;
color: #333;
font-weight: 600;
margin-right: 4px;
}
.value {
font-size: 24px;
font-weight: bold;
color: #ff3b30;
margin: 0 4px;
}
.suffix {
font-size: 14px;
color: #333;
font-weight: 600;
}
}
}
.stats-right {
.icon-circle {
width: 48px;
height: 48px;
border-radius: 16px;
background: #fff8e1;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 24px;
}
}
}
}
}
.list-section {
flex: 1;
padding: 0 16px;
.list-container {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
padding-bottom: 40px;
}
.grid-item {
position: relative;
border-radius: 16px;
overflow: hidden;
background: #fff;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
aspect-ratio: 9/16; // Standard wallpaper ratio
.wallpaper-img {
width: 100%;
height: 100%;
display: block;
}
.date-badge {
position: absolute;
bottom: 8px;
right: 8px;
background: rgba(0, 0, 0, 0.3);
padding: 2px 6px;
border-radius: 4px;
backdrop-filter: blur(4px);
text {
color: #fff;
font-size: 10px;
font-weight: 500;
}
}
}
}
.loading-state,
.empty-state,
.no-more {
text-align: center;
padding: 20px;
color: #999;
font-size: 12px;
}
</style>