diff --git a/pages/profile/badges/badge-wall/index.js b/pages/profile/badges/badge-wall/index.js index f96832c..3966a94 100644 --- a/pages/profile/badges/badge-wall/index.js +++ b/pages/profile/badges/badge-wall/index.js @@ -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 }); diff --git a/pages/tasks/index.js b/pages/tasks/index.js index 8b7a406..f5e3232 100644 --- a/pages/tasks/index.js +++ b/pages/tasks/index.js @@ -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' }); }); }, diff --git a/pages/wiki/identify/index.wxss b/pages/wiki/identify/index.wxss index 000e805..09fb58c 100644 --- a/pages/wiki/identify/index.wxss +++ b/pages/wiki/identify/index.wxss @@ -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 { diff --git a/pages/wiki/index.wxss b/pages/wiki/index.wxss index 4ecabd9..d7066b7 100644 --- a/pages/wiki/index.wxss +++ b/pages/wiki/index.wxss @@ -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); diff --git a/utils/subscribe.js b/utils/subscribe.js index 8c541b8..a2e2c5a 100644 --- a/utils/subscribe.js +++ b/utils/subscribe.js @@ -1,38 +1,47 @@ /** * Request WeChat Mini Program Subscription Message - * Template ID: R7fh3NDpuV8DYqI83HpEQvC8mLJy5xMWFl1qeGN9JIo + * Template ID: iG5GYMPQAgKxIE9zZNOgKS6tCURhM9p9AC8iZ3Uj3uA */ -const TEMPLATE_ID = 'R7fh3NDpuV8DYqI83HpEQvC8mLJy5xMWFl1qeGN9JIo'; +const DEFAULT_TEMPLATE_ID = 'iG5GYMPQAgKxIE9zZNOgKS6tCURhM9p9AC8iZ3Uj3uA'; -export const requestSubscription = () => { +export const requestSubscription = (tmplIds = [DEFAULT_TEMPLATE_ID], silent = false) => { return new Promise((resolve) => { - // Check if subscription capability is available (basic check) if (!wx.requestSubscribeMessage) { - console.warn('Current version does not support subscribe message'); + if (!silent) console.warn('Current version does not support subscribe message'); resolve({ success: false, errMsg: 'Not supported' }); return; } wx.requestSubscribeMessage({ - tmplIds: [TEMPLATE_ID], + tmplIds: tmplIds, success(res) { - if (res[TEMPLATE_ID] === 'accept') { - - resolve({ success: true, status: 'accept' }); - } else { - - resolve({ success: false, status: res[TEMPLATE_ID] }); - } + // If any of the requested IDs are accepted + const isAccepted = tmplIds.some(id => res[id] === 'accept'); + resolve({ success: isAccepted, res }); }, fail(err) { - console.error('Subscription failed', err); - resolve({ success: false, errMsg: err.errMsg }); + if (!silent) console.error('Subscription failed', err); + + // 20004: User closed main switch in settings + if (err.errCode === 20004 && !silent) { + wx.showModal({ + title: '提示', + content: '请在设置中开启订阅消息通知', + confirmText: '去开启', + success: (modalRes) => { + if (modalRes.confirm) { + wx.openSetting(); + } + } + }); + } + resolve({ success: false, error: err }); } }); }); }; -export const checkSubscriptionSettings = () => { +export const checkSubscriptionSettings = (tmplId = DEFAULT_TEMPLATE_ID) => { return new Promise((resolve) => { if (!wx.getSetting) { resolve(undefined); @@ -43,7 +52,7 @@ export const checkSubscriptionSettings = () => { withSubscriptions: true, success(res) { const itemSettings = (res.subscriptionsSetting && res.subscriptionsSetting.itemSettings) || {}; - resolve(itemSettings[TEMPLATE_ID]); + resolve(itemSettings[tmplId]); }, fail() { resolve(undefined);