erer
This commit is contained in:
parent
90dafc305d
commit
c03d3149d2
@ -33,6 +33,48 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="signin-card">
|
||||
<view class="signin-header">
|
||||
<view class="signin-headline">
|
||||
<text class="signin-title">每日签到</text>
|
||||
<text class="signin-desc">已连续签到 {{ signInfo.continuousDays }} 天</text>
|
||||
</view>
|
||||
<view class="signin-badge">+{{ signInfo.todayReward || 0 }} 积分</view>
|
||||
</view>
|
||||
<view class="signin-calendar">
|
||||
<view class="signin-calendar-header">
|
||||
<text class="signin-month">{{ signInfo.monthLabel }}</text>
|
||||
<text class="signin-month-summary">本月已签 {{ signInfo.monthSignedDays || 0 }} 天</text>
|
||||
</view>
|
||||
<view class="signin-weekdays">
|
||||
<text class="signin-weekday" v-for="label in weekdayLabels" :key="label">{{ label }}</text>
|
||||
</view>
|
||||
<view class="signin-calendar-grid">
|
||||
<view class="signin-date" v-for="(day, index) in signCalendar" :key="index"
|
||||
:class="[{ placeholder: day.isPlaceholder, signed: day.signed, today: day.isToday, missed: day.missed }]">
|
||||
<text class="signin-date-number">{{ day.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="signin-footer">
|
||||
<view class="signin-stats">
|
||||
<view class="signin-stat">
|
||||
<text class="signin-stat-value">{{ signInfo.totalPoints }}</text>
|
||||
<text class="signin-stat-label">累计积分</text>
|
||||
</view>
|
||||
<view class="signin-stat">
|
||||
<text class="signin-stat-value">{{ signInfo.continuousDays }}</text>
|
||||
<text class="signin-stat-label">连续天数</text>
|
||||
</view>
|
||||
</view>
|
||||
<button class="signin-btn" :class="{ disabled: signInfo.signedToday || signLoading }"
|
||||
@click="handleSignIn" :disabled="signInfo.signedToday || signLoading">
|
||||
{{ signInfo.signedToday ? '今日已签到' : (signLoading ? '签到中...' : '立即签到') }}
|
||||
</button>
|
||||
<text class="signin-tip">{{ signInfo.nextRewardText || '坚持签到,更多奖励等你拿' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="tabs">
|
||||
<view class="tab-item" v-for="(tab, index) in tabs" :key="tab" :class="{ active: index === currentTab }"
|
||||
@click="handleTabClick(index)">
|
||||
@ -186,6 +228,18 @@ export default {
|
||||
return {
|
||||
userInfo: {},
|
||||
currentTab: 0,
|
||||
signInfo: {
|
||||
signedToday: false,
|
||||
continuousDays: 0,
|
||||
totalPoints: 0,
|
||||
todayReward: 0,
|
||||
nextRewardText: '',
|
||||
monthLabel: '',
|
||||
monthSignedDays: 0
|
||||
},
|
||||
signCalendar: [],
|
||||
signLoading: false,
|
||||
weekdayLabels: ['日', '一', '二', '三', '四', '五', '六'],
|
||||
stats: [
|
||||
{ icon: '🎁', value: '1280', label: '当前积分', color: '#7B43FF' },
|
||||
{ icon: '🛍️', value: '23', label: '总订单数', color: '#2877FF' },
|
||||
@ -315,6 +369,7 @@ export default {
|
||||
return;
|
||||
} else {
|
||||
this.loadProfile();
|
||||
this.loadSignInfo();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -331,6 +386,140 @@ export default {
|
||||
console.warn('加载个人信息失败', error);
|
||||
}
|
||||
},
|
||||
loadSignInfo() {
|
||||
try {
|
||||
return request('xcx/sign_in', 'GET').then(res => {
|
||||
const info = res || {};
|
||||
const monthLabel = info.current_month || this.getMonthLabel();
|
||||
const calendar = this.buildMonthlyCalendar(info.month_records || info.recent_records, monthLabel);
|
||||
this.signCalendar = calendar;
|
||||
const updatedSignInfo = {
|
||||
...this.signInfo,
|
||||
signedToday: !!info.signed_today,
|
||||
continuousDays: info.continuous_days || 0,
|
||||
totalPoints: info.total_points || 0,
|
||||
todayReward: info.today_reward || 0,
|
||||
nextRewardText: info.next_reward_text || '',
|
||||
monthLabel,
|
||||
monthSignedDays: calendar.filter(day => day.signed && !day.isPlaceholder).length
|
||||
};
|
||||
this.signInfo = updatedSignInfo;
|
||||
if (this.stats && this.stats.length) {
|
||||
const newPoints = updatedSignInfo.totalPoints;
|
||||
this.$set(this.stats, 0, {
|
||||
...this.stats[0],
|
||||
value: newPoints ? String(newPoints) : this.stats[0].value
|
||||
});
|
||||
}
|
||||
}).catch(error => {
|
||||
console.warn('加载签到信息失败', error);
|
||||
this.signCalendar = this.buildMonthlyCalendar();
|
||||
this.signInfo = {
|
||||
...this.signInfo,
|
||||
monthLabel: this.getMonthLabel(),
|
||||
monthSignedDays: 0
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('获取签到状态异常', error);
|
||||
this.signCalendar = this.buildMonthlyCalendar();
|
||||
this.signInfo = {
|
||||
...this.signInfo,
|
||||
monthLabel: this.getMonthLabel(),
|
||||
monthSignedDays: 0
|
||||
};
|
||||
}
|
||||
},
|
||||
buildMonthlyCalendar(list = [], referenceMonth = '') {
|
||||
const today = new Date();
|
||||
const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
let year = today.getFullYear();
|
||||
let month = today.getMonth() + 1;
|
||||
if (referenceMonth && /^\d{4}-\d{2}$/.test(referenceMonth)) {
|
||||
const parts = referenceMonth.split('-');
|
||||
year = parseInt(parts[0], 10) || year;
|
||||
month = parseInt(parts[1], 10) || month;
|
||||
}
|
||||
const firstDay = new Date(year, month - 1, 1);
|
||||
const totalDays = new Date(year, month, 0).getDate();
|
||||
const prefixDays = firstDay.getDay();
|
||||
const recordMap = {};
|
||||
if (Array.isArray(list)) {
|
||||
list.forEach(item => {
|
||||
const dateStr = item?.date || item?.day || item?.label;
|
||||
if (dateStr) {
|
||||
const status =
|
||||
item?.checked ??
|
||||
item?.signed ??
|
||||
(typeof item?.status !== 'undefined' ? item.status === 1 : undefined) ??
|
||||
item?.is_signed ??
|
||||
false;
|
||||
recordMap[dateStr] = !!status;
|
||||
}
|
||||
});
|
||||
}
|
||||
const calendar = [];
|
||||
for (let i = 0; i < prefixDays; i++) {
|
||||
calendar.push({ label: '', isPlaceholder: true });
|
||||
}
|
||||
for (let day = 1; day <= totalDays; day++) {
|
||||
const dateStr = `${year}-${this.padZero(month)}-${this.padZero(day)}`;
|
||||
const targetDate = new Date(year, month - 1, day);
|
||||
const isToday = this.isSameDate(targetDate, todayStart);
|
||||
const isFuture = targetDate.getTime() > todayStart.getTime();
|
||||
const signed = !!recordMap[dateStr];
|
||||
calendar.push({
|
||||
label: day,
|
||||
date: dateStr,
|
||||
signed,
|
||||
isToday,
|
||||
isFuture,
|
||||
isPlaceholder: false,
|
||||
missed: !signed && !isFuture && !isToday
|
||||
});
|
||||
}
|
||||
while (calendar.length % 7 !== 0) {
|
||||
calendar.push({ label: '', isPlaceholder: true });
|
||||
}
|
||||
return calendar;
|
||||
},
|
||||
getMonthLabel() {
|
||||
const today = new Date();
|
||||
return `${today.getFullYear()}-${this.padZero(today.getMonth() + 1)}`;
|
||||
},
|
||||
padZero(num) {
|
||||
return num < 10 ? `0${num}` : `${num}`;
|
||||
},
|
||||
isSameDate(dateA, dateB) {
|
||||
return (
|
||||
dateA.getFullYear() === dateB.getFullYear() &&
|
||||
dateA.getMonth() === dateB.getMonth() &&
|
||||
dateA.getDate() === dateB.getDate()
|
||||
);
|
||||
},
|
||||
async handleSignIn() {
|
||||
if (this.signInfo.signedToday || this.signLoading) {
|
||||
return;
|
||||
}
|
||||
this.signLoading = true;
|
||||
try {
|
||||
const res = await request('xcx/sign_in', 'POST');
|
||||
const rewardText = res?.message || '签到成功';
|
||||
uni.showToast({
|
||||
title: rewardText,
|
||||
icon: 'success'
|
||||
});
|
||||
await this.loadSignInfo();
|
||||
} catch (error) {
|
||||
console.error('签到失败', error);
|
||||
uni.showToast({
|
||||
title: '签到失败,请稍后再试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
this.signLoading = false;
|
||||
}
|
||||
},
|
||||
uploadAvatar() {
|
||||
console.log('uploadAvatar 被点击');
|
||||
uni.chooseImage({
|
||||
@ -418,6 +607,181 @@ export default {
|
||||
padding-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.signin-card {
|
||||
margin: 0 24rpx;
|
||||
padding: 32rpx;
|
||||
background: linear-gradient(120deg, #f6f1ff 0%, #ffeef5 100%);
|
||||
border-radius: 28rpx;
|
||||
box-shadow: 0 16rpx 32rpx rgba(139, 64, 255, 0.12);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.signin-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.signin-headline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.signin-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #33195d;
|
||||
}
|
||||
|
||||
.signin-desc {
|
||||
font-size: 26rpx;
|
||||
color: #7b43ff;
|
||||
}
|
||||
|
||||
.signin-badge {
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(255, 60, 141, 0.12);
|
||||
color: #ff3c8d;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signin-calendar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.signin-calendar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.signin-month {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #33195d;
|
||||
}
|
||||
|
||||
.signin-month-summary {
|
||||
font-size: 24rpx;
|
||||
color: #7b43ff;
|
||||
}
|
||||
|
||||
.signin-weekdays {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
font-size: 24rpx;
|
||||
color: #7b43ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.signin-weekday {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.signin-calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 16rpx 12rpx;
|
||||
}
|
||||
|
||||
.signin-date {
|
||||
height: 80rpx;
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border: 2rpx dashed rgba(139, 64, 255, 0.16);
|
||||
color: #4a2d71;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.signin-date.placeholder {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.signin-date.signed {
|
||||
background: linear-gradient(135deg, #8b40ff 0%, #ff3c8d 100%);
|
||||
color: #fff;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.signin-date.today {
|
||||
border-color: #ff3c8d;
|
||||
box-shadow: 0 0 0 3rpx rgba(255, 60, 141, 0.3);
|
||||
}
|
||||
|
||||
.signin-date.missed {
|
||||
border-style: solid;
|
||||
border-color: rgba(255, 60, 141, 0.3);
|
||||
color: rgba(51, 25, 93, 0.4);
|
||||
}
|
||||
|
||||
.signin-date-number {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.signin-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.signin-stats {
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.signin-stat {
|
||||
flex: 1;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 24rpx;
|
||||
padding: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.signin-stat-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #33195d;
|
||||
}
|
||||
|
||||
.signin-stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #7b43ff;
|
||||
}
|
||||
|
||||
.signin-btn {
|
||||
width: 100%;
|
||||
padding: 24rpx 0;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #8b40ff 0%, #ff3c8d 100%);
|
||||
color: #ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.signin-btn.disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.signin-tip {
|
||||
font-size: 24rpx;
|
||||
color: #7b43ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.profile-header {
|
||||
background: linear-gradient(135deg, #8b40ff 0%, #ff3c8d 100%);
|
||||
/* border-radius: 24rpx 24rpx 0 0; */
|
||||
|
||||
@ -2,13 +2,16 @@
|
||||
<view class="subscribe-page">
|
||||
<view class="banner">
|
||||
<view class="banner-head">
|
||||
<view class="banner-icon">
|
||||
<text class="iconfont icon-lihe"></text>
|
||||
</view>
|
||||
<view class="banner-info">
|
||||
<view class="banner-title">盲盒订阅</view>
|
||||
<view class="banner-desc">每月惊喜,专属香氛体验</view>
|
||||
<view class="banner-title-row">
|
||||
<view class="banner-icon">
|
||||
<text class="iconfont icon-lihe"></text>
|
||||
</view>
|
||||
<view class="banner-info">
|
||||
<view class="banner-title">盲盒订阅</view>
|
||||
<view class="banner-desc">每月惊喜,专属香氛体验</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="banner-meta">
|
||||
<view class="meta-item">
|
||||
<view class="meta-label">当前等级</view>
|
||||
@ -162,11 +165,15 @@ export default {
|
||||
}
|
||||
|
||||
.banner-head {
|
||||
/* display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx; */
|
||||
}
|
||||
.banner-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.banner-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
@ -177,6 +184,11 @@ export default {
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
font-size: 60rpx;
|
||||
box-shadow: inset 0 10rpx 20rpx rgba(255, 255, 255, 0.2);
|
||||
text-align: center;
|
||||
}
|
||||
.banner-icon text{
|
||||
font-size: 60rpx;
|
||||
margin-left: 50%;
|
||||
}
|
||||
|
||||
.banner-info {
|
||||
@ -197,8 +209,10 @@ export default {
|
||||
}
|
||||
|
||||
.banner-meta {
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user