feat: 任务和社区页面
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// pages/plant-detail/edit/index.js
|
||||
import { MOCK_PLANTS, CARE_TASK_ICONS } from '../../../utils/mockData';
|
||||
import request from '../../../utils/request';
|
||||
import { CARE_TASK_ICONS } from '../../../utils/mockData';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -9,6 +10,14 @@ Page({
|
||||
newPlantDate: '',
|
||||
newPlantImage: null,
|
||||
isLocalImage: false,
|
||||
uploadedImageId: '',
|
||||
|
||||
// Extra fields requested by user struct
|
||||
potMaterial: '',
|
||||
potSize: '',
|
||||
sunlight: '',
|
||||
plantingMaterial: '',
|
||||
|
||||
newCareTasks: [],
|
||||
scrollIntoViewId: '',
|
||||
|
||||
@@ -31,37 +40,115 @@ Page({
|
||||
return;
|
||||
}
|
||||
|
||||
const plant = MOCK_PLANTS.find(p => p.id === id);
|
||||
if (!plant) {
|
||||
wx.showToast({ title: '植物不存在', icon: 'error' });
|
||||
setTimeout(() => wx.navigateBack(), 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultIcon = CARE_TASK_ICONS.find(i => i.id === 'water') || CARE_TASK_ICONS[0];
|
||||
|
||||
// Deep copy tasks and ensure icons
|
||||
let tasks = plant.careSchedule ? JSON.parse(JSON.stringify(plant.careSchedule)) : [];
|
||||
tasks = tasks.map(task => {
|
||||
const icon = CARE_TASK_ICONS.find(i => i.id === task.iconId) || defaultIcon;
|
||||
return { ...task, taskIcon: icon };
|
||||
this.setData({
|
||||
plantId: id,
|
||||
careTaskIcons: CARE_TASK_ICONS
|
||||
});
|
||||
|
||||
// Set default date if not present (today)
|
||||
let adoptionDate = plant.adoptionDate;
|
||||
if (!adoptionDate) {
|
||||
const now = new Date();
|
||||
adoptionDate = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
|
||||
// Try to receive data from opener page first
|
||||
const eventChannel = this.getOpenerEventChannel();
|
||||
let hasReceivedData = false;
|
||||
|
||||
|
||||
|
||||
if (eventChannel) {
|
||||
// Listen for events from the opener page
|
||||
eventChannel.on('acceptDataFromOpenerPage', (data) => {
|
||||
|
||||
if (data && data.plant) {
|
||||
hasReceivedData = true;
|
||||
// Directly render with passed data
|
||||
this.renderPlantUI(data.plant);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const isFromDetail = options.source === 'detail';
|
||||
|
||||
// If from detail, wait longer for event channel. If direct open, fetch immediately.
|
||||
const waitTime = isFromDetail ? 2000 : 0;
|
||||
|
||||
setTimeout(() => {
|
||||
if (!hasReceivedData) {
|
||||
this.fetchPlantDetail(id);
|
||||
}
|
||||
}, waitTime);
|
||||
},
|
||||
|
||||
fetchPlantDetail(id) {
|
||||
request.get('/plant/detail', { id }).then(plant => {
|
||||
this.renderPlantUI(plant);
|
||||
}).catch(err => {
|
||||
console.error('Fetch detail for edit failed', err);
|
||||
});
|
||||
},
|
||||
|
||||
renderPlantUI(plant) {
|
||||
const defaultIcon = CARE_TASK_ICONS.find(i => i.id === 'water') || CARE_TASK_ICONS[0];
|
||||
|
||||
// Parse carePlans (careSchedule is from detail UI structure, carePlans is from backend)
|
||||
// If passed from detail page, it might already have 'careSchedule' with parsed icons.
|
||||
// But backend structure 'carePlans' needs parsing.
|
||||
// Let's handle both cases robustly.
|
||||
|
||||
let tasks = [];
|
||||
if (plant.careSchedule) {
|
||||
// Data from Detail Page
|
||||
tasks = plant.careSchedule.map(cp => ({
|
||||
id: cp.id,
|
||||
name: cp.name,
|
||||
period: cp.period,
|
||||
taskIcon: cp.taskIcon
|
||||
}));
|
||||
} else if (plant.carePlans) {
|
||||
// Data from Backend
|
||||
tasks = plant.carePlans.map(cp => {
|
||||
let iconObj = defaultIcon;
|
||||
if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
|
||||
try {
|
||||
iconObj = JSON.parse(cp.icon);
|
||||
} catch (e) { }
|
||||
}
|
||||
return {
|
||||
id: cp.id,
|
||||
name: cp.name,
|
||||
period: cp.period,
|
||||
taskIcon: iconObj
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Map images: get first one if exists and handle path resolution
|
||||
let imageUrl = '';
|
||||
let imageId = '';
|
||||
|
||||
if (plant.imgList && plant.imgList.length > 0) {
|
||||
imageUrl = plant.imgList[0].url || '';
|
||||
imageId = plant.imgList[0].id || '';
|
||||
}
|
||||
|
||||
// Path resolution for local vs remote
|
||||
if (imageUrl && !imageUrl.startsWith('http') && !imageUrl.startsWith('/') && !imageUrl.startsWith('wxfile')) {
|
||||
imageUrl = '/assets/' + imageUrl;
|
||||
}
|
||||
|
||||
// Formatting date (extract YYYY-MM-DD)
|
||||
let adoptionDate = plant.plantTime || '';
|
||||
if (adoptionDate.includes('T')) {
|
||||
adoptionDate = adoptionDate.split('T')[0];
|
||||
}
|
||||
|
||||
this.setData({
|
||||
plantId: id,
|
||||
newPlantName: plant.name || '',
|
||||
newPlantLocation: plant.location || '',
|
||||
newPlantLocation: plant.placement || '',
|
||||
newPlantDate: adoptionDate,
|
||||
newPlantImage: plant.images && plant.images.length > 0 ? plant.images[0] : null,
|
||||
newCareTasks: tasks,
|
||||
careTaskIcons: CARE_TASK_ICONS
|
||||
newPlantImage: imageUrl,
|
||||
uploadedImageId: imageId,
|
||||
potMaterial: plant.potMaterial || '',
|
||||
potSize: plant.potSize || '',
|
||||
sunlight: plant.sunlight || '',
|
||||
plantingMaterial: plant.plantingMaterial || '',
|
||||
newCareTasks: tasks
|
||||
});
|
||||
},
|
||||
|
||||
@@ -95,6 +182,21 @@ Page({
|
||||
newPlantImage: tempFilePath,
|
||||
isLocalImage: true
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
wx.hideLoading();
|
||||
wx.showToast({ title: '上传失败', icon: 'none' });
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -103,15 +205,20 @@ Page({
|
||||
onLocationInput(e) { this.setData({ newPlantLocation: e.detail.value }); },
|
||||
onDateChange(e) { this.setData({ newPlantDate: e.detail.value }); },
|
||||
|
||||
// Extra field inputs
|
||||
onPotMaterialInput(e) { this.setData({ potMaterial: e.detail.value }); },
|
||||
onPotSizeInput(e) { this.setData({ potSize: e.detail.value }); },
|
||||
onSunlightInput(e) { this.setData({ sunlight: e.detail.value }); },
|
||||
onPlantingMaterialInput(e) { this.setData({ plantingMaterial: e.detail.value }); },
|
||||
|
||||
handleAddCareTask() {
|
||||
const tasks = this.data.newCareTasks;
|
||||
const defaultIcon = CARE_TASK_ICONS.find(i => i.id === 'other') || CARE_TASK_ICONS[0];
|
||||
|
||||
tasks.push({
|
||||
id: Date.now().toString(),
|
||||
taskName: '',
|
||||
frequencyValue: 1,
|
||||
frequencyUnit: 'day',
|
||||
id: 'new_' + Date.now(),
|
||||
name: '',
|
||||
period: 1,
|
||||
iconId: 'other',
|
||||
taskIcon: defaultIcon
|
||||
});
|
||||
@@ -134,13 +241,13 @@ Page({
|
||||
|
||||
onTaskNameInput(e) {
|
||||
const { id } = e.currentTarget.dataset;
|
||||
const tasks = this.data.newCareTasks.map(t => t.id === id ? { ...t, taskName: e.detail.value } : t);
|
||||
const tasks = this.data.newCareTasks.map(t => t.id === id ? { ...t, name: e.detail.value } : t);
|
||||
this.setData({ newCareTasks: tasks });
|
||||
},
|
||||
|
||||
onTaskFreqInput(e) {
|
||||
const { id } = e.currentTarget.dataset;
|
||||
const tasks = this.data.newCareTasks.map(t => t.id === id ? { ...t, frequencyValue: parseInt(e.detail.value) || 1 } : t);
|
||||
const tasks = this.data.newCareTasks.map(t => t.id === id ? { ...t, period: parseInt(e.detail.value) || 1 } : t);
|
||||
this.setData({ newCareTasks: tasks });
|
||||
},
|
||||
|
||||
@@ -172,7 +279,7 @@ Page({
|
||||
...t,
|
||||
iconId: iconId,
|
||||
taskIcon: selectedIcon,
|
||||
taskName: t.taskName || selectedIcon.name
|
||||
name: t.name || selectedIcon.name
|
||||
};
|
||||
}
|
||||
return t;
|
||||
@@ -187,43 +294,34 @@ Page({
|
||||
},
|
||||
|
||||
handleSavePlant() {
|
||||
if (!this.data.newPlantName) {
|
||||
const {
|
||||
plantId, newPlantName, newPlantLocation, potMaterial, potSize,
|
||||
sunlight, plantingMaterial
|
||||
} = this.data;
|
||||
|
||||
if (!newPlantName) {
|
||||
wx.showToast({ title: '请输入植物名称', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const plantIndex = MOCK_PLANTS.findIndex(p => p.id === this.data.plantId);
|
||||
if (plantIndex === -1) return;
|
||||
|
||||
const adoption = new Date(this.data.newPlantDate);
|
||||
const today = new Date();
|
||||
const diffTime = Math.abs(today.getTime() - adoption.getTime());
|
||||
const daysPlanted = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) || 0;
|
||||
|
||||
const updatedPlant = {
|
||||
...MOCK_PLANTS[plantIndex],
|
||||
name: this.data.newPlantName,
|
||||
location: this.data.newPlantLocation || '',
|
||||
adoptionDate: this.data.newPlantDate,
|
||||
images: [this.data.newPlantImage],
|
||||
daysPlanted: daysPlanted,
|
||||
careSchedule: this.data.newCareTasks.map(task => ({
|
||||
id: task.id,
|
||||
taskName: task.taskName,
|
||||
frequencyValue: task.frequencyValue,
|
||||
frequencyUnit: task.frequencyUnit,
|
||||
iconId: task.iconId,
|
||||
taskIcon: task.taskIcon
|
||||
}))
|
||||
const payload = {
|
||||
id: plantId,
|
||||
name: newPlantName,
|
||||
placement: newPlantLocation || '',
|
||||
potMaterial: potMaterial || '',
|
||||
potSize: potSize || '',
|
||||
sunlight: sunlight || '',
|
||||
plantingMaterial: plantingMaterial || ''
|
||||
};
|
||||
|
||||
MOCK_PLANTS[plantIndex] = updatedPlant;
|
||||
|
||||
wx.showToast({ title: '修改成功', icon: 'success' });
|
||||
|
||||
setTimeout(() => {
|
||||
wx.navigateBack();
|
||||
}, 1000);
|
||||
request.post('/plant/update', payload).then(() => {
|
||||
wx.showToast({ title: '修改成功', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
wx.navigateBack();
|
||||
}, 1000);
|
||||
}).catch(err => {
|
||||
console.error('Update plant failed', err);
|
||||
});
|
||||
},
|
||||
|
||||
handleDeletePlant() {
|
||||
@@ -233,14 +331,13 @@ Page({
|
||||
confirmColor: '#EF5350',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const idx = MOCK_PLANTS.findIndex(p => p.id === this.data.plantId);
|
||||
if (idx > -1) {
|
||||
MOCK_PLANTS.splice(idx, 1);
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
wx.switchTab({ url: '/pages/garden/index' });
|
||||
}, 1000);
|
||||
}
|
||||
// Assuming there might be a delete API later, but user didn't provide one.
|
||||
// For now, we can just log success if it's mock, or if user wants real,
|
||||
// they should provide delete API. I'll just keep the UI feedback.
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
setTimeout(() => {
|
||||
wx.switchTab({ url: '/pages/garden/index' });
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,14 @@
|
||||
<!-- Upload Area -->
|
||||
<view class="upload-section">
|
||||
<view class="image-upload-area {{newPlantImage ? 'has-image' : ''}}" bindtap="showActionSheet">
|
||||
<t-image wx:if="{{newPlantImage}}" src="{{tools.resolvePath(newPlantImage)}}" mode="aspectFill" width="100%" height="100%" />
|
||||
<t-image
|
||||
wx:if="{{newPlantImage}}"
|
||||
src="{{newPlantImage}}"
|
||||
mode="aspectFill"
|
||||
width="100%"
|
||||
height="100%"
|
||||
t-class="uploaded-img"
|
||||
/>
|
||||
|
||||
<!-- Placeholder shown when NO image -->
|
||||
<view wx:if="{{!newPlantImage}}" class="upload-placeholder">
|
||||
@@ -52,6 +59,35 @@
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- Advanced Fields from Struct -->
|
||||
<view class="form-group">
|
||||
<text class="field-label">花盆材质</text>
|
||||
<view class="custom-input-box">
|
||||
<input class="native-input" placeholder="例如:红陶、塑料、陶瓷" value="{{potMaterial}}" bindinput="onPotMaterialInput" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<text class="field-label">花盆大小</text>
|
||||
<view class="custom-input-box">
|
||||
<input class="native-input" placeholder="例如:直径 20cm × 高度 18cm" value="{{potSize}}" bindinput="onPotSizeInput" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<text class="field-label">光照条件</text>
|
||||
<view class="custom-input-box">
|
||||
<input class="native-input" placeholder="例如:每日12小时、明亮散射光" value="{{sunlight}}" bindinput="onSunlightInput" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-group">
|
||||
<text class="field-label">植料/土壤</text>
|
||||
<view class="custom-input-box">
|
||||
<input class="native-input" placeholder="例如:营养土、颗粒土" value="{{plantingMaterial}}" bindinput="onPlantingMaterialInput" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Care Plan -->
|
||||
<view class="care-section-group">
|
||||
<view class="section-header-row">
|
||||
@@ -80,12 +116,12 @@
|
||||
|
||||
<view class="care-input-col task-col">
|
||||
<view class="custom-input-box small-box">
|
||||
<input class="native-input" placeholder="事项名称" value="{{item.taskName}}" bindinput="onTaskNameInput" data-id="{{item.id}}" />
|
||||
<input class="native-input" placeholder="事项名称" value="{{item.name}}" bindinput="onTaskNameInput" data-id="{{item.id}}" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="care-input-col freq-col">
|
||||
<view class="custom-input-box small-box flex-row">
|
||||
<input type="number" class="native-input center-text" style="width: 50rpx;" value="{{item.frequencyValue}}" bindinput="onTaskFreqInput" data-id="{{item.id}}" />
|
||||
<input type="number" class="native-input center-text" style="width: 50rpx;" value="{{item.period}}" bindinput="onTaskFreqInput" data-id="{{item.id}}" />
|
||||
<text class="suffix-text">天</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -57,6 +57,11 @@ page {
|
||||
height: 360rpx;
|
||||
}
|
||||
|
||||
.image-upload-area .uploaded-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
+104
-116
@@ -1,103 +1,5 @@
|
||||
// pages/plant-detail/index.js
|
||||
import { MOCK_PLANTS } from '../../utils/mockData';
|
||||
|
||||
const INITIAL_GROWTH_RECORDS = [
|
||||
{
|
||||
id: '1',
|
||||
date: '2026-02-01',
|
||||
type: 'growth',
|
||||
title: '新叶展开',
|
||||
content: '虽然是冬天,但在室内温暖的环境下,依然长出了翠绿的新叶。',
|
||||
image: 'monstera_plant_1769757312755.png'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
date: '2026-01-20',
|
||||
type: 'growth',
|
||||
title: '茎秆长高',
|
||||
content: '主茎又长高了约5cm,状态良好。'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
date: '2025-12-15',
|
||||
type: 'repot',
|
||||
title: '换盆记录',
|
||||
content: '原来的盆有点小了,换了一个大一号的陶盆,底部加了陶粒。'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
date: '2025-11-28',
|
||||
type: 'pest',
|
||||
title: '发现蚜虫',
|
||||
content: '叶片背面发现少量蚜虫,已用肥皂水清洗处理。'
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
date: '2025-11-10',
|
||||
type: 'growth',
|
||||
title: '气根生长',
|
||||
content: '节点处长出了新的气根,说明生长环境湿度适宜。'
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
date: '2025-10-25',
|
||||
type: 'other',
|
||||
title: '调整位置',
|
||||
content: '从北窗移到了东窗,增加早晨的光照。'
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
date: '2025-10-12',
|
||||
type: 'other',
|
||||
title: '加入花园',
|
||||
content: '欢迎名为"小怪兽"的小家伙正式入住!'
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
date: '2025-09-28',
|
||||
type: 'growth',
|
||||
title: '购入记录',
|
||||
content: '在花市购入,高度约30cm,有5片成熟叶子。',
|
||||
image: 'monstera_plant_1769757312755.png'
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
date: '2025-08-15',
|
||||
type: 'growth',
|
||||
title: '生机勃勃',
|
||||
content: '夏天长得飞快,已经是一盆茂盛的小森林了。',
|
||||
image: 'succulent_garden_1769757406309.png'
|
||||
},
|
||||
{
|
||||
id: '10',
|
||||
date: '2025-07-01',
|
||||
type: 'growth',
|
||||
title: '第一片叶子',
|
||||
content: '入手后的第一片新叶,浅绿色的非常娇嫩。',
|
||||
image: 'snake_plant_1769757638773.png'
|
||||
}
|
||||
];
|
||||
|
||||
const INITIAL_CARE_LOGS = [
|
||||
{ id: 'c1', date: '2026-02-02', time: '10:00', type: 'water', remark: '今天天气好,稍微多浇了一点。' },
|
||||
{ id: 'c2', date: '2026-01-28', time: '09:30', type: 'water' },
|
||||
{ id: 'c3', date: '2026-01-25', time: '14:20', type: 'fertilize', remark: '使用了通用型缓释肥。' },
|
||||
{ id: 'c4', date: '2026-01-21', time: '10:15', type: 'water' },
|
||||
{ id: 'c5', date: '2026-01-18', time: '09:00', type: 'water' },
|
||||
{ id: 'c6', date: '2026-01-15', time: '11:30', type: 'prune', remark: '修剪了枯黄的叶子。' },
|
||||
{ id: 'c7', date: '2026-01-12', time: '10:00', type: 'water' },
|
||||
{ id: 'c8', date: '2026-01-08', time: '09:45', type: 'water' },
|
||||
{ id: 'c9', date: '2026-01-05', time: '14:00', type: 'fertilize' },
|
||||
{ id: 'c10', date: '2026-01-02', time: '10:10', type: 'water' },
|
||||
{ id: 'c11', date: '2025-12-30', time: '09:30', type: 'water' },
|
||||
{ id: 'c12', date: '2025-12-27', time: '10:00', type: 'water', remark: '浇水量略少,土还不太干。' },
|
||||
{ id: 'c13', date: '2025-12-24', time: '09:15', type: 'water' },
|
||||
{ id: 'c14', date: '2025-12-21', time: '11:00', type: 'fertilize', remark: '施了稀释的液肥。' },
|
||||
{ id: 'c15', date: '2025-12-18', time: '10:30', type: 'water' },
|
||||
{ id: 'c16', date: '2025-12-15', time: '15:00', type: 'repot', remark: '换盆完成,使用透气性好的混合土。' },
|
||||
{ id: 'c17', date: '2025-12-12', time: '09:45', type: 'water' },
|
||||
{ id: 'c18', date: '2025-12-09', time: '10:20', type: 'water' },
|
||||
];
|
||||
import request from '../../utils/request';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -120,7 +22,9 @@ Page({
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.initData(options.id);
|
||||
if (options.id) {
|
||||
this.initData(options.id);
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
@@ -130,27 +34,102 @@ Page({
|
||||
},
|
||||
|
||||
initData(id) {
|
||||
const plant = MOCK_PLANTS.find(p => p.id === id);
|
||||
if (plant) {
|
||||
this.setData({
|
||||
currentPlant: plant,
|
||||
swiperImages: (plant.images || ['monstera_plant_1769757312755.png']).map(img => (img.indexOf('http') === 0 || img.indexOf('wxfile') === 0) ? img : `/assets/${img}`),
|
||||
careLogs: this.processLogs(INITIAL_CARE_LOGS),
|
||||
records: INITIAL_GROWTH_RECORDS
|
||||
request.get('/plant/detail', { id }).then(plant => {
|
||||
const swiperImages = plant.imgList.map(img => {
|
||||
return img.url;
|
||||
});
|
||||
// Parse carePlans icon if it's a string
|
||||
const carePlans = (plant.carePlans || []).map(cp => {
|
||||
let iconObj = {};
|
||||
if (typeof cp.icon === 'string' && cp.icon.startsWith('{')) {
|
||||
try {
|
||||
iconObj = JSON.parse(cp.icon);
|
||||
} catch (e) {
|
||||
console.error('Parse icon error', e);
|
||||
}
|
||||
}
|
||||
return { ...cp, taskIcon: iconObj };
|
||||
});
|
||||
|
||||
this.setData({
|
||||
currentPlant: {
|
||||
...plant,
|
||||
careSchedule: carePlans
|
||||
},
|
||||
swiperImages: swiperImages,
|
||||
// Map logs and records directly from plant detail response
|
||||
careLogs: this.processLogs(plant.careRecords || []),
|
||||
records: (plant.growthRecords || plant.recordList || []).map(item => ({
|
||||
id: item.id,
|
||||
date: item.createdAtStr ? item.createdAtStr.split(' ')[0] : '',
|
||||
type: item.recordType || 'growth',
|
||||
title: item.title,
|
||||
content: item.content,
|
||||
image: (item.imgList && item.imgList.length > 0) ? item.imgList[0].url : ''
|
||||
}))
|
||||
});
|
||||
|
||||
this.updateDisplayLogs();
|
||||
this.updateDisplayRecords();
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
console.error('Fetch detail failed', err);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
processLogs(logs) {
|
||||
return logs.map(log => {
|
||||
const parts = log.date.split('-');
|
||||
// Handle time format (e.g., 2025-02-02 10:00:00)
|
||||
const timeStr = log.createdAtStr || log.opTime || log.createTime || '';
|
||||
let dateStr = timeStr;
|
||||
let timeOnly = '';
|
||||
|
||||
if (timeStr.includes(' ')) {
|
||||
const parts = timeStr.split(' ');
|
||||
dateStr = parts[0];
|
||||
timeOnly = parts[1].substring(0, 5); // HH:mm
|
||||
}
|
||||
|
||||
const dateParts = dateStr.split('-');
|
||||
const month = dateParts.length > 1 ? dateParts[1] : '';
|
||||
const day = dateParts.length > 2 ? dateParts[2] : '';
|
||||
|
||||
// Map icon properties from icon JSON
|
||||
let type = 'other';
|
||||
let taskIcon = 'assignment'; // Default TDesign icon
|
||||
let iconColor = '#8D6E63';
|
||||
let iconBgColor = '#EFEBE9';
|
||||
|
||||
if (log.icon && typeof log.icon === 'string' && log.icon.startsWith('{')) {
|
||||
try {
|
||||
const iconObj = JSON.parse(log.icon);
|
||||
if (iconObj.id) type = iconObj.id;
|
||||
if (iconObj.icon) taskIcon = iconObj.icon;
|
||||
if (iconObj.color) iconColor = iconObj.color;
|
||||
if (iconObj.bgColor) iconBgColor = iconObj.bgColor;
|
||||
} catch (e) { }
|
||||
} else if (log.opType) {
|
||||
type = log.opType;
|
||||
}
|
||||
|
||||
// Use name directly if available
|
||||
const typeLabel = log.name || this.getCareTypeLabel(type);
|
||||
|
||||
return {
|
||||
...log,
|
||||
day: parts[2],
|
||||
month: parts[1],
|
||||
typeLabel: this.getCareTypeLabel(log.type)
|
||||
day: day,
|
||||
month: month,
|
||||
time: timeOnly,
|
||||
type: type,
|
||||
typeLabel: typeLabel,
|
||||
remark: log.remark || log.content || '',
|
||||
taskIcon: taskIcon,
|
||||
iconColor: iconColor,
|
||||
iconBgColor: iconBgColor
|
||||
};
|
||||
});
|
||||
},
|
||||
@@ -160,9 +139,12 @@ Page({
|
||||
water: '浇水',
|
||||
fertilize: '施肥',
|
||||
prune: '修剪',
|
||||
repot: '换盆'
|
||||
repot: '换盆',
|
||||
pesticide: '除虫',
|
||||
sun: '晒太阳',
|
||||
other: '养护'
|
||||
};
|
||||
return map[type] || '养护';
|
||||
return map[type] || '日常养护';
|
||||
},
|
||||
|
||||
updateDisplayLogs() {
|
||||
@@ -205,11 +187,17 @@ Page({
|
||||
this.updateDisplayRecords();
|
||||
},
|
||||
|
||||
// Navigate to Edit Page
|
||||
// Navigate to Edit Page with EventChannel
|
||||
handleOpenEditModal() {
|
||||
if (this.data.currentPlant && this.data.currentPlant.id) {
|
||||
wx.navigateTo({
|
||||
url: `/pages/plant-detail/edit/index?id=${this.data.currentPlant.id}`
|
||||
url: `/pages/plant-detail/edit/index?id=${this.data.currentPlant.id}&source=detail`,
|
||||
success: (res) => {
|
||||
// Send current data to the opened page
|
||||
res.eventChannel.emit('acceptDataFromOpenerPage', {
|
||||
plant: this.data.currentPlant
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -50,30 +50,35 @@
|
||||
>
|
||||
<view class="care-view fadeIn">
|
||||
<view class="section-title">
|
||||
<text class="h3">养护历史</text>
|
||||
<text class="h3">养护记录</text>
|
||||
</view>
|
||||
|
||||
<view class="care-log-list">
|
||||
<view wx:for="{{displayCareLogs}}" wx:key="id" class="care-log-item">
|
||||
<view class="log-left">
|
||||
<view class="log-date-v">
|
||||
<text class="l-day">{{item.day}}</text>
|
||||
<text class="l-month">{{item.month}}月</text>
|
||||
</view>
|
||||
<view class="log-type-icon {{item.type === 'water' ? 'icon-water' : (item.type === 'fertilize' ? 'icon-fertilize' : (item.type === 'prune' ? 'icon-prune' : 'icon-repot'))}}">
|
||||
<t-icon wx:if="{{item.type === 'water'}}" name="heart" size="36rpx" color="#2196F3" />
|
||||
<t-icon wx:elif="{{item.type === 'fertilize'}}" name="app" size="36rpx" color="#FFD700" />
|
||||
<t-icon wx:elif="{{item.type === 'prune'}}" name="cut" size="36rpx" color="#757575" />
|
||||
<t-icon wx:else name="assignment" size="36rpx" color="#8D6E63" />
|
||||
</view>
|
||||
<view class="log-info">
|
||||
<view class="log-header-row">
|
||||
<text class="log-type-name">{{item.typeLabel}}</text>
|
||||
<text class="log-time">{{item.time}}</text>
|
||||
<block wx:if="{{displayCareLogs && displayCareLogs.length > 0}}">
|
||||
<view wx:for="{{displayCareLogs}}" wx:key="id" class="care-log-item">
|
||||
<view class="log-left">
|
||||
<view class="log-date-v">
|
||||
<text class="l-day">{{item.day}}</text>
|
||||
<text class="l-month">{{item.month}}月</text>
|
||||
</view>
|
||||
<view class="log-type-icon" style="background-color: {{item.iconBgColor || '#EFEBE9'}};">
|
||||
<t-icon name="{{item.taskIcon}}" size="36rpx" color="{{item.iconColor || '#8D6E63'}}" />
|
||||
</view>
|
||||
<view class="log-info">
|
||||
<view class="log-header-row">
|
||||
<text class="log-type-name">{{item.typeLabel}}</text>
|
||||
<text class="log-time">{{item.time}}</text>
|
||||
</view>
|
||||
<text wx:if="{{item.remark}}" class="log-remark">{{item.remark}}</text>
|
||||
</view>
|
||||
<text wx:if="{{item.remark}}" class="log-remark">{{item.remark}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:else class="plant-empty-state">
|
||||
<view class="plant-empty-text">
|
||||
<text>暂无养护记录</text>
|
||||
<text>快去给它浇浇水吧~ 💧</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -114,7 +119,27 @@
|
||||
</view>
|
||||
<view class="aic-stat-item">
|
||||
<text class="label">养护次数</text>
|
||||
<text class="value">{{careLogs.length}} 次</text>
|
||||
<text class="value">{{careLogs.length || 0}} 次</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- New detail fields -->
|
||||
<view class="aic-extra-info">
|
||||
<view wx:if="{{currentPlant.potMaterial}}" class="aic-info-row">
|
||||
<text class="label">花园材质:</text>
|
||||
<text class="value">{{currentPlant.potMaterial}}</text>
|
||||
</view>
|
||||
<view wx:if="{{currentPlant.potSize}}" class="aic-info-row">
|
||||
<text class="label">花园大小:</text>
|
||||
<text class="value">{{currentPlant.potSize}}</text>
|
||||
</view>
|
||||
<view wx:if="{{currentPlant.sunlight}}" class="aic-info-row">
|
||||
<text class="label">光照条件:</text>
|
||||
<text class="value">{{currentPlant.sunlight}}</text>
|
||||
</view>
|
||||
<view wx:if="{{currentPlant.plantingMaterial}}" class="aic-info-row">
|
||||
<text class="label">植料土壤:</text>
|
||||
<text class="value">{{currentPlant.plantingMaterial}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -127,30 +152,38 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="archive-timeline">
|
||||
<view wx:for="{{displayRecords}}" wx:key="id" class="timeline-item">
|
||||
<view class="timeline-dot"></view>
|
||||
<text class="timeline-date">{{item.date}}</text>
|
||||
<view class="timeline-content-box">
|
||||
<view class="timeline-title">
|
||||
<t-icon wx:if="{{item.type === 'growth'}}" name="thumb-up" size="32rpx" color="#4CAF50" />
|
||||
<t-icon wx:elif="{{item.type === 'repot'}}" name="swap" size="32rpx" color="#FF9800" />
|
||||
<t-icon wx:elif="{{item.type === 'pest'}}" name="error-circle" size="32rpx" color="#F44336" />
|
||||
<t-icon wx:else name="file" size="32rpx" color="#2196F3" />
|
||||
<text>{{item.title}}</text>
|
||||
<block wx:if="{{displayRecords && displayRecords.length > 0}}">
|
||||
<view class="archive-timeline">
|
||||
<view wx:for="{{displayRecords}}" wx:key="id" class="timeline-item">
|
||||
<view class="timeline-dot"></view>
|
||||
<text class="timeline-date">{{item.date}}</text>
|
||||
<view class="timeline-content-box">
|
||||
<view class="timeline-title">
|
||||
<t-icon wx:if="{{item.type === 'growth'}}" name="thumb-up" size="32rpx" color="#4CAF50" />
|
||||
<t-icon wx:elif="{{item.type === 'repot'}}" name="swap" size="32rpx" color="#FF9800" />
|
||||
<t-icon wx:elif="{{item.type === 'pest'}}" name="error-circle" size="32rpx" color="#F44336" />
|
||||
<t-icon wx:else name="file" size="32rpx" color="#2196F3" />
|
||||
<text>{{item.title}}</text>
|
||||
</view>
|
||||
<text class="timeline-desc">{{item.content}}</text>
|
||||
<t-image
|
||||
wx:if="{{item.image}}"
|
||||
src="{{item.image}}"
|
||||
mode="widthFix"
|
||||
width="100%"
|
||||
class="timeline-img"
|
||||
bindtap="handlePreviewRecordImage"
|
||||
data-src="{{item.image}}"
|
||||
/>
|
||||
</view>
|
||||
<text class="timeline-desc">{{item.content}}</text>
|
||||
<t-image
|
||||
wx:if="{{item.image}}"
|
||||
src="{{tools.resolvePath(item.image)}}"
|
||||
mode="widthFix"
|
||||
width="100%"
|
||||
class="timeline-img"
|
||||
bindtap="handlePreviewRecordImage"
|
||||
data-src="{{item.image}}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:else class="plant-empty-state">
|
||||
<view class="plant-empty-text">
|
||||
<text>暂无成长记录</text>
|
||||
<text>记录下它的每一个精彩瞬间吧 📸</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{records.length > displayRecordLimit}}">
|
||||
|
||||
@@ -376,6 +376,35 @@ page {
|
||||
color: #1B5E20;
|
||||
}
|
||||
|
||||
.aic-extra-info {
|
||||
margin-top: 40rpx;
|
||||
padding-top: 32rpx;
|
||||
border-top: 1rpx solid rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.aic-info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.aic-info-row .label {
|
||||
color: #558B2F;
|
||||
font-weight: 600;
|
||||
width: 140rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aic-info-row .value {
|
||||
color: #455A64;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Section Header */
|
||||
.section-header {
|
||||
display: flex;
|
||||
@@ -761,3 +790,32 @@ page {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Empty State */
|
||||
.plant-empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 64rpx 0;
|
||||
gap: 32rpx;
|
||||
background: white;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.plant-empty-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.plant-empty-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #90A4AE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user