feat: 调整样式
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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} `;
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
</view>
|
||||
<view class="topic-list">
|
||||
<view class="topic-tag {{selectedTopics.includes(item) ? 'selected' : ''}}"
|
||||
style="{{selectedTopics.includes(item) ? 'color:' + topicColors[index % topicColors.length] + '; background-color:' + topicBgColors[index % topicBgColors.length] : ''}}"
|
||||
wx:for="{{suggestedTopics}}"
|
||||
wx:key="*this"
|
||||
bindtap="toggleTopic"
|
||||
|
||||
@@ -87,6 +87,7 @@ Page({
|
||||
avatar: avatarObj.url,
|
||||
content: item.content,
|
||||
images: (item.imgList || []).map(img => img.url),
|
||||
location: item.location || '',
|
||||
time: item.createdAtStr || '刚刚',
|
||||
likes: item.hasLiked === 1 ? ['我'] : [],
|
||||
comments: (item.commentList || []).map(c => ({
|
||||
|
||||
@@ -45,6 +45,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Location -->
|
||||
<view wx:if="{{item.location}}" class="post-location">
|
||||
<text>{{item.location}}</text>
|
||||
</view>
|
||||
|
||||
<!-- Meta: Time + Action -->
|
||||
<view class="post-meta">
|
||||
<text class="post-time">{{item.time}}</text>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,8 +23,8 @@ Page({
|
||||
|
||||
showActionSheet: false,
|
||||
actionSheetItems: [
|
||||
{ label: '拍摄', value: 'camera' },
|
||||
{ label: '从手机相册选取', value: 'album' }
|
||||
{ label: '拍照', value: 'camera' },
|
||||
{ label: '从相册选取', value: 'album' }
|
||||
],
|
||||
|
||||
// Icon picker
|
||||
|
||||
+2
-11
@@ -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 };
|
||||
});
|
||||
|
||||
@@ -22,8 +22,8 @@ Page({
|
||||
|
||||
showActionSheet: false,
|
||||
actionSheetItems: [
|
||||
{ label: '拍摄', value: 'camera' },
|
||||
{ label: '从手机相册选取', value: 'album' }
|
||||
{ label: '拍照', value: 'camera' },
|
||||
{ label: '从相册选取', value: 'album' }
|
||||
],
|
||||
|
||||
careTaskIcons: [],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -54,12 +54,15 @@
|
||||
<view class="archive-view fadeIn">
|
||||
<view class="archive-identity-card">
|
||||
<view class="aic-header">
|
||||
<view class="aic-badge">🏆 元老级植物</view>
|
||||
<view class="aic-badge">{{currentPlant.ageBadge.icon || '🌱'}} {{currentPlant.ageBadge.title || '植物新人'}}</view>
|
||||
<view class="aic-location">
|
||||
<t-icon name="location" size="28rpx" color="#388E3C" />
|
||||
<text>{{currentPlant.location || '位置未定'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="aic-desc-box">
|
||||
<text>{{currentPlant.ageBadge.desc}}</text>
|
||||
</view>
|
||||
<view class="aic-stats">
|
||||
<view class="aic-stat-item">
|
||||
<text class="label">入家时间</text>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<view class="about-container">
|
||||
<view class="logo-section">
|
||||
<view class="logo-bg">
|
||||
<t-icon name="flower" size="80rpx" color="#558B2F" />
|
||||
</view>
|
||||
<text class="app-title">植趣</text>
|
||||
<text class="app-version">Version {{appVersion}}</text>
|
||||
</view>
|
||||
|
||||
<view class="desc-content">
|
||||
一款专注于家庭植物养护的小程序。帮助你记录植物成长、制定养护计划、识别未知植物,与花友们分享养花心得。
|
||||
</view>
|
||||
|
||||
<view class="menu-list">
|
||||
<t-cell-group theme="card">
|
||||
<t-cell title="用户协议" arrow hover bind:click="openDoc" data-type="terms" left-icon="file-copy" />
|
||||
</t-cell-group>
|
||||
</view>
|
||||
|
||||
<view class="footer-spacer"></view>
|
||||
|
||||
<view class="footer">
|
||||
<text class="copyright">© 2026 Sundynix · All Rights Reserved</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "成就徽章",
|
||||
"navigationBarTitleText": "等级徽章",
|
||||
"navigationBarBackgroundColor": "#F4F6F0",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<view class="click-hint">点击查看等级详情 ></view>
|
||||
</view>
|
||||
|
||||
<view class="section-title-badges">我的成就</view>
|
||||
<view class="section-title-badges">我的徽章</view>
|
||||
|
||||
<view class="badge-wall-entry" bindtap="openBadgeWall">
|
||||
<view class="entry-bg-bloom"></view>
|
||||
@@ -33,8 +33,8 @@
|
||||
<t-icon name="achievement" size="56rpx" color="#FFD700" />
|
||||
</view>
|
||||
<view class="wall-entry-text">
|
||||
<text class="entry-title">成就徽章墙</text>
|
||||
<text class="entry-desc">查看所有成就与收集进度</text>
|
||||
<text class="entry-title">徽章墙</text>
|
||||
<text class="entry-desc">查看所有徽章与收集进度</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="wall-entry-right">
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Page({});
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "兑换中心",
|
||||
"usingComponents": {
|
||||
"t-empty": "tdesign-miniprogram/empty/empty"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<view class="exchange-page">
|
||||
<t-empty icon="info-circle-filled" description="兑换中心功能正在开发中,敬请期待" />
|
||||
</view>
|
||||
@@ -0,0 +1,9 @@
|
||||
page {
|
||||
background: #F4F6F0;
|
||||
}
|
||||
.exchange-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,73 @@
|
||||
<view class="favorites-page">
|
||||
<view class="category-filter">
|
||||
<view class="filter-chip {{favTab === 'all' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="all">全部</view>
|
||||
<view class="filter-chip {{favTab === 'plant' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="plant">植物</view>
|
||||
<view class="filter-chip {{favTab === 'article' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="article">文章</view>
|
||||
<view class="filter-chip {{favTab === 'plant' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="plant">百科</view>
|
||||
<view class="filter-chip {{favTab === 'post' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="post">动态</view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="fav-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view wx:if="{{filteredFavorites.length > 0}}" class="fav-grid">
|
||||
<view wx:for="{{filteredFavorites}}" wx:key="id" class="fav-card">
|
||||
<t-image src="{{item.image}}" mode="aspectFill" width="100%" height="240rpx" class="fav-img" />
|
||||
<view class="fav-info">
|
||||
<text class="fav-name">{{item.name}}</text>
|
||||
<view class="fav-meta-row">
|
||||
<t-icon name="{{item.type === 'plant' ? 'heart' : 'book'}}" size="28rpx" color="#90A4AE" />
|
||||
<text class="fav-type">{{item.meta}}</text>
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="fav-scroll"
|
||||
enhanced
|
||||
show-scrollbar="{{false}}"
|
||||
bindscrolltolower="onReachBottom"
|
||||
>
|
||||
<view wx:if="{{favorites.length > 0}}" class="fav-list">
|
||||
<t-swipe-cell wx:for="{{favorites}}" wx:key="id">
|
||||
<view class="fav-item" bindtap="goToDetail" data-item="{{item}}">
|
||||
|
||||
<!-- Render Wiki Plant -->
|
||||
<block wx:if="{{item.type === 'plant'}}">
|
||||
<t-image src="{{item.image}}" mode="aspectFill" class="fav-img plant-img" width="160rpx" height="160rpx" />
|
||||
<view class="fav-info">
|
||||
<text class="fav-title">{{item.name}}</text>
|
||||
<text class="fav-subtitle">{{item.latinName}}</text>
|
||||
<view class="fav-meta">
|
||||
<t-tag size="small" variant="light" theme="success">植物百科</t-tag>
|
||||
<text wx:if="{{item.difficultyLabel}}" class="meta-diff">难度: {{item.difficultyLabel}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- Render Community Post -->
|
||||
<block wx:else>
|
||||
<t-image
|
||||
src="{{item.postImage || item.avatar}}"
|
||||
mode="aspectFill"
|
||||
class="fav-img post-img"
|
||||
width="160rpx" height="160rpx"
|
||||
/>
|
||||
<view class="fav-info">
|
||||
<text class="fav-title post-title">{{item.content || '无标题'}}</text>
|
||||
<view class="fav-user-row">
|
||||
<t-avatar image="{{item.avatar}}" size="small" />
|
||||
<text class="user-nickname">{{item.nickname}}</text>
|
||||
</view>
|
||||
<view class="fav-meta">
|
||||
<t-tag size="small" variant="light" theme="primary">社区动态</t-tag>
|
||||
<text class="meta-time">{{item.time}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view slot="right" class="delete-btn" catchtap="onSwipeClick" data-item="{{item}}">
|
||||
<text>删除</text>
|
||||
</view>
|
||||
</t-swipe-cell>
|
||||
</view>
|
||||
<view wx:else class="empty-state">
|
||||
|
||||
<!-- Empty State -->
|
||||
<view wx:elif="{{!isLoading}}" class="empty-state">
|
||||
<t-icon name="star" size="80rpx" color="#E0E0E0" style="margin-bottom: 24rpx;" />
|
||||
<text class="empty-text">暂无收藏内容</text>
|
||||
</view>
|
||||
|
||||
<!-- Loading State -->
|
||||
<view class="loading-footer" wx:if="{{isLoading}}">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
<view wx:if="{{!hasMore && favorites.length > 0}}" class="no-more-text">没有更多了</view>
|
||||
|
||||
<view style="height: 60rpx;"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -16,80 +16,88 @@
|
||||
|
||||
<!-- Record List -->
|
||||
<view wx:else class="record-list">
|
||||
<view wx:for="{{records}}" wx:key="id" class="record-card" bindtap="toggleExpand" data-id="{{item.id}}">
|
||||
<t-swipe-cell wx:for="{{records}}" wx:key="id" class="record-swipe-cell">
|
||||
<view class="record-card" bindtap="toggleExpand" data-id="{{item.id}}">
|
||||
|
||||
<!-- Card Header -->
|
||||
<view class="card-header">
|
||||
<view class="card-thumb">
|
||||
<t-image
|
||||
wx:if="{{item.topImage}}"
|
||||
src="{{item.topImage}}"
|
||||
mode="aspectFill"
|
||||
width="120rpx"
|
||||
height="120rpx"
|
||||
shape="round"
|
||||
/>
|
||||
<view wx:else class="thumb-placeholder">
|
||||
<t-icon name="image" size="48rpx" color="#D1D5DB" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-info">
|
||||
<view class="card-name-row">
|
||||
<text class="card-name">{{item.topName}}</text>
|
||||
<view class="score-badge" style="background: {{item.topScore >= 80 ? '#E8F5E9' : item.topScore >= 50 ? '#FFF8E1' : '#FBE9E7'}}; color: {{item.topScore >= 80 ? '#2E7D32' : item.topScore >= 50 ? '#F57F17' : '#D84315'}}">
|
||||
{{item.topScore}}%
|
||||
<!-- Card Header -->
|
||||
<view class="card-header">
|
||||
<view class="card-thumb">
|
||||
<t-image
|
||||
wx:if="{{item.topImage}}"
|
||||
src="{{item.topImage}}"
|
||||
mode="aspectFill"
|
||||
width="120rpx"
|
||||
height="120rpx"
|
||||
shape="round"
|
||||
/>
|
||||
<view wx:else class="thumb-placeholder">
|
||||
<t-icon name="image" size="48rpx" color="#D1D5DB" />
|
||||
</view>
|
||||
</view>
|
||||
<text class="card-time">{{item.time}}</text>
|
||||
<view wx:if="{{item.otherResults.length > 0}}" class="other-hint">
|
||||
<text>还可能是: </text>
|
||||
<text wx:for="{{item.otherResults}}" wx:for-item="other" wx:key="name" class="other-name">{{other.name}}{{index < item.otherResults.length - 1 ? '、' : ''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="expand-arrow {{expandedId === item.id ? 'expanded' : ''}}">
|
||||
<t-icon name="chevron-down" size="32rpx" color="#C5C5C5" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Expanded Detail -->
|
||||
<view wx:if="{{expandedId === item.id}}" class="card-detail">
|
||||
<!-- Description -->
|
||||
<view wx:if="{{item.topDesc}}" class="detail-desc">
|
||||
<text class="desc-text">{{item.topDesc}}</text>
|
||||
</view>
|
||||
|
||||
<!-- All Results -->
|
||||
<view class="detail-results">
|
||||
<text class="detail-label">识别结果排名</text>
|
||||
<view class="result-bars">
|
||||
<!-- Top result -->
|
||||
<view class="result-bar-item">
|
||||
<text class="bar-name">{{item.topName}}</text>
|
||||
<view class="bar-track">
|
||||
<view class="bar-fill top" style="width: {{item.topScore}}%"></view>
|
||||
</view>
|
||||
<text class="bar-score">{{item.topScore}}%</text>
|
||||
</view>
|
||||
<!-- Other results -->
|
||||
<view wx:for="{{item.otherResults}}" wx:for-item="other" wx:key="name" class="result-bar-item">
|
||||
<text class="bar-name">{{other.name}}</text>
|
||||
<view class="bar-track">
|
||||
<view class="bar-fill" style="width: {{other.score}}%"></view>
|
||||
</view>
|
||||
<text class="bar-score">{{other.score}}%</text>
|
||||
|
||||
<view class="card-info">
|
||||
<view class="card-name-row">
|
||||
<text class="card-name">{{item.topName}}</text>
|
||||
<view class="score-badge" style="background: {{item.topScore >= 80 ? '#E8F5E9' : item.topScore >= 50 ? '#FFF8E1' : '#FBE9E7'}}; color: {{item.topScore >= 80 ? '#2E7D32' : item.topScore >= 50 ? '#F57F17' : '#D84315'}}">
|
||||
{{item.topScore}}%
|
||||
</view>
|
||||
</view>
|
||||
<text class="card-time">{{item.time}}</text>
|
||||
<view wx:if="{{item.otherResults.length > 0}}" class="other-hint">
|
||||
<text>还可能是: </text>
|
||||
<text wx:for="{{item.otherResults}}" wx:for-item="other" wx:key="name" class="other-name">{{other.name}}{{index < item.otherResults.length - 1 ? '、' : ''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="expand-arrow {{expandedId === item.id ? 'expanded' : ''}}">
|
||||
<t-icon name="chevron-down" size="32rpx" color="#C5C5C5" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-meta">
|
||||
<t-icon name="time" size="24rpx" color="#9CA3AF" />
|
||||
<text class="meta-text">{{item.dateStr}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- Expanded Detail -->
|
||||
<view wx:if="{{expandedId === item.id}}" class="card-detail">
|
||||
<!-- Description -->
|
||||
<view wx:if="{{item.topDesc}}" class="detail-desc">
|
||||
<text class="desc-text">{{item.topDesc}}</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<!-- All Results -->
|
||||
<view class="detail-results">
|
||||
<text class="detail-label">识别结果排名</text>
|
||||
<view class="result-bars">
|
||||
<!-- Top result -->
|
||||
<view class="result-bar-item">
|
||||
<text class="bar-name">{{item.topName}}</text>
|
||||
<view class="bar-track">
|
||||
<view class="bar-fill top" style="width: {{item.topScore}}%"></view>
|
||||
</view>
|
||||
<text class="bar-score">{{item.topScore}}%</text>
|
||||
</view>
|
||||
<!-- Other results -->
|
||||
<view wx:for="{{item.otherResults}}" wx:for-item="other" wx:key="name" class="result-bar-item">
|
||||
<text class="bar-name">{{other.name}}</text>
|
||||
<view class="bar-track">
|
||||
<view class="bar-fill" style="width: {{other.score}}%"></view>
|
||||
</view>
|
||||
<text class="bar-score">{{other.score}}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-meta">
|
||||
<t-icon name="time" size="24rpx" color="#9CA3AF" />
|
||||
<text class="meta-text">{{item.dateStr}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view slot="right" class="delete-action" catchtap="deleteLog" data-id="{{item.id}}">
|
||||
<view class="delete-btn">
|
||||
<t-icon name="delete" size="40rpx" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
</t-swipe-cell>
|
||||
|
||||
<!-- Load More -->
|
||||
<view wx:if="{{loading && records.length > 0}}" class="load-more">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+41
-67
@@ -1,39 +1,7 @@
|
||||
<view class="profile-page">
|
||||
|
||||
<!-- ======== ABOUT VIEW ======== -->
|
||||
<view wx:if="{{view === 'about'}}" class="sub-view info-view-anim">
|
||||
<view class="sub-nav" bindtap="goBack">
|
||||
<t-icon name="chevron-left" size="40rpx" />
|
||||
<text class="sub-nav-title">关于我们</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="sub-scroll">
|
||||
<view class="about-section">
|
||||
<view class="about-logo-area">
|
||||
<view class="about-logo">
|
||||
<t-icon name="flower" size="72rpx" color="#558B2F" />
|
||||
</view>
|
||||
<text class="about-app-name">植物护理助手</text>
|
||||
<text class="about-version">版本 {{appVersion}}</text>
|
||||
</view>
|
||||
<view class="about-desc">
|
||||
<text>一款专注于家庭植物养护的小程序。帮助你记录植物成长、制定养护计划、识别未知植物,与花友们分享养花心得。</text>
|
||||
</view>
|
||||
|
||||
<view class="about-menu-list">
|
||||
<view class="about-menu-item" bindtap="openDoc" data-type="terms">
|
||||
<text>用户协议</text>
|
||||
<t-icon name="chevron-right" size="36rpx" color="#CCC" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="about-footer">
|
||||
<text class="about-copyright">© 2026 Sundynix · All Rights Reserved</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- ======== MAIN PROFILE VIEW ======== -->
|
||||
<view wx:else class="main-profile-view">
|
||||
<view class="main-profile-view">
|
||||
<!-- Header -->
|
||||
<view class="profile-header">
|
||||
<view class="user-main">
|
||||
@@ -41,9 +9,20 @@
|
||||
<t-avatar wx:if="{{userAvatar}}" image="{{userAvatar}}" size="120rpx" />
|
||||
<t-avatar wx:else icon="user" size="120rpx" />
|
||||
</view>
|
||||
<view class="user-text" bindtap="openProfileEditor">
|
||||
<view class="user-name">{{userName}}</view>
|
||||
<view class="level-badge">{{userLevelTag || 'Lv.0 园艺新手'}}</view>
|
||||
<view class="user-text">
|
||||
<view class="user-name" bindtap="openProfileEditor">{{userName}}</view>
|
||||
<view class="level-badge" bindtap="goToBadges">{{userLevelTag || 'Lv.0 园艺新手'}}</view>
|
||||
<view class="user-sun-days">
|
||||
<view class="sun-days-item">
|
||||
<t-icon name="sunny" size="24rpx" color="#FBC02D" />
|
||||
<text>{{userSunlight}} 阳光</text>
|
||||
</view>
|
||||
<view class="sun-days-divider"></view>
|
||||
<view class="sun-days-item">
|
||||
<t-icon name="calendar" size="24rpx" color="#66BB6A" />
|
||||
<text>加入 {{joinedDays}} 天</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="settings-btn" bindtap="goToNotificationSettings">
|
||||
@@ -52,30 +31,38 @@
|
||||
</view>
|
||||
|
||||
<!-- Stats Card (Fixed) -->
|
||||
<view class="stats-section">
|
||||
<view class="stats-card">
|
||||
<view class="stat-item">
|
||||
<text class="stat-num">{{plantCount}}</text>
|
||||
<text class="stat-label">植物</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-num">{{taskDoneCount}}</text>
|
||||
<text class="stat-label">养护次数</text>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-num">{{postCount}}</text>
|
||||
<text class="stat-label">动态</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<scroll-view scroll-y class="profile-content" enhanced show-scrollbar="{{false}}" scroll-top="{{scrollTop}}">
|
||||
<!-- Menu -->
|
||||
<view class="profile-menu">
|
||||
<view class="menu-group-title">常用功能</view>
|
||||
|
||||
<view class="menu-item" bindtap="goToExchange">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon-bg" style="background: #FCE4EC">
|
||||
<t-icon name="gift" size="36rpx" color="#E91E63" />
|
||||
</view>
|
||||
<text class="menu-text">兑换中心</text>
|
||||
</view>
|
||||
<view class="menu-right-info">
|
||||
<text class="menu-badge-text">开发中</text>
|
||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="goToBadges">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon-bg" style="background: #F3E5F5">
|
||||
<t-icon name="award" size="36rpx" color="#9C27B0" />
|
||||
</view>
|
||||
<text class="menu-text">等级徽章</text>
|
||||
</view>
|
||||
<view class="menu-right-info">
|
||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="goToFavorites">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon-bg" style="background: #FFF3E0">
|
||||
@@ -106,19 +93,6 @@
|
||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||
</view>
|
||||
|
||||
<view class="menu-item" bindtap="goToBadges">
|
||||
<view class="menu-left">
|
||||
<view class="menu-icon-bg" style="background: #F3E5F5">
|
||||
<t-icon name="award" size="36rpx" color="#9C27B0" />
|
||||
</view>
|
||||
<text class="menu-text">等级徽章</text>
|
||||
</view>
|
||||
<view class="menu-right-info">
|
||||
<!-- <text class="menu-badge-text">已获 3 个</text> -->
|
||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="menu-group-title" style="margin-top: 32rpx;">更多服务</view>
|
||||
|
||||
<view class="menu-item" bindtap="goToAbout">
|
||||
|
||||
+38
-41
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -12,25 +12,29 @@
|
||||
<view wx:if="{{myPublishedPosts.length > 0}}" class="posts-list">
|
||||
<view wx:for="{{myPublishedPosts}}" wx:key="id" class="my-post-card">
|
||||
<view class="my-post-time">{{item.time}}</view>
|
||||
<view class="my-post-content-wrap">
|
||||
<view class="post-header" wx:if="{{item.hasReviewed !== undefined}}">
|
||||
<view class="status-tag pending" wx:if="{{item.hasReviewed === 0}}">待审核</view>
|
||||
<view class="status-tag success" wx:if="{{item.hasReviewed === 1}}">已发布</view>
|
||||
</view>
|
||||
<text class="post-text">{{item.content}}</text>
|
||||
<view wx:if="{{item.images.length > 0}}" class="my-post-images">
|
||||
<t-image wx:for="{{item.images}}" wx:for-item="img" wx:key="*this"
|
||||
src="{{img}}" mode="aspectFill" width="140rpx" height="140rpx"
|
||||
shape="round" />
|
||||
</view>
|
||||
<view class="my-post-footer">
|
||||
<view class="footer-item"><t-icon name="heart" size="28rpx" /> <text>{{item.likes.length}}</text></view>
|
||||
<view class="footer-item"><t-icon name="chat" size="28rpx" /> <text>{{item.comments.length}}</text></view>
|
||||
<view class="footer-item" catchtap="deletePost" data-id="{{item.id}}" style="margin-left:auto; color: #EF5350;">
|
||||
<t-icon name="delete" size="28rpx" />
|
||||
<t-swipe-cell class="post-swipe-cell">
|
||||
<view class="my-post-content-wrap">
|
||||
<view class="post-header" wx:if="{{item.hasReviewed !== undefined}}">
|
||||
<view class="status-tag pending" wx:if="{{item.hasReviewed === 0}}">待审核</view>
|
||||
<view class="status-tag success" wx:if="{{item.hasReviewed === 1}}">已发布</view>
|
||||
</view>
|
||||
<text class="post-text">{{item.content}}</text>
|
||||
<view wx:if="{{item.images.length > 0}}" class="my-post-images">
|
||||
<t-image wx:for="{{item.images}}" wx:for-item="img" wx:key="*this"
|
||||
src="{{img}}" mode="aspectFill" width="140rpx" height="140rpx"
|
||||
shape="round" />
|
||||
</view>
|
||||
<view class="my-post-footer">
|
||||
<view class="footer-item"><t-icon name="heart" size="28rpx" /> <text>{{item.likes.length}}</text></view>
|
||||
<view class="footer-item"><t-icon name="chat" size="28rpx" /> <text>{{item.comments.length}}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view slot="right" class="delete-action" catchtap="deletePost" data-id="{{item.id}}">
|
||||
<view class="delete-btn">
|
||||
<t-icon name="delete" size="40rpx" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
</t-swipe-cell>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:else class="empty-state">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "3.7.1",
|
||||
"appid": "wx52dfc635739a9c19",
|
||||
"appid": "wxb463820bf36dd5d6",
|
||||
"projectname": "plant-mp",
|
||||
"isGameTourist": false,
|
||||
"condition": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"projectname": "plant-mp",
|
||||
"condition": {},
|
||||
"setting": {
|
||||
"urlCheck": true,
|
||||
"urlCheck": false,
|
||||
"coverView": true,
|
||||
"lazyloadPlaceholderEnable": false,
|
||||
"skylineRenderEnable": false,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
+2
-2
@@ -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'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user