feat: 完成任务添加订阅
This commit is contained in:
@@ -25,45 +25,39 @@ Page({
|
||||
this.setData({ isLoading: true });
|
||||
wx.showLoading({ title: '加载中...' });
|
||||
try {
|
||||
// Fetch Config Tree
|
||||
const treeRes = await request.get('/config/badge/tree');
|
||||
// Parallel Fetch: Config Tree & User Badges
|
||||
const [treeRes, userBadgesRes] = await Promise.all([
|
||||
request.get('/config/badge/tree'),
|
||||
request.get('/profile/badge')
|
||||
]);
|
||||
|
||||
const list = Array.isArray(treeRes) ? treeRes : (treeRes.data || []);
|
||||
|
||||
// DEBUG: Force Unlock All
|
||||
// Extract user badge list from response structure { list: [...] }
|
||||
let userBadgeList = [];
|
||||
if (Array.isArray(userBadgesRes)) {
|
||||
userBadgeList = userBadgesRes;
|
||||
} else if (userBadgesRes && Array.isArray(userBadgesRes.list)) {
|
||||
userBadgeList = userBadgesRes.list;
|
||||
} else if (userBadgesRes && userBadgesRes.data) {
|
||||
userBadgeList = userBadgesRes.data || [];
|
||||
}
|
||||
|
||||
// Populate Achieved Map using Badge ID (not Record ID)
|
||||
let achievedMap = {};
|
||||
list.forEach(dim => {
|
||||
if (dim.groups) {
|
||||
dim.groups.forEach(grp => {
|
||||
if (grp.badges) {
|
||||
grp.badges.forEach(b => {
|
||||
achievedMap[b.id] = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
userBadgeList.forEach(b => {
|
||||
const badgeId = b.badgeId || (b.badge ? b.badge.id : null);
|
||||
if (badgeId) {
|
||||
achievedMap[badgeId] = b;
|
||||
}
|
||||
});
|
||||
|
||||
// Original logic commented out for debug
|
||||
/*
|
||||
try {
|
||||
const profile = await request.get('/profile/detail');
|
||||
if (profile && profile.achievedBadges) {
|
||||
profile.achievedBadges.forEach(b => {
|
||||
const id = typeof b === 'string' ? b : b.id;
|
||||
achievedMap[id] = true;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// Silent fail
|
||||
}
|
||||
*/
|
||||
|
||||
this.setData({
|
||||
dimensions: list,
|
||||
achievedMap
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Fetch badge tree failed', e);
|
||||
console.error('Fetch badge data failed', e);
|
||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||
} finally {
|
||||
this.setData({ isLoading: false });
|
||||
|
||||
+61
-56
@@ -1,5 +1,6 @@
|
||||
// pages/tasks/index.js
|
||||
import request from '../../utils/request';
|
||||
import { requestSubscription } from '../../utils/subscribe';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -239,71 +240,75 @@ Page({
|
||||
const taskId = this.data.completingTask.id;
|
||||
const remark = this.data.remark || '';
|
||||
|
||||
wx.showLoading({ title: '提交中...', mask: true });
|
||||
// Attempt to subscribe (silent mode avoids error popups if disabled)
|
||||
// This encourages "Always Allow" behavior for seamless experience
|
||||
requestSubscription(undefined, true).then(() => {
|
||||
wx.showLoading({ title: '提交中...', mask: true });
|
||||
|
||||
request.post('/plant/completeTask', {
|
||||
taskId: taskId,
|
||||
remark: remark
|
||||
}).then(res => {
|
||||
wx.hideLoading();
|
||||
request.post('/plant/completeTask', {
|
||||
taskId: taskId,
|
||||
remark: remark
|
||||
}).then(res => {
|
||||
wx.hideLoading();
|
||||
|
||||
// Handle Rewards
|
||||
const queue = [];
|
||||
// Check if res has level up or badge data
|
||||
// Note: res is already data.data from request.js
|
||||
if (res && res.isLevelUp && res.currentLevel) {
|
||||
queue.push({ type: 'level', data: res.currentLevel });
|
||||
}
|
||||
// Handle Rewards
|
||||
const queue = [];
|
||||
// Check if res has level up or badge data
|
||||
// Note: res is already data.data from request.js
|
||||
if (res && res.isLevelUp && res.currentLevel) {
|
||||
queue.push({ type: 'level', data: res.currentLevel });
|
||||
}
|
||||
|
||||
// Check for Badge using IsGetBadge flag (allowing for casing variance)
|
||||
if (res && (res.IsGetBadge === true || res.isGetBadge === true) && res.newBadge) {
|
||||
queue.push({ type: 'badge', data: res.newBadge });
|
||||
}
|
||||
// Check for Badge using IsGetBadge flag (allowing for casing variance)
|
||||
if (res && (res.IsGetBadge === true || res.isGetBadge === true) && res.newBadge) {
|
||||
queue.push({ type: 'badge', data: res.newBadge });
|
||||
}
|
||||
|
||||
this._popupQueue = queue;
|
||||
this._popupQueue = queue;
|
||||
|
||||
// Optimistic UI Update Logic
|
||||
const groups = this.data.groupedTasks;
|
||||
let updated = false;
|
||||
// Need to deep clone possibly, but here we modify and set back
|
||||
for (let g of groups) {
|
||||
// g is an object. groupedTasks is array of objects.
|
||||
if (g.tasks) {
|
||||
const t = g.tasks.find(x => x && x.id === taskId);
|
||||
if (t) {
|
||||
t.isCompleted = true;
|
||||
t.isOverdue = false;
|
||||
updated = true;
|
||||
break;
|
||||
// Optimistic UI Update Logic
|
||||
const groups = this.data.groupedTasks;
|
||||
let updated = false;
|
||||
// Need to deep clone possibly, but here we modify and set back
|
||||
for (let g of groups) {
|
||||
// g is an object. groupedTasks is array of objects.
|
||||
if (g.tasks) {
|
||||
const t = g.tasks.find(x => x && x.id === taskId);
|
||||
if (t) {
|
||||
t.isCompleted = true;
|
||||
t.isOverdue = false;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger Animation and Close Modal
|
||||
this.setData({
|
||||
completingTask: null,
|
||||
remark: '',
|
||||
groupedTasks: groups, // Update UI
|
||||
tasks: groups,
|
||||
showSunshine: true
|
||||
// Trigger Animation and Close Modal
|
||||
this.setData({
|
||||
completingTask: null,
|
||||
remark: '',
|
||||
groupedTasks: groups, // Update UI
|
||||
tasks: groups,
|
||||
showSunshine: true
|
||||
});
|
||||
|
||||
// Hide Animation after duration and Start showing modals
|
||||
setTimeout(() => {
|
||||
this.setData({ showSunshine: false });
|
||||
// Show rewards after sunshine animation
|
||||
if (this._popupQueue.length > 0) {
|
||||
this.processPopupQueue();
|
||||
}
|
||||
}, 1000); // 1.0s delay usually covers animation
|
||||
|
||||
// Sync with backend silently
|
||||
this.fetchTodayTasks();
|
||||
|
||||
}).catch(err => {
|
||||
wx.hideLoading();
|
||||
console.error('Complete task failed', err);
|
||||
wx.showToast({ title: '操作失败', icon: 'none' });
|
||||
});
|
||||
|
||||
// Hide Animation after duration and Start showing modals
|
||||
setTimeout(() => {
|
||||
this.setData({ showSunshine: false });
|
||||
// Show rewards after sunshine animation
|
||||
if (this._popupQueue.length > 0) {
|
||||
this.processPopupQueue();
|
||||
}
|
||||
}, 1000); // 1.0s delay usually covers animation
|
||||
|
||||
// Sync with backend silently
|
||||
this.fetchTodayTasks();
|
||||
|
||||
}).catch(err => {
|
||||
wx.hideLoading();
|
||||
console.error('Complete task failed', err);
|
||||
wx.showToast({ title: '操作失败', icon: 'none' });
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -10,30 +10,32 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
height: 100vh;
|
||||
padding: 48rpx;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 20%; /* Push up visually */
|
||||
}
|
||||
|
||||
.state-card {
|
||||
background: #fff;
|
||||
border-radius: 40rpx;
|
||||
padding: 56rpx 48rpx;
|
||||
border-radius: 48rpx;
|
||||
padding: 64rpx 48rpx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
box-shadow: 0 12rpx 40rpx rgba(85, 139, 47, 0.08);
|
||||
gap: 40rpx;
|
||||
box-shadow: 0 20rpx 60rpx rgba(85, 139, 47, 0.15);
|
||||
}
|
||||
|
||||
/* ========== Loading State ========== */
|
||||
.loading-image-wrap {
|
||||
width: 280rpx;
|
||||
height: 280rpx;
|
||||
border-radius: 32rpx;
|
||||
width: 440rpx;
|
||||
height: 440rpx;
|
||||
border-radius: 40rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.1);
|
||||
box-shadow: 0 12rpx 40rpx rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.loading-preview {
|
||||
|
||||
@@ -60,12 +60,12 @@
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 36rpx;
|
||||
padding: 0 28rpx;
|
||||
height: 72rpx;
|
||||
background: #fff;
|
||||
border-radius: 36rpx;
|
||||
margin-right: 24rpx;
|
||||
font-size: 28rpx;
|
||||
margin-right: 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #546E7A;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04);
|
||||
|
||||
Reference in New Issue
Block a user