feat: 后端版本迁移修改
This commit is contained in:
@@ -16,7 +16,7 @@ App({
|
|||||||
wx.login({
|
wx.login({
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code) {
|
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;
|
const token = data.token;
|
||||||
|
|
||||||
if (token && typeof token === 'string') {
|
if (token && typeof token === 'string') {
|
||||||
@@ -25,7 +25,7 @@ App({
|
|||||||
if (this._resolveLogin) this._resolveLogin(token);
|
if (this._resolveLogin) this._resolveLogin(token);
|
||||||
|
|
||||||
// Background Profile Update
|
// Background Profile Update
|
||||||
request.get('/profile/detail').then(userDetail => {
|
request.get('/plant/profile/detail').then(userDetail => {
|
||||||
if (userDetail) {
|
if (userDetail) {
|
||||||
wx.setStorageSync('userInfo', userDetail);
|
wx.setStorageSync('userInfo', userDetail);
|
||||||
this.globalData.userInfo = userDetail;
|
this.globalData.userInfo = userDetail;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
fetchTopics() {
|
fetchTopics() {
|
||||||
request.get('/topic/list').then(res => {
|
request.get('/plant/topic/list').then(res => {
|
||||||
const list = res.list || [];
|
const list = res.list || [];
|
||||||
const topics = list.map(t => t.title);
|
const topics = list.map(t => t.title);
|
||||||
if (topics.length > 0) {
|
if (topics.length > 0) {
|
||||||
@@ -229,8 +229,8 @@ Page({
|
|||||||
if (images.length > 0) {
|
if (images.length > 0) {
|
||||||
const uploadPromises = images.map(filePath => {
|
const uploadPromises = images.map(filePath => {
|
||||||
return request.upload(filePath).then(res => {
|
return request.upload(filePath).then(res => {
|
||||||
// Res structure: { file: { id: "...", url: "..." } }
|
// Backend returns flat FileInfo: {id, url, name, ...}
|
||||||
return res && res.file ? res.file.id : null;
|
return res ? res.id : null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ Page({
|
|||||||
ossIds: ossIds
|
ossIds: ossIds
|
||||||
};
|
};
|
||||||
|
|
||||||
await request.post('/post/publish', payload);
|
await request.post('/plant/post/publish', payload);
|
||||||
|
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '发布成功', icon: 'success' });
|
wx.showToast({ title: '发布成功', icon: 'success' });
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ Page({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Correct API Endpoint and Params
|
// 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: [], ... } }
|
// Handle response structure: { code: 200, data: { list: [], ... } }
|
||||||
// OR if request.js unwraps it: { list: [], ... }
|
// OR if request.js unwraps it: { list: [], ... }
|
||||||
@@ -183,7 +183,7 @@ Page({
|
|||||||
const type = post.likedByMe ? 2 : 1;
|
const type = post.likedByMe ? 2 : 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await request.get('/post/like', { id: postId, type });
|
await request.get('/plant/post/like', { id: postId, type });
|
||||||
|
|
||||||
// Optimistic Update
|
// Optimistic Update
|
||||||
const updatedPosts = this.data.posts.map(p => {
|
const updatedPosts = this.data.posts.map(p => {
|
||||||
@@ -225,7 +225,7 @@ Page({
|
|||||||
const type = post.isFavorited ? 2 : 1; // 1: Collect, 2: Cancel
|
const type = post.isFavorited ? 2 : 1; // 1: Collect, 2: Cancel
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await request.get('/post/star', { id: postId, type });
|
await request.get('/plant/post/star', { id: postId, type });
|
||||||
|
|
||||||
// Optimistic Update
|
// Optimistic Update
|
||||||
const updatedPosts = this.data.posts.map(p => {
|
const updatedPosts = this.data.posts.map(p => {
|
||||||
@@ -283,7 +283,7 @@ Page({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await request.post('/post/comment', {
|
await request.post('/plant/post/comment', {
|
||||||
postId: commentingPostId,
|
postId: commentingPostId,
|
||||||
content: commentText.trim()
|
content: commentText.trim()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -97,11 +97,9 @@ Page({
|
|||||||
request.upload(tempFilePath).then(data => {
|
request.upload(tempFilePath).then(data => {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
|
|
||||||
// User provided response format: { data: { file: { url: ..., id: ... } } }
|
// request.js unwraps data, response is flat FileInfo {id, url, name, ...}
|
||||||
// request.js unwraps 'data', so 'data' here is { file: { ... } }
|
const imageUrl = data?.url || '';
|
||||||
const fileData = data?.file || {};
|
const imageId = data?.id || '';
|
||||||
const imageUrl = fileData.url;
|
|
||||||
const imageId = fileData.id;
|
|
||||||
|
|
||||||
if (imageUrl && imageId) {
|
if (imageUrl && imageId) {
|
||||||
this.setData({
|
this.setData({
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ Page({
|
|||||||
|
|
||||||
async loadBanners() {
|
async loadBanners() {
|
||||||
try {
|
try {
|
||||||
const res = await request.get('/plantBanner/activeList');
|
const res = await request.get('/plant/banner/activeList');
|
||||||
const list = (res.list || []).map(item => item.image ? item.image.url : '');
|
const list = (res.list || []).map(item => item.imageUrl || '');
|
||||||
this.setData({ bannerList: list.filter(Boolean) });
|
this.setData({ bannerList: list.filter(Boolean) });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Load banners failed', err);
|
console.error('Load banners failed', err);
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ Page({
|
|||||||
|
|
||||||
if (eventChannel) {
|
if (eventChannel) {
|
||||||
eventChannel.on('acceptDataFromOpenerPage', (data) => {
|
eventChannel.on('acceptDataFromOpenerPage', (data) => {
|
||||||
if (data && data.plant) {
|
if (data) {
|
||||||
hasReceivedData = true;
|
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];
|
const defaultIcon = CARE_TASK_ICONS.find(i => i.id === 'water') || CARE_TASK_ICONS[0];
|
||||||
|
|
||||||
let tasks = [];
|
let tasks = [];
|
||||||
if (plant.careSchedule) {
|
const carePlansSrc = isRawResponse ? (data.carePlans || []) : (data.carePlans || plant.careSchedule || []);
|
||||||
tasks = plant.careSchedule.map(cp => ({
|
if (carePlansSrc && carePlansSrc.length > 0) {
|
||||||
id: cp.id, name: cp.name, period: cp.period,
|
tasks = carePlansSrc.map(cp => {
|
||||||
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 => {
|
|
||||||
let iconObj = defaultIcon;
|
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) { }
|
try { iconObj = JSON.parse(cp.icon); } catch (e) { }
|
||||||
}
|
}
|
||||||
const iconStr = JSON.stringify(iconObj);
|
const iconStr = JSON.stringify(iconObj);
|
||||||
@@ -89,12 +93,11 @@ Page({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let imageUrl = '', imageId = '';
|
let imageUrl = '', imageId = '';
|
||||||
if (plant.imgList && plant.imgList.length > 0) {
|
if (imgList && imgList.length > 0) {
|
||||||
imageUrl = plant.imgList[0].url || '';
|
imageUrl = imgList[0].url || '';
|
||||||
imageId = plant.imgList[0].id || '';
|
imageId = imgList[0].id || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let adoptionDate = plant.plantTime || '';
|
let adoptionDate = plant.plantTime || '';
|
||||||
if (adoptionDate.includes('T')) adoptionDate = adoptionDate.split('T')[0];
|
if (adoptionDate.includes('T')) adoptionDate = adoptionDate.split('T')[0];
|
||||||
|
|
||||||
@@ -142,9 +145,10 @@ Page({
|
|||||||
wx.showLoading({ title: '上传中...' });
|
wx.showLoading({ title: '上传中...' });
|
||||||
request.upload(tempFilePath).then(data => {
|
request.upload(tempFilePath).then(data => {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
const fileData = data?.file || {};
|
const imageUrl = data?.url || '';
|
||||||
if (fileData.id) {
|
const imageId = data?.id || '';
|
||||||
this.setData({ uploadedImageId: fileData.id, newPlantImage: fileData.url });
|
if (imageId) {
|
||||||
|
this.setData({ uploadedImageId: imageId, newPlantImage: imageUrl });
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Upload Area -->
|
<!-- Upload Area -->
|
||||||
<view class="upload-section">
|
<view class="upload-section">
|
||||||
<view class="image-upload-area {{newPlantImage ? 'has-image' : ''}}" bindtap="showActionSheet">
|
<view class="image-upload-area {{newPlantImage ? 'has-image' : ''}}">
|
||||||
<t-image
|
<t-image
|
||||||
wx:if="{{newPlantImage}}"
|
wx:if="{{newPlantImage}}"
|
||||||
src="{{newPlantImage}}"
|
src="{{newPlantImage}}"
|
||||||
@@ -23,10 +23,6 @@
|
|||||||
<t-icon name="upload" size="64rpx" color="#999" />
|
<t-icon name="upload" size="64rpx" color="#999" />
|
||||||
<text>点击设置封面图</text>
|
<text>点击设置封面图</text>
|
||||||
</view>
|
</view>
|
||||||
<view wx:if="{{newPlantImage}}" class="edit-overlay">
|
|
||||||
<t-icon name="camera" size="32rpx" color="#FFF" />
|
|
||||||
<text>更换照片</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@@ -61,10 +61,7 @@ Page({
|
|||||||
if (this.data.image) {
|
if (this.data.image) {
|
||||||
const uploadRes = await request.upload(this.data.image);
|
const uploadRes = await request.upload(this.data.image);
|
||||||
// Correctly extract ID from nested 'file' object based on API response
|
// Correctly extract ID from nested 'file' object based on API response
|
||||||
if (uploadRes && uploadRes.file && uploadRes.file.id) {
|
if (uploadRes && uploadRes.id) {
|
||||||
ossIds.push(uploadRes.file.id);
|
|
||||||
} else if (uploadRes && uploadRes.id) {
|
|
||||||
// Fallback just in case
|
|
||||||
ossIds.push(uploadRes.id);
|
ossIds.push(uploadRes.id);
|
||||||
} else {
|
} else {
|
||||||
console.warn('Upload response structure mismatch:', uploadRes);
|
console.warn('Upload response structure mismatch:', uploadRes);
|
||||||
|
|||||||
+20
-12
@@ -36,11 +36,17 @@ Page({
|
|||||||
|
|
||||||
initData(id) {
|
initData(id) {
|
||||||
request.get('/plant/detail', { id }).then(plant => {
|
request.get('/plant/detail', { id }).then(plant => {
|
||||||
const swiperImages = plant.imgList.map(img => {
|
// Legacy handler returns {plant: PlantInfo, carePlans: [...], growthRecords: [...], imgList: [...], careRecords: [...]}
|
||||||
return img.url;
|
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
|
// Parse carePlans icon if it's a string
|
||||||
const carePlans = (plant.carePlans || []).map(cp => {
|
const carePlans = carePlansList.map(cp => {
|
||||||
let iconObj = {};
|
let iconObj = {};
|
||||||
if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
|
if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
|
||||||
try {
|
try {
|
||||||
@@ -54,16 +60,16 @@ Page({
|
|||||||
|
|
||||||
// Calculate days planted and format date
|
// Calculate days planted and format date
|
||||||
let adoptionDate = '未知';
|
let adoptionDate = '未知';
|
||||||
const daysPlanted = calculateDaysSince(plant.plantTime);
|
const daysPlanted = calculateDaysSince(plantData.plantTime);
|
||||||
const ageBadge = getPlantAgeBadge(daysPlanted);
|
const ageBadge = getPlantAgeBadge(daysPlanted);
|
||||||
if (plant.plantTime) {
|
if (plantData.plantTime) {
|
||||||
adoptionDate = plant.plantTime.split('T')[0];
|
adoptionDate = plantData.plantTime.split('T')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
currentPlant: {
|
currentPlant: {
|
||||||
...plant,
|
...plantData,
|
||||||
location: plant.placement || '',
|
location: plantData.placement || '',
|
||||||
adoptionDate: adoptionDate,
|
adoptionDate: adoptionDate,
|
||||||
daysPlanted: daysPlanted,
|
daysPlanted: daysPlanted,
|
||||||
ageBadge: ageBadge,
|
ageBadge: ageBadge,
|
||||||
@@ -71,8 +77,8 @@ Page({
|
|||||||
},
|
},
|
||||||
swiperImages: swiperImages,
|
swiperImages: swiperImages,
|
||||||
// Map logs and records directly from plant detail response
|
// Map logs and records directly from plant detail response
|
||||||
careLogs: this.processLogs(plant.careRecords || []),
|
careLogs: this.processLogs(careRecordsList),
|
||||||
records: (plant.growthRecords || plant.recordList || []).map(item => {
|
records: growthRecordsList.map(item => {
|
||||||
// Extract image URL safely
|
// Extract image URL safely
|
||||||
let imageUrl = '';
|
let imageUrl = '';
|
||||||
if (item.imgList && item.imgList.length > 0) {
|
if (item.imgList && item.imgList.length > 0) {
|
||||||
@@ -235,7 +241,9 @@ Page({
|
|||||||
success: (res) => {
|
success: (res) => {
|
||||||
// Send current data to the opened page
|
// Send current data to the opened page
|
||||||
res.eventChannel.emit('acceptDataFromOpenerPage', {
|
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ Page({
|
|||||||
try {
|
try {
|
||||||
// Parallel Fetch: Config Tree & User Badges
|
// Parallel Fetch: Config Tree & User Badges
|
||||||
const [treeRes, userBadgesRes] = await Promise.all([
|
const [treeRes, userBadgesRes] = await Promise.all([
|
||||||
request.get('/config/badge/tree'),
|
request.get('/plant/config/badge/tree'),
|
||||||
request.get('/profile/badge')
|
request.get('/plant/profile/badge')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const list = Array.isArray(treeRes) ? treeRes : (treeRes.data || []);
|
const list = Array.isArray(treeRes) ? treeRes : (treeRes.data || []);
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ Page({
|
|||||||
wx.showLoading({ title: '加载中...' });
|
wx.showLoading({ title: '加载中...' });
|
||||||
try {
|
try {
|
||||||
const [levelRes, profileRes] = await Promise.all([
|
const [levelRes, profileRes] = await Promise.all([
|
||||||
request.get('/config/level/list'),
|
request.get('/plant/config/level/list'),
|
||||||
request.get('/profile/detail')
|
request.get('/plant/profile/detail')
|
||||||
]);
|
]);
|
||||||
this.processData(levelRes, profileRes);
|
this.processData(levelRes, profileRes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Page({
|
|||||||
wx.showLoading({ title: '加载中...' });
|
wx.showLoading({ title: '加载中...' });
|
||||||
try {
|
try {
|
||||||
// Fetch levels
|
// Fetch levels
|
||||||
const levelRes = await request.get('/config/level/list');
|
const levelRes = await request.get('/plant/config/level/list');
|
||||||
|
|
||||||
|
|
||||||
let list = [];
|
let list = [];
|
||||||
@@ -47,7 +47,7 @@ Page({
|
|||||||
// Fetch profile if sunlight not passed
|
// Fetch profile if sunlight not passed
|
||||||
let currentSunlight = passedSunlight;
|
let currentSunlight = passedSunlight;
|
||||||
if (currentSunlight === undefined) {
|
if (currentSunlight === undefined) {
|
||||||
const profileRes = await request.get('/profile/detail');
|
const profileRes = await request.get('/plant/profile/detail');
|
||||||
|
|
||||||
currentSunlight = profileRes.totalSunlight || 0;
|
currentSunlight = profileRes.totalSunlight || 0;
|
||||||
this.setData({ currentSunlight });
|
this.setData({ currentSunlight });
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Page({
|
|||||||
|
|
||||||
async fetchProfile() {
|
async fetchProfile() {
|
||||||
try {
|
try {
|
||||||
const res = await request.get('/profile/detail');
|
const res = await request.get('/plant/profile/detail');
|
||||||
if (res) {
|
if (res) {
|
||||||
this.setData({ currentSunlight: res.currentSunlight || 0 });
|
this.setData({ currentSunlight: res.currentSunlight || 0 });
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ Page({
|
|||||||
this.setData({ isLoading: true, current: 1, items: [] });
|
this.setData({ isLoading: true, current: 1, items: [] });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await request.get('/exchange/list', {
|
const res = await request.get('/plant/exchange/list', {
|
||||||
current: this.data.current,
|
current: this.data.current,
|
||||||
pageSize: this.data.pageSize,
|
pageSize: this.data.pageSize,
|
||||||
type: this.data.activeType
|
type: this.data.activeType
|
||||||
@@ -52,35 +52,11 @@ Page({
|
|||||||
const rawList = (res && res.list) ? res.list : [];
|
const rawList = (res && res.list) ? res.list : [];
|
||||||
const total = (res && res.total) ? res.total : 0;
|
const total = (res && res.total) ? res.total : 0;
|
||||||
|
|
||||||
const now = Date.now();
|
// Backend ExchangeItemInfo: {id, name, desc, imgId, cost, stock, status}
|
||||||
const list = rawList.map(item => {
|
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,
|
...item,
|
||||||
hasTimeLimit: hasStart || hasEnd,
|
isActive: item.status === 1 || !item.status
|
||||||
timeLabel,
|
}));
|
||||||
notStarted,
|
|
||||||
hasEnded,
|
|
||||||
isActive
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
items: append ? [...this.data.items, ...list] : list,
|
items: append ? [...this.data.items, ...list] : list,
|
||||||
@@ -115,12 +91,8 @@ Page({
|
|||||||
// Redeem Flow
|
// Redeem Flow
|
||||||
onItemTap(e) {
|
onItemTap(e) {
|
||||||
const item = e.currentTarget.dataset.item;
|
const item = e.currentTarget.dataset.item;
|
||||||
if (item.notStarted) {
|
if (!item.isActive) {
|
||||||
wx.showToast({ title: '活动尚未开始', icon: 'none' });
|
wx.showToast({ title: '当前不可兑换', icon: 'none' });
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (item.hasEnded) {
|
|
||||||
wx.showToast({ title: '活动已结束', icon: 'none' });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (item.stock === 0) {
|
if (item.stock === 0) {
|
||||||
@@ -164,7 +136,7 @@ Page({
|
|||||||
|
|
||||||
wx.showLoading({ title: '兑换中...', mask: true });
|
wx.showLoading({ title: '兑换中...', mask: true });
|
||||||
try {
|
try {
|
||||||
await request.post('/exchange/redeem', {
|
await request.post('/plant/exchange/redeem', {
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
...this.data.redeemForm
|
...this.data.redeemForm
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Page({
|
|||||||
if (this.data.activeStatus) {
|
if (this.data.activeStatus) {
|
||||||
params.status = 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 : [];
|
let list = (res && res.list) ? res.list : [];
|
||||||
const total = (res && res.total) ? res.total : 0;
|
const total = (res && res.total) ? res.total : 0;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ Page({
|
|||||||
if (this.data.favTab === 'plant') classType = 1;
|
if (this.data.favTab === 'plant') classType = 1;
|
||||||
if (this.data.favTab === 'post') classType = 2;
|
if (this.data.favTab === 'post') classType = 2;
|
||||||
|
|
||||||
request.post('/profile/star', {
|
request.post('/plant/profile/star', {
|
||||||
current,
|
current,
|
||||||
pageSize: this.data.pageSize,
|
pageSize: this.data.pageSize,
|
||||||
class: classType
|
class: classType
|
||||||
@@ -111,7 +111,7 @@ Page({
|
|||||||
const item = e.currentTarget.dataset.item;
|
const item = e.currentTarget.dataset.item;
|
||||||
const { id, type } = 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({
|
wx.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Page({
|
|||||||
async loadRecords() {
|
async loadRecords() {
|
||||||
this.setData({ loading: true });
|
this.setData({ loading: true });
|
||||||
try {
|
try {
|
||||||
const res = await request.post('/classify/myClassifyLog', {
|
const res = await request.post('/plant/classify/myClassifyLog', {
|
||||||
page: this.data.page,
|
page: this.data.page,
|
||||||
pageSize: this.data.pageSize,
|
pageSize: this.data.pageSize,
|
||||||
});
|
});
|
||||||
@@ -89,7 +89,7 @@ Page({
|
|||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
wx.showLoading({ title: '删除中' });
|
wx.showLoading({ title: '删除中' });
|
||||||
request.post('/classify/deleteClassifyLog', { ids: [id] }).then(() => {
|
request.post('/plant/classify/deleteClassifyLog', { ids: [id] }).then(() => {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '删除成功', icon: 'success' });
|
wx.showToast({ title: '删除成功', icon: 'success' });
|
||||||
const newRecords = this.data.records.filter(r => r.id !== id);
|
const newRecords = this.data.records.filter(r => r.id !== id);
|
||||||
|
|||||||
+23
-24
@@ -56,39 +56,43 @@ Page({
|
|||||||
|
|
||||||
// ======== User Info ========
|
// ======== User Info ========
|
||||||
loadUserInfo() {
|
loadUserInfo() {
|
||||||
request.get('/profile/detail').then(res => {
|
request.get('/plant/profile/detail').then(res => {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
|
|
||||||
// Map stats and level info
|
// Backend returns PlantUserProfile: {nickName, avatarId, levelId, currentSunlight, ...}
|
||||||
const avatarUrl = res.avatar && res.avatar.url ? res.avatar.url : '';
|
// Fetch avatar URL from file service if avatarId exists
|
||||||
const levelInfo = res.level || {};
|
const avatarId = res.avatarId || '';
|
||||||
const levelTag = levelInfo.level ? `Lv.${levelInfo.level} ${levelInfo.title || ''}` : '';
|
let avatarUrl = '';
|
||||||
|
if (avatarId) {
|
||||||
|
request.get('/file/' + avatarId).then(fileInfo => {
|
||||||
|
if (fileInfo && fileInfo.url) {
|
||||||
|
this.setData({ userAvatar: fileInfo.url });
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
userName: res.nickname || '植物爱好者',
|
userName: res.nickName || '植物爱好者',
|
||||||
userAvatar: avatarUrl,
|
userAvatar: avatarUrl,
|
||||||
currentAvatarId: res.avatarId || (res.avatar ? res.avatar.id : ''),
|
currentAvatarId: avatarId,
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
plantCount: res.plantCount || 0,
|
plantCount: res.plantCount || 0,
|
||||||
taskDoneCount: res.careCount || 0,
|
taskDoneCount: res.careCount || 0,
|
||||||
postCount: res.postCount || 0,
|
postCount: res.postCount || 0,
|
||||||
|
|
||||||
// Level (if available)
|
// Level (just store levelId, detail fetched on badges page)
|
||||||
userLevel: levelInfo.level || 0,
|
userLevel: res.levelId || '',
|
||||||
userLevelTag: levelTag,
|
userLevelTag: '',
|
||||||
|
|
||||||
// EXP / Sunlight
|
// EXP / Sunlight
|
||||||
userExp: res.currentSunlight || 0,
|
userExp: res.currentSunlight || 0,
|
||||||
userSunlight: res.currentSunlight || 0,
|
userSunlight: res.currentSunlight || 0,
|
||||||
joinedDays: calculateDaysSince(res.createdAt)
|
joinedDays: 0 // createdAt not available in profile response
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update global cache
|
// Update global cache
|
||||||
const info = {
|
const info = { ...res, avatarId: avatarId };
|
||||||
...res,
|
|
||||||
avatarId: res.avatarId || (res.avatar ? res.avatar.id : '')
|
|
||||||
};
|
|
||||||
app.globalData.userInfo = info;
|
app.globalData.userInfo = info;
|
||||||
wx.setStorageSync('userInfo', info);
|
wx.setStorageSync('userInfo', info);
|
||||||
|
|
||||||
@@ -216,9 +220,7 @@ Page({
|
|||||||
// Upload new avatar if changed
|
// Upload new avatar if changed
|
||||||
if (isAvatarChanged) {
|
if (isAvatarChanged) {
|
||||||
const uploadRes = await request.upload(tempAvatar);
|
const uploadRes = await request.upload(tempAvatar);
|
||||||
if (uploadRes && uploadRes.file && uploadRes.file.id) {
|
if (uploadRes && uploadRes.id) {
|
||||||
finalAvatarId = uploadRes.file.id;
|
|
||||||
} else if (uploadRes && uploadRes.id) {
|
|
||||||
finalAvatarId = uploadRes.id;
|
finalAvatarId = uploadRes.id;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Avatar upload failed, no ID returned');
|
throw new Error('Avatar upload failed, no ID returned');
|
||||||
@@ -232,7 +234,7 @@ Page({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Call API
|
// Call API
|
||||||
await request.post('/profile/update', updatePayload);
|
await request.post('/plant/profile/update', updatePayload);
|
||||||
|
|
||||||
// Update UI State
|
// Update UI State
|
||||||
this.setData({
|
this.setData({
|
||||||
@@ -246,13 +248,10 @@ Page({
|
|||||||
|
|
||||||
// Update Global Data
|
// Update Global Data
|
||||||
const userInfo = app.globalData.userInfo || {};
|
const userInfo = app.globalData.userInfo || {};
|
||||||
userInfo.nickname = finalNickname;
|
userInfo.nickName = finalNickname;
|
||||||
userInfo.name = finalNickname;
|
|
||||||
// Update avatar structure in global store too
|
|
||||||
if (isAvatarChanged) {
|
if (isAvatarChanged) {
|
||||||
userInfo.avatar = { ...(userInfo.avatar || {}), url: tempAvatar, id: finalAvatarId };
|
|
||||||
}
|
|
||||||
userInfo.avatarId = finalAvatarId;
|
userInfo.avatarId = finalAvatarId;
|
||||||
|
}
|
||||||
|
|
||||||
app.globalData.userInfo = userInfo;
|
app.globalData.userInfo = userInfo;
|
||||||
wx.setStorageSync('userInfo', userInfo);
|
wx.setStorageSync('userInfo', userInfo);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Page({
|
|||||||
const { pageSize } = this.data;
|
const { pageSize } = this.data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await request.post('/post/myPost', {
|
const res = await request.post('/plant/post/myPost', {
|
||||||
current,
|
current,
|
||||||
pageSize
|
pageSize
|
||||||
});
|
});
|
||||||
@@ -107,7 +107,7 @@ Page({
|
|||||||
wx.showLoading({ title: '删除中...' });
|
wx.showLoading({ title: '删除中...' });
|
||||||
|
|
||||||
// Use new API: POST /post/delete with ids array
|
// 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.hideLoading();
|
||||||
wx.showToast({ title: '已删除', icon: 'success' });
|
wx.showToast({ title: '已删除', icon: 'success' });
|
||||||
// Remove from list locally
|
// Remove from list locally
|
||||||
|
|||||||
+55
-42
@@ -71,59 +71,69 @@ Page({
|
|||||||
|
|
||||||
fetchTodayTasks() {
|
fetchTodayTasks() {
|
||||||
request.get('/plant/todayTask').then(res => {
|
request.get('/plant/todayTask').then(res => {
|
||||||
// Check if res is array (list of PlantTaskVO)
|
// Backend returns flat CareTaskInfo list: [{id, plantId, planId, name, icon, targetAction, dueDate, status}]
|
||||||
const list = Array.isArray(res) ? res : (res.list || []);
|
const tasks = Array.isArray(res) ? res : (res.list || []);
|
||||||
this.processTaskData(list);
|
this.processTaskData(tasks);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('Fetch tasks failed', err);
|
console.error('Fetch tasks failed', err);
|
||||||
wx.stopPullDownRefresh();
|
wx.stopPullDownRefresh();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
processTaskData(plantTaskVOList) {
|
processTaskData(tasks) {
|
||||||
let totalPacketTasks = 0;
|
let totalPacketTasks = 0;
|
||||||
let completedPacketTasks = 0;
|
let completedPacketTasks = 0;
|
||||||
|
|
||||||
const groups = plantTaskVOList.map(vo => {
|
// Count stats from flat task list
|
||||||
const plant = vo.MyPlant || vo.myPlant;
|
tasks.forEach(t => {
|
||||||
if (!plant) return null;
|
|
||||||
|
|
||||||
// Parse Image
|
|
||||||
let imageUrl = '';
|
|
||||||
if (plant.imgList && plant.imgList.length > 0) {
|
|
||||||
imageUrl = plant.imgList[0].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++;
|
totalPacketTasks++;
|
||||||
if (t.status == 2 || t.status == 3) {
|
if (t.status == 2 || t.status == 3) {
|
||||||
completedPacketTasks++;
|
completedPacketTasks++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Filter and Map Tasks for Display
|
// 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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 = [];
|
||||||
|
|
||||||
|
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
|
const displayTasks = rawTasks
|
||||||
.filter(t => t.status == 1 || t.status == 2)
|
.filter(t => t.status == 1 || t.status == 2)
|
||||||
.map(t => {
|
.map(t => {
|
||||||
// Status: 1 Pending, 2 Done
|
|
||||||
const isCompleted = t.status == 2;
|
const isCompleted = t.status == 2;
|
||||||
|
|
||||||
// Parse Icon
|
// Parse icon JSON
|
||||||
let taskIcon = null;
|
let taskIcon = null;
|
||||||
if (t.icon && t.icon.startsWith('{')) {
|
if (t.icon && t.icon.startsWith('{')) {
|
||||||
try {
|
try { taskIcon = JSON.parse(t.icon); } catch (e) {}
|
||||||
taskIcon = JSON.parse(t.icon);
|
|
||||||
} catch (e) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check overdue (only for pending tasks)
|
// Check overdue (only for pending tasks)
|
||||||
@@ -136,6 +146,7 @@ Page({
|
|||||||
|
|
||||||
if (due < today) {
|
if (due < today) {
|
||||||
isOverdue = true;
|
isOverdue = true;
|
||||||
|
hasOverdue = true;
|
||||||
const diffTime = Math.abs(today - due);
|
const diffTime = Math.abs(today - due);
|
||||||
overdueDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
overdueDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
}
|
}
|
||||||
@@ -147,23 +158,21 @@ Page({
|
|||||||
taskIcon: taskIcon,
|
taskIcon: taskIcon,
|
||||||
isOverdue: isOverdue,
|
isOverdue: isOverdue,
|
||||||
overdueDays: overdueDays,
|
overdueDays: overdueDays,
|
||||||
plantName: plant.name,
|
plantName: plantName,
|
||||||
isCompleted: isCompleted,
|
isCompleted: isCompleted,
|
||||||
original: t
|
original: t
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sorting Removed: Tasks stay in original order
|
if (displayTasks.length === 0) return;
|
||||||
// displayTasks.sort((a, b) => {
|
|
||||||
// if (a.isCompleted === b.isCompleted) return 0;
|
|
||||||
// return a.isCompleted ? 1 : -1;
|
|
||||||
// });
|
|
||||||
|
|
||||||
plantGroup.tasks = displayTasks;
|
groups.push({
|
||||||
|
plantName: plantName,
|
||||||
if (plantGroup.tasks.length === 0) return null;
|
plantImage: imageUrl,
|
||||||
return plantGroup;
|
tasks: displayTasks,
|
||||||
}).filter(g => g !== null);
|
hasOverdue: hasOverdue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Calculate Progress
|
// Calculate Progress
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
@@ -185,6 +194,10 @@ Page({
|
|||||||
});
|
});
|
||||||
|
|
||||||
wx.stopPullDownRefresh();
|
wx.stopPullDownRefresh();
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Fetch plant details failed', err);
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleTaskClick(e) {
|
handleTaskClick(e) {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Page({
|
|||||||
const current = reset ? 1 : this.data.current;
|
const current = reset ? 1 : this.data.current;
|
||||||
this.setData({ loading: true });
|
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 => {
|
.then(res => {
|
||||||
const items = (res.list || []).map(item => ({
|
const items = (res.list || []).map(item => ({
|
||||||
...item,
|
...item,
|
||||||
@@ -82,7 +82,7 @@ Page({
|
|||||||
content: '确定删除这条问答记录吗?',
|
content: '确定删除这条问答记录吗?',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
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' });
|
wx.showToast({ title: '已删除', icon: 'success' });
|
||||||
this.fetchHistory(true);
|
this.fetchHistory(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ Page({
|
|||||||
|
|
||||||
request.stream('/plant/chat/stream', { query }, {
|
request.stream('/plant/chat/stream', { query }, {
|
||||||
onChunk: (res) => {
|
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:"..."})
|
// Detect non-SSE JSON error (e.g. quota exceeded returns {code:7, msg:"..."})
|
||||||
if (!text.startsWith('data: ')) {
|
if (!text.startsWith('data: ')) {
|
||||||
|
|||||||
+44
-39
@@ -26,10 +26,10 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadPlantDetail(id) {
|
loadPlantDetail(id) {
|
||||||
request.get('/wiki/detail', { id: id }).then(res => {
|
request.get('/plant/wiki/detail', { id: id }).then(res => {
|
||||||
const item = res || null;
|
const item = res || null;
|
||||||
|
|
||||||
if (!item) {
|
if (!item || !item.wiki) {
|
||||||
wx.showToast({ title: '未找到该植物', icon: 'none' });
|
wx.showToast({ title: '未找到该植物', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -44,57 +44,62 @@ Page({
|
|||||||
setPlantData(item) {
|
setPlantData(item) {
|
||||||
if (!item) return;
|
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);
|
const swiperList = (item.imgList || []).map(img => img.url);
|
||||||
|
|
||||||
// Parse lists
|
// Parse lists
|
||||||
const commonPests = item.pestsDiseases
|
const commonPests = wiki.pestsDiseases
|
||||||
? item.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
|
? wiki.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
const aliasesList = item.aliases
|
const aliasesList = wiki.aliases
|
||||||
? item.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
? wiki.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
const reproductionList = item.reproductionMethod
|
const reproductionList = wiki.reproductionMethod
|
||||||
? item.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
? wiki.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const diffLabels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' };
|
const diffLabels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' };
|
||||||
|
|
||||||
const plant = {
|
const plant = {
|
||||||
id: item.id,
|
id: wiki.id,
|
||||||
name: item.name,
|
name: wiki.name,
|
||||||
latinName: item.latinName || '',
|
latinName: wiki.latinName || '',
|
||||||
aliases: item.aliases || '',
|
aliases: wiki.aliases || '',
|
||||||
aliasesList,
|
aliasesList,
|
||||||
genus: item.genus || '',
|
genus: wiki.genus || '',
|
||||||
distributionArea: item.distributionArea || '',
|
distributionArea: wiki.distributionArea || '',
|
||||||
difficulty: item.difficulty || 0,
|
difficulty: wiki.difficulty || 0,
|
||||||
difficultyLabel: diffLabels[item.difficulty] || '未知',
|
difficultyLabel: diffLabels[wiki.difficulty] || '未知',
|
||||||
isHot: item.isHot === 1,
|
isHot: wiki.isHot === true || wiki.isHot === 1,
|
||||||
lifeCycle: item.lifeCycle || '',
|
lifeCycle: wiki.lifeCycle || '',
|
||||||
growthHabit: item.growthHabit || '',
|
growthHabit: wiki.growthHabit || '',
|
||||||
reproductionMethod: item.reproductionMethod || '',
|
reproductionMethod: wiki.reproductionMethod || '',
|
||||||
reproductionList,
|
reproductionList,
|
||||||
lightIntensity: item.lightIntensity || '',
|
lightIntensity: wiki.lightIntensity || '',
|
||||||
lightType: item.lightType || '',
|
lightType: wiki.lightType || '',
|
||||||
optimalTempPeriod: item.optimalTempPeriod || '',
|
optimalTempPeriod: wiki.optimalTempPeriod || '',
|
||||||
stem: item.stem || '',
|
stem: wiki.stem || '',
|
||||||
foliageType: item.foliageType || '',
|
foliageType: wiki.foliageType || '',
|
||||||
foliageColor: item.foliageColor || '',
|
foliageColor: wiki.foliageColor || '',
|
||||||
foliageShape: item.foliageShape || '',
|
foliageShape: wiki.foliageShape || '',
|
||||||
height: item.height || 0,
|
height: wiki.height || 0,
|
||||||
floweringPeriod: item.floweringPeriod || '',
|
floweringPeriod: wiki.floweringPeriod || '',
|
||||||
floweringColor: item.floweringColor || '',
|
floweringColor: wiki.floweringColor || '',
|
||||||
floweringShape: item.floweringShape || '',
|
floweringShape: wiki.floweringShape || '',
|
||||||
flowerDiameter: item.flowerDiameter || 0,
|
flowerDiameter: wiki.floweringDiameter || 0,
|
||||||
fruit: item.fruit || '',
|
fruit: wiki.fruit || '',
|
||||||
pestsDiseases: item.pestsDiseases || '',
|
pestsDiseases: wiki.pestsDiseases || '',
|
||||||
commonPests,
|
commonPests,
|
||||||
classes: (item.classes || []).map(c => c.name),
|
classes: (item.classIds || []).map(id => ({ id })),
|
||||||
imgList: item.imgList || [],
|
imgList: item.imgList || [],
|
||||||
isFavorited: (item.hasStar === 1 || item.hasStar === '1')
|
isFavorited: wiki.isStar === true || wiki.isStar === 1
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
@@ -109,7 +114,7 @@ Page({
|
|||||||
const { id, isFavorited } = this.data.plant;
|
const { id, isFavorited } = this.data.plant;
|
||||||
const type = isFavorited ? 2 : 1;
|
const type = isFavorited ? 2 : 1;
|
||||||
|
|
||||||
request.get('/wiki/star', { id, type }).then(() => {
|
request.get('/plant/wiki/star', { id, type }).then(() => {
|
||||||
const newStatus = !isFavorited;
|
const newStatus = !isFavorited;
|
||||||
this.setData({
|
this.setData({
|
||||||
'plant.isFavorited': newStatus
|
'plant.isFavorited': newStatus
|
||||||
|
|||||||
@@ -24,10 +24,13 @@ Page({
|
|||||||
this.classifyPlant(imagePath);
|
this.classifyPlant(imagePath);
|
||||||
},
|
},
|
||||||
|
|
||||||
classifyPlant(filePath) {
|
async classifyPlant(filePath) {
|
||||||
this.setData({ isLoading: true, hasError: false });
|
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 || [];
|
const results = res.result || [];
|
||||||
|
|
||||||
// Map results with percentage scores
|
// Map results with percentage scores
|
||||||
@@ -46,10 +49,10 @@ Page({
|
|||||||
topResult: mappedResults.length > 0 ? mappedResults[0] : null,
|
topResult: mappedResults.length > 0 ? mappedResults[0] : null,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
} catch (err) {
|
||||||
console.error('Classify failed', err);
|
console.error('Classify failed', err);
|
||||||
this.setData({ isLoading: false, hasError: true });
|
this.setData({ isLoading: false, hasError: true });
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Retry identification
|
// Retry identification
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<view class="state-card">
|
<view class="state-card">
|
||||||
<view class="loading-image-wrap">
|
<view class="loading-image-wrap">
|
||||||
<image src="{{imagePath}}" mode="aspectFill" class="loading-preview" bindtap="previewImage" />
|
<image src="{{imagePath}}" mode="aspectFill" class="loading-preview" bindtap="previewImage" />
|
||||||
|
<view class="scan-overlay"></view>
|
||||||
<view class="scan-line"></view>
|
<view class="scan-line"></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="loading-info">
|
<view class="loading-info">
|
||||||
|
|||||||
@@ -44,21 +44,38 @@
|
|||||||
display: block;
|
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 {
|
.scan-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 4rpx;
|
height: 8rpx;
|
||||||
background: linear-gradient(90deg, transparent, #558B2F, transparent);
|
background: linear-gradient(90deg, rgba(85, 139, 47, 0) 0%, rgba(139, 195, 74, 1) 50%, rgba(85, 139, 47, 0) 100%);
|
||||||
animation: scan 2s ease-in-out infinite;
|
animation: scan 2.2s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
|
||||||
box-shadow: 0 0 16rpx rgba(85, 139, 47, 0.5);
|
box-shadow: 0 0 20rpx rgba(139, 195, 74, 0.9), 0 0 40rpx rgba(85, 139, 47, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes scan {
|
@keyframes scan {
|
||||||
0% { top: 0; }
|
0% { top: 0%; opacity: 0.8; }
|
||||||
50% { top: 100%; }
|
50% { top: 98%; opacity: 1; }
|
||||||
100% { top: 0; }
|
100% { top: 0%; opacity: 0.8; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-info {
|
.loading-info {
|
||||||
|
|||||||
+5
-5
@@ -50,7 +50,7 @@ Page({
|
|||||||
|
|
||||||
// Fetch categories from API
|
// Fetch categories from API
|
||||||
fetchCategories() {
|
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 : []);
|
const list = (res && res.list) || (Array.isArray(res) ? res : []);
|
||||||
this.setData({ categories: list });
|
this.setData({ categories: list });
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -83,7 +83,7 @@ Page({
|
|||||||
params.classId = [this.data.activeCategory];
|
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 data = res || {};
|
||||||
const list = data.list || [];
|
const list = data.list || [];
|
||||||
const total = data.total || 0;
|
const total = data.total || 0;
|
||||||
@@ -96,8 +96,8 @@ Page({
|
|||||||
aliases: item.aliases || '',
|
aliases: item.aliases || '',
|
||||||
genus: item.genus || '',
|
genus: item.genus || '',
|
||||||
difficulty: item.difficulty || 0,
|
difficulty: item.difficulty || 0,
|
||||||
isHot: item.isHot === 1,
|
isHot: item.isHot === true || item.isHot === 1,
|
||||||
isFavorited: item.hasStar === 1,
|
isFavorited: item.hasStar === 1 || item.hasStar === '1',
|
||||||
image: (item.imgList && item.imgList.length > 0) ? item.imgList[0].url : '',
|
image: (item.imgList && item.imgList.length > 0) ? item.imgList[0].url : '',
|
||||||
classes: (item.classes || []).map(c => c.name),
|
classes: (item.classes || []).map(c => c.name),
|
||||||
// Pass the full item for detail navigation
|
// Pass the full item for detail navigation
|
||||||
@@ -140,7 +140,7 @@ Page({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Attempting consistent API pattern
|
// Attempting consistent API pattern
|
||||||
await request.get('/wiki/star', { id, type });
|
await request.get('/plant/wiki/star', { id, type });
|
||||||
|
|
||||||
const key = `displayedList[${index}].isFavorited`;
|
const key = `displayedList[${index}].isFavorited`;
|
||||||
this.setData({
|
this.setData({
|
||||||
|
|||||||
+2
-2
@@ -3,8 +3,8 @@
|
|||||||
"packOptions": {
|
"packOptions": {
|
||||||
"ignore": [
|
"ignore": [
|
||||||
{
|
{
|
||||||
"type": "glob",
|
"value": "assets/*.png",
|
||||||
"value": "assets/*.png"
|
"type": "glob"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": []
|
"include": []
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"libVersion": "3.14.1",
|
"libVersion": "3.16.1",
|
||||||
"projectname": "plant-mp",
|
"projectname": "plant-mp",
|
||||||
"condition": {},
|
"condition": {},
|
||||||
"setting": {
|
"setting": {
|
||||||
|
|||||||
+3
-3
@@ -132,7 +132,7 @@ class WxRequest {
|
|||||||
upload(filePath, name = 'file', formData = {}) {
|
upload(filePath, name = 'file', formData = {}) {
|
||||||
// Prepare config
|
// Prepare config
|
||||||
let config = {
|
let config = {
|
||||||
url: this.baseUrl + '/oss/upload',
|
url: this.baseUrl + '/file/upload',
|
||||||
header: { ...this.header }, // Copy default headers
|
header: { ...this.header }, // Copy default headers
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
name: name,
|
name: name,
|
||||||
@@ -320,8 +320,8 @@ class WxRequest {
|
|||||||
|
|
||||||
// Initialize with default instance
|
// Initialize with default instance
|
||||||
const request = new WxRequest({
|
const request = new WxRequest({
|
||||||
//baseUrl: 'http://192.168.0.184:8889',
|
baseUrl: 'http://192.168.100.2:8888/api',
|
||||||
baseUrl: 'https://go.sundynix.cn/api',
|
//baseUrl: 'https://go.sundynix.cn/api',
|
||||||
header: {
|
header: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user