Files
spring-festival-greetings/pages/index/index.vue
2026-02-25 23:42:38 +08:00

1172 lines
28 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="greeting-info">{{ greetingText }}</text>
<view class="name-row">
<text class="user-name" @tap="handleLogin">
{{ userInfo.nickName || "点击登录" }}
</text>
<view class="user-points" v-if="isLoggedIn">
<text class="points-label">积分</text>
<text class="points-value">{{ userPoints }}</text>
</view>
</view>
</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="lucky-color-tag"> 幸运色{{ luckyColor }} </view>
</view>
<view class="card-bottom">
<button class="open-lucky-btn" @tap="onOpenLucky">
<uni-icons
type="star-filled"
size="18"
color="#d81e06"
style="margin-right: 12rpx"
/>
<text>{{
signInfo.isSignedToday ? "今日已开启" : "开启今日好运"
}}</text>
</button>
<view class="week-sign-section">
<view class="sign-header">
<text class="sign-title">已连续签到 {{ continuousDays }} </text>
<text class="sign-tip">连续7天得大奖</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="14"
color="#fff"
/>
<uni-icons
v-else-if="day.isToday && !day.isSigned"
type="plus"
size="14"
color="#d81e06"
/>
<text v-else class="dot"></text>
</view>
</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/download')">
<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 === 'wallpaper' }"
@tap="switchTab('wallpaper')"
>壁纸</view
>
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'avatar' }"
@tap="switchTab('avatar')"
>头像</view
>
<view
class="rank-tab-mini"
:class="{ active: currentTab === 'card' }"
@tap="switchTab('card')"
>贺卡</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="getThumbUrl(item?.resourceInfo?.url)"
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="index <= 3">HOT</view>
<text class="usage-count">{{ item.usageCount }} 人在用</text>
</view>
</view>
<view class="rank-action" v-if="item.scene === 'card'">
<view class="create-btn" @tap.stop="handleCreate(item)">
<text>去制作</text>
</view>
</view>
<view class="rank-action" v-else>
<view class="download-btn" @tap.stop="handleDownload(item)">
<uni-icons type="download" size="14" color="#fff" />
</view>
</view>
</view>
</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 />
<!-- 运势抽奖弹窗 -->
<LuckyPopup ref="luckyPopupRef" />
<RewardAd ref="rewardAdRef" @onReward="handleAdReward" />
</view>
</template>
<script setup>
import { ref, onMounted, computed } from "vue";
import { getStatusBarHeight } from "@/utils/system";
import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app";
import { useUserStore } from "@/stores/user";
import {
getRecommendList,
getRankList,
abilityCheck,
watchAdReward,
} from "@/api/system";
import { saveRemoteImageToLocal, saveRecordRequest } from "@/utils/common";
import { getUserSignInfo, userSignIn } from "@/api/user";
import { avatarDownloadRecord } from "@/api/avatar";
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 userInfo = computed(() => userStore?.userInfo || {});
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
const userPoints = computed(() => userStore.userInfo.points || 0);
const signInfo = ref({}); // 用户签到信息
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;
const isFuture = dateStr > todayStr;
if (signedDays.includes(i + 1)) {
isSigned = true;
}
days.push({
label: labels[i],
date: dateStr,
isToday,
isSigned,
isFuture,
});
}
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(98);
const luckyColor = ref("赤金朱红");
const continuousDays = computed(() => signInfo.value.continuousDays || 0);
const currentMonthDays = computed(() => signInfo.value.totalDays || 0);
const totalMonthDays = computed(() => {
const date = new Date();
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
}); // 当前月总天数
const currentTab = ref("wallpaper");
// 模拟农历数据
const lunarDate = ref({
year: "2026",
month: "正月",
day: "初一",
yi: "开光、祈福、求嗣",
ji: "安葬、动土",
});
const rankingList = ref([]);
onMounted(() => {
fetchUserSingInfo(); // 获取用户签到信息
fetchRankingData();
});
onShow(() => {
if (userStore.userInfo) {
// userInfo.value = userStore.userInfo; // 已改为 computed
}
});
const fetchUserSingInfo = async () => {
if (!isLoggedIn.value) return;
const res = await getUserSignInfo();
signInfo.value = res || {};
};
const fetchRankingData = async () => {
try {
const res = await getRankList(currentTab.value);
if (res && Array.isArray(res)) {
rankingList.value = res.slice(0, 10);
} else {
rankingList.value = [];
}
} catch (e) {
console.error("fetchRankingData error", e);
rankingList.value = [];
}
};
const switchTab = (tab) => {
if (currentTab.value === tab) return;
currentTab.value = tab;
fetchRankingData();
};
const onNoticeTap = () => {
uni.showToast({ title: "暂无新消息", icon: "none" });
};
const onWalletTap = () => {
uni.navigateTo({ url: "/pages/mine/vip" });
};
const onOpenLucky = async () => {
if (!isLoggedIn.value) {
uni.$emit("show-login-popup");
return;
}
if (signInfo.value.isSignedToday) {
luckyPopupRef.value?.open();
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;
} else {
signInfo.value.totalDays = (signInfo.value.totalDays || 0) + 1;
}
// Update signedDays locally
if (!signInfo.value.signedDays) {
signInfo.value.signedDays = [];
}
const today = new Date(signInfo.value.today || new Date());
const dayIndex = today.getDay() || 7;
if (!signInfo.value.signedDays.includes(dayIndex)) {
signInfo.value.signedDays.push(dayIndex);
}
// Update user assets (points)
userStore.fetchUserAssets();
luckyPopupRef.value?.open();
} else {
uni.showToast({ title: "签到失败", icon: "none" });
}
} catch (e) {
console.error(e);
uni.showToast({ title: "网络错误,请稍后重试", icon: "none" });
} finally {
uni.hideLoading();
}
};
const navTo = (url) => {
uni.navigateTo({ url });
};
const handleCreate = (item) => {
uni.setStorageSync("RECOMMEND_CARD_DATA", {
recommendId: item.targetId,
imageUrl: item?.resourceInfo?.url,
});
uni.navigateTo({ url: "/pages/make/index" });
// uni.navigateTo({
// url: "/pages/make/index",
// success: () => {
// // Pass the template ID to the make page via global event or storage since switchTab doesn't support query params directly
// // However, for better UX, we can use a global state or event bus.
// // Or simply navigate if it's just switching tabs.
// // Since the user wants to use the selected template, we should try to pass it.
// // Uni-app's switchTab doesn't support params. We can use a global variable or store.
// // Here we will use uni.$emit to notify the make page after switching.
// setTimeout(() => {
// uni.$emit("select-template", item.id);
// }, 100);
// },
// });
};
const handleDownload = async (item) => {
if (!isLoggedIn.value) {
uni.$emit("show-login-popup");
return;
}
const type =
item.type === "wallpaper" ? "wallpaper_download" : "avatar_download";
const abilityRes = await abilityCheck(type);
if (!abilityRes.canUse) {
if (
abilityRes?.blockType === "need_share" &&
abilityRes?.message === "分享可继续"
) {
uni.showToast({
title: "分享给好友即可下载",
icon: "none",
});
return;
}
if (
abilityRes?.blockType === "need_ad" &&
abilityRes?.message === "观看广告可继续"
) {
uni.showModal({
title: "积分不足",
content: "观看广告可获得50积分继续下载",
success: (res) => {
if (res.confirm) {
rewardAdRef.value.show();
}
},
});
return;
}
uni.showToast({
title: "您今日下载次数已用完,明日再试",
icon: "none",
});
return;
}
uni.showLoading({ title: "下载中..." });
try {
const promises = [
saveRemoteImageToLocal(item.resourceInfo.url),
saveRecordRequest("", item.id, type, item.resourceInfo.url),
];
if (item.type === "avatar") {
promises.push(
avatarDownloadRecord({ id: item.id, url: item.resourceInfo.url }),
);
}
await Promise.all(promises);
await userStore.fetchUserAssets();
uni.showToast({ title: "保存成功 消耗 20 积分" });
} catch (e) {
console.error("Download failed", 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" });
}
};
const onRankItemTap = (item) => {
if (item.type === "avatar") {
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;
.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 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;
.lucky-color-tag {
background: #fff;
padding: 12rpx 36rpx;
border-radius: 40rpx;
font-size: 26rpx;
color: #d81e06;
font-weight: 600;
box-shadow: 0 4rpx 12rpx rgba(216, 30, 6, 0.15);
letter-spacing: 2rpx;
}
}
.card-bottom {
z-index: 2;
.open-lucky-btn {
width: 100%;
height: 108rpx;
background: #fff;
border-radius: 54rpx;
display: flex;
align-items: center;
justify-content: center;
color: #d81e06;
font-size: 34rpx;
font-weight: 800;
margin-bottom: 32rpx;
box-shadow: 0 8rpx 20rpx rgba(216, 30, 6, 0.2);
letter-spacing: 2rpx;
&::after {
border: none;
}
}
.week-sign-section {
width: 100%;
.sign-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.sign-title {
font-size: 24rpx;
color: #a85a5a;
font-weight: bold;
}
.sign-tip {
font-size: 20rpx;
color: #a85a5a;
opacity: 0.7;
}
}
.week-days {
display: flex;
justify-content: space-between;
.day-item {
display: flex;
flex-direction: column;
align-items: center;
.day-label {
font-size: 20rpx;
color: #a85a5a;
margin-bottom: 12rpx;
opacity: 0.8;
}
.status-icon {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
display: flex;
align-items: center;
justify-content: center;
.dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #a85a5a;
opacity: 0.3;
}
}
&.is-today {
.day-label {
font-weight: bold;
opacity: 1;
}
.status-icon {
background: #fff;
box-shadow: 0 4rpx 12rpx rgba(216, 30, 6, 0.1);
}
}
&.is-signed {
.status-icon {
background: #d81e06;
box-shadow: 0 4rpx 12rpx rgba(216, 30, 6, 0.2);
}
.day-label {
color: #d81e06;
}
}
}
}
}
}
}
/* 通用 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-action {
.download-btn {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: linear-gradient(135deg, #ff9800, #ff5722);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(255, 87, 34, 0.3);
&:active {
opacity: 0.9;
transform: scale(0.95);
}
}
.create-btn {
padding: 8rpx 20rpx;
background: linear-gradient(135deg, #ff3b30, #ff1744);
color: #fff;
border-radius: 24rpx;
font-size: 22rpx;
font-weight: bold;
box-shadow: 0 4rpx 12rpx rgba(255, 23, 68, 0.2);
&:active {
opacity: 0.9;
transform: scale(0.95);
}
}
}
}
}
.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;
}
}
</style>