diff --git a/api/user.js b/api/user.js index 2dfe350..71d8ef8 100644 --- a/api/user.js +++ b/api/user.js @@ -1,8 +1,15 @@ import { request } from "@/utils/request.js"; -export const getUserSignInfo = async (data) => { +export const getUserSignInfo = async () => { return request({ url: "/api/sign/info", method: "GET", }); }; + +export const userSignIn = async () => { + return request({ + url: "/api/sign/in", + method: "POST", + }); +}; diff --git a/pages/index/index.vue b/pages/index/index.vue index 016a7cc..6be2b8e 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -67,18 +67,35 @@ signInfo.isSignedToday ? "今日已开启" : "开启今日好运" }} - - - 已连续领好运 {{ continuousDays }} 天 - {{ currentMonthDays }} / {{ totalMonthDays }} + + + 已连续签到 {{ continuousDays }} 天 + 连续7天得大奖 - + + class="day-item" + v-for="(day, index) in weekDays" + :key="index" + :class="{ 'is-today': day.isToday, 'is-signed': day.isSigned }" + > + {{ day.label }} + + + + + + @@ -215,7 +232,7 @@ import { getStatusBarHeight } from "@/utils/system"; import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app"; import { useUserStore } from "@/stores/user"; import { getRecommendList } from "@/api/system"; -import { getUserSignInfo } from "@/api/user"; +import { getUserSignInfo, userSignIn } from "@/api/user"; import LoginPopup from "@/components/LoginPopup/LoginPopup.vue"; import LuckyPopup from "@/components/LuckyPopup/LuckyPopup.vue"; @@ -228,6 +245,79 @@ const userInfo = computed(() => userStore?.userInfo || {}); const isLoggedIn = computed(() => !!userStore.userInfo.nickName); 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 = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]; + + 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 (signInfo.value.continuousDays > 0) { + const continuous = signInfo.value.continuousDays; + const signedToday = signInfo.value.isSignedToday; + + const streakEndDate = new Date(todayStr); + if (!signedToday) { + streakEndDate.setDate(streakEndDate.getDate() - 1); + } + + const streakStartDate = new Date(streakEndDate); + streakStartDate.setDate(streakEndDate.getDate() - continuous + 1); + + const checkDate = new Date(dateStr); + const checkTime = checkDate.getTime(); + // Reset time components to avoid issues + const sTime = new Date( + streakStartDate.getFullYear(), + streakStartDate.getMonth(), + streakStartDate.getDate(), + ).getTime(); + const eTime = new Date( + streakEndDate.getFullYear(), + streakEndDate.getMonth(), + streakEndDate.getDate(), + ).getTime(); + const cTime = new Date( + checkDate.getFullYear(), + checkDate.getMonth(), + checkDate.getDate(), + ).getTime(); + + if (cTime >= sTime && cTime <= eTime) { + 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 "凌晨好"; @@ -331,12 +421,42 @@ const onWalletTap = () => { uni.navigateTo({ url: "/pages/mine/vip" }); }; -const onOpenLucky = () => { +const onOpenLucky = async () => { if (!isLoggedIn.value) { uni.$emit("show-login-popup"); return; } - luckyPopupRef.value?.open(); + + 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 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) => { @@ -554,27 +674,84 @@ onShareTimeline(() => { } } - .progress-section { - .progress-info { + .week-sign-section { + width: 100%; + + .sign-header { display: flex; justify-content: space-between; - font-size: 20rpx; - color: #a85a5a; - margin-bottom: 12rpx; - opacity: 0.8; + 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; + } } - .progress-bar-bg { - width: 100%; - height: 8rpx; - background: rgba(255, 255, 255, 0.3); - border-radius: 4rpx; - overflow: hidden; + .week-days { + display: flex; + justify-content: space-between; - .progress-bar-fill { - height: 100%; - background: #d81e06; // 或渐变色 - border-radius: 4rpx; + .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; + } + } } } }