Files
spring-festival-greetings/pages/index/index.vue
2026-03-04 12:35:59 +08:00

1268 lines
30 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 + 54 + 'px' }"
>
<!-- 顶部用户信息栏 -->
<view
class="header-section"
:style="{ paddingTop: statusBarHeight + 4 + 'px' }"
>
<view class="user-info">
<image
class="user-avatar"
:src="userInfo.avatarUrl || '/static/default-avatar.png'"
mode="aspectFill"
/>
<view class="user-texts">
<text class="greeting-info">{{ greetingText }}</text>
<view class="name-row">
<text class="user-name" @tap="handleLogin">
{{ userInfo.nickName || "点击登录" }}
</text>
<view
class="user-points"
v-if="isLoggedIn"
@tap.stop="showPointsRules"
>
<text class="points-label">积分</text>
<text class="points-value">{{ userPoints }}</text>
<uni-icons
type="help"
size="14"
color="#ff5722"
style="margin-left: 4rpx"
/>
</view>
</view>
</view>
</view>
</view>
<!-- Lucky Card (New Design) -->
<view class="lucky-card">
<view class="card-toggle" @tap="toggleCardMode">
<uni-icons
:type="cardMode === 'lucky' ? 'calendar' : 'star'"
size="18"
color="#d81e06"
/>
</view>
<!-- Lucky Mode -->
<block v-if="cardMode === 'lucky'">
<view class="lucky-left">
<view class="date-group">
<text class="date-val">{{ currentDate.day }}</text>
<view class="date-sub">
<text class="date-month">{{ currentDate.month }}</text>
<text class="date-year">{{ currentDate.year }}</text>
</view>
</view>
</view>
<view class="lucky-divider"></view>
<view class="lucky-middle">
<view class="lucky-tags">
<text class="tag-yi"> {{ lunarDate.yi.split("、")[0] }}</text>
<text class="tag-ji"> {{ lunarDate.ji.split("、")[0] }}</text>
</view>
<text class="lucky-lunar"
>农历 {{ lunarDate.month }}{{ lunarDate.day }}</text
>
</view>
<button class="lucky-btn" @tap="onOpenLucky">
<uni-icons
type="star-filled"
size="16"
color="#fff"
style="margin-right: 4rpx"
/>
<text>{{
signInfo.isSignedToday ? "今日好运已开启" : "签到抽取好运"
}}</text>
</button>
</block>
<!-- Calendar Mode -->
<block v-else>
<view class="calendar-mode">
<view class="sign-header">
<text class="sign-title"
>本周已连续签到
<text class="highlight">{{ signInfo.continuousDays || 0 }}</text>
</text
>
<text class="sign-tip">连续7天得100积分</text>
</view>
<view class="week-days">
<view
class="day-item"
v-for="(day, index) in weekDays"
:key="index"
:class="{ 'is-today': day.isToday, 'is-signed': day.isSigned }"
>
<text class="day-label">{{ day.label }}</text>
<view class="status-icon">
<uni-icons
v-if="day.isSigned"
type="checkmarkempty"
size="12"
color="#fff"
/>
<uni-icons
v-else-if="day.isToday && !day.isSigned"
type="plus"
size="12"
color="#d81e06"
/>
<text v-else class="dot"></text>
</view>
</view>
</view>
</view>
</block>
</view>
<!-- Special Topic Card -->
<view class="special-topic-card" v-if="specialTopic">
<view class="topic-left">
<text class="topic-title">{{ specialTopic.sceneName }}</text>
<text class="topic-subtitle">{{ specialTopic.sceneDesc }}</text>
<text class="topic-blessing">{{ specialTopic.sceneBlessing }}</text>
<button class="topic-btn" @tap="navToSpecial">
{{ specialTopic.btnText }}
<uni-icons type="right" size="12" color="#fff" />
</button>
</view>
<view class="topic-right">
<image
class="topic-img"
:src="specialTopic.imageUrl"
mode="aspectFit"
/>
<!-- Decor circles -->
<view class="decor-circle circle-1"></view>
<view class="decor-circle circle-2"></view>
</view>
</view>
<!-- Make Card Scenes -->
<view class="section-container">
<view class="section-header">
<view class="title-left-decor"></view>
<text class="section-title">问候·祝福</text>
</view>
<scroll-view class="scene-scroll" scroll-x="true" show-scrollbar="false">
<view class="scene-list">
<view
class="scene-item"
v-for="(item, index) in sceneList"
:key="index"
@tap="navToMake(item)"
>
<view class="scene-icon-box">
<image :src="item.icon" mode="aspectFit" class="scene-icon" />
</view>
<text class="scene-name">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- Popular Wallpapers -->
<view class="section-container">
<view class="section-header">
<view class="title-left-decor"></view>
<text class="section-title">热门壁纸</text>
<view class="header-right" @tap="navTo('/pages/wallpaper/index')">
<view class="more-btn">
<text class="more-text">查看全部</text>
<uni-icons type="right" size="12" color="#ff5722" />
</view>
</view>
</view>
<scroll-view
class="wallpaper-scroll"
scroll-x="true"
show-scrollbar="false"
>
<view class="wallpaper-list">
<view
class="wallpaper-item"
v-for="item in wallpaperList"
:key="item.id"
@tap="navToWallpaper(item)"
>
<image
:src="getThumbUrl(item.imageUrl)"
mode="aspectFill"
class="wallpaper-img"
/>
<text class="wallpaper-name">{{ item.title }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- Trending Avatars -->
<view class="section-container">
<view class="section-header">
<view class="title-left-decor"></view>
<text class="section-title">爆款头像</text>
<view class="header-right" @tap="navTo('/pages/avatar/download')">
<view class="more-btn">
<text class="more-text">查看全部</text>
<uni-icons type="right" size="12" color="#ff5722" />
</view>
</view>
</view>
<scroll-view class="avatar-scroll" scroll-x="true" show-scrollbar="false">
<view class="avatar-list">
<view
class="avatar-item"
v-for="item in avatarList"
:key="item.id"
@tap="navToAvatar(item)"
>
<image
:src="getThumbUrl(item.imageUrl)"
mode="aspectFill"
class="avatar-img"
/>
<text class="avatar-name">{{ item.title }}</text>
</view>
</view>
</scroll-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> -->
<!-- 登录弹窗 -->
<LoginPopup @logind="handleLoginSuccess" />
<!-- 运势抽奖弹窗 -->
<LuckyPopup ref="luckyPopupRef" />
<RewardAd ref="rewardAdRef" @onReward="handleAdReward" />
<!-- 积分规则弹窗 -->
<uni-popup ref="rulesPopup" type="center">
<view class="rules-modal">
<view class="rules-header">
<text class="rules-title">积分获取规则</text>
<uni-icons
type="closeempty"
size="20"
color="#999"
@tap="closeRules"
/>
</view>
<view class="rules-content">
<view class="rule-item">
<uni-icons type="paperplane-filled" size="18" color="#ff9800" />
<text class="rule-text">分享好友+10积分 (每日限3次)</text>
</view>
<view class="rule-item">
<uni-icons type="person-filled" size="18" color="#ff9800" />
<text class="rule-text">每日登录+20积分</text>
</view>
<view class="rule-item">
<uni-icons type="calendar-filled" size="18" color="#ff9800" />
<text class="rule-text">每日签到+20积分</text>
</view>
<view class="rule-item">
<uni-icons type="videocam-filled" size="18" color="#ff9800" />
<text class="rule-text">观看广告+50积分</text>
</view>
</view>
<button class="rules-btn" @tap="closeRules">我知道了</button>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, computed } from "vue";
import { getStatusBarHeight } from "@/utils/system";
import {
onShareAppMessage,
onShareTimeline,
onShow,
onPullDownRefresh,
onLoad,
} from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import {
watchAdReward,
getRandomRecommendList,
getCardSpecialTopic,
getShareReward,
} from "@/api/system";
import { getUserSignInfo, userSignIn, getUserLuckInfo } from "@/api/user";
import calendar from "@/utils/lunar";
import { getShareToken, saveViewRequest } from "@/utils/common.js";
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
import LuckyPopup from "@/components/LuckyPopup/LuckyPopup.vue";
import RewardAd from "@/components/RewardAd/RewardAd.vue";
const userStore = useUserStore();
const statusBarHeight = ref(getStatusBarHeight());
const loginPopupRef = ref(null);
const luckyPopupRef = ref(null);
const rewardAdRef = ref(null);
const rulesPopup = ref(null);
const userInfo = computed(() => userStore?.userInfo || {});
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
const userPoints = computed(() => userStore.userInfo.points || 0);
const signInfo = ref({}); // 用户签到信息
const cardMode = ref("lucky"); // 'lucky' or 'calendar'
const toggleCardMode = () => {
cardMode.value = cardMode.value === "lucky" ? "calendar" : "lucky";
};
const weekDays = computed(() => {
const now = new Date();
const todayStr =
signInfo.value.today ||
`${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
const current = new Date(todayStr);
const day = current.getDay() || 7; // 1 (Mon) - 7 (Sun)
const monday = new Date(current);
monday.setDate(current.getDate() - day + 1);
const days = [];
const labels = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
const signedDays = signInfo.value.signedDays || [];
for (let i = 0; i < 7; i++) {
const d = new Date(monday);
d.setDate(monday.getDate() + i);
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const da = String(d.getDate()).padStart(2, "0");
const dateStr = `${y}-${m}-${da}`;
let isSigned = false;
const isToday = dateStr === todayStr;
if (signedDays.includes(i + 1)) {
isSigned = true;
}
days.push({
label: labels[i],
date: dateStr,
isToday,
isSigned,
});
}
return days;
});
const greetingText = computed(() => {
const hour = new Date().getHours();
if (hour < 6) return "凌晨好";
if (hour < 9) return "早上好";
if (hour < 12) return "上午好";
if (hour < 14) return "中午好";
if (hour < 17) return "下午好";
if (hour < 19) return "傍晚好";
return "晚上好";
});
const getThumbUrl = (url) => {
if (!url) return "";
return `${url}?imageView2/1/w/200/h/200/q/80`;
};
const handleLogin = () => {
if (!userInfo.value.nickName) {
uni.$emit("show-login-popup");
}
};
const luckyScore = ref(92);
const currentDate = computed(() => {
const now = new Date();
return {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate(),
};
});
// 农历数据
const lunarDate = computed(() => {
const date = new Date();
const lunar = calendar.solar2lunar(date);
return {
year: lunar.lYear,
month: lunar.monthCn,
day: lunar.dayCn,
yi: "团聚、访友、祈福",
ji: "远行、搬家",
};
});
const sceneList = ref([
{ id: 3, name: "每日问候", icon: "/static/icon/yunshi.png", scene: "daily" },
{
id: 2,
name: "生日纪念",
icon: "/static/icon/guashi.png",
scene: "birthday",
},
{ id: 4, name: "情感表达", icon: "/static/icon/hongbao.png", scene: "love" },
{
id: 1,
name: "节日祝福",
icon: "/static/icon/celebrate.png",
scene: "holiday",
},
]);
const wallpaperList = ref([]);
const avatarList = ref([]);
onLoad((options) => {
if (options.shareToken) {
saveViewRequest(options.shareToken, "index");
}
fetchUserSingInfo(); // 获取用户签到信息
fetchHomeData();
fetchSpecialTopic();
});
onShow(() => {
fetchHomeData();
fetchUserSingInfo();
fetchSpecialTopic();
});
onPullDownRefresh(() => {
fetchHomeData();
fetchUserSingInfo();
fetchSpecialTopic();
});
const specialTopic = ref(null);
const fetchSpecialTopic = async () => {
try {
const res = await getCardSpecialTopic();
if (res && res.sceneName) {
specialTopic.value = res;
} else {
specialTopic.value = null;
}
} catch (e) {
console.error("fetchSpecialTopic error", e);
}
};
const navToSpecial = () => {
if (specialTopic.value && specialTopic.value.scene) {
uni.navigateTo({
url: `/pages/make/index?scene=${specialTopic.value.scene}`,
});
}
};
const fetchUserSingInfo = async () => {
if (!isLoggedIn.value) return;
const res = await getUserSignInfo();
signInfo.value = res || {};
};
const handleLoginSuccess = () => {
fetchUserSingInfo();
};
const fetchHomeData = async () => {
try {
const [wallpapers, avatars] = await Promise.all([
getRandomRecommendList("wallpaper_download"),
getRandomRecommendList("avatar_download"),
]);
if (wallpapers && Array.isArray(wallpapers)) {
wallpaperList.value = wallpapers;
}
if (avatars && Array.isArray(avatars)) {
avatarList.value = avatars;
}
} catch (e) {
console.error("fetchHomeData error", e);
} finally {
uni.stopPullDownRefresh();
}
};
const navTo = (url) => {
uni.navigateTo({ url });
};
const navToMake = (item) => {
if (item.scene === "daily") {
uni.navigateTo({ url: "/pages/greeting/daily" });
return;
}
uni.navigateTo({ url: `/pages/make/index?scene=${item.scene}` });
};
const navToWallpaper = (item) => {
const imageUrl = encodeURIComponent(item.imageUrl || "");
uni.navigateTo({
url: `/pages/wallpaper/detail?id=${item.id}&imageUrl=${imageUrl}&categoryId=${item.categoryId}`,
});
};
const navToAvatar = (item) => {
const query = `recommendId=${item.id || ""}&type=avatar&imageUrl=${encodeURIComponent(item?.imageUrl || "")}`;
uni.navigateTo({ url: `/pages/avatar/index?${query}` });
};
const onOpenLucky = async () => {
if (!isLoggedIn.value) {
uni.$emit("show-login-popup");
return;
}
if (signInfo.value.isSignedToday) {
uni.showLoading({ title: "加载中..." });
try {
const luckRes = await getUserLuckInfo();
luckyPopupRef.value?.open(luckRes, true);
} catch (e) {
console.error(e);
uni.showToast({ title: "获取好运信息失败", icon: "none" });
} finally {
uni.hideLoading();
}
return;
}
uni.showLoading({ title: "开启好运...", mask: true });
try {
const res = await userSignIn();
if (res && res.success) {
signInfo.value.continuousDays = res.continuousDays;
signInfo.value.isSignedToday = true;
if (typeof res.totalDays === "number") {
signInfo.value.totalDays = res.totalDays;
}
fetchUserSingInfo();
userStore.fetchUserAssets();
// 获取好运信息
const luckRes = await getUserLuckInfo();
luckyPopupRef.value?.open(luckRes, false);
uni.showToast({ title: "获取40积分", icon: "none" });
} else {
uni.showToast({ title: "签到失败", icon: "none" });
}
} catch (e) {
console.error(e);
uni.showToast({ title: "网络错误,请稍后重试", icon: "none" });
} finally {
uni.hideLoading();
}
};
const handleAdReward = async (token) => {
try {
const res = await watchAdReward(token);
if (res) {
uni.showToast({
title: "获得50积分",
icon: "success",
});
await userStore.fetchUserAssets();
}
} catch (e) {
console.error("Reward claim failed", e);
uni.showToast({ title: "奖励发放失败", icon: "none" });
}
};
onShareAppMessage(async () => {
const shareToken = await getShareToken("index");
getShareReward().then((res) => {
if (isLoggedIn && res && res.success) {
userStore.fetchUserAssets();
}
});
return {
title: "开启你的2026新春好运",
path: "/pages/index/index?shareToken=" + shareToken,
};
});
onShareTimeline(() => {
return {
title: "开启你的2026新春好运",
};
});
const showPointsRules = () => {
rulesPopup.value.open();
};
const closeRules = () => {
rulesPopup.value.close();
};
</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 {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 32rpx;
padding-right: 32rpx;
padding-bottom: 24rpx;
background-color: rgba(251, 251, 249, 0.95);
backdrop-filter: blur(20rpx);
.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;
.greeting-info {
font-size: 20rpx;
color: #999;
margin-bottom: 2rpx;
}
.name-row {
display: flex;
align-items: center;
.user-name {
font-size: 26rpx;
font-weight: bold;
color: #333;
margin-right: 12rpx;
}
.user-points {
display: flex;
align-items: center;
background: rgba(255, 152, 0, 0.1);
border: 1rpx solid rgba(255, 152, 0, 0.3);
border-radius: 20rpx;
padding: 2rpx 10rpx;
.points-label {
font-size: 16rpx;
color: #ff9800;
margin-right: 4rpx;
}
.points-value {
font-size: 20rpx;
color: #ff5722;
font-weight: bold;
}
}
}
}
}
}
/* Lucky Card */
.lucky-card {
width: 100%;
height: 240rpx;
background: #fff;
border-radius: 32rpx;
padding: 32rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 48rpx;
box-shadow: 0 12rpx 32rpx rgba(216, 30, 6, 0.08);
position: relative;
.card-toggle {
position: absolute;
top: 16rpx;
right: 16rpx;
width: 48rpx;
height: 48rpx;
background: rgba(216, 30, 6, 0.05);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.lucky-left {
display: flex;
flex-direction: column;
align-items: flex-start;
.date-group {
display: flex;
align-items: baseline;
margin-bottom: 8rpx;
.date-val {
font-size: 64rpx;
font-weight: 800;
color: #d81e06;
line-height: 1;
margin-right: 8rpx;
}
.date-sub {
display: flex;
flex-direction: column;
.date-month {
font-size: 24rpx;
font-weight: bold;
color: #d81e06;
line-height: 1.2;
}
.date-year {
font-size: 20rpx;
color: #d81e06;
opacity: 0.6;
line-height: 1.2;
}
}
}
}
.lucky-divider {
width: 2rpx;
height: 64rpx;
background: #eee;
margin: 0 24rpx;
}
.lucky-middle {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.lucky-tags {
display: flex;
gap: 12rpx;
margin-bottom: 12rpx;
.tag-yi,
.tag-ji {
background: #f5f5f5;
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-size: 20rpx;
color: #666;
}
.tag-yi {
color: #d81e06;
background: rgba(216, 30, 6, 0.05);
}
}
.lucky-lunar {
font-size: 22rpx;
color: #999;
}
}
.lucky-btn {
width: 210rpx;
height: 72rpx;
background: linear-gradient(135deg, #b8860b 0%, #d4a017 100%);
border-radius: 36rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24rpx;
font-weight: bold;
box-shadow: 0 8rpx 16rpx rgba(184, 134, 11, 0.3);
margin: 0;
padding: 0;
&::after {
border: none;
}
}
.calendar-mode {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
.sign-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding-right: 64rpx;
.sign-title {
font-size: 28rpx;
color: #333;
font-weight: bold;
.highlight {
color: #d81e06;
font-size: 32rpx;
margin: 0 4rpx;
}
}
.sign-tip {
font-size: 20rpx;
color: #999;
}
}
.week-days {
display: flex;
justify-content: space-between;
width: 100%;
.day-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
.day-label {
font-size: 20rpx;
color: #999;
margin-bottom: 12rpx;
}
.status-icon {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
.dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #ccc;
}
}
&.is-today {
.day-label {
color: #d81e06;
font-weight: bold;
}
.status-icon {
background: rgba(216, 30, 6, 0.1);
}
}
&.is-signed {
.status-icon {
background: #d81e06;
box-shadow: 0 4rpx 8rpx rgba(216, 30, 6, 0.2);
}
}
}
}
}
}
/* Special Topic Card */
.special-topic-card {
width: 100%;
height: 220rpx;
background: linear-gradient(135deg, #fff9f0 0%, #fff3e0 100%);
border-radius: 32rpx;
padding: 32rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 48rpx;
box-shadow: 0 12rpx 32rpx rgba(255, 152, 0, 0.08);
position: relative;
overflow: hidden;
.topic-left {
display: flex;
flex-direction: column;
z-index: 2;
.topic-title {
font-size: 36rpx;
font-weight: 800;
color: #d81e06;
margin-bottom: 8rpx;
}
.topic-subtitle {
font-size: 24rpx;
color: #d81e06;
font-weight: bold;
margin-bottom: 12rpx;
}
.topic-blessing {
font-size: 22rpx;
color: #888;
margin-bottom: 24rpx;
}
.topic-btn {
display: inline-flex;
align-items: center;
justify-content: center;
background: #d81e06;
color: #fff;
font-size: 22rpx;
padding: 10rpx 24rpx;
border-radius: 24rpx;
margin: 0;
line-height: 1.2;
box-shadow: 0 4rpx 12rpx rgba(216, 30, 6, 0.2);
}
}
.topic-right {
position: relative;
width: 200rpx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.topic-img {
width: 160rpx;
height: 160rpx;
z-index: 2;
}
.decor-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
}
.circle-1 {
width: 120rpx;
height: 120rpx;
right: 20rpx;
top: 10rpx;
}
.circle-2 {
width: 80rpx;
height: 80rpx;
right: 80rpx;
bottom: 20rpx;
}
}
}
/* 通用 Section 样式 */
.section-container {
margin-bottom: 28rpx;
.section-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.title-left-decor {
width: 8rpx;
height: 32rpx;
background: #d81e06;
border-radius: 4rpx;
margin-right: 16rpx;
}
.section-title {
font-size: 34rpx;
font-weight: 800;
color: #333;
flex: 1;
letter-spacing: 1rpx;
}
.more-btn {
display: flex;
align-items: center;
background: #ffebee;
padding: 8rpx 20rpx;
border-radius: 24rpx;
transition: all 0.3s;
&:active {
opacity: 0.8;
background: #ffcdd2;
}
.more-text {
font-size: 22rpx;
color: #ff5722;
font-weight: 500;
margin-right: 2rpx;
}
}
}
}
/* Scene Scroll */
.scene-scroll {
white-space: nowrap;
width: 100%;
.scene-list {
display: flex;
padding-bottom: 24rpx; /* Space for shadow */
.scene-item {
display: inline-flex;
flex-direction: column;
align-items: center;
margin-right: 32rpx;
width: 140rpx;
&:last-child {
margin-right: 0;
}
.scene-icon-box {
width: 120rpx;
height: 120rpx;
background: #fff;
border-radius: 32rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04);
border: 2rpx solid #fff;
}
.scene-icon {
width: 64rpx;
height: 64rpx;
}
.scene-name {
font-size: 24rpx;
color: #333;
font-weight: 500;
}
}
}
}
/* Wallpaper Scroll */
.wallpaper-scroll {
white-space: nowrap;
width: 100%;
.wallpaper-list {
display: flex;
padding-bottom: 24rpx;
.wallpaper-item {
display: inline-flex;
flex-direction: column;
align-items: center;
margin-right: 24rpx;
width: 200rpx;
.wallpaper-img {
width: 200rpx;
height: 320rpx;
border-radius: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
}
.wallpaper-name {
font-size: 24rpx;
color: #666;
width: 100%;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
/* Avatar Scroll */
.avatar-scroll {
white-space: nowrap;
width: 100%;
.avatar-list {
display: flex;
padding-bottom: 24rpx;
.avatar-item {
display: inline-flex;
flex-direction: column;
align-items: center;
margin-right: 24rpx;
width: 160rpx;
.avatar-img {
width: 160rpx;
height: 160rpx;
border-radius: 32rpx;
margin-bottom: 12rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.05);
border: 4rpx solid #fff;
}
.avatar-name {
font-size: 22rpx;
color: #999;
width: 100%;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.avatar-more-btn {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160rpx;
height: 160rpx;
background: #d81e06;
border-radius: 32rpx;
box-shadow: 0 8rpx 24rpx rgba(216, 30, 6, 0.2);
vertical-align: top; /* Align with other inline-flex items */
.more-label {
font-size: 22rpx;
color: #fff;
margin-top: 8rpx;
font-weight: bold;
}
}
}
}
.more-text {
font-size: 22rpx;
color: #999;
}
/* Rules Modal */
.rules-modal {
width: 600rpx;
background: #fff;
border-radius: 32rpx;
padding: 40rpx;
box-sizing: border-box;
.rules-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
.rules-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.rules-content {
margin-bottom: 40rpx;
.rule-item {
display: flex;
align-items: center;
margin-bottom: 24rpx;
background: #fff9f0;
padding: 20rpx;
border-radius: 16rpx;
&:last-child {
margin-bottom: 0;
}
.rule-text {
font-size: 26rpx;
color: #333;
margin-left: 16rpx;
}
}
}
.rules-btn {
background: linear-gradient(135deg, #ff9800 0%, #ff5722 100%);
color: #fff;
font-size: 28rpx;
font-weight: bold;
border-radius: 40rpx;
height: 80rpx;
line-height: 80rpx;
margin: 0;
&::after {
border: none;
}
}
}
</style>