Files
spring-festival-greetings/pages/avatar/detail.vue
2026-01-28 10:55:39 +08:00

579 lines
11 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="avatar-detail-page" :style="{ paddingTop: navBarHeight + 'px' }">
<!-- Custom Navbar -->
<view
class="nav-bar"
:style="{
height: navBarHeight + 'px',
paddingTop: statusBarHeight + 'px',
}"
>
<view class="nav-content">
<view class="back" @tap="goBack"></view>
<text class="nav-title">新春头像详情</text>
</view>
</view>
<view class="content-wrap">
<!-- User Info -->
<view class="user-info-section" v-if="detailData">
<image
:src="detailData.from?.avatar || defaultAvatar"
class="user-avatar"
mode="aspectFill"
/>
<view class="user-text">
<view class="name-row">
<text class="nickname">{{
detailData.from?.nickname || "神秘用户"
}}</text>
<view class="tag">马年专属</view>
</view>
<text class="action-text">换上了新春头像</text>
</view>
</view>
<!-- Main Image Card -->
<view class="main-card">
<view class="card-inner">
<image
v-if="detailData?.imageUrl"
:src="detailData.imageUrl"
class="generated-avatar"
mode="aspectFill"
@tap="previewImage"
/>
<view class="loading-box" v-else>
<text>加载中...</text>
</view>
<!-- Decorative Elements -->
<view class="card-footer-text">
<text class="icon">🌸</text> 2026 丙午马年限定
</view>
</view>
</view>
<!-- Action Buttons -->
<view class="action-group">
<button class="btn primary-btn" @tap="goToMake">
<text class="icon">🎨</text> 我也要领同款制作
</button>
</view>
<!-- Recommended Frames -->
<view class="section recommended-section">
<view class="section-header">
<view class="left">
<view class="bar"></view>
<text class="title">热门新春头像框</text>
</view>
<text class="more" @tap="goToMake">查看全部</text>
</view>
<view class="frame-grid">
<view
class="frame-item"
v-for="(item, index) in frameList"
:key="index"
@tap="goToMake"
>
<view class="frame-img-box">
<image :src="item.url" class="frame-img" mode="aspectFit" />
</view>
<text class="frame-name">{{ item.name || "新春相框" }}</text>
</view>
</view>
</view>
<!-- Wallpaper Banner -->
<view class="wallpaper-banner" @tap="goToFortune">
<view class="banner-icon">
<text>🏮</text>
</view>
<view class="banner-content">
<text class="banner-title">去抽取新年运势</text>
<text class="banner-desc">每日一签开启你的新年好运</text>
</view>
<text class="banner-arrow"></text>
</view>
<view class="wallpaper-banner" @tap="goToGreeting">
<view class="banner-icon">
<text>🧧</text>
</view>
<view class="banner-content">
<text class="banner-title">去制作新年贺卡</text>
<text class="banner-desc">定制专属祝福传递浓浓年味</text>
</view>
<text class="banner-arrow"></text>
</view>
<view class="wallpaper-banner" @tap="goToWallpaper">
<view class="banner-icon">
<text>🖼</text>
</view>
<view class="banner-content">
<text class="banner-title">去挑选新年壁纸</text>
<text class="banner-desc">精选新年壁纸让手机也过年</text>
</view>
<text class="banner-arrow"></text>
</view>
<!-- Footer -->
<view class="page-footer">
<view class="footer-line">
<text class="line"></text>
<text class="text">2026 HAPPY NEW YEAR</text>
<text class="line"></text>
</view>
<text class="footer-sub">新春祝福 · 传递温情</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { getBavBarHeight } from "@/utils/system";
import { getAvatarFrameList } from "@/api/avatar.js";
import { getPageDetail } from "@/api/system.js";
const defaultAvatar =
"https://file.lihailezzc.com/resource/d9b329082b32f8305101f708593a4882.png";
const detailData = ref(null);
const frameList = ref([]);
const shareToken = ref("");
const navBarHeight = ref(64);
const statusBarHeight = ref(20);
onLoad((options) => {
if (options.shareToken) {
shareToken.value = options.shareToken;
fetchDetail();
}
fetchFrames();
});
onMounted(() => {
const sysInfo = uni.getSystemInfoSync();
statusBarHeight.value = sysInfo.statusBarHeight;
navBarHeight.value = getBavBarHeight();
});
const goBack = () => {
// Check if can go back, otherwise go home
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.switchTab({ url: "/pages/index/index" });
}
};
const fetchDetail = async () => {
try {
// uni.showLoading({ title: "加载中..." });
const res = await getPageDetail(shareToken.value);
if (res) {
detailData.value = res;
}
} catch (e) {
console.error(e);
// uni.showToast({ title: "获取详情失败", icon: "none" });
} finally {
// uni.hideLoading();
}
};
const fetchFrames = async () => {
try {
const res = await getAvatarFrameList(1);
if (res) {
const list = Array.isArray(res) ? res : res.list || [];
frameList.value = list.slice(0, 3); // Take first 3
}
} catch (e) {
console.error(e);
}
};
const previewImage = () => {
if (detailData.value?.imageUrl) {
uni.previewImage({
urls: [detailData.value.imageUrl],
});
}
};
const goToMake = () => {
uni.navigateTo({
url: "/pages/avatar/index",
});
};
const goToFortune = () => {
uni.navigateTo({
url: "/pages/fortune/index",
});
};
const goToGreeting = () => {
uni.switchTab({ url: "/pages/make/index" });
};
const goToWallpaper = () => {
uni.navigateTo({
url: "/pages/wallpaper/index",
});
};
</script>
<style lang="scss" scoped>
.avatar-detail-page {
min-height: 100vh;
background: #ffffff;
box-sizing: border-box;
}
.nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
box-sizing: border-box;
background: #ffffff;
}
.nav-content {
display: flex;
align-items: center;
height: 100%;
padding: 0 24rpx;
}
.back {
font-size: 50rpx;
margin-right: 24rpx;
line-height: 1;
color: #333;
}
.nav-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
flex: 1;
text-align: center;
margin-right: 50rpx; /* Balance back button */
}
.content-wrap {
padding: 30rpx 40rpx 60rpx;
}
/* User Info */
.user-info-section {
display: flex;
align-items: center;
margin-bottom: 40rpx;
}
.user-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
border: 4rpx solid #fff;
margin-right: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.user-text {
flex: 1;
}
.name-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.nickname {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-right: 16rpx;
}
.tag {
background: #ff3b30;
color: #fff;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 99rpx;
}
.action-text {
font-size: 24rpx;
color: #999;
}
/* Main Card */
.main-card {
background: #ffffff;
border-radius: 40rpx;
padding: 24rpx;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.06);
margin-bottom: 60rpx;
border: 2rpx solid #f5f5f5;
}
.card-inner {
position: relative;
background: #fff9f9;
border-radius: 30rpx;
padding: 60rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.generated-avatar {
width: 400rpx;
height: 400rpx;
border-radius: 20rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
border: 8rpx solid #ff3b30;
}
.loading-box {
width: 400rpx;
height: 400rpx;
display: flex;
align-items: center;
justify-content: center;
color: #999;
background: #f0f0f0;
border-radius: 20rpx;
}
.decor-tag {
position: absolute;
top: 30rpx;
right: 30rpx;
background: #fff;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
color: #ff3b30;
}
.card-footer-text {
margin-top: 40rpx;
font-size: 28rpx;
color: #ff3b30;
font-weight: bold;
display: flex;
align-items: center;
background: #fff;
padding: 10rpx 30rpx;
border-radius: 99rpx;
box-shadow: 0 4rpx 10rpx rgba(255, 59, 48, 0.1);
}
.card-footer-text .icon {
margin-right: 10rpx;
}
/* Buttons */
.action-group {
margin-bottom: 60rpx;
}
.btn {
height: 100rpx;
border-radius: 99rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
margin-bottom: 30rpx;
border: none;
}
.btn::after {
border: none;
}
.primary-btn {
background: #ff3b30;
color: #fff;
box-shadow: 0 10rpx 20rpx rgba(255, 59, 48, 0.3);
}
.secondary-btn {
background: #f5f5f5;
color: #333;
}
.btn .icon {
margin-right: 16rpx;
font-size: 36rpx;
}
/* Recommended Section */
.section {
margin-bottom: 40rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-header .left {
display: flex;
align-items: center;
}
.section-header .bar {
width: 8rpx;
height: 32rpx;
background: #ff3b30;
border-radius: 4rpx;
margin-right: 16rpx;
}
.section-header .title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.section-header .more {
font-size: 24rpx;
color: #999;
}
.frame-grid {
display: flex;
justify-content: space-between;
}
.frame-item {
width: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
background: #f8f8f8;
border-radius: 24rpx;
padding: 20rpx;
}
.frame-img-box {
width: 140rpx;
height: 140rpx;
margin-bottom: 16rpx;
border-radius: 50%;
overflow: hidden;
background: #fff;
}
.frame-img {
width: 100%;
height: 100%;
}
.frame-name {
font-size: 24rpx;
color: #333;
font-weight: 500;
}
/* Wallpaper Banner */
.wallpaper-banner {
background: #f8f8f8;
border-radius: 24rpx;
padding: 30rpx;
display: flex;
align-items: center;
margin-bottom: 60rpx;
}
.banner-icon {
width: 80rpx;
height: 80rpx;
background: #fff;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
}
.banner-icon text {
font-size: 40rpx;
color: #ff3b30;
}
.banner-content {
flex: 1;
}
.banner-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.banner-desc {
font-size: 22rpx;
color: #999;
}
.banner-arrow {
font-size: 36rpx;
color: #ccc;
}
/* Footer */
.page-footer {
text-align: center;
padding-bottom: 40rpx;
}
.footer-line {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.footer-line .line {
width: 60rpx;
height: 2rpx;
background: #eee;
}
.footer-line .text {
font-size: 20rpx;
color: #ccc;
margin: 0 20rpx;
letter-spacing: 2rpx;
}
.footer-sub {
font-size: 20rpx;
color: #ddd;
}
</style>