Files
spring-festival-greetings/pages/index/index.vue
2026-02-24 15:35:55 +08:00

827 lines
18 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="home-container"
:style="{
paddingTop: statusBarHeight + 12 + 'px',
}"
>
<!-- 顶部用户信息栏 -->
<view class="header-section">
<view class="user-info">
<image
class="user-avatar"
:src="userInfo.avatarUrl || '/static/default-avatar.png'"
mode="aspectFill"
/>
<view class="user-texts">
<text class="year-info">2026 岁在丙午</text>
<text class="user-name"
>您好{{ userInfo.nickName || "新春福星" }}</text
>
</view>
</view>
</view>
<!-- 主卡片 (Lucky Status) -->
<view class="main-card">
<view class="card-bg-decor"></view>
<view class="card-top">
<view class="lucky-status">
<text class="status-label">LUCKY STATUS</text>
<text class="status-value"
>{{ luckyScore }}<text class="percent">%</text></text
>
</view>
<view class="date-box">
<view class="date-row">
<uni-icons type="calendar" size="14" color="#8b5a5a" />
<text class="date-text"
>{{ lunarDate.year }}-{{ lunarDate.month
}}{{ lunarDate.day }}</text
>
</view>
<view class="yi-ji-row">
<text class="label"></text
><text class="content">{{ lunarDate.yi }}</text>
</view>
<view class="yi-ji-row">
<text class="label"></text
><text class="content">{{ lunarDate.ji }}</text>
</view>
</view>
</view>
<view class="card-center">
<view class="fire-circle">
<image
src="/static/icon/fire.png"
mode="aspectFit"
class="fire-icon"
/>
</view>
<view class="lucky-color-tag"> 幸运色{{ luckyColor }} </view>
</view>
<view class="card-bottom">
<button class="open-lucky-btn" @tap="onOpenLucky">
<uni-icons
type="star-filled"
size="16"
color="#fff"
style="margin-right: 8rpx"
/>
开启今日好运
</button>
<view class="progress-section">
<view class="progress-info">
<text>已开启 2026 鸿运周期</text>
<text>{{ progressDays }} / 15</text>
</view>
<view class="progress-bar-bg">
<view
class="progress-bar-fill"
:style="{ width: (progressDays / 15) * 100 + '%' }"
></view>
</view>
</view>
</view>
</view>
<!-- 大家都在用 -->
<view class="section-container compact-section">
<view class="section-header">
<view class="title-left-decor"></view>
<uni-icons
type="heart-filled"
size="18"
color="#d81e06"
style="margin-right: 8rpx"
/>
<text class="section-title">大家都在用</text>
</view>
<view class="feature-row">
<view class="feature-item" @tap="navTo('/pages/avatar/index')">
<view class="left-content">
<view class="icon-wrap yellow-bg">
<image
src="/static/icon/guashi.png"
mode="aspectFit"
class="feature-icon"
/>
</view>
<view class="text-content">
<text class="feature-name">制作今日头像</text>
<text class="feature-desc">限定 · 如意边框</text>
</view>
</view>
</view>
<view class="feature-item" @tap="navTo('/pages/wallpaper/index')">
<view class="left-content">
<view class="icon-wrap blue-bg">
<image
src="/static/icon/bizhi.png"
mode="aspectFit"
class="feature-icon"
/>
</view>
<view class="text-content">
<text class="feature-name">好运祝福壁纸</text>
<text class="feature-desc">高清 · 福气盈门</text>
</view>
</view>
</view>
</view>
</view>
<!-- 排行榜单 -->
<view class="section-container">
<view class="section-header">
<view class="title-left-decor"></view>
<text class="section-title">排行榜单</text>
<view class="rank-tabs-mini">
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'all' }"
@tap="switchTab('all')"
>全部</view
>
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'frame' }"
@tap="switchTab('frame')"
>头像框</view
>
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'card' }"
@tap="switchTab('card')"
>贺卡</view
>
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'wallpaper' }"
@tap="switchTab('wallpaper')"
>壁纸</view
>
</view>
</view>
<!-- Ranking List -->
<view class="rank-list">
<view
class="rank-item"
v-for="(item, index) in rankingList"
:key="item.id"
@tap="onRankItemTap(item)"
>
<view class="rank-num" :class="'rank-' + (index + 1)">{{
index + 1
}}</view>
<image :src="item.thumb" mode="aspectFill" class="rank-thumb" />
<view class="rank-info">
<view class="rank-title-row">
<text class="rank-title">{{ item.title }}</text>
</view>
<view class="rank-meta">
<view class="tag-hot" v-if="item.isHot">HOT</view>
<text class="usage-count">{{ item.usageCount }} 人在用</text>
</view>
</view>
<view class="rank-likes">
<uni-icons type="heart-filled" size="16" color="#a85a5a" />
<text class="likes-num">{{ item.likes }}</text>
</view>
</view>
</view>
<view class="rank-footer"> 查看前 10 </view>
</view>
<!-- Floating Share Button -->
<button class="float-share-btn" open-type="share">
<uni-icons type="upload" size="18" color="#d81e06" />
<text>分享今日运势</text>
</button>
<view class="bottom-spacer"></view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { getStatusBarHeight } from "@/utils/system";
import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import { getRecommendList } from "@/api/system";
const userStore = useUserStore();
const statusBarHeight = ref(getStatusBarHeight());
const userInfo = ref(userStore?.userInfo || {});
const luckyScore = ref(98);
const luckyColor = ref("赤金朱红");
const progressDays = ref(1);
const currentTab = ref("all");
// 模拟农历数据
const lunarDate = ref({
year: "2026",
month: "正月",
day: "初一",
yi: "开光、祈福、求嗣",
ji: "安葬、动土",
});
const rankingList = ref([]);
onMounted(() => {
fetchRankingData();
});
onShow(() => {
if (userStore.userInfo) {
userInfo.value = userStore.userInfo;
}
});
const fetchRankingData = async () => {
// 模拟数据或调用 API
// 实际项目中可以调用 getRecommendList 并根据 currentTab 筛选
// 这里先使用 Mock 数据展示 UI
rankingList.value = [
{
id: 1,
title: "龙马精神限定框",
thumb: "https://file.lihailezzc.com/resource/avatar_frame_horse_gold.png",
usageCount: "2.4w",
likes: "12.8k",
isHot: true,
type: "frame",
},
{
id: 2,
title: "锦绣江山动态壁纸",
thumb: "https://file.lihailezzc.com/resource/wallpaper_snow_red.png",
usageCount: "1.8w",
likes: "9.2k",
isHot: false,
type: "wallpaper",
},
{
id: 3,
title: "万事大吉贺卡模板",
thumb: "https://file.lihailezzc.com/resource/card_template_1.png",
usageCount: "9.5k",
likes: "4.1k",
isHot: false,
type: "card",
},
];
};
const switchTab = (tab) => {
currentTab.value = tab;
// TODO: 根据 tab 筛选或重新请求数据
// 此处仅做 UI 演示,实际需对接筛选逻辑
};
const onNoticeTap = () => {
uni.showToast({ title: "暂无新消息", icon: "none" });
};
const onWalletTap = () => {
uni.navigateTo({ url: "/pages/mine/vip" });
};
const onOpenLucky = () => {
uni.navigateTo({ url: "/pages/fortune/index" });
};
const navTo = (url) => {
uni.navigateTo({ url });
};
const onRankItemTap = (item) => {
if (item.type === "frame") {
uni.navigateTo({ url: `/pages/avatar/index?id=${item.id}` });
} else if (item.type === "card") {
uni.switchTab({ url: "/pages/make/index" });
} else if (item.type === "wallpaper") {
uni.navigateTo({ url: `/pages/wallpaper/index` });
}
};
onShareAppMessage(() => {
return {
title: "开启你的2026新春好运",
path: "/pages/index/index",
};
});
onShareTimeline(() => {
return {
title: "开启你的2026新春好运",
};
});
</script>
<style lang="scss" scoped>
.home-container {
min-height: 100vh;
background-color: #fbfbf9; /* 柔和的米色背景 */
padding-left: 32rpx;
padding-right: 32rpx;
padding-bottom: 120rpx;
box-sizing: border-box;
}
/* 顶部用户信息 */
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.user-info {
display: flex;
align-items: center;
.user-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
border: 2rpx solid #fff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
margin-right: 16rpx;
}
.user-texts {
display: flex;
flex-direction: column;
.year-info {
font-size: 20rpx;
color: #999;
margin-bottom: 2rpx;
}
.user-name {
font-size: 26rpx;
font-weight: bold;
color: #333;
}
}
}
}
/* 主卡片 (Lucky Status) */
.main-card {
position: relative;
width: 100%;
height: 640rpx;
background: linear-gradient(180deg, #fcece8 0%, #f7dcd6 100%);
border-radius: 40rpx;
padding: 40rpx;
box-sizing: border-box;
box-shadow: 0 20rpx 40rpx rgba(189, 87, 87, 0.15);
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
margin-bottom: 48rpx;
.card-bg-decor {
position: absolute;
top: -50rpx;
right: -50rpx;
width: 300rpx;
height: 300rpx;
background: radial-gradient(
circle,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0) 70%
);
border-radius: 50%;
}
.card-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
z-index: 2;
.lucky-status {
display: flex;
flex-direction: column;
.status-label {
font-size: 22rpx;
color: #a85a5a;
letter-spacing: 2rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.status-value {
font-size: 88rpx;
font-weight: bold;
color: #a85a5a;
line-height: 1;
.percent {
font-size: 40rpx;
margin-left: 4rpx;
}
}
}
.date-box {
background: rgba(255, 255, 255, 0.6);
border-radius: 20rpx;
padding: 20rpx;
backdrop-filter: blur(10rpx);
.date-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.date-text {
font-size: 24rpx;
color: #8b5a5a;
font-weight: bold;
margin-left: 8rpx;
}
}
.yi-ji-row {
font-size: 20rpx;
margin-bottom: 4rpx;
color: #666;
.label {
color: #999;
}
.content {
color: #555;
}
}
}
}
.card-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1;
z-index: 2;
.fire-circle {
width: 140rpx;
height: 140rpx;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 24rpx;
box-shadow: 0 8rpx 24rpx rgba(168, 90, 90, 0.1);
.fire-icon {
width: 64rpx;
height: 64rpx;
}
}
.lucky-color-tag {
background: #fff;
padding: 10rpx 32rpx;
border-radius: 30rpx;
font-size: 24rpx;
color: #d81e06;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(216, 30, 6, 0.1);
}
}
.card-bottom {
z-index: 2;
.open-lucky-btn {
width: 100%;
height: 96rpx;
background: rgba(255, 255, 255, 0.4);
border: 2rpx solid rgba(255, 255, 255, 0.6);
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 24rpx;
backdrop-filter: blur(5rpx);
}
.progress-section {
.progress-info {
display: flex;
justify-content: space-between;
font-size: 20rpx;
color: #a85a5a;
margin-bottom: 12rpx;
opacity: 0.8;
}
.progress-bar-bg {
width: 100%;
height: 8rpx;
background: rgba(255, 255, 255, 0.3);
border-radius: 4rpx;
overflow: hidden;
.progress-bar-fill {
height: 100%;
background: #d81e06; // 或渐变色
border-radius: 4rpx;
}
}
}
}
}
/* 通用 Section 样式 */
.section-container {
margin-bottom: 48rpx;
.section-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.title-left-decor {
width: 6rpx;
height: 28rpx;
background: #d81e06;
border-radius: 4rpx;
margin-right: 12rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
flex: 1;
}
.header-right {
display: flex;
align-items: center;
.refresh-text {
font-size: 22rpx;
color: #999;
margin-left: 6rpx;
}
}
}
}
/* 大家都在用 */
.compact-section {
.section-header {
margin-bottom: 20rpx;
}
}
.feature-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
.feature-item {
width: 48%;
background: #fff;
border-radius: 24rpx;
padding: 20rpx;
display: flex;
align-items: center;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
box-sizing: border-box;
margin-bottom: 20rpx;
.left-content {
display: flex;
align-items: center;
width: 100%;
.icon-wrap {
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
flex-shrink: 0;
&.yellow-bg {
background: #fff8e1;
}
&.blue-bg {
background: #e3f2fd;
}
.feature-icon {
width: 36rpx;
height: 36rpx;
}
}
.text-content {
display: flex;
flex-direction: column;
overflow: hidden;
.feature-name {
font-size: 26rpx;
font-weight: bold;
color: #333;
margin-bottom: 2rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.feature-desc {
font-size: 20rpx;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
/* 排行榜单 */
.rank-tabs-mini {
display: flex;
margin-left: auto; /* Push to right */
background: #eee;
border-radius: 30rpx;
padding: 4rpx;
.rank-tab-mini {
padding: 8rpx 20rpx;
border-radius: 26rpx;
font-size: 22rpx;
color: #666;
transition: all 0.3s;
&.active {
background: #fff;
color: #333;
font-weight: bold;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
}
}
.rank-list {
background: #fff;
border-radius: 32rpx;
padding: 16rpx 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.03);
.rank-item {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 2rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.rank-num {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: bold;
color: #999;
margin-right: 24rpx;
border-radius: 50%;
&.rank-1 {
background: #ffd700;
color: #fff;
}
&.rank-2 {
background: #c0c0c0;
color: #fff;
}
&.rank-3 {
background: #cd7f32;
color: #fff;
}
}
.rank-thumb {
width: 88rpx;
height: 88rpx;
border-radius: 16rpx;
background: #eee;
margin-right: 24rpx;
}
.rank-info {
flex: 1;
.rank-title-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.rank-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
}
.rank-meta {
display: flex;
align-items: center;
.tag-hot {
font-size: 18rpx;
color: #d81e06;
background: #ffebee;
padding: 2rpx 8rpx;
border-radius: 8rpx;
margin-right: 12rpx;
font-weight: 600;
}
.usage-count {
font-size: 22rpx;
color: #999;
}
}
}
.rank-likes {
display: flex;
align-items: center;
.likes-num {
font-size: 24rpx;
color: #a85a5a;
margin-left: 8rpx;
font-weight: 500;
}
}
}
}
.rank-footer {
text-align: center;
font-size: 22rpx;
color: #ccc;
padding: 24rpx 0;
}
/* 悬浮分享按钮 */
.float-share-btn {
position: fixed;
right: 32rpx;
bottom: 60rpx; /* 避开 tabBar */
background: #fff;
border-radius: 40rpx;
padding: 16rpx 32rpx;
display: flex;
align-items: center;
box-shadow: 0 8rpx 24rpx rgba(216, 30, 6, 0.2);
border: 2rpx solid #ffebee;
z-index: 100;
text {
font-size: 26rpx;
color: #d81e06;
font-weight: 600;
margin-left: 12rpx;
}
}
.bottom-spacer {
height: 120rpx;
}
</style>