150 lines
4.4 KiB
JavaScript
150 lines
4.4 KiB
JavaScript
const { request, uploadImage, ensureLogin, cents } = require('../../utils/api.js')
|
|
|
|
Page({
|
|
data: {
|
|
loading: false,
|
|
errorMessage: '',
|
|
activeTab: 'hall',
|
|
hallTasks: [],
|
|
myTasks: [],
|
|
stats: { byStatus: {}, pendingSettlementText: 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) {
|
|
return {
|
|
byStatus: stats.byStatus || {},
|
|
pendingSettlementText: cents(stats.pendingSettlementCents),
|
|
}
|
|
}
|
|
|
|
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())}`
|
|
}
|