diff --git a/app.js b/app.js
index 42ebb66..9608f5e 100644
--- a/app.js
+++ b/app.js
@@ -16,7 +16,7 @@ App({
wx.login({
success: res => {
if (res.code) {
- request.get('/auth/miniLogin', { code: res.code }).then(async (data) => {
+ request.post('/auth/miniLogin', { code: res.code, clientId: 'sundynix-plant' }).then(async (data) => {
const token = data.token;
if (token && typeof token === 'string') {
@@ -25,7 +25,7 @@ App({
if (this._resolveLogin) this._resolveLogin(token);
// Background Profile Update
- request.get('/profile/detail').then(userDetail => {
+ request.get('/plant/profile/detail').then(userDetail => {
if (userDetail) {
wx.setStorageSync('userInfo', userDetail);
this.globalData.userInfo = userDetail;
diff --git a/pages/community/create/index.js b/pages/community/create/index.js
index e1eefa9..adefeec 100644
--- a/pages/community/create/index.js
+++ b/pages/community/create/index.js
@@ -24,7 +24,7 @@ Page({
},
fetchTopics() {
- request.get('/topic/list').then(res => {
+ request.get('/plant/topic/list').then(res => {
const list = res.list || [];
const topics = list.map(t => t.title);
if (topics.length > 0) {
@@ -229,8 +229,8 @@ Page({
if (images.length > 0) {
const uploadPromises = images.map(filePath => {
return request.upload(filePath).then(res => {
- // Res structure: { file: { id: "...", url: "..." } }
- return res && res.file ? res.file.id : null;
+ // Backend returns flat FileInfo: {id, url, name, ...}
+ return res ? res.id : null;
});
});
@@ -257,7 +257,7 @@ Page({
ossIds: ossIds
};
- await request.post('/post/publish', payload);
+ await request.post('/plant/post/publish', payload);
wx.hideLoading();
wx.showToast({ title: '发布成功', icon: 'success' });
diff --git a/pages/community/index.js b/pages/community/index.js
index f11744a..4e1eda3 100644
--- a/pages/community/index.js
+++ b/pages/community/index.js
@@ -69,7 +69,7 @@ Page({
try {
// Correct API Endpoint and Params
- const res = await request.post('/post/page', { current, pageSize, hasReviewed: 1 });
+ const res = await request.post('/plant/post/page', { current, pageSize, hasReviewed: 1 });
// Handle response structure: { code: 200, data: { list: [], ... } }
// OR if request.js unwraps it: { list: [], ... }
@@ -183,7 +183,7 @@ Page({
const type = post.likedByMe ? 2 : 1;
try {
- await request.get('/post/like', { id: postId, type });
+ await request.get('/plant/post/like', { id: postId, type });
// Optimistic Update
const updatedPosts = this.data.posts.map(p => {
@@ -225,7 +225,7 @@ Page({
const type = post.isFavorited ? 2 : 1; // 1: Collect, 2: Cancel
try {
- await request.get('/post/star', { id: postId, type });
+ await request.get('/plant/post/star', { id: postId, type });
// Optimistic Update
const updatedPosts = this.data.posts.map(p => {
@@ -283,7 +283,7 @@ Page({
}
try {
- await request.post('/post/comment', {
+ await request.post('/plant/post/comment', {
postId: commentingPostId,
content: commentText.trim()
});
diff --git a/pages/garden/add/index.js b/pages/garden/add/index.js
index 7f446a7..9f61efb 100644
--- a/pages/garden/add/index.js
+++ b/pages/garden/add/index.js
@@ -97,11 +97,9 @@ Page({
request.upload(tempFilePath).then(data => {
wx.hideLoading();
- // User provided response format: { data: { file: { url: ..., id: ... } } }
- // request.js unwraps 'data', so 'data' here is { file: { ... } }
- const fileData = data?.file || {};
- const imageUrl = fileData.url;
- const imageId = fileData.id;
+ // request.js unwraps data, response is flat FileInfo {id, url, name, ...}
+ const imageUrl = data?.url || '';
+ const imageId = data?.id || '';
if (imageUrl && imageId) {
this.setData({
diff --git a/pages/garden/index.js b/pages/garden/index.js
index 30e92a6..f1a2219 100644
--- a/pages/garden/index.js
+++ b/pages/garden/index.js
@@ -107,8 +107,8 @@ Page({
async loadBanners() {
try {
- const res = await request.get('/plantBanner/activeList');
- const list = (res.list || []).map(item => item.image ? item.image.url : '');
+ const res = await request.get('/plant/banner/activeList');
+ const list = (res.list || []).map(item => item.imageUrl || '');
this.setData({ bannerList: list.filter(Boolean) });
} catch (err) {
console.error('Load banners failed', err);
diff --git a/pages/plant-detail/edit/index.js b/pages/plant-detail/edit/index.js
index 2aee1c9..c6606ff 100644
--- a/pages/plant-detail/edit/index.js
+++ b/pages/plant-detail/edit/index.js
@@ -42,9 +42,9 @@ Page({
if (eventChannel) {
eventChannel.on('acceptDataFromOpenerPage', (data) => {
- if (data && data.plant) {
+ if (data) {
hasReceivedData = true;
- this.renderPlantUI(data.plant);
+ this.renderPlantUI(data);
}
});
}
@@ -63,20 +63,24 @@ Page({
});
},
- renderPlantUI(plant) {
+ renderPlantUI(data) {
+ if (!data) return;
+
+ // Detect if data is the raw API response or a flat eventChannel payload
+ const isRawResponse = data.hasOwnProperty('plant') && typeof data.plant === 'object';
+ const plant = isRawResponse ? data.plant : (data.plant || data);
+ const imgList = data.imgList || [];
+
const defaultIcon = CARE_TASK_ICONS.find(i => i.id === 'water') || CARE_TASK_ICONS[0];
let tasks = [];
- if (plant.careSchedule) {
- tasks = plant.careSchedule.map(cp => ({
- id: cp.id, name: cp.name, period: cp.period,
- taskIcon: cp.taskIcon, isNew: false,
- _original: { name: cp.name, period: cp.period, icon: JSON.stringify(cp.taskIcon || {}) }
- }));
- } else if (plant.carePlans) {
- tasks = plant.carePlans.map(cp => {
+ const carePlansSrc = isRawResponse ? (data.carePlans || []) : (data.carePlans || plant.careSchedule || []);
+ if (carePlansSrc && carePlansSrc.length > 0) {
+ tasks = carePlansSrc.map(cp => {
let iconObj = defaultIcon;
- if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
+ if (cp.taskIcon) {
+ iconObj = cp.taskIcon;
+ } else if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
try { iconObj = JSON.parse(cp.icon); } catch (e) { }
}
const iconStr = JSON.stringify(iconObj);
@@ -89,12 +93,11 @@ Page({
}
let imageUrl = '', imageId = '';
- if (plant.imgList && plant.imgList.length > 0) {
- imageUrl = plant.imgList[0].url || '';
- imageId = plant.imgList[0].id || '';
+ if (imgList && imgList.length > 0) {
+ imageUrl = imgList[0].url || '';
+ imageId = imgList[0].id || '';
}
-
let adoptionDate = plant.plantTime || '';
if (adoptionDate.includes('T')) adoptionDate = adoptionDate.split('T')[0];
@@ -142,9 +145,10 @@ Page({
wx.showLoading({ title: '上传中...' });
request.upload(tempFilePath).then(data => {
wx.hideLoading();
- const fileData = data?.file || {};
- if (fileData.id) {
- this.setData({ uploadedImageId: fileData.id, newPlantImage: fileData.url });
+ const imageUrl = data?.url || '';
+ const imageId = data?.id || '';
+ if (imageId) {
+ this.setData({ uploadedImageId: imageId, newPlantImage: imageUrl });
}
}).catch(() => {
wx.hideLoading();
diff --git a/pages/plant-detail/edit/index.wxml b/pages/plant-detail/edit/index.wxml
index 8380327..200cdf0 100644
--- a/pages/plant-detail/edit/index.wxml
+++ b/pages/plant-detail/edit/index.wxml
@@ -10,7 +10,7 @@
>
-
+
点击设置封面图
-
-
- 更换照片
-
diff --git a/pages/plant-detail/growth-record/index.js b/pages/plant-detail/growth-record/index.js
index c025df8..e67aba4 100644
--- a/pages/plant-detail/growth-record/index.js
+++ b/pages/plant-detail/growth-record/index.js
@@ -61,10 +61,7 @@ Page({
if (this.data.image) {
const uploadRes = await request.upload(this.data.image);
// Correctly extract ID from nested 'file' object based on API response
- if (uploadRes && uploadRes.file && uploadRes.file.id) {
- ossIds.push(uploadRes.file.id);
- } else if (uploadRes && uploadRes.id) {
- // Fallback just in case
+ if (uploadRes && uploadRes.id) {
ossIds.push(uploadRes.id);
} else {
console.warn('Upload response structure mismatch:', uploadRes);
diff --git a/pages/plant-detail/index.js b/pages/plant-detail/index.js
index afb7a48..f539878 100644
--- a/pages/plant-detail/index.js
+++ b/pages/plant-detail/index.js
@@ -36,11 +36,17 @@ Page({
initData(id) {
request.get('/plant/detail', { id }).then(plant => {
- const swiperImages = plant.imgList.map(img => {
- return img.url;
- });
+ // Legacy handler returns {plant: PlantInfo, carePlans: [...], growthRecords: [...], imgList: [...], careRecords: [...]}
+ const plantData = plant.plant || {};
+ const carePlansList = plant.carePlans || [];
+ const careRecordsList = plant.careRecords || [];
+ const growthRecordsList = plant.growthRecords || [];
+ const imgList = plant.imgList || [];
+
+ const swiperImages = imgList.map(img => img.url);
+
// Parse carePlans icon if it's a string
- const carePlans = (plant.carePlans || []).map(cp => {
+ const carePlans = carePlansList.map(cp => {
let iconObj = {};
if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
try {
@@ -54,16 +60,16 @@ Page({
// Calculate days planted and format date
let adoptionDate = '未知';
- const daysPlanted = calculateDaysSince(plant.plantTime);
+ const daysPlanted = calculateDaysSince(plantData.plantTime);
const ageBadge = getPlantAgeBadge(daysPlanted);
- if (plant.plantTime) {
- adoptionDate = plant.plantTime.split('T')[0];
+ if (plantData.plantTime) {
+ adoptionDate = plantData.plantTime.split('T')[0];
}
this.setData({
currentPlant: {
- ...plant,
- location: plant.placement || '',
+ ...plantData,
+ location: plantData.placement || '',
adoptionDate: adoptionDate,
daysPlanted: daysPlanted,
ageBadge: ageBadge,
@@ -71,8 +77,8 @@ Page({
},
swiperImages: swiperImages,
// Map logs and records directly from plant detail response
- careLogs: this.processLogs(plant.careRecords || []),
- records: (plant.growthRecords || plant.recordList || []).map(item => {
+ careLogs: this.processLogs(careRecordsList),
+ records: growthRecordsList.map(item => {
// Extract image URL safely
let imageUrl = '';
if (item.imgList && item.imgList.length > 0) {
@@ -235,7 +241,9 @@ Page({
success: (res) => {
// Send current data to the opened page
res.eventChannel.emit('acceptDataFromOpenerPage', {
- plant: this.data.currentPlant
+ plant: this.data.currentPlant,
+ imgList: this.data.swiperImages.map((url, i) => ({ id: i === 0 ? this.data.currentPlant.imageId || 'primary' : '', url })),
+ carePlans: this.data.currentPlant.careSchedule
});
}
});
diff --git a/pages/profile/badges/badge-wall/index.js b/pages/profile/badges/badge-wall/index.js
index 3966a94..585ea92 100644
--- a/pages/profile/badges/badge-wall/index.js
+++ b/pages/profile/badges/badge-wall/index.js
@@ -27,8 +27,8 @@ Page({
try {
// Parallel Fetch: Config Tree & User Badges
const [treeRes, userBadgesRes] = await Promise.all([
- request.get('/config/badge/tree'),
- request.get('/profile/badge')
+ request.get('/plant/config/badge/tree'),
+ request.get('/plant/profile/badge')
]);
const list = Array.isArray(treeRes) ? treeRes : (treeRes.data || []);
diff --git a/pages/profile/badges/index.js b/pages/profile/badges/index.js
index 88c273f..669cca7 100644
--- a/pages/profile/badges/index.js
+++ b/pages/profile/badges/index.js
@@ -30,8 +30,8 @@ Page({
wx.showLoading({ title: '加载中...' });
try {
const [levelRes, profileRes] = await Promise.all([
- request.get('/config/level/list'),
- request.get('/profile/detail')
+ request.get('/plant/config/level/list'),
+ request.get('/plant/profile/detail')
]);
this.processData(levelRes, profileRes);
} catch (e) {
diff --git a/pages/profile/badges/level-detail/index.js b/pages/profile/badges/level-detail/index.js
index 20437c3..f2bf963 100644
--- a/pages/profile/badges/level-detail/index.js
+++ b/pages/profile/badges/level-detail/index.js
@@ -25,7 +25,7 @@ Page({
wx.showLoading({ title: '加载中...' });
try {
// Fetch levels
- const levelRes = await request.get('/config/level/list');
+ const levelRes = await request.get('/plant/config/level/list');
let list = [];
@@ -47,7 +47,7 @@ Page({
// Fetch profile if sunlight not passed
let currentSunlight = passedSunlight;
if (currentSunlight === undefined) {
- const profileRes = await request.get('/profile/detail');
+ const profileRes = await request.get('/plant/profile/detail');
currentSunlight = profileRes.totalSunlight || 0;
this.setData({ currentSunlight });
diff --git a/pages/profile/exchange/index.js b/pages/profile/exchange/index.js
index 5427a03..bd74bc7 100644
--- a/pages/profile/exchange/index.js
+++ b/pages/profile/exchange/index.js
@@ -30,7 +30,7 @@ Page({
async fetchProfile() {
try {
- const res = await request.get('/profile/detail');
+ const res = await request.get('/plant/profile/detail');
if (res) {
this.setData({ currentSunlight: res.currentSunlight || 0 });
}
@@ -44,7 +44,7 @@ Page({
this.setData({ isLoading: true, current: 1, items: [] });
}
try {
- const res = await request.get('/exchange/list', {
+ const res = await request.get('/plant/exchange/list', {
current: this.data.current,
pageSize: this.data.pageSize,
type: this.data.activeType
@@ -52,35 +52,11 @@ Page({
const rawList = (res && res.list) ? res.list : [];
const total = (res && res.total) ? res.total : 0;
- const now = Date.now();
- const list = rawList.map(item => {
- const hasStart = !!item.startTime;
- const hasEnd = !!item.endTime;
- const startTs = hasStart ? new Date(item.startTime).getTime() : 0;
- const endTs = hasEnd ? new Date(item.endTime).getTime() : 0;
-
- const notStarted = hasStart && now < startTs;
- const hasEnded = hasEnd && now > endTs;
- const isActive = !notStarted && !hasEnded;
-
- let timeLabel = '';
- if (hasStart && hasEnd) {
- timeLabel = this.formatDate(item.startTime) + ' ~ ' + this.formatDate(item.endTime);
- } else if (hasStart) {
- timeLabel = this.formatDate(item.startTime) + ' 起';
- } else if (hasEnd) {
- timeLabel = '截止 ' + this.formatDate(item.endTime);
- }
-
- return {
- ...item,
- hasTimeLimit: hasStart || hasEnd,
- timeLabel,
- notStarted,
- hasEnded,
- isActive
- };
- });
+ // Backend ExchangeItemInfo: {id, name, desc, imgId, cost, stock, status}
+ const list = rawList.map(item => ({
+ ...item,
+ isActive: item.status === 1 || !item.status
+ }));
this.setData({
items: append ? [...this.data.items, ...list] : list,
@@ -115,12 +91,8 @@ Page({
// Redeem Flow
onItemTap(e) {
const item = e.currentTarget.dataset.item;
- if (item.notStarted) {
- wx.showToast({ title: '活动尚未开始', icon: 'none' });
- return;
- }
- if (item.hasEnded) {
- wx.showToast({ title: '活动已结束', icon: 'none' });
+ if (!item.isActive) {
+ wx.showToast({ title: '当前不可兑换', icon: 'none' });
return;
}
if (item.stock === 0) {
@@ -164,7 +136,7 @@ Page({
wx.showLoading({ title: '兑换中...', mask: true });
try {
- await request.post('/exchange/redeem', {
+ await request.post('/plant/exchange/redeem', {
itemId: item.id,
quantity: 1,
...this.data.redeemForm
diff --git a/pages/profile/exchange/orders/index.js b/pages/profile/exchange/orders/index.js
index c8ace9b..d899491 100644
--- a/pages/profile/exchange/orders/index.js
+++ b/pages/profile/exchange/orders/index.js
@@ -44,7 +44,7 @@ Page({
if (this.data.activeStatus) {
params.status = this.data.activeStatus;
}
- const res = await request.get('/exchange/orders', params);
+ const res = await request.get('/plant/exchange/orders', params);
let list = (res && res.list) ? res.list : [];
const total = (res && res.total) ? res.total : 0;
diff --git a/pages/profile/favorites/index.js b/pages/profile/favorites/index.js
index 208fa10..4d835d1 100644
--- a/pages/profile/favorites/index.js
+++ b/pages/profile/favorites/index.js
@@ -33,7 +33,7 @@ Page({
if (this.data.favTab === 'plant') classType = 1;
if (this.data.favTab === 'post') classType = 2;
- request.post('/profile/star', {
+ request.post('/plant/profile/star', {
current,
pageSize: this.data.pageSize,
class: classType
@@ -111,7 +111,7 @@ Page({
const item = e.currentTarget.dataset.item;
const { id, type } = item;
- const apiPath = type === 'plant' ? '/wiki/star' : '/post/star';
+ const apiPath = type === 'plant' ? '/plant/wiki/star' : '/plant/post/star';
wx.showModal({
title: '提示',
diff --git a/pages/profile/identify-history/index.js b/pages/profile/identify-history/index.js
index 6532157..bfca187 100644
--- a/pages/profile/identify-history/index.js
+++ b/pages/profile/identify-history/index.js
@@ -18,7 +18,7 @@ Page({
async loadRecords() {
this.setData({ loading: true });
try {
- const res = await request.post('/classify/myClassifyLog', {
+ const res = await request.post('/plant/classify/myClassifyLog', {
page: this.data.page,
pageSize: this.data.pageSize,
});
@@ -89,7 +89,7 @@ Page({
success: (res) => {
if (res.confirm) {
wx.showLoading({ title: '删除中' });
- request.post('/classify/deleteClassifyLog', { ids: [id] }).then(() => {
+ request.post('/plant/classify/deleteClassifyLog', { ids: [id] }).then(() => {
wx.hideLoading();
wx.showToast({ title: '删除成功', icon: 'success' });
const newRecords = this.data.records.filter(r => r.id !== id);
diff --git a/pages/profile/index.js b/pages/profile/index.js
index e0c2627..f621746 100644
--- a/pages/profile/index.js
+++ b/pages/profile/index.js
@@ -56,39 +56,43 @@ Page({
// ======== User Info ========
loadUserInfo() {
- request.get('/profile/detail').then(res => {
+ request.get('/plant/profile/detail').then(res => {
if (!res) return;
- // Map stats and level info
- const avatarUrl = res.avatar && res.avatar.url ? res.avatar.url : '';
- const levelInfo = res.level || {};
- const levelTag = levelInfo.level ? `Lv.${levelInfo.level} ${levelInfo.title || ''}` : '';
+ // Backend returns PlantUserProfile: {nickName, avatarId, levelId, currentSunlight, ...}
+ // Fetch avatar URL from file service if avatarId exists
+ const avatarId = res.avatarId || '';
+ let avatarUrl = '';
+ if (avatarId) {
+ request.get('/file/' + avatarId).then(fileInfo => {
+ if (fileInfo && fileInfo.url) {
+ this.setData({ userAvatar: fileInfo.url });
+ }
+ }).catch(() => {});
+ }
this.setData({
- userName: res.nickname || '植物爱好者',
+ userName: res.nickName || '植物爱好者',
userAvatar: avatarUrl,
- currentAvatarId: res.avatarId || (res.avatar ? res.avatar.id : ''),
+ currentAvatarId: avatarId,
// Stats
plantCount: res.plantCount || 0,
taskDoneCount: res.careCount || 0,
postCount: res.postCount || 0,
- // Level (if available)
- userLevel: levelInfo.level || 0,
- userLevelTag: levelTag,
+ // Level (just store levelId, detail fetched on badges page)
+ userLevel: res.levelId || '',
+ userLevelTag: '',
// EXP / Sunlight
userExp: res.currentSunlight || 0,
userSunlight: res.currentSunlight || 0,
- joinedDays: calculateDaysSince(res.createdAt)
+ joinedDays: 0 // createdAt not available in profile response
});
// Update global cache
- const info = {
- ...res,
- avatarId: res.avatarId || (res.avatar ? res.avatar.id : '')
- };
+ const info = { ...res, avatarId: avatarId };
app.globalData.userInfo = info;
wx.setStorageSync('userInfo', info);
@@ -216,9 +220,7 @@ Page({
// Upload new avatar if changed
if (isAvatarChanged) {
const uploadRes = await request.upload(tempAvatar);
- if (uploadRes && uploadRes.file && uploadRes.file.id) {
- finalAvatarId = uploadRes.file.id;
- } else if (uploadRes && uploadRes.id) {
+ if (uploadRes && uploadRes.id) {
finalAvatarId = uploadRes.id;
} else {
throw new Error('Avatar upload failed, no ID returned');
@@ -232,7 +234,7 @@ Page({
};
// Call API
- await request.post('/profile/update', updatePayload);
+ await request.post('/plant/profile/update', updatePayload);
// Update UI State
this.setData({
@@ -246,13 +248,10 @@ Page({
// Update Global Data
const userInfo = app.globalData.userInfo || {};
- userInfo.nickname = finalNickname;
- userInfo.name = finalNickname;
- // Update avatar structure in global store too
+ userInfo.nickName = finalNickname;
if (isAvatarChanged) {
- userInfo.avatar = { ...(userInfo.avatar || {}), url: tempAvatar, id: finalAvatarId };
+ userInfo.avatarId = finalAvatarId;
}
- userInfo.avatarId = finalAvatarId;
app.globalData.userInfo = userInfo;
wx.setStorageSync('userInfo', userInfo);
diff --git a/pages/profile/posts/index.js b/pages/profile/posts/index.js
index b398fca..fe51bdd 100644
--- a/pages/profile/posts/index.js
+++ b/pages/profile/posts/index.js
@@ -36,7 +36,7 @@ Page({
const { pageSize } = this.data;
try {
- const res = await request.post('/post/myPost', {
+ const res = await request.post('/plant/post/myPost', {
current,
pageSize
});
@@ -107,7 +107,7 @@ Page({
wx.showLoading({ title: '删除中...' });
// Use new API: POST /post/delete with ids array
- request.post('/post/delete', { ids: [postId] }).then(() => {
+ request.post('/plant/post/delete', { ids: [postId] }).then(() => {
wx.hideLoading();
wx.showToast({ title: '已删除', icon: 'success' });
// Remove from list locally
diff --git a/pages/tasks/index.js b/pages/tasks/index.js
index 049688f..7398053 100644
--- a/pages/tasks/index.js
+++ b/pages/tasks/index.js
@@ -71,120 +71,133 @@ Page({
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);
+ // Backend returns flat CareTaskInfo list: [{id, plantId, planId, name, icon, targetAction, dueDate, status}]
+ const tasks = Array.isArray(res) ? res : (res.list || []);
+ this.processTaskData(tasks);
}).catch(err => {
console.error('Fetch tasks failed', err);
wx.stopPullDownRefresh();
});
},
- processTaskData(plantTaskVOList) {
+ processTaskData(tasks) {
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) {
- imageUrl = plant.imgList[0].url || '';
+ // Count stats from flat task list
+ tasks.forEach(t => {
+ totalPacketTasks++;
+ if (t.status == 2 || t.status == 3) {
+ completedPacketTasks++;
}
+ });
- const plantGroup = {
- plantName: plant.name,
- plantImage: imageUrl,
- tasks: [], // Placeholder, will fill below
- hasOverdue: vo.hasExpired
- };
+ // Group tasks by plantId, collect unique plant IDs
+ const plantTaskMap = {};
+ const plantIds = [];
+ tasks.forEach(t => {
+ if (!plantTaskMap[t.plantId]) {
+ plantTaskMap[t.plantId] = [];
+ plantIds.push(t.plantId);
+ }
+ plantTaskMap[t.plantId].push(t);
+ });
- const rawTasks = vo.tasks || [];
+ // Fetch plant details for each unique plantId in parallel
+ Promise.all(plantIds.map(id =>
+ request.get('/plant/detail', { id }).catch(() => null)
+ )).then(plantDetails => {
+ const groups = [];
- // 1. Update Global Counters
- rawTasks.forEach(t => {
- totalPacketTasks++;
- if (t.status == 2 || t.status == 3) {
- completedPacketTasks++;
+ plantIds.forEach((plantId, idx) => {
+ const plantDetail = plantDetails[idx];
+ const plant = plantDetail ? plantDetail.plant : null;
+ const imgList = plantDetail ? plantDetail.imgList : null;
+
+ const plantName = plant ? plant.name : '未知植物';
+ let imageUrl = '';
+ if (imgList && imgList.length > 0) {
+ imageUrl = imgList[0].url || '';
}
+
+ const rawTasks = plantTaskMap[plantId] || [];
+
+ // Check if any task is overdue
+ let hasOverdue = false;
+
+ const displayTasks = rawTasks
+ .filter(t => t.status == 1 || t.status == 2)
+ .map(t => {
+ const isCompleted = t.status == 2;
+
+ // Parse icon JSON
+ 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;
+ hasOverdue = 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: plantName,
+ isCompleted: isCompleted,
+ original: t
+ };
+ });
+
+ if (displayTasks.length === 0) return;
+
+ groups.push({
+ plantName: plantName,
+ plantImage: imageUrl,
+ tasks: displayTasks,
+ hasOverdue: hasOverdue
+ });
});
- // 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;
+ // Calculate Progress
+ let progress = 0;
+ if (totalPacketTasks > 0) {
+ progress = Math.round((completedPacketTasks / totalPacketTasks) * 100);
+ }
- // Parse Icon
- let taskIcon = null;
- if (t.icon && t.icon.startsWith('{')) {
- try {
- taskIcon = JSON.parse(t.icon);
- } catch (e) { }
- }
+ // Sorting Groups: Overdue first
+ groups.sort((a, b) => {
+ if (a.hasOverdue && !b.hasOverdue) return -1;
+ if (!a.hasOverdue && b.hasOverdue) return 1;
+ return 0;
+ });
- // 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);
+ this.setData({
+ groupedTasks: groups,
+ progress,
+ tasks: groups
+ });
- 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;
+ wx.stopPullDownRefresh();
+ }).catch(err => {
+ console.error('Fetch plant details failed', err);
+ wx.stopPullDownRefresh();
});
-
- this.setData({
- groupedTasks: groups,
- progress,
- tasks: groups
- });
-
- wx.stopPullDownRefresh();
},
handleTaskClick(e) {
diff --git a/pages/wiki/chat/history/index.js b/pages/wiki/chat/history/index.js
index 1073cb8..7f8c1a4 100644
--- a/pages/wiki/chat/history/index.js
+++ b/pages/wiki/chat/history/index.js
@@ -23,7 +23,7 @@ Page({
const current = reset ? 1 : this.data.current;
this.setData({ loading: true });
- request.get('/plant/chat/history', { current, pageSize: this.data.pageSize })
+ request.post('/plant/chat/history', { current, pageSize: this.data.pageSize })
.then(res => {
const items = (res.list || []).map(item => ({
...item,
@@ -82,7 +82,7 @@ Page({
content: '确定删除这条问答记录吗?',
success: (res) => {
if (res.confirm) {
- request.post('/plant/chat/history/delete', { id }).then(() => {
+ request.post('/plant/chat/history/delete', { ids: [id] }).then(() => {
wx.showToast({ title: '已删除', icon: 'success' });
this.fetchHistory(true);
});
diff --git a/pages/wiki/chat/index.js b/pages/wiki/chat/index.js
index d779032..2dcb6f2 100644
--- a/pages/wiki/chat/index.js
+++ b/pages/wiki/chat/index.js
@@ -96,7 +96,7 @@ Page({
request.stream('/plant/chat/stream', { query }, {
onChunk: (res) => {
- const text = this._decode(res.data);
+ const text = this._decode(res.chunk || res.data);
// Detect non-SSE JSON error (e.g. quota exceeded returns {code:7, msg:"..."})
if (!text.startsWith('data: ')) {
diff --git a/pages/wiki/detail/index.js b/pages/wiki/detail/index.js
index 2f516ab..47b7cc7 100644
--- a/pages/wiki/detail/index.js
+++ b/pages/wiki/detail/index.js
@@ -26,10 +26,10 @@ Page({
},
loadPlantDetail(id) {
- request.get('/wiki/detail', { id: id }).then(res => {
+ request.get('/plant/wiki/detail', { id: id }).then(res => {
const item = res || null;
- if (!item) {
+ if (!item || !item.wiki) {
wx.showToast({ title: '未找到该植物', icon: 'none' });
return;
}
@@ -44,57 +44,62 @@ Page({
setPlantData(item) {
if (!item) return;
- wx.setNavigationBarTitle({ title: item.name });
+ // 兼容两种数据来源:
+ // 1. 从 API 获取: {wiki: {name:...}, imgList: [...]}
+ // 2. 从列表页导航: {id:..., name:..., imgList: [...]} (raw item)
+ const wiki = item.wiki || item;
- // Prepare swiper list
+ wx.setNavigationBarTitle({ title: wiki.name });
+
+ // Prepare swiper list — imgList 在顶层
const swiperList = (item.imgList || []).map(img => img.url);
// Parse lists
- const commonPests = item.pestsDiseases
- ? item.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
+ const commonPests = wiki.pestsDiseases
+ ? wiki.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
: [];
- const aliasesList = item.aliases
- ? item.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
+ const aliasesList = wiki.aliases
+ ? wiki.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
: [];
- const reproductionList = item.reproductionMethod
- ? item.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
+ const reproductionList = wiki.reproductionMethod
+ ? wiki.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
: [];
const diffLabels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' };
const plant = {
- id: item.id,
- name: item.name,
- latinName: item.latinName || '',
- aliases: item.aliases || '',
+ id: wiki.id,
+ name: wiki.name,
+ latinName: wiki.latinName || '',
+ aliases: wiki.aliases || '',
aliasesList,
- genus: item.genus || '',
- distributionArea: item.distributionArea || '',
- difficulty: item.difficulty || 0,
- difficultyLabel: diffLabels[item.difficulty] || '未知',
- isHot: item.isHot === 1,
- lifeCycle: item.lifeCycle || '',
- growthHabit: item.growthHabit || '',
- reproductionMethod: item.reproductionMethod || '',
+ genus: wiki.genus || '',
+ distributionArea: wiki.distributionArea || '',
+ difficulty: wiki.difficulty || 0,
+ difficultyLabel: diffLabels[wiki.difficulty] || '未知',
+ isHot: wiki.isHot === true || wiki.isHot === 1,
+ lifeCycle: wiki.lifeCycle || '',
+ growthHabit: wiki.growthHabit || '',
+ reproductionMethod: wiki.reproductionMethod || '',
reproductionList,
- lightIntensity: item.lightIntensity || '',
- lightType: item.lightType || '',
- optimalTempPeriod: item.optimalTempPeriod || '',
- stem: item.stem || '',
- foliageType: item.foliageType || '',
- foliageColor: item.foliageColor || '',
- foliageShape: item.foliageShape || '',
- height: item.height || 0,
- floweringPeriod: item.floweringPeriod || '',
- floweringColor: item.floweringColor || '',
- floweringShape: item.floweringShape || '',
- flowerDiameter: item.flowerDiameter || 0,
- fruit: item.fruit || '',
- pestsDiseases: item.pestsDiseases || '',
+ lightIntensity: wiki.lightIntensity || '',
+ lightType: wiki.lightType || '',
+ optimalTempPeriod: wiki.optimalTempPeriod || '',
+ stem: wiki.stem || '',
+ foliageType: wiki.foliageType || '',
+ foliageColor: wiki.foliageColor || '',
+ foliageShape: wiki.foliageShape || '',
+ height: wiki.height || 0,
+ floweringPeriod: wiki.floweringPeriod || '',
+ floweringColor: wiki.floweringColor || '',
+ floweringShape: wiki.floweringShape || '',
+ flowerDiameter: wiki.floweringDiameter || 0,
+ fruit: wiki.fruit || '',
+ pestsDiseases: wiki.pestsDiseases || '',
commonPests,
- classes: (item.classes || []).map(c => c.name),
+ classes: (item.classIds || []).map(id => ({ id })),
imgList: item.imgList || [],
- isFavorited: (item.hasStar === 1 || item.hasStar === '1')
+ isFavorited: wiki.isStar === true || wiki.isStar === 1
};
this.setData({
@@ -109,7 +114,7 @@ Page({
const { id, isFavorited } = this.data.plant;
const type = isFavorited ? 2 : 1;
- request.get('/wiki/star', { id, type }).then(() => {
+ request.get('/plant/wiki/star', { id, type }).then(() => {
const newStatus = !isFavorited;
this.setData({
'plant.isFavorited': newStatus
diff --git a/pages/wiki/identify/index.js b/pages/wiki/identify/index.js
index c221ac9..f4bc4e0 100644
--- a/pages/wiki/identify/index.js
+++ b/pages/wiki/identify/index.js
@@ -24,10 +24,13 @@ Page({
this.classifyPlant(imagePath);
},
- classifyPlant(filePath) {
+ async classifyPlant(filePath) {
this.setData({ isLoading: true, hasError: false });
- request.uploadToUrl('/classify/plant', filePath, 'file').then(res => {
+ try {
+ // Directly upload file to classify endpoint without uploading to MinIO file service
+ const res = await request.uploadToUrl('/plant/classify/plant', filePath);
+
const results = res.result || [];
// Map results with percentage scores
@@ -46,10 +49,10 @@ Page({
topResult: mappedResults.length > 0 ? mappedResults[0] : null,
isLoading: false
});
- }).catch(err => {
+ } catch (err) {
console.error('Classify failed', err);
this.setData({ isLoading: false, hasError: true });
- });
+ }
},
// Retry identification
diff --git a/pages/wiki/identify/index.wxml b/pages/wiki/identify/index.wxml
index d12e746..22e55ff 100644
--- a/pages/wiki/identify/index.wxml
+++ b/pages/wiki/identify/index.wxml
@@ -6,6 +6,7 @@
+
diff --git a/pages/wiki/identify/index.wxss b/pages/wiki/identify/index.wxss
index 09fb58c..1258449 100644
--- a/pages/wiki/identify/index.wxss
+++ b/pages/wiki/identify/index.wxss
@@ -44,21 +44,38 @@
display: block;
}
-/* Scan line animation */
+/* Scan overlay breathing effect */
+.scan-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(180deg, rgba(85, 139, 47, 0) 30%, rgba(85, 139, 47, 0.15) 100%);
+ animation: pulseBg 2s ease-in-out infinite alternate;
+ pointer-events: none;
+}
+
+@keyframes pulseBg {
+ 0% { opacity: 0.2; }
+ 100% { opacity: 0.65; }
+}
+
+/* Premium neon scan line animation */
.scan-line {
position: absolute;
left: 0;
right: 0;
- height: 4rpx;
- background: linear-gradient(90deg, transparent, #558B2F, transparent);
- animation: scan 2s ease-in-out infinite;
- box-shadow: 0 0 16rpx rgba(85, 139, 47, 0.5);
+ height: 8rpx;
+ background: linear-gradient(90deg, rgba(85, 139, 47, 0) 0%, rgba(139, 195, 74, 1) 50%, rgba(85, 139, 47, 0) 100%);
+ animation: scan 2.2s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
+ box-shadow: 0 0 20rpx rgba(139, 195, 74, 0.9), 0 0 40rpx rgba(85, 139, 47, 0.5);
}
@keyframes scan {
- 0% { top: 0; }
- 50% { top: 100%; }
- 100% { top: 0; }
+ 0% { top: 0%; opacity: 0.8; }
+ 50% { top: 98%; opacity: 1; }
+ 100% { top: 0%; opacity: 0.8; }
}
.loading-info {
diff --git a/pages/wiki/index.js b/pages/wiki/index.js
index 782acbf..7dccf8d 100644
--- a/pages/wiki/index.js
+++ b/pages/wiki/index.js
@@ -50,7 +50,7 @@ Page({
// Fetch categories from API
fetchCategories() {
- request.get('/wiki-class/list').then(res => {
+ request.get('/plant/wiki-class/list').then(res => {
const list = (res && res.list) || (Array.isArray(res) ? res : []);
this.setData({ categories: list });
}).catch(err => {
@@ -83,7 +83,7 @@ Page({
params.classId = [this.data.activeCategory];
}
- return request.post('/wiki/page', params).then(res => {
+ return request.post('/plant/wiki/page', params).then(res => {
const data = res || {};
const list = data.list || [];
const total = data.total || 0;
@@ -96,8 +96,8 @@ Page({
aliases: item.aliases || '',
genus: item.genus || '',
difficulty: item.difficulty || 0,
- isHot: item.isHot === 1,
- isFavorited: item.hasStar === 1,
+ isHot: item.isHot === true || item.isHot === 1,
+ isFavorited: item.hasStar === 1 || item.hasStar === '1',
image: (item.imgList && item.imgList.length > 0) ? item.imgList[0].url : '',
classes: (item.classes || []).map(c => c.name),
// Pass the full item for detail navigation
@@ -140,7 +140,7 @@ Page({
try {
// Attempting consistent API pattern
- await request.get('/wiki/star', { id, type });
+ await request.get('/plant/wiki/star', { id, type });
const key = `displayedList[${index}].isFavorited`;
this.setData({
diff --git a/project.config.json b/project.config.json
index c7de474..dcb6cc9 100644
--- a/project.config.json
+++ b/project.config.json
@@ -3,8 +3,8 @@
"packOptions": {
"ignore": [
{
- "type": "glob",
- "value": "assets/*.png"
+ "value": "assets/*.png",
+ "type": "glob"
}
],
"include": []
diff --git a/project.private.config.json b/project.private.config.json
index e1ffeca..8d4ed2f 100644
--- a/project.private.config.json
+++ b/project.private.config.json
@@ -1,5 +1,5 @@
{
- "libVersion": "3.14.1",
+ "libVersion": "3.16.1",
"projectname": "plant-mp",
"condition": {},
"setting": {
diff --git a/utils/request.js b/utils/request.js
index c085f83..fe3e4d4 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -132,7 +132,7 @@ class WxRequest {
upload(filePath, name = 'file', formData = {}) {
// Prepare config
let config = {
- url: this.baseUrl + '/oss/upload',
+ url: this.baseUrl + '/file/upload',
header: { ...this.header }, // Copy default headers
filePath: filePath,
name: name,
@@ -320,8 +320,8 @@ class WxRequest {
// Initialize with default instance
const request = new WxRequest({
- //baseUrl: 'http://192.168.0.184:8889',
- baseUrl: 'https://go.sundynix.cn/api',
+ baseUrl: 'http://192.168.100.2:8888/api',
+ //baseUrl: 'https://go.sundynix.cn/api',
header: {
'Content-Type': 'application/json'
}