feat: 任务和社区页面
This commit is contained in:
+158
-39
@@ -1,5 +1,5 @@
|
||||
// pages/tasks/index.js
|
||||
import { MOCK_TASKS_DATA } from '../../utils/mockData';
|
||||
import request from '../../utils/request';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -11,8 +11,7 @@ Page({
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({ tasks: MOCK_TASKS_DATA });
|
||||
this.processTasks();
|
||||
this.fetchTodayTasks();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
@@ -20,49 +19,136 @@ Page({
|
||||
this.getTabBar()) {
|
||||
this.getTabBar().setData({ selected: 1 });
|
||||
}
|
||||
// Refresh on show
|
||||
this.fetchTodayTasks();
|
||||
},
|
||||
|
||||
processTasks() {
|
||||
const { tasks } = this.data;
|
||||
|
||||
// Calculate Progress (Simulated)
|
||||
const completedCount = 3; // Mocked existing completed
|
||||
const initialTotal = MOCK_TASKS_DATA.length + completedCount;
|
||||
const currentCompleted = completedCount + (MOCK_TASKS_DATA.length - tasks.length);
|
||||
const progress = Math.min(100, Math.round((currentCompleted / initialTotal) * 100));
|
||||
|
||||
// Grouping
|
||||
const groups = {};
|
||||
tasks.forEach(task => {
|
||||
if (!groups[task.plantName]) {
|
||||
groups[task.plantName] = {
|
||||
plantName: task.plantName,
|
||||
plantImage: task.plantImage,
|
||||
tasks: [],
|
||||
hasOverdue: false
|
||||
};
|
||||
}
|
||||
groups[task.plantName].tasks.push(task);
|
||||
if (task.isOverdue) groups[task.plantName].hasOverdue = true;
|
||||
fetchTodayTasks() {
|
||||
request.get('/plant/todayTask').then(res => {
|
||||
// Check if res is array (list of PlantTaskVO)
|
||||
const list = Array.isArray(res) ? res : (res.list || []);
|
||||
this.processTaskData(list);
|
||||
}).catch(err => {
|
||||
console.error('Fetch tasks failed', err);
|
||||
wx.stopPullDownRefresh();
|
||||
});
|
||||
},
|
||||
|
||||
// Sorting
|
||||
const sortedGroups = Object.values(groups).sort((a, b) => {
|
||||
processTaskData(plantTaskVOList) {
|
||||
let totalPacketTasks = 0;
|
||||
let completedPacketTasks = 0;
|
||||
|
||||
const groups = plantTaskVOList.map(vo => {
|
||||
const plant = vo.MyPlant || vo.myPlant;
|
||||
if (!plant) return null;
|
||||
|
||||
// Parse Image
|
||||
let imageUrl = '';
|
||||
if (plant.imgList && plant.imgList.length > 0) {
|
||||
let url = plant.imgList[0].url;
|
||||
if (url && !url.startsWith('http') && !url.startsWith('/') && !url.startsWith('wxfile')) {
|
||||
imageUrl = '/assets/' + url;
|
||||
} else {
|
||||
imageUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
const plantGroup = {
|
||||
plantName: plant.name,
|
||||
plantImage: imageUrl,
|
||||
tasks: [], // Placeholder, will fill below
|
||||
hasOverdue: vo.hasExpired
|
||||
};
|
||||
|
||||
const rawTasks = vo.tasks || [];
|
||||
|
||||
// 1. Update Global Counters
|
||||
rawTasks.forEach(t => {
|
||||
totalPacketTasks++;
|
||||
if (t.status == 2 || t.status == 3) {
|
||||
completedPacketTasks++;
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Filter and Map Tasks for Display
|
||||
const displayTasks = rawTasks
|
||||
.filter(t => t.status == 1 || t.status == 2)
|
||||
.map(t => {
|
||||
// Status: 1 Pending, 2 Done
|
||||
const isCompleted = t.status == 2;
|
||||
|
||||
// Parse Icon
|
||||
let taskIcon = null;
|
||||
if (t.icon && t.icon.startsWith('{')) {
|
||||
try {
|
||||
taskIcon = JSON.parse(t.icon);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
// Check overdue (only for pending tasks)
|
||||
let isOverdue = false;
|
||||
let overdueDays = 0;
|
||||
if (!isCompleted && t.dueDate) {
|
||||
const due = new Date(t.dueDate);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
if (due < today) {
|
||||
isOverdue = true;
|
||||
const diffTime = Math.abs(today - due);
|
||||
overdueDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: t.id,
|
||||
taskType: t.name,
|
||||
taskIcon: taskIcon,
|
||||
isOverdue: isOverdue,
|
||||
overdueDays: overdueDays,
|
||||
plantName: plant.name,
|
||||
isCompleted: isCompleted,
|
||||
original: t
|
||||
};
|
||||
});
|
||||
|
||||
// Sorting Removed: Tasks stay in original order
|
||||
// displayTasks.sort((a, b) => {
|
||||
// if (a.isCompleted === b.isCompleted) return 0;
|
||||
// return a.isCompleted ? 1 : -1;
|
||||
// });
|
||||
|
||||
plantGroup.tasks = displayTasks;
|
||||
|
||||
if (plantGroup.tasks.length === 0) return null;
|
||||
return plantGroup;
|
||||
}).filter(g => g !== null);
|
||||
|
||||
// Calculate Progress
|
||||
let progress = 0;
|
||||
if (totalPacketTasks > 0) {
|
||||
progress = Math.round((completedPacketTasks / totalPacketTasks) * 100);
|
||||
}
|
||||
|
||||
// Sorting Groups: Overdue first
|
||||
groups.sort((a, b) => {
|
||||
if (a.hasOverdue && !b.hasOverdue) return -1;
|
||||
if (!a.hasOverdue && b.hasOverdue) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
groupedTasks: sortedGroups,
|
||||
progress
|
||||
groupedTasks: groups,
|
||||
progress,
|
||||
tasks: groups
|
||||
});
|
||||
|
||||
wx.stopPullDownRefresh();
|
||||
},
|
||||
|
||||
handleTaskClick(e) {
|
||||
// e.currentTarget.dataset.task might differ if TDesign changes event structure,
|
||||
// but 'data-task' on t-button works similarly in Miniprogram.
|
||||
const task = e.currentTarget.dataset.task;
|
||||
if (task.isCompleted) return;
|
||||
this.setData({
|
||||
completingTask: task,
|
||||
remark: ''
|
||||
@@ -70,7 +156,6 @@ Page({
|
||||
},
|
||||
|
||||
onPopupVisibleChange(e) {
|
||||
// Handle both TDesign event and manual close tap
|
||||
const visible = e.detail ? e.detail.visible : e.currentTarget.dataset.visible;
|
||||
if (!visible) {
|
||||
this.setData({ completingTask: null });
|
||||
@@ -85,15 +170,49 @@ Page({
|
||||
if (!this.data.completingTask) return;
|
||||
|
||||
const taskId = this.data.completingTask.id;
|
||||
// Filter out the completed task
|
||||
const newTasks = this.data.tasks.filter(t => t.id !== taskId);
|
||||
const remark = this.data.remark || '';
|
||||
|
||||
this.setData({
|
||||
tasks: newTasks,
|
||||
completingTask: null
|
||||
}, () => {
|
||||
this.processTasks();
|
||||
wx.showLoading({ title: '提交中...' });
|
||||
|
||||
request.post('/plant/completeTask', {
|
||||
taskId: taskId,
|
||||
remark: remark
|
||||
}).then(() => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '已完成', icon: 'success' });
|
||||
|
||||
// Optimistic UI Update
|
||||
const groups = this.data.groupedTasks;
|
||||
let updated = false;
|
||||
for (let g of groups) {
|
||||
const t = g.tasks.find(x => x.id === taskId);
|
||||
if (t) {
|
||||
t.isCompleted = true;
|
||||
t.isOverdue = false;
|
||||
// Do NOT sort. Just update status.
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
this.setData({ groupedTasks: groups, tasks: groups, completingTask: null, remark: '' });
|
||||
} else {
|
||||
this.setData({ completingTask: null, remark: '' });
|
||||
}
|
||||
|
||||
// Sync with backend
|
||||
this.fetchTodayTasks();
|
||||
}).catch(err => {
|
||||
wx.hideLoading();
|
||||
console.error('Complete task failed', err);
|
||||
wx.showToast({ title: '操作失败', icon: 'none' });
|
||||
});
|
||||
},
|
||||
|
||||
gotoGarden() {
|
||||
wx.switchTab({
|
||||
url: '/pages/garden/index'
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user