const { request, uploadImage, ensureLogin, cents } = require('../../utils/api.js') Page({ data: { loading: false, errorMessage: '', activeTab: 'hall', hallTasks: [], myTasks: [], stats: { byStatus: {}, taskTotal: 0, completed: 0, rejected: 0, completionRate: 0, pendingSettlementText: cents(0), settledRewardText: cents(0), }, selectedPhotosByTask: {}, }, onShow() { this.refreshAll() }, async refreshAll() { this.setData({ loading: true, errorMessage: '' }) try { await ensureLogin() const [hall, mine, stats] = await Promise.all([ request('/cleaning/tasks/hall?page=1&pageSize=20'), request('/cleaning/tasks/mine?page=1&pageSize=20'), request('/cleaning/stats'), ]) this.setData({ hallTasks: (hall.data.items || []).map(formatTask), myTasks: (mine.data.items || []).map(formatTask), stats: formatStats(stats.data), }) } catch (error) { this.setData({ errorMessage: error.message || '保洁任务加载失败' }) } finally { this.setData({ loading: false }) } }, switchTab(event) { const tab = event.currentTarget.dataset.tab if (!tab || tab === this.data.activeTab) return this.setData({ activeTab: tab }) }, async claimTask(event) { await this.mutateTask(event.currentTarget.dataset.taskId, 'claim') }, async startTask(event) { await this.mutateTask(event.currentTarget.dataset.taskId, 'start') }, async choosePhotos(event) { const taskId = event.currentTarget.dataset.taskId if (!taskId) return try { const result = await wxChooseMedia(9) const current = this.data.selectedPhotosByTask[taskId] || [] const next = current.concat(result.tempFiles.map((item) => item.tempFilePath)).slice(0, 9) this.setData({ [`selectedPhotosByTask.${taskId}`]: next }) } catch (error) { this.setData({ errorMessage: error.message || '选择照片失败' }) } }, async submitTask(event) { const taskId = event.currentTarget.dataset.taskId const localPhotos = this.data.selectedPhotosByTask[taskId] || [] if (localPhotos.length === 0) { this.setData({ errorMessage: '请先上传至少一张保洁照片' }) return } this.setData({ loading: true, errorMessage: '' }) let photoUrls = [] try { await ensureLogin() const uploaded = await Promise.all(localPhotos.map((filePath) => uploadImage(`/cleaning/tasks/${encodeURIComponent(taskId)}/photos`, filePath) )) photoUrls = uploaded.map((item) => item.data.publicUrl).filter(Boolean) } catch (error) { this.setData({ loading: false, errorMessage: error.message || '照片上传失败' }) return } await this.mutateTask(event.currentTarget.dataset.taskId, 'submit', { photoUrls }) this.setData({ [`selectedPhotosByTask.${taskId}`]: [] }) }, async reworkTask(event) { await this.mutateTask(event.currentTarget.dataset.taskId, 'rework') }, async mutateTask(taskId, action, data = {}) { if (!taskId) return this.setData({ loading: true, errorMessage: '' }) try { await ensureLogin() await request(`/cleaning/tasks/${encodeURIComponent(taskId)}/${action}`, { method: 'POST', data, }) await this.refreshAll() } catch (error) { this.setData({ errorMessage: error.message || '保洁任务处理失败' }) } finally { this.setData({ loading: false }) } }, }) function formatTask(task) { return { ...task, rewardText: cents(task.rewardCents), timeText: formatDate(task.updatedAt || task.createdAt), canClaim: task.status === 'WAITING', canStart: task.status === 'CLAIMED', canSubmit: task.status === 'STARTED', canRework: task.status === 'REJECTED', canUpload: task.status === 'STARTED' || task.status === 'REJECTED', } } function formatStats(stats) { const taskTotal = Number(stats.taskTotal || 0) const completed = Number(stats.completed || 0) const rejected = Number(stats.rejected || 0) return { byStatus: stats.byStatus || {}, taskTotal, completed, rejected, completionRate: Number(stats.completionRate || 0), pendingSettlementText: cents(stats.pendingSettlementCents), settledRewardText: cents(stats.settledRewardCents), } } function wxChooseMedia(count) { return new Promise((resolve, reject) => { wx.chooseMedia({ count, mediaType: ['image'], sourceType: ['album', 'camera'], sizeType: ['compressed'], success: resolve, fail: reject, }) }) } function formatDate(value) { if (!value) return '' const date = new Date(value) const pad = (input) => String(input).padStart(2, '0') return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}` }