diff --git a/app.json b/app.json index 399dff5..90faf10 100644 --- a/app.json +++ b/app.json @@ -17,7 +17,9 @@ "pages/profile/badges/level-detail/index", "pages/profile/badges/badge-wall/index", "pages/profile/favorites/index", - "pages/profile/posts/index" + "pages/profile/posts/index", + "pages/profile/about/index", + "pages/profile/exchange/index" ], "window": { "backgroundTextStyle": "light", diff --git a/pages/community/create/index.js b/pages/community/create/index.js index 05adaf3..f17260d 100644 --- a/pages/community/create/index.js +++ b/pages/community/create/index.js @@ -13,16 +13,26 @@ Page({ showImageSheet: false, imageSheetItems: [ { label: '拍照', value: 'camera' }, - { label: '从相册选择', value: 'album' } - ] + { label: '从相册选取', value: 'album' } + ], + topicColors: ['#558B2F', '#1976D2', '#7B1FA2', '#F57C00', '#C2185B', '#00796B'], + topicBgColors: ['#E8F5E9', '#E3F2FD', '#F3E5F5', '#FFF3E0', '#FCE4EC', '#E0F2F1'] }, onLoad() { - // No draft loading + this.fetchTopics(); }, - onUnload() { - // No draft saving + fetchTopics() { + request.get('/topic/list').then(res => { + const list = res.list || []; + const topics = list.map(t => t.title); + if (topics.length > 0) { + this.setData({ suggestedTopics: topics }); + } + }).catch(err => { + console.error('Fetch topics failed', err); + }); }, onContentInput(e) { @@ -105,7 +115,6 @@ Page({ itemList: ['设为封面', '删除'], success: (res) => { if (res.tapIndex === 0) { - // Move to first position const images = [...this.data.images]; const [img] = images.splice(index, 1); images.unshift(img); @@ -121,14 +130,33 @@ Page({ chooseLocation() { wx.chooseLocation({ success: (res) => { - this.setData({ location: res.name || res.address }); + const formatted = this.formatLocation(res.address, res.name); + this.setData({ location: formatted }); }, - fail: () => { - // User cancelled or no permission - } + fail: () => { } }); }, + formatLocation(address, name) { + if (!address) return name || ''; + + // Municipalities + const munis = ['北京', '上海', '天津', '重庆']; + for (let m of munis) { + if (address.startsWith(m)) { + return m; + } + } + + // Standard: Prov + City (Simplify names) + const match = address.match(/^(.+?)(?:省|自治区)(.+?)(?:市|自治州|地区|盟)/); + if (match) { + return `${match[1]}.${match[2]}`; + } + + return name || address; + }, + toggleTopic(e) { const topic = e.currentTarget.dataset.topic; const hashtag = `#${topic} `; diff --git a/pages/community/create/index.wxml b/pages/community/create/index.wxml index e52630d..1e523b4 100644 --- a/pages/community/create/index.wxml +++ b/pages/community/create/index.wxml @@ -78,6 +78,7 @@ img.url), + location: item.location || '', time: item.createdAtStr || '刚刚', likes: item.hasLiked === 1 ? ['我'] : [], comments: (item.commentList || []).map(c => ({ diff --git a/pages/community/index.wxml b/pages/community/index.wxml index ed6c89d..db294ed 100644 --- a/pages/community/index.wxml +++ b/pages/community/index.wxml @@ -45,6 +45,11 @@ + + + {{item.location}} + + {{item.time}} diff --git a/pages/community/index.wxss b/pages/community/index.wxss index 2304a5e..9bcdf05 100644 --- a/pages/community/index.wxss +++ b/pages/community/index.wxss @@ -165,6 +165,13 @@ page { aspect-ratio: 1; } +/* Location */ +.post-location { + font-size: 24rpx; + color: #576b95; + margin-bottom: 12rpx; +} + /* Post Meta */ .post-meta { display: flex; diff --git a/pages/garden/add/index.js b/pages/garden/add/index.js index d0b14bb..7f446a7 100644 --- a/pages/garden/add/index.js +++ b/pages/garden/add/index.js @@ -23,8 +23,8 @@ Page({ showActionSheet: false, actionSheetItems: [ - { label: '拍摄', value: 'camera' }, - { label: '从手机相册选取', value: 'album' } + { label: '拍照', value: 'camera' }, + { label: '从相册选取', value: 'album' } ], // Icon picker diff --git a/pages/garden/index.js b/pages/garden/index.js index 229333a..9c985fd 100644 --- a/pages/garden/index.js +++ b/pages/garden/index.js @@ -1,5 +1,6 @@ // pages/garden/index.js import request from '../../utils/request'; +import { calculateDaysSince } from '../../utils/dateUtil'; Page({ data: { @@ -71,17 +72,7 @@ Page({ } // Calculate days - let days = 1; - if (item.plantTime) { - try { - const start = new Date(item.plantTime).getTime(); - const now = Date.now(); - const diff = now - start; - if (diff > 0) { - days = Math.ceil(diff / (1000 * 60 * 60 * 24)); - } - } catch (e) { } - } + const days = calculateDaysSince(item.plantTime); return { ...item, images: [imageUrl], daysPlanted: days }; }); diff --git a/pages/plant-detail/edit/index.js b/pages/plant-detail/edit/index.js index ecd14d3..2aee1c9 100644 --- a/pages/plant-detail/edit/index.js +++ b/pages/plant-detail/edit/index.js @@ -22,8 +22,8 @@ Page({ showActionSheet: false, actionSheetItems: [ - { label: '拍摄', value: 'camera' }, - { label: '从手机相册选取', value: 'album' } + { label: '拍照', value: 'camera' }, + { label: '从相册选取', value: 'album' } ], careTaskIcons: [], diff --git a/pages/plant-detail/index.js b/pages/plant-detail/index.js index fd245c6..3a0bfb5 100644 --- a/pages/plant-detail/index.js +++ b/pages/plant-detail/index.js @@ -1,5 +1,6 @@ // pages/plant-detail/index.js import request from '../../utils/request'; +import { calculateDaysSince, getPlantAgeBadge } from '../../utils/dateUtil'; Page({ data: { @@ -53,14 +54,9 @@ Page({ // Calculate days planted and format date let adoptionDate = '未知'; - let daysPlanted = 0; + const daysPlanted = calculateDaysSince(plant.plantTime); + const ageBadge = getPlantAgeBadge(daysPlanted); if (plant.plantTime) { - const start = new Date(plant.plantTime); - const now = new Date(); - const diffTime = now - start; - if (diffTime > 0) { - daysPlanted = Math.floor(diffTime / (1000 * 60 * 60 * 24)); - } adoptionDate = plant.plantTime.split('T')[0]; } @@ -70,6 +66,7 @@ Page({ location: plant.placement || '', adoptionDate: adoptionDate, daysPlanted: daysPlanted, + ageBadge: ageBadge, careSchedule: carePlans }, swiperImages: swiperImages, diff --git a/pages/plant-detail/index.wxml b/pages/plant-detail/index.wxml index eef8920..45cb712 100644 --- a/pages/plant-detail/index.wxml +++ b/pages/plant-detail/index.wxml @@ -54,12 +54,15 @@ - 🏆 元老级植物 + {{currentPlant.ageBadge.icon || '🌱'}} {{currentPlant.ageBadge.title || '植物新人'}} {{currentPlant.location || '位置未定'}} + + {{currentPlant.ageBadge.desc}} + 入家时间 diff --git a/pages/plant-detail/index.wxss b/pages/plant-detail/index.wxss index 3cdcdca..366c7dd 100644 --- a/pages/plant-detail/index.wxss +++ b/pages/plant-detail/index.wxss @@ -343,6 +343,17 @@ page { gap: 8rpx; } +.aic-desc-box { + background: #F1F8E9; + padding: 24rpx; + border-radius: 24rpx; + font-size: 26rpx; + color: #33691E; + line-height: 1.5; + text-align: left; + margin-bottom: 32rpx; +} + .aic-stats { display: flex; justify-content: space-between; diff --git a/pages/profile/about/index.js b/pages/profile/about/index.js new file mode 100644 index 0000000..ed04708 --- /dev/null +++ b/pages/profile/about/index.js @@ -0,0 +1,24 @@ +Page({ + data: { + appVersion: '1.0.0' + }, + onLoad() { + const accountInfo = wx.getAccountInfoSync(); + if (accountInfo && accountInfo.miniProgram) { + this.setData({ + appVersion: accountInfo.miniProgram.version || '1.0.0' + }); + } + }, + openDoc(e) { + if (wx.openPrivacyContract) { + wx.openPrivacyContract({ + fail: () => { + wx.showToast({ title: '无法打开协议', icon: 'none' }); + } + }); + } else { + wx.showToast({ title: '当前微信版本不支持查看', icon: 'none' }); + } + } +}); diff --git a/pages/profile/about/index.json b/pages/profile/about/index.json new file mode 100644 index 0000000..492f788 --- /dev/null +++ b/pages/profile/about/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "关于植趣", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group" + } +} \ No newline at end of file diff --git a/pages/profile/about/index.wxml b/pages/profile/about/index.wxml new file mode 100644 index 0000000..5188f7e --- /dev/null +++ b/pages/profile/about/index.wxml @@ -0,0 +1,25 @@ + + + + + + 植趣 + Version {{appVersion}} + + + + 一款专注于家庭植物养护的小程序。帮助你记录植物成长、制定养护计划、识别未知植物,与花友们分享养花心得。 + + + + + + + + + + + + © 2026 Sundynix · All Rights Reserved + + diff --git a/pages/profile/about/index.wxss b/pages/profile/about/index.wxss new file mode 100644 index 0000000..c265105 --- /dev/null +++ b/pages/profile/about/index.wxss @@ -0,0 +1,69 @@ +page { + background: #F4F6F0; +} +.about-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 48rpx 32rpx; + min-height: 100vh; + box-sizing: border-box; +} + +.logo-section { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 64rpx; + margin-bottom: 48rpx; +} + +.logo-bg { + width: 160rpx; + height: 160rpx; + background: #fff; + border-radius: 32rpx; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 8rpx 32rpx rgba(85, 139, 47, 0.1); + margin-bottom: 24rpx; +} + +.app-title { + font-size: 40rpx; + font-weight: 600; + color: #333; + margin-bottom: 8rpx; +} + +.app-version { + font-size: 24rpx; + color: #999; +} + +.desc-content { + font-size: 28rpx; + color: #666; + line-height: 1.6; + text-align: center; + margin-bottom: 64rpx; + padding: 0 40rpx; +} + +.menu-list { + width: 100%; +} + +.footer-spacer { + flex: 1; +} + +.footer { + padding: 32rpx 0; +} + +.copyright { + font-size: 22rpx; + color: #ccc; +} diff --git a/pages/profile/badges/index.json b/pages/profile/badges/index.json index 7d934ce..c8d1573 100644 --- a/pages/profile/badges/index.json +++ b/pages/profile/badges/index.json @@ -1,5 +1,5 @@ { - "navigationBarTitleText": "成就徽章", + "navigationBarTitleText": "等级徽章", "navigationBarBackgroundColor": "#F4F6F0", "navigationBarTextStyle": "black", "usingComponents": { diff --git a/pages/profile/badges/index.wxml b/pages/profile/badges/index.wxml index 6b075c8..da8431c 100644 --- a/pages/profile/badges/index.wxml +++ b/pages/profile/badges/index.wxml @@ -24,7 +24,7 @@ 点击查看等级详情 > - 我的成就 + 我的徽章 @@ -33,8 +33,8 @@ - 成就徽章墙 - 查看所有成就与收集进度 + 徽章墙 + 查看所有徽章与收集进度 diff --git a/pages/profile/exchange/index.js b/pages/profile/exchange/index.js new file mode 100644 index 0000000..560d44d --- /dev/null +++ b/pages/profile/exchange/index.js @@ -0,0 +1 @@ +Page({}); diff --git a/pages/profile/exchange/index.json b/pages/profile/exchange/index.json new file mode 100644 index 0000000..b250e0f --- /dev/null +++ b/pages/profile/exchange/index.json @@ -0,0 +1,6 @@ +{ + "navigationBarTitleText": "兑换中心", + "usingComponents": { + "t-empty": "tdesign-miniprogram/empty/empty" + } +} \ No newline at end of file diff --git a/pages/profile/exchange/index.wxml b/pages/profile/exchange/index.wxml new file mode 100644 index 0000000..44bef42 --- /dev/null +++ b/pages/profile/exchange/index.wxml @@ -0,0 +1,3 @@ + + + diff --git a/pages/profile/exchange/index.wxss b/pages/profile/exchange/index.wxss new file mode 100644 index 0000000..b1ffcd0 --- /dev/null +++ b/pages/profile/exchange/index.wxss @@ -0,0 +1,9 @@ +page { + background: #F4F6F0; +} +.exchange-page { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; +} diff --git a/pages/profile/favorites/index.js b/pages/profile/favorites/index.js index bce09e8..208fa10 100644 --- a/pages/profile/favorites/index.js +++ b/pages/profile/favorites/index.js @@ -2,33 +2,146 @@ import request from '../../../utils/request'; Page({ data: { - favTab: 'all', + favTab: 'all', // 'all', 'plant', 'post' favorites: [], - filteredFavorites: [] + current: 1, + pageSize: 10, + hasMore: true, + isLoading: false }, onLoad() { - this.loadFavorites(); - }, - - loadFavorites() { - // TODO: Call API - this.filterFavorites(); + this.fetchFavorites(true); }, onFavTabChange(e) { const val = e.currentTarget.dataset.value; + if (val === this.data.favTab) return; this.setData({ favTab: val }, () => { - this.filterFavorites(); + this.fetchFavorites(true); }); }, - filterFavorites() { - const { favorites, favTab } = this.data; - const filtered = favorites.filter(item => { - if (favTab === 'all') return true; - return item.type === favTab; + fetchFavorites(reset = false) { + if (this.data.isLoading) return; + if (!reset && !this.data.hasMore) return; + + this.setData({ isLoading: true }); + + const current = reset ? 1 : this.data.current; + let classType = 0; + if (this.data.favTab === 'plant') classType = 1; + if (this.data.favTab === 'post') classType = 2; + + request.post('/profile/star', { + current, + pageSize: this.data.pageSize, + class: classType + }).then(res => { + const list = res.list || []; + const total = res.total || 0; + + const mappedList = list.map(wrapper => { + const type = wrapper.type; // 1=Wiki, 2=Post + const isPlant = type === 1; + const entity = isPlant ? wrapper.wiki : wrapper.post; + + if (!entity) return null; + + // Image Extraction + let image = ''; + if (entity.imgList && entity.imgList.length > 0) { + image = entity.imgList[0].url; + } + + if (isPlant) { + return { + id: entity.id, + type: 'plant', + name: entity.name, + latinName: entity.latinName, + difficultyLabel: this.getDifficultyLabel(entity.difficulty), + image: image + }; + } else { + // Post + return { + id: entity.id, + type: 'post', + content: entity.content, + postImage: image, + avatar: entity.publisher && entity.publisher.avatar ? entity.publisher.avatar : '', // Publisher is null in sample + nickname: entity.publisher && entity.publisher.nickname ? entity.publisher.nickname : '花友', + time: wrapper.createdAtStr + }; + } + }).filter(item => item !== null); + + if (reset) { + this.setData({ + favorites: mappedList, + current: 2, + hasMore: mappedList.length < total, + isLoading: false + }); + } else { + this.setData({ + favorites: [...this.data.favorites, ...mappedList], + current: current + 1, + hasMore: (this.data.favorites.length + mappedList.length) < total, + isLoading: false + }); + } + }).catch(err => { + console.error('Fetch favorites failed', err); + this.setData({ isLoading: false }); }); - this.setData({ filteredFavorites: filtered }); + }, + + getDifficultyLabel(level) { + const labels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' }; + return labels[level] || '未知'; + }, + + onSwipeClick(e) { + // Handle Delete + // Data item is bound to the swipe-cell or passed via dataset + // If bind:click is on swipe-cell, e.target might not have the item if not set. + // We set data-item on swipe-cell. + const item = e.currentTarget.dataset.item; + const { id, type } = item; + + const apiPath = type === 'plant' ? '/wiki/star' : '/post/star'; + + wx.showModal({ + title: '提示', + content: '确定要取消收藏吗?', + success: (modRes) => { + if (modRes.confirm) { + request.get(apiPath, { id, type: 2 }).then(() => { + wx.showToast({ title: '已删除', icon: 'success' }); + // Remove from list + const newList = this.data.favorites.filter(i => i.id !== id); + this.setData({ favorites: newList }); + }).catch(err => { + console.error('Delete favorite failed', err); + wx.showToast({ title: '删除失败', icon: 'none' }); + }); + } + } + }); + }, + + goToDetail(e) { + const item = e.currentTarget.dataset.item; + if (item.type === 'plant') { + wx.navigateTo({ url: `/pages/wiki/detail/index?id=${item.id}` }); + } else { + wx.showToast({ title: '暂不支持查看动态详情', icon: 'none' }); + } + }, + + onReachBottom() { + this.fetchFavorites(false); } }); diff --git a/pages/profile/favorites/index.json b/pages/profile/favorites/index.json index 892735f..35afdfc 100644 --- a/pages/profile/favorites/index.json +++ b/pages/profile/favorites/index.json @@ -2,6 +2,10 @@ "navigationBarTitleText": "我的收藏", "usingComponents": { "t-icon": "tdesign-miniprogram/icon/icon", - "t-image": "tdesign-miniprogram/image/image" + "t-image": "tdesign-miniprogram/image/image", + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-avatar": "tdesign-miniprogram/avatar/avatar", + "t-loading": "tdesign-miniprogram/loading/loading" } } \ No newline at end of file diff --git a/pages/profile/favorites/index.wxml b/pages/profile/favorites/index.wxml index 8d19ef4..e73ffd3 100644 --- a/pages/profile/favorites/index.wxml +++ b/pages/profile/favorites/index.wxml @@ -1,27 +1,73 @@ 全部 - 植物 - 文章 + 百科 + 动态 - - - - - - {{item.name}} - - - {{item.meta}} + + + + + + + + + + {{item.name}} + {{item.latinName}} + + 植物百科 + 难度: {{item.difficultyLabel}} + + + + + + + + + {{item.content || '无标题'}} + + + {{item.nickname}} + + + 社区动态 + {{item.time}} + + + - - + + 删除 + + - + + + 暂无收藏内容 + + + + + + 没有更多了 + diff --git a/pages/profile/favorites/index.wxss b/pages/profile/favorites/index.wxss index eba094b..17db10f 100644 --- a/pages/profile/favorites/index.wxss +++ b/pages/profile/favorites/index.wxss @@ -12,6 +12,7 @@ display: flex; gap: 16rpx; margin-bottom: 24rpx; + flex-shrink: 0; } .filter-chip { @@ -26,10 +27,10 @@ } .filter-chip.active { - background: #333; + background: #558B2F; color: #fff; font-weight: 600; - box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1); + box-shadow: 0 4rpx 12rpx rgba(85, 139, 47, 0.2); } .fav-scroll { @@ -38,47 +39,91 @@ width: 100%; } -.fav-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20rpx; -} - -.fav-card { - background: white; - border-radius: 20rpx; - overflow: hidden; - box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03); +.fav-list { display: flex; flex-direction: column; + gap: 24rpx; +} + +.fav-item { + background: white; + border-radius: 20rpx; + padding: 24rpx; + display: flex; + gap: 24rpx; + box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.02); +} + +.fav-item:active { + background: #fafafa; } .fav-img { + width: 160rpx; + height: 160rpx; + border-radius: 16rpx; + flex-shrink: 0; background: #f0f0f0; } .fav-info { - padding: 16rpx 20rpx; + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + min-width: 0; + height: 160rpx; } -.fav-name { - display: block; - font-size: 28rpx; +.fav-title { + font-size: 30rpx; font-weight: 600; color: #1F2937; - margin-bottom: 8rpx; + margin-bottom: 4rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + line-height: 1.4; } -.fav-meta-row { +.fav-subtitle { + font-size: 24rpx; + color: #9CA3AF; + font-style: italic; + margin-bottom: auto; /* Push meta down */ + display: block; +} + +.fav-user-row { display: flex; align-items: center; - gap: 8rpx; + gap: 12rpx; + margin-bottom: auto; /* Push meta down */ + margin-top: 8rpx; } -.fav-type { +.user-nickname { + font-size: 24rpx; + color: #6B7280; +} + +.fav-meta { + display: flex; + align-items: center; + gap: 16rpx; + margin-top: 12rpx; +} + +.meta-diff { + font-size: 22rpx; + color: #F59E0B; + background: #FFFBEB; + padding: 4rpx 12rpx; + border-radius: 8rpx; +} + +.meta-time { font-size: 22rpx; color: #9CA3AF; } @@ -96,3 +141,30 @@ color: #9CA3AF; margin-top: 16rpx; } + +.loading-footer { + padding: 32rpx; + display: flex; + justify-content: center; +} + +.no-more-text { + text-align: center; + padding: 32rpx; + color: #ccc; + font-size: 24rpx; +} + +.delete-btn { + background-color: #EF4444; + color: white; + width: 140rpx; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 500; + border-top-right-radius: 20rpx; + border-bottom-right-radius: 20rpx; +} diff --git a/pages/profile/identify-history/index.js b/pages/profile/identify-history/index.js index 0956e34..6532157 100644 --- a/pages/profile/identify-history/index.js +++ b/pages/profile/identify-history/index.js @@ -80,6 +80,30 @@ Page({ }); }, + deleteLog(e) { + const id = e.currentTarget.dataset.id; + wx.showModal({ + title: '提示', + content: '确定要删除这条识别记录吗?', + confirmColor: '#EF5350', + success: (res) => { + if (res.confirm) { + wx.showLoading({ title: '删除中' }); + request.post('/classify/deleteClassifyLog', { ids: [id] }).then(() => { + wx.hideLoading(); + wx.showToast({ title: '删除成功', icon: 'success' }); + const newRecords = this.data.records.filter(r => r.id !== id); + this.setData({ records: newRecords }); + }).catch(err => { + wx.hideLoading(); + console.error('Delete log failed', err); + wx.showToast({ title: '删除失败', icon: 'none' }); + }); + } + } + }); + }, + _formatTime(dateStr) { if (!dateStr) return ''; const d = new Date(dateStr); diff --git a/pages/profile/identify-history/index.json b/pages/profile/identify-history/index.json index ef92009..433ab0c 100644 --- a/pages/profile/identify-history/index.json +++ b/pages/profile/identify-history/index.json @@ -4,6 +4,7 @@ "t-icon": "tdesign-miniprogram/icon/icon", "t-image": "tdesign-miniprogram/image/image", "t-empty": "tdesign-miniprogram/empty/empty", + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell", "t-loading": "tdesign-miniprogram/loading/loading" } } \ No newline at end of file diff --git a/pages/profile/identify-history/index.wxml b/pages/profile/identify-history/index.wxml index c5b5d1e..22743ae 100644 --- a/pages/profile/identify-history/index.wxml +++ b/pages/profile/identify-history/index.wxml @@ -16,80 +16,88 @@ - + + - - - - - - - - - - - - {{item.topName}} - - {{item.topScore}}% + + + + + + - - {{item.time}} - - 还可能是: - {{other.name}}{{index < item.otherResults.length - 1 ? '、' : ''}} - - - - - - - - - - - - - {{item.topDesc}} - - - - - 识别结果排名 - - - - {{item.topName}} - - - - {{item.topScore}}% - - - {{other.name}} - - - - {{other.score}}% + + + + {{item.topName}} + + {{item.topScore}}% + + + {{item.time}} + + 还可能是: + {{other.name}}{{index < item.otherResults.length - 1 ? '、' : ''}} + + + + + - - - - {{item.dateStr}} - - + + + + + {{item.topDesc}} + - + + + 识别结果排名 + + + + {{item.topName}} + + + + {{item.topScore}}% + + + + {{other.name}} + + + + {{other.score}}% + + + + + + + {{item.dateStr}} + + + + + + + + + + + diff --git a/pages/profile/identify-history/index.wxss b/pages/profile/identify-history/index.wxss index d1716d9..8ca433f 100644 --- a/pages/profile/identify-history/index.wxss +++ b/pages/profile/identify-history/index.wxss @@ -276,3 +276,23 @@ font-size: 24rpx; color: #D1D5DB; } + +.delete-action { + display: flex; + align-items: center; + justify-content: center; + width: 140rpx; + height: 100%; + padding-left: 20rpx; +} + +.delete-btn { + width: 88rpx; + height: 88rpx; + border-radius: 50%; + background: #EF5350; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4rpx 12rpx rgba(239, 83, 80, 0.4); +} diff --git a/pages/profile/index.js b/pages/profile/index.js index 8ea62da..208a639 100644 --- a/pages/profile/index.js +++ b/pages/profile/index.js @@ -1,5 +1,6 @@ // pages/profile/index.js import request from '../../utils/request'; +import { calculateDaysSince } from '../../utils/dateUtil'; const app = getApp(); @@ -78,7 +79,9 @@ Page({ userLevelTag: levelTag, // EXP / Sunlight - userExp: res.currentSunlight || 0 + userExp: res.currentSunlight || 0, + userSunlight: res.currentSunlight || 0, + joinedDays: calculateDaysSince(res.createdAt) }); // Update global cache @@ -111,6 +114,10 @@ Page({ }, // ======== Menu Actions ======== + goToExchange() { + wx.navigateTo({ url: '/pages/profile/exchange/index' }); + }, + goToIdentifyHistory() { wx.navigateTo({ url: '/pages/profile/identify-history/index' }); }, @@ -129,7 +136,7 @@ Page({ }, goToAbout() { - this.setData({ view: 'about' }); + wx.navigateTo({ url: '/pages/profile/about/index' }); }, openDoc(e) { diff --git a/pages/profile/index.wxml b/pages/profile/index.wxml index dd3a2c7..15020e5 100644 --- a/pages/profile/index.wxml +++ b/pages/profile/index.wxml @@ -1,39 +1,7 @@ - - - - - 关于我们 - - - - - - 植物护理助手 - 版本 {{appVersion}} - - - 一款专注于家庭植物养护的小程序。帮助你记录植物成长、制定养护计划、识别未知植物,与花友们分享养花心得。 - - - - - 用户协议 - - - - - © 2026 Sundynix · All Rights Reserved - - - - - - + @@ -41,9 +9,20 @@ - - {{userName}} - {{userLevelTag || 'Lv.0 园艺新手'}} + + {{userName}} + {{userLevelTag || 'Lv.0 园艺新手'}} + + + + {{userSunlight}} 阳光 + + + + + 加入 {{joinedDays}} 天 + + @@ -52,30 +31,38 @@ - - - - {{plantCount}} - 植物 - - - - {{taskDoneCount}} - 养护次数 - - - - {{postCount}} - 动态 - - - + 常用功能 + + + + + + 兑换中心 + + + 开发中 + + + + + + + + + + 等级徽章 + + + + + + @@ -106,19 +93,6 @@ - - - - - - 等级徽章 - - - - - - - 更多服务 diff --git a/pages/profile/index.wxss b/pages/profile/index.wxss index b6b6a2a..6856ac6 100644 --- a/pages/profile/index.wxss +++ b/pages/profile/index.wxss @@ -344,8 +344,10 @@ border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; box-shadow: 0 8rpx 30rpx rgba(0,0,0,0.02); - margin-bottom: 24rpx; + margin-bottom: 0; flex-shrink: 0; + position: relative; + z-index: 102; } .user-main { @@ -385,6 +387,32 @@ font-weight: 600; } +.user-sun-days { + display: flex; + align-items: center; + gap: 16rpx; + background: rgba(255,255,255,0.6); + padding: 4rpx 16rpx; + border-radius: 20rpx; + align-self: flex-start; + border: 1rpx solid rgba(0,0,0,0.05); +} + +.sun-days-item { + display: flex; + align-items: center; + gap: 6rpx; + font-size: 22rpx; + color: #555; + font-weight: 500; +} + +.sun-days-divider { + width: 2rpx; + height: 18rpx; + background: #DDD; +} + .settings-btn { padding: 16rpx; } @@ -408,46 +436,7 @@ scrollbar-width: none; } -.stats-section { - padding: 0 32rpx; - flex-shrink: 0; -} -/* Stats Card */ -.stats-card { - display: flex; - background: #fff; - padding: 40rpx 0; - border-radius: 32rpx; - box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.02); - margin-bottom: 32rpx; -} - -.stat-item { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - gap: 8rpx; -} - -.stat-num { - font-size: 40rpx; - font-weight: 800; - color: #374151; -} - -.stat-label { - font-size: 24rpx; - color: #9CA3AF; -} - -.stat-divider { - width: 2rpx; - height: 60%; - background: #F3F4F6; - align-self: center; -} /* Profile Menu */ .profile-menu { @@ -460,7 +449,15 @@ font-size: 26rpx; color: #9CA3AF; font-weight: 600; - margin-left: 12rpx; + + position: sticky; + top: 0; + z-index: 100; + background: #F4F6F0; + + /* negative margin to cover side padding of parent */ + margin: 0 -32rpx; + padding: 44rpx 32rpx 16rpx 44rpx; /* 32rpx padding + 12rpx visual indent */ } .menu-item { diff --git a/pages/profile/posts/index.json b/pages/profile/posts/index.json index 348452e..bb5708d 100644 --- a/pages/profile/posts/index.json +++ b/pages/profile/posts/index.json @@ -3,6 +3,7 @@ "disableScroll": true, "usingComponents": { "t-icon": "tdesign-miniprogram/icon/icon", - "t-image": "tdesign-miniprogram/image/image" + "t-image": "tdesign-miniprogram/image/image", + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell" } } \ No newline at end of file diff --git a/pages/profile/posts/index.wxml b/pages/profile/posts/index.wxml index 8449762..57d2792 100644 --- a/pages/profile/posts/index.wxml +++ b/pages/profile/posts/index.wxml @@ -12,25 +12,29 @@ {{item.time}} - - - 待审核 - 已发布 - - {{item.content}} - - - - + + + + + + diff --git a/pages/profile/posts/index.wxss b/pages/profile/posts/index.wxss index 9e532a8..c544595 100644 --- a/pages/profile/posts/index.wxss +++ b/pages/profile/posts/index.wxss @@ -161,10 +161,34 @@ -/* Hide Scrollbar Globally */ ::-webkit-scrollbar { width: 0; height: 0; color: transparent; display: none; } + +.post-swipe-cell { + flex: 1; + min-width: 0; +} + +.delete-action { + display: flex; + align-items: center; + justify-content: center; + width: 140rpx; + height: 100%; + padding-left: 20rpx; +} + +.delete-btn { + width: 88rpx; + height: 88rpx; + border-radius: 50%; + background: #EF5350; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4rpx 12rpx rgba(239, 83, 80, 0.4); +} diff --git a/pages/wiki/detail/index.js b/pages/wiki/detail/index.js index 2c3fd4a..b14650c 100644 --- a/pages/wiki/detail/index.js +++ b/pages/wiki/detail/index.js @@ -110,10 +110,20 @@ Page({ const type = isFavorited ? 2 : 1; request.get('/wiki/star', { id, type }).then(() => { + const newStatus = !isFavorited; this.setData({ - 'plant.isFavorited': !isFavorited + 'plant.isFavorited': newStatus }); wx.showToast({ title: type === 1 ? '已收藏' : '已取消', icon: 'success' }); + + // Sync with previous page (Wiki List) + const pages = getCurrentPages(); + if (pages.length > 1) { + const prevPage = pages[pages.length - 2]; + if (prevPage.updateItemFavoriteStatus) { + prevPage.updateItemFavoriteStatus(id, newStatus); + } + } }).catch(err => { console.error('Toggle favorite failed', err); wx.showToast({ title: '操作失败', icon: 'none' }); diff --git a/pages/wiki/index.js b/pages/wiki/index.js index 866986e..72a1629 100644 --- a/pages/wiki/index.js +++ b/pages/wiki/index.js @@ -146,6 +146,16 @@ Page({ } }, + updateItemFavoriteStatus(id, isFavorited) { + const index = this.data.displayedList.findIndex(i => i.id == id); + if (index === -1) return; + + const key = `displayedList[${index}].isFavorited`; + this.setData({ + [key]: isFavorited + }); + }, + // Search Input Handler (debounced) onSearchInput(e) { const value = e.detail.value; diff --git a/project.config.json b/project.config.json index 42ca7fd..c7de474 100644 --- a/project.config.json +++ b/project.config.json @@ -53,7 +53,7 @@ }, "compileType": "miniprogram", "libVersion": "3.7.1", - "appid": "wx52dfc635739a9c19", + "appid": "wxb463820bf36dd5d6", "projectname": "plant-mp", "isGameTourist": false, "condition": { diff --git a/project.private.config.json b/project.private.config.json index 1a50a2c..e1ffeca 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -3,7 +3,7 @@ "projectname": "plant-mp", "condition": {}, "setting": { - "urlCheck": true, + "urlCheck": false, "coverView": true, "lazyloadPlaceholderEnable": false, "skylineRenderEnable": false, diff --git a/utils/dateUtil.js b/utils/dateUtil.js new file mode 100644 index 0000000..b26384c --- /dev/null +++ b/utils/dateUtil.js @@ -0,0 +1,28 @@ +const calculateDaysSince = (dateStr) => { + if (!dateStr) return 1; // Default to 1 day if date missing + + // Use timestamps to calculate difference + const start = new Date(dateStr).getTime(); + const now = new Date().getTime(); + + if (isNaN(start)) return 1; + + const diffTime = Math.abs(now - start); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + + return diffDays || 1; // Return 1 even if diffDays is 0 (just joined) +}; + +const getPlantAgeBadge = (days) => { + if (days <= 15) return { icon: '🌱', title: '职场萌新', desc: '还在适应新家的光照和水土,随时可能“离职”。' }; + if (days <= 60) return { icon: '🌿', title: '正式员工', desc: '已经度过了缓苗期,冒出了第一片新叶。' }; + if (days <= 180) return { icon: '🪴', title: '资深住户', desc: '经历了季节交替的考验,已经完全融入了你的家居环境。' }; + if (days <= 365) return { icon: '🌳', title: '元老级导师', desc: '跨越了冬夏两季,生命力顽强,建议作为传家宝培养。' }; + if (days <= 1095) return { icon: '🏅', title: '荣誉守护神', desc: '它已经不是植物了,是这个家的“不动产”和家庭成员。' }; + return { icon: '🧚‍♀️', title: '植物成精', desc: '建议尊称它一声“绿植大仙”,它可能比你更了解阳台的风水。' }; +}; + +module.exports = { + calculateDaysSince, + getPlantAgeBadge +}; diff --git a/utils/request.js b/utils/request.js index 34dd597..7c84974 100644 --- a/utils/request.js +++ b/utils/request.js @@ -270,8 +270,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.0.184:8889', + //baseUrl: 'https://go.sundynix.cn/api', header: { 'Content-Type': 'application/json' }