fix: check-in
This commit is contained in:
@@ -1,8 +1,15 @@
|
|||||||
import { request } from "@/utils/request.js";
|
import { request } from "@/utils/request.js";
|
||||||
|
|
||||||
export const getUserSignInfo = async (data) => {
|
export const getUserSignInfo = async () => {
|
||||||
return request({
|
return request({
|
||||||
url: "/api/sign/info",
|
url: "/api/sign/info",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const userSignIn = async () => {
|
||||||
|
return request({
|
||||||
|
url: "/api/sign/in",
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -67,18 +67,35 @@
|
|||||||
signInfo.isSignedToday ? "今日已开启" : "开启今日好运"
|
signInfo.isSignedToday ? "今日已开启" : "开启今日好运"
|
||||||
}}</text>
|
}}</text>
|
||||||
</button>
|
</button>
|
||||||
<view class="progress-section">
|
<view class="week-sign-section">
|
||||||
<view class="progress-info">
|
<view class="sign-header">
|
||||||
<text>已连续领好运 {{ continuousDays }} 天</text>
|
<text class="sign-title">已连续签到 {{ continuousDays }} 天</text>
|
||||||
<text>{{ currentMonthDays }} / {{ totalMonthDays }}</text>
|
<text class="sign-tip">连续7天得大奖</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="progress-bar-bg">
|
<view class="week-days">
|
||||||
<view
|
<view
|
||||||
class="progress-bar-fill"
|
class="day-item"
|
||||||
:style="{
|
v-for="(day, index) in weekDays"
|
||||||
width: (currentMonthDays / totalMonthDays) * 100 + '%',
|
:key="index"
|
||||||
}"
|
:class="{ 'is-today': day.isToday, 'is-signed': day.isSigned }"
|
||||||
></view>
|
>
|
||||||
|
<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>
|
</view>
|
||||||
@@ -215,7 +232,7 @@ import { getStatusBarHeight } from "@/utils/system";
|
|||||||
import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app";
|
import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import { getRecommendList } from "@/api/system";
|
import { getRecommendList } from "@/api/system";
|
||||||
import { getUserSignInfo } from "@/api/user";
|
import { getUserSignInfo, userSignIn } from "@/api/user";
|
||||||
|
|
||||||
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||||||
import LuckyPopup from "@/components/LuckyPopup/LuckyPopup.vue";
|
import LuckyPopup from "@/components/LuckyPopup/LuckyPopup.vue";
|
||||||
@@ -228,6 +245,79 @@ const userInfo = computed(() => userStore?.userInfo || {});
|
|||||||
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
|
const isLoggedIn = computed(() => !!userStore.userInfo.nickName);
|
||||||
const signInfo = ref({}); // 用户签到信息
|
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 greetingText = computed(() => {
|
||||||
const hour = new Date().getHours();
|
const hour = new Date().getHours();
|
||||||
if (hour < 6) return "凌晨好";
|
if (hour < 6) return "凌晨好";
|
||||||
@@ -331,12 +421,42 @@ const onWalletTap = () => {
|
|||||||
uni.navigateTo({ url: "/pages/mine/vip" });
|
uni.navigateTo({ url: "/pages/mine/vip" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenLucky = () => {
|
const onOpenLucky = async () => {
|
||||||
if (!isLoggedIn.value) {
|
if (!isLoggedIn.value) {
|
||||||
uni.$emit("show-login-popup");
|
uni.$emit("show-login-popup");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signInfo.value.isSignedToday) {
|
||||||
luckyPopupRef.value?.open();
|
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) => {
|
const navTo = (url) => {
|
||||||
@@ -554,27 +674,84 @@ onShareTimeline(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-section {
|
.week-sign-section {
|
||||||
.progress-info {
|
width: 100%;
|
||||||
|
|
||||||
|
.sign-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
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;
|
font-size: 20rpx;
|
||||||
color: #a85a5a;
|
color: #a85a5a;
|
||||||
margin-bottom: 12rpx;
|
margin-bottom: 12rpx;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar-bg {
|
.status-icon {
|
||||||
width: 100%;
|
width: 44rpx;
|
||||||
height: 8rpx;
|
height: 44rpx;
|
||||||
background: rgba(255, 255, 255, 0.3);
|
border-radius: 50%;
|
||||||
border-radius: 4rpx;
|
background: rgba(255, 255, 255, 0.4);
|
||||||
overflow: hidden;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
.progress-bar-fill {
|
.dot {
|
||||||
height: 100%;
|
width: 8rpx;
|
||||||
background: #d81e06; // 或渐变色
|
height: 8rpx;
|
||||||
border-radius: 4rpx;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user