feat: 样式修改
This commit is contained in:
@@ -8,13 +8,23 @@ App({
|
|||||||
// Send res.code to backend to swap for openId, sessionKey, unionId
|
// Send res.code to backend to swap for openId, sessionKey, unionId
|
||||||
if (res.code) {
|
if (res.code) {
|
||||||
request.get('/auth/miniLogin', { code: res.code }).then(data => {
|
request.get('/auth/miniLogin', { code: res.code }).then(data => {
|
||||||
// Assuming the token is in data.token or data itself
|
// Response structure based on user input: { user: {...}, token: "...", expiresAt: ... }
|
||||||
const token = data.token || data;
|
// Note: request.js might return data.user directly if it unwraps 'data'
|
||||||
|
// But looking at previous request.js usage, it seems to return the 'data' field of the response.
|
||||||
|
// Let's handle both cases safely.
|
||||||
|
|
||||||
|
const token = data.token;
|
||||||
|
const user = data.user;
|
||||||
|
|
||||||
if (token && typeof token === 'string') {
|
if (token && typeof token === 'string') {
|
||||||
wx.setStorageSync('token', token);
|
wx.setStorageSync('token', token);
|
||||||
console.log('Login successful, token stored');
|
if (user) {
|
||||||
|
wx.setStorageSync('userInfo', user);
|
||||||
|
this.globalData.userInfo = user;
|
||||||
|
}
|
||||||
|
console.log('Login successful, user info stored');
|
||||||
} else {
|
} else {
|
||||||
console.warn('Login response did not contain a valid token string', data);
|
console.warn('Login response did not contain a valid token', data);
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('Login failed', err);
|
console.error('Login failed', err);
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"pages/plant-detail/edit/index",
|
"pages/plant-detail/edit/index",
|
||||||
"pages/plant-detail/index",
|
"pages/plant-detail/index",
|
||||||
"pages/wiki/detail/index",
|
"pages/wiki/detail/index",
|
||||||
"pages/wiki/identify/index"
|
"pages/wiki/identify/index",
|
||||||
|
"pages/profile/identify-history/index"
|
||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"backgroundTextStyle": "light",
|
"backgroundTextStyle": "light",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ page {
|
|||||||
--primary-light: #9CCC65;
|
--primary-light: #9CCC65;
|
||||||
--primary-dark: #33691E;
|
--primary-dark: #33691E;
|
||||||
--secondary: #8D6E63;
|
--secondary: #8D6E63;
|
||||||
--bg-garden: #F1F8E9;
|
--bg-garden: #F4F6F0;
|
||||||
--bg-card: rgba(255, 255, 255, 0.9);
|
--bg-card: rgba(255, 255, 255, 0.9);
|
||||||
--text-main: #263238;
|
--text-main: #263238;
|
||||||
--text-muted: #78909C;
|
--text-muted: #78909C;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/** pages/community/create/index.wxss **/
|
/** pages/community/create/index.wxss **/
|
||||||
page {
|
page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #fff;
|
background: #F4F6F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-post-page {
|
.create-post-page {
|
||||||
|
|||||||
@@ -125,8 +125,9 @@
|
|||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- Floating Action Button -->
|
<!-- Floating Action Button -->
|
||||||
<view class="fab" bindtap="goToCreatePost">
|
<view class="floating-add-btn" bindtap="goToCreatePost">
|
||||||
<t-icon name="add" size="48rpx" color="#fff" />
|
<t-icon name="add" size="40rpx" color="#FFF" />
|
||||||
|
<text>发布动态</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Comment Input Mask -->
|
<!-- Comment Input Mask -->
|
||||||
|
|||||||
+13
-11
@@ -5,7 +5,7 @@ page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.community-page {
|
.community-page {
|
||||||
background-color: #fff;
|
background-color: #F4F6F0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -298,25 +298,27 @@ page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Floating Action Button */
|
/* Floating Action Button */
|
||||||
.fab {
|
.floating-add-btn {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 40rpx;
|
right: 40rpx;
|
||||||
bottom: 200rpx;
|
bottom: 60rpx;
|
||||||
width: 112rpx;
|
background: #558B2F;
|
||||||
height: 112rpx;
|
color: white;
|
||||||
background: linear-gradient(135deg, #689F38, #558B2F);
|
padding: 24rpx 40rpx;
|
||||||
border-radius: 50%;
|
border-radius: 60rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
gap: 12rpx;
|
||||||
box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
|
box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
transition: all 0.2s;
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fab:active {
|
.floating-add-btn:active {
|
||||||
transform: scale(0.92);
|
transform: scale(0.92);
|
||||||
box-shadow: 0 6rpx 16rpx rgba(85, 139, 47, 0.3);
|
box-shadow: 0 4rpx 16rpx rgba(85, 139, 47, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* WeChat Style Action Container */
|
/* WeChat Style Action Container */
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"navigationBarTitleText": "我的花园",
|
"navigationBarTitleText": "我的花园",
|
||||||
"usingComponents": {
|
"usingComponents": {
|
||||||
"t-fab": "tdesign-miniprogram/fab/fab",
|
|
||||||
"t-popup": "tdesign-miniprogram/popup/popup",
|
"t-popup": "tdesign-miniprogram/popup/popup",
|
||||||
"t-input": "tdesign-miniprogram/input/input",
|
"t-input": "tdesign-miniprogram/input/input",
|
||||||
"t-button": "tdesign-miniprogram/button/button",
|
"t-button": "tdesign-miniprogram/button/button",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.add-plant-page {
|
.add-plant-page {
|
||||||
background-color: #F5F7F5;
|
background-color: #F4F6F0;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -15,7 +15,7 @@ page {
|
|||||||
.page-content {
|
.page-content {
|
||||||
height: calc(100vh - 140rpx - env(safe-area-inset-bottom));
|
height: calc(100vh - 140rpx - env(safe-area-inset-bottom));
|
||||||
padding: 24rpx 32rpx;
|
padding: 24rpx 32rpx;
|
||||||
background: #F5F7F5;
|
background: #F4F6F0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
/>
|
/>
|
||||||
<view class="header-gradient"></view>
|
<view class="header-gradient"></view>
|
||||||
|
|
||||||
<!-- Custom Carousel Indicators -->
|
<!-- Image Counter -->
|
||||||
<view class="carousel-indicators">
|
<view class="carousel-counter" wx:if="{{swiperImages.length > 0}}">
|
||||||
<view wx:for="{{swiperImages}}" wx:key="index" class="carousel-dot {{index === activeImageIndex ? 'active' : ''}}"></view>
|
<text>{{activeImageIndex + 1}} / {{swiperImages.length}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
@@ -56,29 +56,21 @@ page {
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Carousel Indicators */
|
/* Image Counter Badge */
|
||||||
.carousel-indicators {
|
.carousel-counter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100rpx;
|
bottom: 100rpx;
|
||||||
right: 48rpx;
|
right: 48rpx;
|
||||||
display: flex;
|
background: rgba(0, 0, 0, 0.45);
|
||||||
gap: 12rpx;
|
backdrop-filter: blur(6px);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
color: white;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 6rpx 18rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
z-index: 30;
|
z-index: 30;
|
||||||
}
|
letter-spacing: 2rpx;
|
||||||
|
|
||||||
.carousel-dot {
|
|
||||||
width: 12rpx;
|
|
||||||
height: 12rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(255, 255, 255, 0.4);
|
|
||||||
transition: all 0.3s;
|
|
||||||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-dot.active {
|
|
||||||
background: white;
|
|
||||||
width: 24rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-info {
|
.header-info {
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import request from '../../../utils/request';
|
||||||
|
|
||||||
|
Page({
|
||||||
|
data: {
|
||||||
|
records: [],
|
||||||
|
loading: true,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
total: 0,
|
||||||
|
hasMore: true,
|
||||||
|
expandedId: '', // Track which card is expanded
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.loadRecords();
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadRecords() {
|
||||||
|
this.setData({ loading: true });
|
||||||
|
try {
|
||||||
|
const res = await request.post('/classify/myClassifyLog', {
|
||||||
|
page: this.data.page,
|
||||||
|
pageSize: this.data.pageSize,
|
||||||
|
});
|
||||||
|
const list = (res.list || []).map(item => this._transformRecord(item));
|
||||||
|
this.setData({
|
||||||
|
records: this.data.page === 1 ? list : [...this.data.records, ...list],
|
||||||
|
total: res.total || 0,
|
||||||
|
hasMore: list.length >= this.data.pageSize,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Load identify history failed', err);
|
||||||
|
this.setData({ loading: false });
|
||||||
|
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_transformRecord(item) {
|
||||||
|
const allResults = item.allResults || [];
|
||||||
|
const topResult = allResults[0] || {};
|
||||||
|
const otherResults = allResults.slice(1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
time: this._formatTime(item.createdAt),
|
||||||
|
dateStr: item.createdAtStr || '',
|
||||||
|
topName: topResult.name || '未知植物',
|
||||||
|
topScore: topResult.score ? Math.round(topResult.score * 100) : 0,
|
||||||
|
topImage: topResult.baike_info?.image_url || '',
|
||||||
|
topDesc: topResult.baike_info?.description || '',
|
||||||
|
topBaikeUrl: topResult.baike_info?.baike_url || '',
|
||||||
|
otherResults: otherResults.map(r => ({
|
||||||
|
name: r.name || '未知',
|
||||||
|
score: r.score ? Math.round(r.score * 100) : 0,
|
||||||
|
hasInfo: !!r.baike_info,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleExpand(e) {
|
||||||
|
const id = e.currentTarget.dataset.id;
|
||||||
|
this.setData({
|
||||||
|
expandedId: this.data.expandedId === id ? '' : id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onReachBottom() {
|
||||||
|
if (!this.data.hasMore || this.data.loading) return;
|
||||||
|
this.setData({ page: this.data.page + 1 }, () => {
|
||||||
|
this.loadRecords();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onPullDownRefresh() {
|
||||||
|
this.setData({ page: 1, hasMore: true }, () => {
|
||||||
|
this.loadRecords().then(() => {
|
||||||
|
wx.stopPullDownRefresh();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_formatTime(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMs = now - d;
|
||||||
|
const diffMin = Math.floor(diffMs / 60000);
|
||||||
|
if (diffMin < 1) return '刚刚';
|
||||||
|
if (diffMin < 60) return diffMin + '分钟前';
|
||||||
|
const diffHour = Math.floor(diffMin / 60);
|
||||||
|
if (diffHour < 24) return diffHour + '小时前';
|
||||||
|
const diffDay = Math.floor(diffHour / 24);
|
||||||
|
if (diffDay < 7) return diffDay + '天前';
|
||||||
|
|
||||||
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = d.getDate().toString().padStart(2, '0');
|
||||||
|
const hour = d.getHours().toString().padStart(2, '0');
|
||||||
|
const min = d.getMinutes().toString().padStart(2, '0');
|
||||||
|
return `${month}-${day} ${hour}:${min}`;
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"navigationBarTitleText": "识别记录",
|
||||||
|
"usingComponents": {
|
||||||
|
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||||
|
"t-image": "tdesign-miniprogram/image/image",
|
||||||
|
"t-empty": "tdesign-miniprogram/empty/empty",
|
||||||
|
"t-loading": "tdesign-miniprogram/loading/loading"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<view class="history-page">
|
||||||
|
|
||||||
|
<!-- Loading -->
|
||||||
|
<view wx:if="{{loading && records.length === 0}}" class="loading-wrap">
|
||||||
|
<t-loading theme="circular" size="48rpx" text="加载中..." />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Empty -->
|
||||||
|
<view wx:elif="{{!loading && records.length === 0}}" class="empty-wrap">
|
||||||
|
<view class="empty-icon">
|
||||||
|
<t-icon name="scan" size="120rpx" color="#D1D5DB" />
|
||||||
|
</view>
|
||||||
|
<text class="empty-title">暂无识别记录</text>
|
||||||
|
<text class="empty-hint">去百科页面拍照识别植物吧</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Record List -->
|
||||||
|
<view wx:else class="record-list">
|
||||||
|
<view wx:for="{{records}}" wx:key="id" 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}}%
|
||||||
|
</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>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="detail-meta">
|
||||||
|
<t-icon name="time" size="24rpx" color="#9CA3AF" />
|
||||||
|
<text class="meta-text">{{item.dateStr}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Load More -->
|
||||||
|
<view wx:if="{{loading && records.length > 0}}" class="load-more">
|
||||||
|
<t-loading theme="circular" size="36rpx" text="加载更多..." />
|
||||||
|
</view>
|
||||||
|
<view wx:elif="{{!hasMore && records.length > 0}}" class="no-more">
|
||||||
|
<text>— 没有更多了 —</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
/* pages/profile/identify-history/index.wxss */
|
||||||
|
|
||||||
|
.history-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #F4F6F0;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading & Empty */
|
||||||
|
.loading-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
background: #F3F4F6;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #6B7280;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-hint {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Record List */
|
||||||
|
.record-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
padding-bottom: 60rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 28rpx;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.02);
|
||||||
|
transition: box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-card:active {
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Header */
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-thumb {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb-placeholder {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #F3F4F6;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1F2937;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 4rpx 14rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other-hint {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other-name {
|
||||||
|
color: #6B7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-arrow {
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
padding: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-arrow.expanded {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Detail (Expanded) */
|
||||||
|
.card-detail {
|
||||||
|
margin-top: 28rpx;
|
||||||
|
padding-top: 28rpx;
|
||||||
|
border-top: 2rpx solid #F3F4F6;
|
||||||
|
animation: fadeSlideDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeSlideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-16rpx);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Description */
|
||||||
|
.detail-desc {
|
||||||
|
background: #F4F6F0;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
margin-bottom: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: #4B5563;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Result Bars */
|
||||||
|
.detail-results {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6B7280;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-bars {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-bar-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-name {
|
||||||
|
width: 120rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #374151;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-track {
|
||||||
|
flex: 1;
|
||||||
|
height: 16rpx;
|
||||||
|
background: #F3F4F6;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: linear-gradient(90deg, #A5D6A7, #66BB6A);
|
||||||
|
transition: width 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-fill.top {
|
||||||
|
background: linear-gradient(90deg, #66BB6A, #388E3C);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar-score {
|
||||||
|
width: 80rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6B7280;
|
||||||
|
text-align: right;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Meta */
|
||||||
|
.detail-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
padding-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load More */
|
||||||
|
.load-more {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #D1D5DB;
|
||||||
|
}
|
||||||
+290
-74
@@ -1,52 +1,163 @@
|
|||||||
// pages/profile/index.js
|
// pages/profile/index.js
|
||||||
import { MOCK_FAVORITES, MOCK_BADGES, MOCK_POSTS } from '../../utils/mockData';
|
import request from '../../utils/request';
|
||||||
|
|
||||||
|
const app = getApp();
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
view: 'profile', // profile, favorites, posts, badges
|
view: 'profile', // profile, favorites, posts, about
|
||||||
favTab: 'all', // all, plant, article
|
|
||||||
postsTab: 'published', // published, drafts
|
|
||||||
|
|
||||||
|
// User Info
|
||||||
|
userName: '植物爱好者',
|
||||||
|
userAvatar: '',
|
||||||
|
userLevel: '', // Reserved for future level system
|
||||||
|
userLevelTag: '', // e.g. 'Lv.4 资深植人'
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
plantCount: 0,
|
||||||
|
taskDoneCount: 0,
|
||||||
|
postCount: 0,
|
||||||
|
|
||||||
|
// Favorites
|
||||||
|
favTab: 'all',
|
||||||
favorites: [],
|
favorites: [],
|
||||||
filteredFavorites: [],
|
filteredFavorites: [],
|
||||||
|
|
||||||
|
// Posts
|
||||||
|
postsTab: 'published',
|
||||||
myPublishedPosts: [],
|
myPublishedPosts: [],
|
||||||
myDrafts: [],
|
myDrafts: [],
|
||||||
badges: []
|
|
||||||
|
// App version
|
||||||
|
appVersion: '1.0.0'
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options) {
|
onLoad() {
|
||||||
this.setData({
|
this.loadUserInfo();
|
||||||
favorites: MOCK_FAVORITES,
|
|
||||||
badges: MOCK_BADGES
|
|
||||||
});
|
|
||||||
this.filterFavorites();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
if (typeof this.getTabBar === 'function' &&
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||||
this.getTabBar()) {
|
this.getTabBar().setData({ selected: 4 });
|
||||||
this.getTabBar().setData({
|
|
||||||
selected: 4 // Index 4 is Profile
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh posts data
|
|
||||||
this.loadMyPosts();
|
|
||||||
this.loadDrafts();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ======== User Info ========
|
||||||
|
loadUserInfo() {
|
||||||
|
// Try to get from globalData or storage
|
||||||
|
const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo');
|
||||||
|
if (userInfo && userInfo.name) {
|
||||||
|
this.setData({
|
||||||
|
userName: userInfo.name || '植物爱好者',
|
||||||
|
userAvatar: userInfo.avatarUrl || userInfo.avatar || ''
|
||||||
|
});
|
||||||
|
return; // Use cached data, no API call
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only fetch from backend if no cached info
|
||||||
|
request.get('/user/info').then(user => {
|
||||||
|
if (!user) return;
|
||||||
|
const avatarUrl = user.avatar ? user.avatar.url : '';
|
||||||
|
this.setData({
|
||||||
|
userName: user.name || '植物爱好者',
|
||||||
|
userAvatar: avatarUrl
|
||||||
|
});
|
||||||
|
const info = {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
avatarUrl: avatarUrl,
|
||||||
|
account: user.account,
|
||||||
|
phone: user.phone,
|
||||||
|
avatarId: user.avatarId
|
||||||
|
};
|
||||||
|
app.globalData.userInfo = info;
|
||||||
|
wx.setStorageSync('userInfo', info);
|
||||||
|
}).catch(() => { });
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Stats ========
|
||||||
|
loadStats() {
|
||||||
|
// Fetch plant count
|
||||||
|
request.post('/plant/page', { current: 1, pageSize: 1 }).then(res => {
|
||||||
|
this.setData({ plantCount: res.total || 0 });
|
||||||
|
}).catch(() => { });
|
||||||
|
|
||||||
|
// Fetch post count - user's own posts
|
||||||
|
request.post('/post/page', { current: 1, pageSize: 1, onlyMine: true }).then(res => {
|
||||||
|
this.setData({ postCount: res.total || 0 });
|
||||||
|
}).catch(() => { });
|
||||||
|
|
||||||
|
// Fetch completed tasks count
|
||||||
|
request.get('/plant/taskCount').then(res => {
|
||||||
|
this.setData({ taskDoneCount: res || 0 });
|
||||||
|
}).catch(() => { });
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Navigation ========
|
||||||
|
setView(e) {
|
||||||
|
const view = e.currentTarget.dataset.view;
|
||||||
|
this.setData({ view });
|
||||||
|
|
||||||
|
if (view === 'favorites') {
|
||||||
|
this.loadFavorites();
|
||||||
|
} else if (view === 'posts') {
|
||||||
|
this.loadMyPosts();
|
||||||
|
this.loadDrafts();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
this.setData({ view: 'profile' });
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Favorites ========
|
||||||
|
loadFavorites() {
|
||||||
|
// TODO: Call favorites API when available
|
||||||
|
// request.get('/user/favorites').then(...)
|
||||||
|
this.filterFavorites();
|
||||||
|
},
|
||||||
|
|
||||||
|
onFavTabChange(e) {
|
||||||
|
this.setData({ favTab: e.detail.value }, () => {
|
||||||
|
this.filterFavorites();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
filterFavorites() {
|
||||||
|
const { favorites, favTab } = this.data;
|
||||||
|
const filtered = favorites.filter(item => {
|
||||||
|
if (favTab === 'all') return true;
|
||||||
|
return item.type === favTab;
|
||||||
|
});
|
||||||
|
this.setData({ filteredFavorites: filtered });
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Posts ========
|
||||||
loadMyPosts() {
|
loadMyPosts() {
|
||||||
// Get published posts by current user
|
request.post('/post/page', { current: 1, pageSize: 50, onlyMine: true }).then(res => {
|
||||||
const myPosts = MOCK_POSTS.filter(p => p.user === '我的花园');
|
const records = res.records || res.list || [];
|
||||||
this.setData({ myPublishedPosts: myPosts });
|
const posts = records.map(item => {
|
||||||
|
const publisher = item.publisher || {};
|
||||||
|
const imgList = item.imgList || [];
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
content: item.content || '',
|
||||||
|
time: this._formatTime(item.createdAt || item.createTime),
|
||||||
|
images: imgList.map(img => img.url),
|
||||||
|
likes: item.likeList || [],
|
||||||
|
comments: item.commentList || []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.setData({ myPublishedPosts: posts });
|
||||||
|
}).catch(() => {
|
||||||
|
this.setData({ myPublishedPosts: [] });
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
loadDrafts() {
|
loadDrafts() {
|
||||||
// Load drafts from storage
|
|
||||||
try {
|
try {
|
||||||
const draft = wx.getStorageSync('post_draft');
|
const draft = wx.getStorageSync('post_draft');
|
||||||
if (draft && (draft.content || (draft.images && draft.images.length > 0))) {
|
if (draft && (draft.content || (draft.images && draft.images.length > 0))) {
|
||||||
// Convert single draft to array for consistency
|
|
||||||
this.setData({
|
this.setData({
|
||||||
myDrafts: [{
|
myDrafts: [{
|
||||||
id: 'draft_1',
|
id: 'draft_1',
|
||||||
@@ -63,39 +174,10 @@ Page({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setView(e) {
|
|
||||||
const view = e.currentTarget.dataset.view;
|
|
||||||
this.setData({ view });
|
|
||||||
|
|
||||||
// Refresh data when entering posts view
|
|
||||||
if (view === 'posts') {
|
|
||||||
this.loadMyPosts();
|
|
||||||
this.loadDrafts();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onFavTabChange(e) {
|
|
||||||
const tab = e.detail.value;
|
|
||||||
this.setData({ favTab: tab }, () => {
|
|
||||||
this.filterFavorites();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onPostsTabChange(e) {
|
onPostsTabChange(e) {
|
||||||
const tab = e.detail.value;
|
this.setData({ postsTab: e.detail.value });
|
||||||
this.setData({ postsTab: tab });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
filterFavorites() {
|
|
||||||
const { favorites, favTab } = this.data;
|
|
||||||
const filtered = favorites.filter(item => {
|
|
||||||
if (favTab === 'all') return true;
|
|
||||||
return item.type === favTab;
|
|
||||||
});
|
|
||||||
this.setData({ filteredFavorites: filtered });
|
|
||||||
},
|
|
||||||
|
|
||||||
// Delete a published post
|
|
||||||
deletePost(e) {
|
deletePost(e) {
|
||||||
const postId = e.currentTarget.dataset.id;
|
const postId = e.currentTarget.dataset.id;
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
@@ -103,42 +185,176 @@ Page({
|
|||||||
content: '确定要删除这条动态吗?',
|
content: '确定要删除这条动态吗?',
|
||||||
confirmColor: '#EF5350',
|
confirmColor: '#EF5350',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (!res.confirm) return;
|
||||||
// Remove from MOCK_POSTS
|
wx.showLoading({ title: '删除中...' });
|
||||||
const idx = MOCK_POSTS.findIndex(p => p.id === postId);
|
request.get('/post/delete', { id: postId }).then(() => {
|
||||||
if (idx > -1) {
|
wx.hideLoading();
|
||||||
MOCK_POSTS.splice(idx, 1);
|
|
||||||
}
|
|
||||||
this.loadMyPosts();
|
this.loadMyPosts();
|
||||||
wx.showToast({ title: '已删除', icon: 'success' });
|
wx.showToast({ title: '已删除', icon: 'success' });
|
||||||
}
|
}).catch(() => {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showToast({ title: '删除失败', icon: 'none' });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Edit a draft
|
editDraft() {
|
||||||
editDraft(e) {
|
wx.navigateTo({ url: '/pages/community/create/index' });
|
||||||
// Navigate to create page, which will load the draft
|
|
||||||
wx.navigateTo({
|
|
||||||
url: '/pages/community/create/index'
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Delete a draft
|
deleteDraft() {
|
||||||
deleteDraft(e) {
|
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '删除草稿',
|
title: '删除草稿',
|
||||||
content: '确定要删除这份草稿吗?',
|
content: '确定要删除这份草稿吗?',
|
||||||
confirmColor: '#EF5350',
|
confirmColor: '#EF5350',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) {
|
if (!res.confirm) return;
|
||||||
try {
|
try { wx.removeStorageSync('post_draft'); } catch (e) { }
|
||||||
wx.removeStorageSync('post_draft');
|
|
||||||
} catch (e) { }
|
|
||||||
this.setData({ myDrafts: [] });
|
this.setData({ myDrafts: [] });
|
||||||
wx.showToast({ title: '已删除', icon: 'success' });
|
wx.showToast({ title: '已删除', icon: 'success' });
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Menu Actions ========
|
||||||
|
goToIdentifyHistory() {
|
||||||
|
wx.navigateTo({ url: '/pages/profile/identify-history/index' });
|
||||||
|
},
|
||||||
|
|
||||||
|
goToNotificationSettings() {
|
||||||
|
// Open WeChat notification settings
|
||||||
|
wx.openSetting({
|
||||||
|
success: (res) => {
|
||||||
|
console.log('Settings opened', res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
goToAbout() {
|
||||||
|
this.setData({ view: 'about' });
|
||||||
|
},
|
||||||
|
|
||||||
|
goToAgreement() {
|
||||||
|
// TODO: Navigate to agreement page or show inline
|
||||||
|
wx.showToast({ title: '功能开发中', icon: 'none' });
|
||||||
|
},
|
||||||
|
|
||||||
|
goToPrivacy() {
|
||||||
|
// TODO: Navigate to privacy page or show inline
|
||||||
|
wx.showToast({ title: '功能开发中', icon: 'none' });
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Profile Editor Popup ========
|
||||||
|
openProfileEditor() {
|
||||||
|
this.setData({
|
||||||
|
showProfileEditor: true,
|
||||||
|
tempAvatar: '',
|
||||||
|
tempNickname: this.data.userName === '植物爱好者' ? '' : this.data.userName
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeProfileEditor() {
|
||||||
|
this.setData({ showProfileEditor: false });
|
||||||
|
},
|
||||||
|
|
||||||
|
onProfilePopupChange(e) {
|
||||||
|
if (!e.detail.visible) {
|
||||||
|
this.setData({ showProfileEditor: false });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// WeChat native chooseAvatar callback
|
||||||
|
onChooseAvatar(e) {
|
||||||
|
const avatarUrl = e.detail.avatarUrl;
|
||||||
|
if (avatarUrl) {
|
||||||
|
this.setData({ tempAvatar: avatarUrl });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onNicknameInput(e) {
|
||||||
|
this.setData({ tempNickname: e.detail.value });
|
||||||
|
},
|
||||||
|
|
||||||
|
onNicknameBlur(e) {
|
||||||
|
// WeChat nickname type may return value on blur
|
||||||
|
if (e.detail.value) {
|
||||||
|
this.setData({ tempNickname: e.detail.value });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveProfile() {
|
||||||
|
const { tempAvatar, tempNickname } = this.data;
|
||||||
|
|
||||||
|
if (!tempAvatar && !tempNickname) {
|
||||||
|
wx.showToast({ title: '请选择头像或输入昵称', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.showLoading({ title: '保存中...', mask: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatePayload = {};
|
||||||
|
|
||||||
|
// 1. Upload avatar if changed
|
||||||
|
if (tempAvatar) {
|
||||||
|
const data = await request.upload(tempAvatar);
|
||||||
|
const fileData = data?.file || {};
|
||||||
|
if (fileData.id) {
|
||||||
|
updatePayload.avatar_id = fileData.id;
|
||||||
|
// Update local display
|
||||||
|
this.setData({ userAvatar: fileData.url || tempAvatar });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Set name if provided
|
||||||
|
if (tempNickname) {
|
||||||
|
updatePayload.name = tempNickname;
|
||||||
|
this.setData({ userName: tempNickname });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Call update API
|
||||||
|
if (Object.keys(updatePayload).length > 0) {
|
||||||
|
await request.post('/user/update', updatePayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.hideLoading();
|
||||||
|
this.setData({ showProfileEditor: false });
|
||||||
|
wx.showToast({ title: '资料已更新', icon: 'success' });
|
||||||
|
|
||||||
|
// Update globalData
|
||||||
|
const userInfo = app.globalData.userInfo || {};
|
||||||
|
if (updatePayload.name) userInfo.name = updatePayload.name;
|
||||||
|
if (updatePayload.avatar_id) userInfo.avatarId = updatePayload.avatar_id;
|
||||||
|
app.globalData.userInfo = userInfo;
|
||||||
|
} catch (err) {
|
||||||
|
wx.hideLoading();
|
||||||
|
console.error('Save profile failed', err);
|
||||||
|
wx.showToast({ title: '保存失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Utilities ========
|
||||||
|
_formatTime(dateStr) {
|
||||||
|
if (!dateStr) return '';
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMs = now - d;
|
||||||
|
const diffMin = Math.floor(diffMs / 60000);
|
||||||
|
if (diffMin < 1) return '刚刚';
|
||||||
|
if (diffMin < 60) return diffMin + '分钟前';
|
||||||
|
const diffHour = Math.floor(diffMin / 60);
|
||||||
|
if (diffHour < 24) return diffHour + '小时前';
|
||||||
|
const diffDay = Math.floor(diffHour / 24);
|
||||||
|
if (diffDay < 7) return diffDay + '天前';
|
||||||
|
|
||||||
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||||
|
const day = d.getDate().toString().padStart(2, '0');
|
||||||
|
return `${month}-${day}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======== Reserved: Future Level/Badge System ========
|
||||||
|
// These methods will be implemented when the backend supports level/badge APIs
|
||||||
|
// loadLevelInfo() { request.get('/user/level').then(...) },
|
||||||
|
// loadBadges() { request.get('/user/badges').then(...) },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"navigationBarTitleText": "个人中心",
|
"navigationBarTitleText": "个人中心",
|
||||||
|
"disableScroll": true,
|
||||||
"usingComponents": {
|
"usingComponents": {
|
||||||
"t-grid": "tdesign-miniprogram/grid/grid",
|
|
||||||
"t-grid-item": "tdesign-miniprogram/grid-item/grid-item",
|
|
||||||
"t-cell": "tdesign-miniprogram/cell/cell",
|
"t-cell": "tdesign-miniprogram/cell/cell",
|
||||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||||
"t-avatar": "tdesign-miniprogram/avatar/avatar",
|
"t-avatar": "tdesign-miniprogram/avatar/avatar",
|
||||||
"t-image": "tdesign-miniprogram/image/image",
|
"t-image": "tdesign-miniprogram/image/image",
|
||||||
"t-tag": "tdesign-miniprogram/tag/tag",
|
"t-tag": "tdesign-miniprogram/tag/tag",
|
||||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||||
"t-badge": "tdesign-miniprogram/badge/badge",
|
"t-tabs": "tdesign-miniprogram/tabs/tabs",
|
||||||
"t-progress": "tdesign-miniprogram/progress/progress"
|
"t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
|
||||||
|
"t-button": "tdesign-miniprogram/button/button",
|
||||||
|
"t-popup": "tdesign-miniprogram/popup/popup"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+203
-123
@@ -1,117 +1,77 @@
|
|||||||
<wxs src="../../utils/tools.wxs" module="tools" />
|
|
||||||
<view class="profile-page">
|
<view class="profile-page">
|
||||||
<!-- Sub-views handled by conditional rendering to match prototype single-page feel -->
|
|
||||||
|
|
||||||
<!-- FAVORITES VIEW -->
|
<!-- ======== FAVORITES VIEW ======== -->
|
||||||
<view wx:if="{{view === 'favorites'}}" class="favorites-page info-view-anim">
|
<view wx:if="{{view === 'favorites'}}" class="sub-view info-view-anim">
|
||||||
<view class="back-nav sticky-nav">
|
<view class="sub-nav" bindtap="goBack">
|
||||||
<t-button variant="text" icon="arrow-left" bind:tap="setView" data-view="profile">我的收藏</t-button>
|
<t-icon name="chevron-left" size="40rpx" />
|
||||||
|
<text class="sub-nav-title">我的收藏</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<t-tabs value="{{favTab}}" bind:change="onFavTabChange" theme="card">
|
<view class="category-filter">
|
||||||
<t-tab-panel label="全部" value="all" />
|
<view class="filter-chip {{favTab === 'all' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="all">全部</view>
|
||||||
<t-tab-panel label="植物" value="plant" />
|
<view class="filter-chip {{favTab === 'plant' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="plant">植物</view>
|
||||||
<t-tab-panel label="文章" value="article" />
|
<view class="filter-chip {{favTab === 'article' ? 'active' : ''}}" bindtap="onFavTabChange" data-value="article">文章</view>
|
||||||
</t-tabs>
|
</view>
|
||||||
|
|
||||||
<view class="tab-content">
|
<scroll-view scroll-y class="sub-scroll">
|
||||||
<view class="fav-grid">
|
<view wx:if="{{filteredFavorites.length > 0}}" class="fav-grid">
|
||||||
<block wx:if="{{filteredFavorites.length > 0}}">
|
|
||||||
<view wx:for="{{filteredFavorites}}" wx:key="id" class="fav-card">
|
<view wx:for="{{filteredFavorites}}" wx:key="id" class="fav-card">
|
||||||
<t-image src="{{item.image}}" class="fav-img" mode="aspectFill" width="100%" height="240rpx" />
|
<t-image src="{{item.image}}" mode="aspectFill" width="100%" height="240rpx" class="fav-img" />
|
||||||
<view class="fav-info">
|
<view class="fav-info">
|
||||||
<text class="fav-name">{{item.name}}</text>
|
<text class="fav-name">{{item.name}}</text>
|
||||||
<view class="fav-meta-row">
|
<view class="fav-meta-row">
|
||||||
<t-icon name="{{item.type === 'plant' ? 'heart' : 'book'}}" size="32rpx" color="#90A4AE" />
|
<t-icon name="{{item.type === 'plant' ? 'heart' : 'book'}}" size="28rpx" color="#90A4AE" />
|
||||||
<text class="fav-type">{{item.meta}}</text>
|
<text class="fav-type">{{item.meta}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
<block wx:else>
|
|
||||||
<view class="empty-state">
|
|
||||||
<t-icon name="star" size="64rpx" color="#ccc" />
|
|
||||||
<text style="margin-top: 16rpx;">暂无收藏内容</text>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
|
<view wx:else class="empty-state">
|
||||||
|
<text class="empty-text">暂无收藏内容</text>
|
||||||
</view>
|
</view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- POSTS VIEW -->
|
<!-- ======== POSTS VIEW ======== -->
|
||||||
<view wx:elif="{{view === 'posts'}}" class="posts-page-detail info-view-anim">
|
<view wx:elif="{{view === 'posts'}}" class="sub-view info-view-anim">
|
||||||
<view class="back-nav sticky-nav">
|
<view class="sub-nav" bindtap="goBack">
|
||||||
<t-button variant="text" icon="arrow-left" bind:tap="setView" data-view="profile">我的发布</t-button>
|
<t-icon name="chevron-left" size="40rpx" />
|
||||||
|
<text class="sub-nav-title">我的发布</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Tabs for Published / Drafts -->
|
<scroll-view scroll-y class="sub-scroll">
|
||||||
<t-tabs value="{{postsTab}}" bind:change="onPostsTabChange" theme="card">
|
|
||||||
<t-tab-panel label="已发布" value="published" />
|
|
||||||
<t-tab-panel label="草稿箱" value="drafts" />
|
|
||||||
</t-tabs>
|
|
||||||
|
|
||||||
<!-- Published Posts -->
|
|
||||||
<view wx:if="{{postsTab === 'published'}}" class="my-posts-list">
|
|
||||||
<block wx:if="{{myPublishedPosts.length > 0}}">
|
|
||||||
<view wx:for="{{myPublishedPosts}}" wx:key="id" class="my-post-card">
|
<view wx:for="{{myPublishedPosts}}" wx:key="id" class="my-post-card">
|
||||||
<view class="my-post-time">{{item.time}}</view>
|
<view class="my-post-time">{{item.time}}</view>
|
||||||
<view class="my-post-content-wrap">
|
<view class="my-post-content-wrap">
|
||||||
<text class="post-text">{{item.content}}</text>
|
<text class="post-text">{{item.content}}</text>
|
||||||
<view wx:if="{{item.images.length > 0}}" class="my-post-images">
|
<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="160rpx" height="160rpx" style="margin-right: 16rpx; display: inline-block; border-radius: 8rpx;" />
|
<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>
|
||||||
<view class="my-post-footer">
|
<view class="my-post-footer">
|
||||||
<view class="footer-item"><t-icon name="heart" size="32rpx" /> <text>{{item.likes.length}}</text></view>
|
<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="32rpx" /> <text>{{item.comments.length}}</text></view>
|
<view class="footer-item"><t-icon name="chat" size="28rpx" /> <text>{{item.comments.length}}</text></view>
|
||||||
<view class="footer-item delete-btn" bindtap="deletePost" data-id="{{item.id}}"><t-icon name="delete" size="32rpx" color="#EF5350" /></view>
|
<view class="footer-item" catchtap="deletePost" data-id="{{item.id}}" style="margin-left:auto; color: #EF5350;">
|
||||||
|
<t-icon name="delete" size="28rpx" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
<view wx:else class="empty-state">
|
|
||||||
<t-icon name="file-copy" size="64rpx" color="#ccc" />
|
|
||||||
<text style="margin-top: 16rpx;">暂无已发布的动态</text>
|
|
||||||
</view>
|
</view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Draft Posts -->
|
<!-- ======== BADGES VIEW ======== -->
|
||||||
<view wx:if="{{postsTab === 'drafts'}}" class="my-posts-list">
|
<view wx:elif="{{view === 'badges'}}" class="sub-view info-view-anim">
|
||||||
<block wx:if="{{myDrafts.length > 0}}">
|
<view class="sub-nav" bindtap="goBack">
|
||||||
<view wx:for="{{myDrafts}}" wx:key="id" class="my-post-card draft-card">
|
<t-icon name="chevron-left" size="40rpx" />
|
||||||
<view class="draft-badge">草稿</view>
|
<text class="sub-nav-title">成就徽章</text>
|
||||||
<view class="my-post-content-wrap">
|
|
||||||
<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="160rpx" height="160rpx" style="margin-right: 16rpx; display: inline-block; border-radius: 8rpx;" />
|
|
||||||
</view>
|
|
||||||
<view class="my-post-footer">
|
|
||||||
<view class="footer-item edit-btn" bindtap="editDraft" data-index="{{index}}">
|
|
||||||
<t-icon name="edit" size="32rpx" color="#558B2F" />
|
|
||||||
<text style="color: #558B2F;">编辑</text>
|
|
||||||
</view>
|
|
||||||
<view class="footer-item delete-btn" bindtap="deleteDraft" data-index="{{index}}">
|
|
||||||
<t-icon name="delete" size="32rpx" color="#EF5350" />
|
|
||||||
<text style="color: #EF5350;">删除</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
<view wx:else class="empty-state">
|
|
||||||
<t-icon name="file-add" size="64rpx" color="#ccc" />
|
|
||||||
<text style="margin-top: 16rpx;">暂无草稿</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- BADGES VIEW -->
|
<scroll-view scroll-y class="sub-scroll">
|
||||||
<view wx:elif="{{view === 'badges'}}" class="badges-page info-view-anim">
|
<!-- Level Card -->
|
||||||
<view class="back-nav sticky-nav">
|
|
||||||
<t-button variant="text" icon="arrow-left" bind:tap="setView" data-view="profile">成就徽章</t-button>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<scroll-view scroll-y class="badges-content">
|
|
||||||
<view class="level-card-large">
|
<view class="level-card-large">
|
||||||
|
<view class="level-card-bg"></view>
|
||||||
<view class="level-header">
|
<view class="level-header">
|
||||||
<view class="level-info-large">
|
<view class="level-info-large">
|
||||||
<text class="level-label">当前等级</text>
|
<text class="level-label">当前等级</text>
|
||||||
@@ -119,82 +79,202 @@
|
|||||||
</view>
|
</view>
|
||||||
<t-icon name="trophy" size="80rpx" color="#FFD700" />
|
<t-icon name="trophy" size="80rpx" color="#FFD700" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="level-progress-section">
|
<view class="level-progress-section">
|
||||||
<view class="progress-text">
|
<view class="progress-text">
|
||||||
<text>经验值</text>
|
<text>经验值</text>
|
||||||
<text>350 / 500</text>
|
<text>350 / 500</text>
|
||||||
</view>
|
</view>
|
||||||
<t-progress percentage="70" theme="plump" color="#FFD700" track-color="rgba(255,255,255,0.3)" />
|
<view class="level-progress-bar-bg">
|
||||||
|
<view class="level-progress-bar-fill" style="width: 70%;"></view>
|
||||||
|
</view>
|
||||||
<text class="next-level-tip">距离 Lv.5 园艺大师 还需 150 经验</text>
|
<text class="next-level-tip">距离 Lv.5 园艺大师 还需 150 经验</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="section-title-badges">所有徽章 (3/6)</view>
|
<view class="section-title-badges">所有徽章 (3/6)</view>
|
||||||
<t-grid column="{{3}}" gutter="24rpx">
|
|
||||||
<t-grid-item wx:for="{{badges}}" wx:key="id" text="{{item.name}}" description="{{item.desc}}" image="{{item.unlocked ? '/assets/icons/'+item.icon+'.png' : '/assets/icons/lock.png'}}" />
|
<view class="badges-grid">
|
||||||
<!-- TDesign grid item image might need full path or use slot for svg if needed, using png for now -->
|
<view wx:for="{{badges}}" wx:key="id" class="badge-item {{item.unlocked ? 'unlocked' : 'locked'}}">
|
||||||
</t-grid>
|
<view class="badge-icon-circle" style="background: {{item.unlocked ? item.color + '20' : '#F5F5F5'}}">
|
||||||
|
<t-icon wx:if="{{item.unlocked}}" name="{{item.iconName}}" size="48rpx" color="{{item.color}}" />
|
||||||
|
<t-icon wx:else name="lock-on" size="40rpx" color="#BDBDBD" />
|
||||||
|
</view>
|
||||||
|
<text class="badge-name">{{item.name}}</text>
|
||||||
|
<text class="badge-desc">{{item.desc}}</text>
|
||||||
|
<text wx:if="{{item.progress}}" class="badge-progress">{{item.progress}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- MAIN PROFILE VIEW -->
|
<!-- ======== ABOUT VIEW ======== -->
|
||||||
|
<view wx:elif="{{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-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 wx:else class="main-profile-view">
|
||||||
|
<!-- Header -->
|
||||||
<view class="profile-header">
|
<view class="profile-header">
|
||||||
<view class="user-main">
|
<view class="user-main">
|
||||||
<view class="user-avatar">
|
<view class="user-avatar" bindtap="openProfileEditor">
|
||||||
<t-avatar image="https://api.dicebear.com/7.x/avataaars/svg?seed=Lucky" size="large" />
|
<t-avatar wx:if="{{userAvatar}}" image="{{userAvatar}}" size="120rpx" />
|
||||||
|
<t-avatar wx:else icon="user" size="120rpx" />
|
||||||
</view>
|
</view>
|
||||||
<view class="user-text">
|
<view class="user-text" bindtap="openProfileEditor">
|
||||||
<text class="user-name">布偶猫园长</text>
|
<view class="user-name">{{userName}}</view>
|
||||||
<t-tag theme="warning" variant="light" size="small">Lv.4 资深植人</t-tag>
|
<view class="level-badge">Lv.4 资深植人</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<t-icon name="setting" size="48rpx" />
|
<view class="settings-btn" bindtap="goToNotificationSettings">
|
||||||
|
<t-icon name="setting" size="40rpx" color="#666" />
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view scroll-y class="profile-content">
|
<!-- Stats Card (Fixed) -->
|
||||||
<view class="stats-grid">
|
<view class="stats-section">
|
||||||
<view class="stat-col">
|
<view class="stats-card">
|
||||||
<text class="stat-num">12</text>
|
<view class="stat-item">
|
||||||
|
<text class="stat-num">{{plantCount}}</text>
|
||||||
<text class="stat-label">植物</text>
|
<text class="stat-label">植物</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-col">
|
<view class="stat-divider"></view>
|
||||||
<text class="stat-num">328</text>
|
<view class="stat-item">
|
||||||
|
<text class="stat-num">{{taskDoneCount}}</text>
|
||||||
<text class="stat-label">养护</text>
|
<text class="stat-label">养护</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-col">
|
<view class="stat-divider"></view>
|
||||||
<text class="stat-num">15</text>
|
<view class="stat-item">
|
||||||
<text class="stat-label">关注</text>
|
<text class="stat-num">{{postCount}}</text>
|
||||||
|
<text class="stat-label">动态</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<scroll-view scroll-y class="profile-content" enhanced show-scrollbar="{{false}}">
|
||||||
|
<!-- Menu -->
|
||||||
<view class="profile-menu">
|
<view class="profile-menu">
|
||||||
<t-cell-group title="常用功能" theme="card">
|
<view class="menu-group-title">常用功能</view>
|
||||||
<t-cell title="我的收藏" hover arrow bind:tap="setView" data-view="favorites">
|
|
||||||
<t-icon slot="left-icon" name="star" color="#FFA000" style="margin-right: 16rpx;" />
|
|
||||||
</t-cell>
|
|
||||||
<t-cell title="我的发布" hover arrow bind:tap="setView" data-view="posts">
|
|
||||||
<t-icon slot="left-icon" name="file-copy" color="#1976D2" style="margin-right: 16rpx;" />
|
|
||||||
</t-cell>
|
|
||||||
<t-cell title="识别记录" hover arrow>
|
|
||||||
<t-icon slot="left-icon" name="scan" color="#388E3C" style="margin-right: 16rpx;" />
|
|
||||||
</t-cell>
|
|
||||||
<t-cell title="成就徽章" note="已获 3 个" hover arrow bind:tap="setView" data-view="badges">
|
|
||||||
<t-icon slot="left-icon" name="control-platform" color="#AB47BC" style="margin-right: 16rpx;" />
|
|
||||||
</t-cell>
|
|
||||||
</t-cell-group>
|
|
||||||
|
|
||||||
<t-cell-group title="更多服务" theme="card" style="margin-top: 24rpx;">
|
|
||||||
<t-cell title="帮助与反馈" hover arrow>
|
|
||||||
<t-icon slot="left-icon" name="help-circle" color="#757575" style="margin-right: 16rpx;" />
|
|
||||||
</t-cell>
|
|
||||||
</t-cell-group>
|
|
||||||
|
|
||||||
|
<view class="menu-item" bindtap="setView" data-view="favorites">
|
||||||
|
<view class="menu-left">
|
||||||
|
<view class="menu-icon-bg" style="background: #FFF3E0">
|
||||||
|
<t-icon name="star" size="36rpx" color="#FF9800" />
|
||||||
|
</view>
|
||||||
|
<text class="menu-text">我的收藏</text>
|
||||||
|
</view>
|
||||||
|
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Spacer -->
|
<view class="menu-item" bindtap="setView" data-view="posts">
|
||||||
<view style="height: 40rpx;"></view>
|
<view class="menu-left">
|
||||||
|
<view class="menu-icon-bg" style="background: #E3F2FD">
|
||||||
|
<t-icon name="file-copy" size="36rpx" color="#2196F3" />
|
||||||
|
</view>
|
||||||
|
<text class="menu-text">我的发布</text>
|
||||||
|
</view>
|
||||||
|
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="menu-item" bindtap="goToIdentifyHistory">
|
||||||
|
<view class="menu-left">
|
||||||
|
<view class="menu-icon-bg" style="background: #E8F5E9">
|
||||||
|
<t-icon name="scan" size="36rpx" color="#4CAF50" />
|
||||||
|
</view>
|
||||||
|
<text class="menu-text">识别记录</text>
|
||||||
|
</view>
|
||||||
|
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="menu-item" bindtap="setView" data-view="badges">
|
||||||
|
<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">
|
||||||
|
<view class="menu-left">
|
||||||
|
<view class="menu-icon-bg" style="background: #F5F5F5">
|
||||||
|
<t-icon name="help-circle" size="36rpx" color="#616161" />
|
||||||
|
</view>
|
||||||
|
<text class="menu-text">帮助与关于</text>
|
||||||
|
</view>
|
||||||
|
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view style="height: 100rpx;"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- Profile Edit Popup -->
|
||||||
|
<t-popup visible="{{showProfileEditor}}" placement="bottom" bind:visible-change="onProfilePopupChange">
|
||||||
|
<view class="profile-edit-popup">
|
||||||
|
<view class="popup-header">
|
||||||
|
<text class="popup-title">编辑资料</text>
|
||||||
|
<view class="popup-close" bindtap="closeProfileEditor">
|
||||||
|
<t-icon name="close" size="40rpx" color="#999" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="edit-row" bindtap="onChooseAvatar">
|
||||||
|
<text class="edit-row-label">头像</text>
|
||||||
|
<view class="edit-row-right">
|
||||||
|
<t-avatar
|
||||||
|
wx:if="{{tempAvatar || userAvatar}}"
|
||||||
|
image="{{tempAvatar || userAvatar}}"
|
||||||
|
size="96rpx"
|
||||||
|
/>
|
||||||
|
<t-avatar wx:else icon="user" size="96rpx" />
|
||||||
|
<t-icon name="chevron-right" size="32rpx" color="#C5C5C5" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="edit-row">
|
||||||
|
<text class="edit-row-label">昵称</text>
|
||||||
|
<input
|
||||||
|
class="nickname-input"
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入昵称"
|
||||||
|
placeholder-style="color: #C5C5C5;"
|
||||||
|
value="{{tempNickname}}"
|
||||||
|
bindinput="onNicknameInput"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="edit-actions">
|
||||||
|
<t-button theme="primary" block shape="round" bind:tap="saveProfile">保存</t-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</t-popup>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
+578
-104
@@ -1,15 +1,49 @@
|
|||||||
/** pages/profile/index.wxss **/
|
/** pages/profile/index.wxss **/
|
||||||
|
|
||||||
.profile-page {
|
.profile-page {
|
||||||
background-color: #F4F6F0;
|
background: #F4F6F0;
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
position: relative;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex; flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* ======== Sub-view Navigation ======== */
|
||||||
|
.sub-view {
|
||||||
|
background: #F4F6F0;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 30rpx 24rpx;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #111827;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-nav-title {
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-scroll {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-bottom: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.info-view-anim {
|
.info-view-anim {
|
||||||
animation: slideInRight 0.3s cubic-bezier(0.25, 1, 0.5, 1);
|
animation: slideInRight 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideInRight {
|
@keyframes slideInRight {
|
||||||
@@ -17,133 +51,573 @@
|
|||||||
to { transform: translateX(0); opacity: 1; }
|
to { transform: translateX(0); opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.sticky-nav {
|
/* ======== Category Filter (Custom Chips) ======== */
|
||||||
position: sticky; top: 0; z-index: 100; background: white;
|
.category-filter {
|
||||||
border-bottom: 2rpx solid #f0f0f0;
|
display: flex;
|
||||||
padding: 20rpx;
|
gap: 16rpx;
|
||||||
|
padding: 0 24rpx 16rpx;
|
||||||
|
background: #fff;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content { padding: 32rpx; }
|
.filter-chip {
|
||||||
|
padding: 8rpx 24rpx;
|
||||||
|
background: #fff;
|
||||||
|
border: 2rpx solid #E5E7EB;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
/* Favorites Grid */
|
.filter-chip.active {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Favorites Grid ======== */
|
||||||
.fav-grid {
|
.fav-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 24rpx;
|
gap: 20rpx;
|
||||||
margin-top: 24rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fav-card {
|
.fav-card {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 24rpx;
|
border-radius: 20rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.fav-img { width: 100%; display: block; background: #f0f0f0; }
|
.fav-img {
|
||||||
|
background: #f0f0f0;
|
||||||
.fav-info { padding: 20rpx; }
|
|
||||||
.fav-name { font-size: 28rpx; font-weight: 700; color: #37474F; margin-bottom: 12rpx; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
||||||
.fav-meta-row { display: flex; align-items: center; gap: 8rpx; }
|
|
||||||
.fav-type { font-size: 20rpx; color: #90A4AE; }
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
||||||
padding: 80rpx 0; color: #B0BEC5; font-size: 28rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Posts View */
|
.fav-info {
|
||||||
.my-posts-list { padding: 40rpx; }
|
padding: 16rpx 20rpx;
|
||||||
.my-post-card { display: flex; gap: 24rpx; margin-bottom: 48rpx; position: relative; }
|
|
||||||
.my-post-time { font-size: 24rpx; color: #B0BEC5; width: 140rpx; flex-shrink: 0; text-align: right; }
|
|
||||||
.my-post-content-wrap { flex: 1; border-left: 4rpx solid #ECEFF1; padding-left: 24rpx; padding-bottom: 24rpx; }
|
|
||||||
.my-post-images { margin: 16rpx 0; white-space: nowrap; overflow-x: auto; }
|
|
||||||
.my-post-footer { display: flex; gap: 32rpx; margin-top: 16rpx; }
|
|
||||||
.footer-item { display: flex; align-items: center; gap: 8rpx; font-size: 24rpx; color: #78909C; }
|
|
||||||
|
|
||||||
/* Draft Card */
|
|
||||||
.draft-card {
|
|
||||||
background: #FFFDE7;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
padding: 20rpx;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.draft-card .my-post-content-wrap {
|
.fav-name {
|
||||||
border-left: 4rpx solid #FFC107;
|
display: block;
|
||||||
}
|
font-size: 28rpx;
|
||||||
|
|
||||||
.draft-badge {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background: #FFC107;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 20rpx;
|
|
||||||
padding: 4rpx 16rpx;
|
|
||||||
border-radius: 8rpx 0 8rpx 0;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
color: #1F2937;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Action Buttons */
|
.fav-meta-row {
|
||||||
.edit-btn, .delete-btn {
|
display: flex;
|
||||||
cursor: pointer;
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-btn:active, .delete-btn:active {
|
.fav-type {
|
||||||
opacity: 0.7;
|
font-size: 22rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Badges View */
|
/* ======== Posts Styles (Refined) ======== */
|
||||||
.badges-content { padding: 40rpx; background: white; height: 100%; }
|
.my-post-card {
|
||||||
|
display: flex;
|
||||||
|
gap: 24rpx;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-post-time {
|
||||||
|
width: 100rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 500;
|
||||||
|
padding-top: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-post-content-wrap {
|
||||||
|
flex: 1;
|
||||||
|
background: #fff;
|
||||||
|
padding: 24rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #1F2937;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-post-images {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-post-footer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 32rpx;
|
||||||
|
border-top: 1rpx solid #F3F4F6;
|
||||||
|
padding-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Badges View ======== */
|
||||||
.level-card-large {
|
.level-card-large {
|
||||||
background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);
|
background: linear-gradient(135deg, #2c3e50 0%, #4ca1af 100%);
|
||||||
border-radius: 40rpx;
|
border-radius: 40rpx;
|
||||||
padding: 48rpx;
|
|
||||||
margin-bottom: 60rpx;
|
|
||||||
color: #E65100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.level-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 40rpx; }
|
|
||||||
.level-label { font-size: 26rpx; opacity: 0.8; display: block; }
|
|
||||||
.level-value { font-size: 48rpx; font-weight: 800; display: block; }
|
|
||||||
|
|
||||||
.level-progress-section { }
|
|
||||||
.progress-text { display: flex; justify-content: space-between; font-size: 24rpx; font-weight: 600; margin-bottom: 12rpx; }
|
|
||||||
.next-level-tip { font-size: 22rpx; margin-top: 16rpx; display: block; opacity: 0.8; }
|
|
||||||
|
|
||||||
.section-title-badges { font-size: 32rpx; font-weight: 700; color: #333; margin-bottom: 32rpx; }
|
|
||||||
|
|
||||||
/* Basic TDesign Grid Item styling override if needed */
|
|
||||||
.t-grid-item__content { padding: 24rpx 0 !important; }
|
|
||||||
|
|
||||||
/* Main Profile */
|
|
||||||
.main-profile-view { display: flex; flex-direction: column; height: 100%; }
|
|
||||||
|
|
||||||
.profile-header {
|
|
||||||
padding: 40rpx 48rpx;
|
|
||||||
background: white;
|
|
||||||
display: flex; justify-content: space-between; align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-main { display: flex; align-items: center; gap: 32rpx; }
|
|
||||||
.user-text { display: flex; flex-direction: column; gap: 12rpx; }
|
|
||||||
.user-name { font-size: 40rpx; font-weight: 800; color: var(--text-main); }
|
|
||||||
|
|
||||||
.stats-grid {
|
|
||||||
display: flex; justify-content: space-around;
|
|
||||||
padding: 40rpx;
|
padding: 40rpx;
|
||||||
margin: 24rpx 40rpx;
|
color: white;
|
||||||
background: white;
|
margin-bottom: 48rpx;
|
||||||
border-radius: 32rpx;
|
box-shadow: 0 20rpx 40rpx rgba(44, 62, 80, 0.2);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-card-bg {
|
||||||
|
position: absolute;
|
||||||
|
top: -100rpx;
|
||||||
|
right: -100rpx;
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-info-large {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
opacity: 0.8;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-value {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-progress-section {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-progress-bar-bg {
|
||||||
|
height: 16rpx;
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
border: 2rpx solid rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-progress-bar-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #FFD700, #FDB931);
|
||||||
|
border-radius: 8rpx;
|
||||||
|
box-shadow: 0 0 12rpx rgba(255, 215, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-level-tip {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: rgba(255,255,255,0.7);
|
||||||
|
display: block;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title-badges {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1F2937;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badges-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-item {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 32rpx 16rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.02);
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-col { display: flex; flex-direction: column; align-items: center; gap: 4rpx; }
|
.badge-item.locked {
|
||||||
.stat-num { font-size: 36rpx; font-weight: 800; color: var(--text-main); }
|
background: #F8F9FA;
|
||||||
.stat-label { font-size: 22rpx; color: #90A4AE; }
|
border: 2rpx dashed #E5E7EB;
|
||||||
|
box-shadow: none;
|
||||||
.profile-menu {
|
}
|
||||||
padding: 0 32rpx;
|
|
||||||
|
.badge-icon-circle {
|
||||||
|
width: 88rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-name {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #374151;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-desc {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-progress {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
background: #F3F4F6;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ======== Main Profile View ======== */
|
||||||
|
.main-profile-view {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-header {
|
||||||
|
background: linear-gradient(180deg, #E8F5E9 0%, #FFFFFF 100%);
|
||||||
|
padding: 32rpx 40rpx;
|
||||||
|
/* Extra padding top handled by structure relative to status bar usually,
|
||||||
|
but standard padding is fine here */
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom-left-radius: 48rpx;
|
||||||
|
border-bottom-right-radius: 48rpx;
|
||||||
|
box-shadow: 0 8rpx 30rpx rgba(0,0,0,0.02);
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-main {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 128rpx;
|
||||||
|
height: 128rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 6rpx solid #fff;
|
||||||
|
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #1F2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-badge {
|
||||||
|
align-self: flex-start;
|
||||||
|
font-size: 22rpx;
|
||||||
|
background: #DCEDC8;
|
||||||
|
color: #33691E;
|
||||||
|
padding: 6rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-btn {
|
||||||
|
padding: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-content {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
padding-bottom: 120rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide all scrollbars globally on this page */
|
||||||
|
.profile-page ::-webkit-scrollbar {
|
||||||
|
display: none !important;
|
||||||
|
width: 0 !important;
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-page scroll-view {
|
||||||
|
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 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-group-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
background: #fff;
|
||||||
|
padding: 32rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.015);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:active {
|
||||||
|
background: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon-bg {
|
||||||
|
width: 72rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-right-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-badge-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #6B7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edit Popup Styles */
|
||||||
|
.profile-edit-popup {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 40rpx 40rpx 0 0;
|
||||||
|
padding: 0 48rpx;
|
||||||
|
padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40rpx 0 20rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-close {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Edit Form Rows */
|
||||||
|
.edit-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx 0;
|
||||||
|
border-bottom: 2rpx solid #F3F4F6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-row-label {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #374151;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-row-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nickname-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #111827;
|
||||||
|
text-align: right;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-actions {
|
||||||
|
padding-top: 60rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* About Section */
|
||||||
|
.about-section {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-logo-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-logo {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
background: linear-gradient(135deg, #E8F5E9 0%, #C8E6C9 100%);
|
||||||
|
border-radius: 48rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.1);
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-app-name {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1F2937;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-version {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-desc {
|
||||||
|
background: #fff;
|
||||||
|
padding: 40rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: #4B5563;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.02);
|
||||||
|
margin-bottom: 40rpx;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #F8F9FA;
|
background-color: #F4F6F0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-45
@@ -5,63 +5,75 @@ Page({
|
|||||||
data: {
|
data: {
|
||||||
plant: null,
|
plant: null,
|
||||||
activeImageIndex: 0,
|
activeImageIndex: 0,
|
||||||
swiperList: []
|
swiperList: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
const eventChannel = this.getOpenerEventChannel();
|
||||||
|
let loadedFromEvent = false;
|
||||||
|
|
||||||
|
if (eventChannel && eventChannel.on) {
|
||||||
|
eventChannel.on('acceptDataFromOpenerPage', (res) => {
|
||||||
|
if (res.data) {
|
||||||
|
this.setPlantData(res.data);
|
||||||
|
loadedFromEvent = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (options.id) {
|
if (options.id) {
|
||||||
|
// Give event channel a chance to fire
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!loadedFromEvent && !this.data.plant) {
|
||||||
this.loadPlantDetail(options.id);
|
this.loadPlantDetail(options.id);
|
||||||
}
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPlantDetail(id) {
|
loadPlantDetail(id) {
|
||||||
// Fetch detail via wiki/page with specific ID
|
request.get('/wiki/detail', { id: id }).then(res => {
|
||||||
// Since there's no /wiki/detail endpoint, we use /wiki/page to get it
|
const item = res || null;
|
||||||
request.post('/wiki/page', {
|
|
||||||
current: 1,
|
|
||||||
pageSize: 1,
|
|
||||||
id: id
|
|
||||||
}).then(res => {
|
|
||||||
const data = res || {};
|
|
||||||
const list = data.list || [];
|
|
||||||
const item = list.length > 0 ? list[0] : null;
|
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
wx.showToast({ title: '未找到该植物', icon: 'none' });
|
wx.showToast({ title: '未找到该植物', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Page Title
|
this.setPlantData(item);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Load plant detail failed', err);
|
||||||
|
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setPlantData(item) {
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
wx.setNavigationBarTitle({ title: item.name });
|
wx.setNavigationBarTitle({ title: item.name });
|
||||||
|
|
||||||
// Prepare swiper list from imgList
|
// Prepare swiper list
|
||||||
const swiperList = (item.imgList || []).map(img => img.url);
|
const swiperList = (item.imgList || []).map(img => img.url);
|
||||||
|
|
||||||
// Parse pest/disease list
|
// Parse lists
|
||||||
const commonPests = item.pestsDiseases
|
const commonPests = item.pestsDiseases
|
||||||
? item.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
|
? item.pestsDiseases.split(',').map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Parse aliases
|
|
||||||
const aliasesList = item.aliases
|
const aliasesList = item.aliases
|
||||||
? item.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
? item.aliases.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Parse reproduction methods
|
|
||||||
const reproductionList = item.reproductionMethod
|
const reproductionList = item.reproductionMethod
|
||||||
? item.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
? item.reproductionMethod.split(/[,,、]/).map(s => s.trim()).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Difficulty label
|
|
||||||
const diffLabels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' };
|
const diffLabels = { 1: '简单', 2: '中等', 3: '较难', 4: '困难', 5: '专家' };
|
||||||
|
|
||||||
// Map API data to display model
|
|
||||||
const plant = {
|
const plant = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
latinName: item.latinName || '',
|
latinName: item.latinName || '',
|
||||||
aliases: item.aliases || '',
|
aliases: item.aliases || '',
|
||||||
aliasesList: aliasesList,
|
aliasesList,
|
||||||
genus: item.genus || '',
|
genus: item.genus || '',
|
||||||
distributionArea: item.distributionArea || '',
|
distributionArea: item.distributionArea || '',
|
||||||
difficulty: item.difficulty || 0,
|
difficulty: item.difficulty || 0,
|
||||||
@@ -70,49 +82,29 @@ Page({
|
|||||||
lifeCycle: item.lifeCycle || '',
|
lifeCycle: item.lifeCycle || '',
|
||||||
growthHabit: item.growthHabit || '',
|
growthHabit: item.growthHabit || '',
|
||||||
reproductionMethod: item.reproductionMethod || '',
|
reproductionMethod: item.reproductionMethod || '',
|
||||||
reproductionList: reproductionList,
|
reproductionList,
|
||||||
|
|
||||||
// Light
|
|
||||||
lightIntensity: item.lightIntensity || '',
|
lightIntensity: item.lightIntensity || '',
|
||||||
lightType: item.lightType || '',
|
lightType: item.lightType || '',
|
||||||
|
|
||||||
// Temperature
|
|
||||||
optimalTempPeriod: item.optimalTempPeriod || '',
|
optimalTempPeriod: item.optimalTempPeriod || '',
|
||||||
|
|
||||||
// Morphology
|
|
||||||
stem: item.stem || '',
|
stem: item.stem || '',
|
||||||
foliageType: item.foliageType || '',
|
foliageType: item.foliageType || '',
|
||||||
foliageColor: item.foliageColor || '',
|
foliageColor: item.foliageColor || '',
|
||||||
foliageShape: item.foliageShape || '',
|
foliageShape: item.foliageShape || '',
|
||||||
height: item.height || 0,
|
height: item.height || 0,
|
||||||
|
|
||||||
// Flowering
|
|
||||||
floweringPeriod: item.floweringPeriod || '',
|
floweringPeriod: item.floweringPeriod || '',
|
||||||
floweringColor: item.floweringColor || '',
|
floweringColor: item.floweringColor || '',
|
||||||
floweringShape: item.floweringShape || '',
|
floweringShape: item.floweringShape || '',
|
||||||
flowerDiameter: item.flowerDiameter || 0,
|
flowerDiameter: item.flowerDiameter || 0,
|
||||||
|
|
||||||
// Fruit
|
|
||||||
fruit: item.fruit || '',
|
fruit: item.fruit || '',
|
||||||
|
|
||||||
// Pests
|
|
||||||
pestsDiseases: item.pestsDiseases || '',
|
pestsDiseases: item.pestsDiseases || '',
|
||||||
commonPests: commonPests,
|
commonPests,
|
||||||
|
|
||||||
// Classes
|
|
||||||
classes: (item.classes || []).map(c => c.name),
|
classes: (item.classes || []).map(c => c.name),
|
||||||
|
|
||||||
// Images
|
|
||||||
imgList: item.imgList || []
|
imgList: item.imgList || []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setData({
|
this.setData({
|
||||||
plant: plant,
|
plant,
|
||||||
swiperList: swiperList
|
swiperList
|
||||||
});
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Load plant detail failed', err);
|
|
||||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,45 @@
|
|||||||
<!--pages/wiki/detail/index.wxml-->
|
<!--pages/wiki/detail/index.wxml-->
|
||||||
<view class="wiki-detail" wx:if="{{plant}}">
|
<view class="wiki-detail" wx:if="{{plant}}">
|
||||||
<!-- Header Area -->
|
<!-- Image Carousel -->
|
||||||
<view class="wd-header">
|
<view class="wd-header">
|
||||||
<view class="wd-gallery-container">
|
|
||||||
<t-swiper
|
<t-swiper
|
||||||
t-class="custom-swiper"
|
t-class="custom-swiper"
|
||||||
current="{{activeImageIndex}}"
|
current="{{activeImageIndex}}"
|
||||||
bind:change="onSwiperChange"
|
bind:change="onSwiperChange"
|
||||||
height="500rpx"
|
height="480rpx"
|
||||||
list="{{swiperList}}"
|
list="{{swiperList}}"
|
||||||
navigation="{{ { type: '' } }}"
|
navigation="{{ { type: '' } }}"
|
||||||
/>
|
/>
|
||||||
<view class="wd-gradient-overlay"></view>
|
<view class="wd-counter" wx:if="{{swiperList.length > 0}}">
|
||||||
|
<text>{{activeImageIndex + 1}} / {{swiperList.length}}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Custom Indicators -->
|
<!-- Plant Name Card -->
|
||||||
<view class="wd-indicators" wx:if="{{swiperList.length > 1}}">
|
<view class="wd-name-card">
|
||||||
<view
|
<view class="wd-name-row">
|
||||||
wx:for="{{swiperList}}"
|
|
||||||
wx:key="index"
|
|
||||||
class="wd-dot {{index === activeImageIndex ? 'active' : ''}}"
|
|
||||||
></view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- Header Overlay Info -->
|
|
||||||
<view class="wd-overlay">
|
|
||||||
<view class="wd-title">
|
|
||||||
<text class="wd-name">{{plant.name}}</text>
|
<text class="wd-name">{{plant.name}}</text>
|
||||||
<text class="wd-scientific">{{plant.latinName}}</text>
|
<text class="wd-scientific" wx:if="{{plant.latinName}}">{{plant.latinName}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="wd-badges">
|
<view class="wd-badges" wx:if="{{plant.genus || plant.classes.length > 0}}">
|
||||||
<text class="wd-badge" wx:if="{{plant.genus}}">{{plant.genus}}</text>
|
<text class="wd-badge" wx:if="{{plant.genus}}">{{plant.genus}}</text>
|
||||||
<text class="wd-badge" wx:for="{{plant.classes}}" wx:key="*this">{{item}}</text>
|
<text class="wd-badge" wx:for="{{plant.classes}}" wx:key="*this">{{item}}</text>
|
||||||
<text class="wd-badge">难度: {{plant.difficultyLabel}}</text>
|
<text class="wd-badge difficulty">难度: {{plant.difficultyLabel}}</text>
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Content Area -->
|
<!-- Content Scroll -->
|
||||||
<view class="wd-content-wrapper">
|
|
||||||
<scroll-view class="wd-content" scroll-y enhanced show-scrollbar="{{false}}">
|
<scroll-view class="wd-content" scroll-y enhanced show-scrollbar="{{false}}">
|
||||||
|
|
||||||
<!-- Growth Habit Section -->
|
<!-- Growth Habit -->
|
||||||
<section class="wd-section" wx:if="{{plant.growthHabit}}">
|
<view class="wd-section" wx:if="{{plant.growthHabit}}">
|
||||||
<view class="wd-card">
|
<view class="wd-card">
|
||||||
<text class="wd-text">{{plant.growthHabit}}</text>
|
<text class="wd-text">{{plant.growthHabit}}</text>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Basic Info Section -->
|
<!-- Basic Info -->
|
||||||
<section class="wd-section">
|
<view class="wd-section">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="info-circle" size="40rpx" color="#558B2F" />
|
<t-icon name="info-circle" size="40rpx" color="#558B2F" />
|
||||||
<text>基础档案</text>
|
<text>基础档案</text>
|
||||||
@@ -74,40 +64,35 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Care Guide Section -->
|
<!-- Care Guide -->
|
||||||
<section class="wd-section">
|
<view class="wd-section">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="sunny" size="40rpx" color="#558B2F" />
|
<t-icon name="sunny" size="40rpx" color="#558B2F" />
|
||||||
<text>养护指南</text>
|
<text>养护指南</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="wd-card">
|
<view class="wd-card">
|
||||||
<!-- Light -->
|
|
||||||
<view class="requirement-item" wx:if="{{plant.lightIntensity}}">
|
<view class="requirement-item" wx:if="{{plant.lightIntensity}}">
|
||||||
<view class="req-icon">
|
<view class="req-icon light">
|
||||||
<t-icon name="sunny" size="40rpx" color="#558B2F" />
|
<t-icon name="sunny" size="40rpx" color="#F59E0B" />
|
||||||
</view>
|
</view>
|
||||||
<view class="req-content">
|
<view class="req-content">
|
||||||
<text class="req-title">光照</text>
|
<text class="req-title">光照</text>
|
||||||
<text class="req-desc">{{plant.lightIntensity}}</text>
|
<text class="req-desc">{{plant.lightIntensity}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Temperature -->
|
|
||||||
<view class="requirement-item" wx:if="{{plant.optimalTempPeriod}}">
|
<view class="requirement-item" wx:if="{{plant.optimalTempPeriod}}">
|
||||||
<view class="req-icon">
|
<view class="req-icon temp">
|
||||||
<t-icon name="pin" size="40rpx" color="#558B2F" />
|
<t-icon name="pin" size="40rpx" color="#EF4444" />
|
||||||
</view>
|
</view>
|
||||||
<view class="req-content">
|
<view class="req-content">
|
||||||
<text class="req-title">适宜温度</text>
|
<text class="req-title">适宜温度</text>
|
||||||
<text class="req-desc">{{plant.optimalTempPeriod}}</text>
|
<text class="req-desc">{{plant.optimalTempPeriod}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Reproduction -->
|
|
||||||
<view class="requirement-item" wx:if="{{plant.reproductionMethod}}">
|
<view class="requirement-item" wx:if="{{plant.reproductionMethod}}">
|
||||||
<view class="req-icon">
|
<view class="req-icon repro">
|
||||||
<t-icon name="fork-node" size="40rpx" color="#558B2F" />
|
<t-icon name="fork-node" size="40rpx" color="#558B2F" />
|
||||||
</view>
|
</view>
|
||||||
<view class="req-content">
|
<view class="req-content">
|
||||||
@@ -116,10 +101,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Morphology Section -->
|
<!-- Morphology -->
|
||||||
<section class="wd-section" wx:if="{{plant.stem || plant.foliageShape || plant.foliageColor}}">
|
<view class="wd-section" wx:if="{{plant.stem || plant.foliageShape || plant.foliageColor}}">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="tree-round-dot" size="40rpx" color="#558B2F" />
|
<t-icon name="tree-round-dot" size="40rpx" color="#558B2F" />
|
||||||
<text>形态特征</text>
|
<text>形态特征</text>
|
||||||
@@ -144,10 +129,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Flowering Section -->
|
<!-- Flowering -->
|
||||||
<section class="wd-section" wx:if="{{plant.floweringPeriod || plant.floweringColor}}">
|
<view class="wd-section" wx:if="{{plant.floweringPeriod || plant.floweringColor}}">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="flower" size="40rpx" color="#558B2F" />
|
<t-icon name="flower" size="40rpx" color="#558B2F" />
|
||||||
<text>开花信息</text>
|
<text>开花信息</text>
|
||||||
@@ -172,10 +157,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Fruit Section -->
|
<!-- Fruit -->
|
||||||
<section class="wd-section" wx:if="{{plant.fruit}}">
|
<view class="wd-section" wx:if="{{plant.fruit}}">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="apple" size="40rpx" color="#558B2F" />
|
<t-icon name="apple" size="40rpx" color="#558B2F" />
|
||||||
<text>果实</text>
|
<text>果实</text>
|
||||||
@@ -183,12 +168,12 @@
|
|||||||
<view class="wd-card">
|
<view class="wd-card">
|
||||||
<text class="wd-text">{{plant.fruit}}</text>
|
<text class="wd-text">{{plant.fruit}}</text>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
</view>
|
||||||
|
|
||||||
<!-- Pests & Diseases Section -->
|
<!-- Pests -->
|
||||||
<section class="wd-section" wx:if="{{plant.commonPests.length > 0}}">
|
<view class="wd-section" wx:if="{{plant.commonPests.length > 0}}">
|
||||||
<view class="section-title">
|
<view class="section-title">
|
||||||
<t-icon name="error-circle" size="40rpx" color="#558B2F" />
|
<t-icon name="error-circle" size="40rpx" color="#EF4444" />
|
||||||
<text>常见病虫害</text>
|
<text>常见病虫害</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="wd-card">
|
<view class="wd-card">
|
||||||
@@ -196,15 +181,13 @@
|
|||||||
<text wx:for="{{plant.commonPests}}" wx:key="*this" class="pest-tag">{{item}}</text>
|
<text wx:for="{{plant.commonPests}}" wx:key="*this" class="pest-tag">{{item}}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Bottom Spacer -->
|
|
||||||
<view style="height: 100rpx;"></view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view style="height: 120rpx;"></view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- Loading State -->
|
<!-- Loading -->
|
||||||
<view wx:if="{{!plant}}" class="wiki-detail-loading">
|
<view wx:if="{{!plant}}" class="wiki-detail-loading">
|
||||||
<t-loading theme="circular" size="64rpx" text="加载中..." />
|
<t-loading theme="circular" size="64rpx" text="加载中..." />
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
+109
-187
@@ -3,201 +3,139 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #F9FAFB;
|
background: #F4F6F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Page Layout */
|
|
||||||
page {
|
page {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hide Scrollbar Globally */
|
/* ======== Image Carousel ======== */
|
||||||
::-webkit-scrollbar {
|
|
||||||
display: none !important;
|
|
||||||
width: 0 !important;
|
|
||||||
height: 0 !important;
|
|
||||||
color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
scroll-view ::-webkit-scrollbar {
|
|
||||||
display: none !important;
|
|
||||||
width: 0 !important;
|
|
||||||
height: 0 !important;
|
|
||||||
color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Header Area */
|
|
||||||
.wd-header {
|
.wd-header {
|
||||||
height: 500rpx;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #000;
|
flex-shrink: 0;
|
||||||
|
background: #E8E8E8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content Wrapper handles the flex growth and positioning overlap */
|
|
||||||
.wd-content-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
position: relative;
|
|
||||||
margin-top: -32rpx;
|
|
||||||
z-index: 20;
|
|
||||||
overflow: hidden; /* Ensure scroll-view is contained */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Content Scroll View fills the wrapper */
|
|
||||||
.wd-content {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force override TDesign swiper radius */
|
|
||||||
.custom-swiper {
|
.custom-swiper {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
overflow: hidden;
|
|
||||||
--td-swiper-radius: 0px !important;
|
--td-swiper-radius: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-swiper .t-swiper {
|
.wd-counter {
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
t-swiper {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-gallery-container {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-gradient-overlay {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
bottom: 20rpx;
|
||||||
left: 0;
|
right: 24rpx;
|
||||||
right: 0;
|
background: rgba(0, 0, 0, 0.45);
|
||||||
height: 240rpx;
|
backdrop-filter: blur(6px);
|
||||||
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), transparent);
|
-webkit-backdrop-filter: blur(6px);
|
||||||
pointer-events: none;
|
color: white;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 6rpx 18rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
z-index: 20;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Name Card ======== */
|
||||||
|
.wd-name-card {
|
||||||
|
background: white;
|
||||||
|
padding: 32rpx 36rpx 28rpx;
|
||||||
|
margin: 0 0 24rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-indicators {
|
.wd-name-row {
|
||||||
position: absolute;
|
margin-bottom: 16rpx;
|
||||||
bottom: 24rpx;
|
|
||||||
right: 24rpx;
|
|
||||||
display: flex;
|
|
||||||
gap: 12rpx;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-dot {
|
|
||||||
width: 12rpx;
|
|
||||||
height: 12rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(255, 255, 255, 0.4);
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-dot.active {
|
|
||||||
background: white;
|
|
||||||
width: 24rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 32rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-start;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-name {
|
.wd-name {
|
||||||
font-size: 56rpx;
|
display: block;
|
||||||
color: #FFFFFF;
|
font-size: 48rpx;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
margin-bottom: 8rpx;
|
color: #1F2937;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-scientific {
|
.wd-scientific {
|
||||||
font-size: 32rpx;
|
display: block;
|
||||||
color: rgba(255, 255, 255, 0.8);
|
font-size: 28rpx;
|
||||||
|
color: #9CA3AF;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-family: serif;
|
font-family: Georgia, 'Times New Roman', serif;
|
||||||
margin-bottom: 24rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-badges {
|
.wd-badges {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16rpx;
|
gap: 12rpx;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-badge {
|
.wd-badge {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: #F0F7EB;
|
||||||
backdrop-filter: blur(4px);
|
color: #558B2F;
|
||||||
padding: 8rpx 20rpx;
|
padding: 8rpx 20rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 20rpx;
|
||||||
font-size: 24rpx;
|
font-size: 22rpx;
|
||||||
color: #FFFFFF;
|
font-weight: 600;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wd-badge.difficulty {
|
||||||
|
background: #FFF8E1;
|
||||||
|
color: #F57F17;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Scrollable Content ======== */
|
||||||
|
.wd-content {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Sections ======== */
|
||||||
.wd-section {
|
.wd-section {
|
||||||
margin-bottom: 32rpx;
|
margin-bottom: 28rpx;
|
||||||
animation: fadeIn 0.5s ease-out;
|
padding: 0 28rpx;
|
||||||
padding: 0 32rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First section specific override: Adjust padding and background to create the seamless rounded look */
|
|
||||||
.wd-section:first-child {
|
|
||||||
padding: 0;
|
|
||||||
margin-bottom: 48rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wd-section:first-child .wd-card {
|
|
||||||
border-top-left-radius: 40rpx;
|
|
||||||
border-top-right-radius: 40rpx;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
padding: 48rpx 32rpx;
|
|
||||||
box-shadow: none; /* Seamless blend */
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from { opacity: 0; transform: translateY(20rpx); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
gap: 14rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 20rpx;
|
||||||
padding-left: 8rpx;
|
padding-left: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title text {
|
.section-title text {
|
||||||
font-size: 34rpx;
|
font-size: 32rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #111827;
|
color: #1F2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======== Cards ======== */
|
||||||
|
.wd-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
padding: 28rpx 32rpx;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-text {
|
.wd-text {
|
||||||
font-size: 30rpx;
|
font-size: 28rpx;
|
||||||
line-height: 1.6;
|
line-height: 1.7;
|
||||||
color: #4B5563;
|
color: #4B5563;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======== Info Grid ======== */
|
||||||
.wd-grid {
|
.wd-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 32rpx;
|
gap: 28rpx 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-stat-item {
|
.wd-stat-item {
|
||||||
@@ -207,62 +145,72 @@ t-swiper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wd-label {
|
.wd-label {
|
||||||
font-size: 24rpx;
|
font-size: 22rpx;
|
||||||
color: #9CA3AF;
|
color: #9CA3AF;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 1rpx;
|
letter-spacing: 1rpx;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-value {
|
.wd-value {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1F2937;
|
color: #1F2937;
|
||||||
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wd-card {
|
/* ======== Requirement Items ======== */
|
||||||
background: white;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
padding: 24rpx;
|
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.02);
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Requirement Items (Compact) */
|
|
||||||
.requirement-item {
|
.requirement-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 24rpx;
|
gap: 24rpx;
|
||||||
margin-bottom: 24rpx;
|
padding: 24rpx 0;
|
||||||
padding-bottom: 24rpx;
|
|
||||||
border-bottom: 2rpx solid #F3F4F6;
|
border-bottom: 2rpx solid #F3F4F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.requirement-item:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.requirement-item:last-child {
|
.requirement-item:last-child {
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.req-icon {
|
.req-icon {
|
||||||
width: 80rpx;
|
width: 84rpx;
|
||||||
height: 80rpx;
|
height: 84rpx;
|
||||||
background: #F1F8E9;
|
border-radius: 22rpx;
|
||||||
border-radius: 20rpx;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.req-icon.light {
|
||||||
|
background: #FFFBEB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.req-icon.temp {
|
||||||
|
background: #FEF2F2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.req-icon.repro {
|
||||||
|
background: #F0FDF4;
|
||||||
|
}
|
||||||
|
|
||||||
.req-content {
|
.req-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.req-title {
|
.req-title {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 30rpx;
|
font-size: 28rpx;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1F2937;
|
color: #1F2937;
|
||||||
margin-bottom: 8rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.req-desc {
|
.req-desc {
|
||||||
@@ -271,7 +219,7 @@ t-swiper {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FAQ / Pests */
|
/* ======== Pest Tags ======== */
|
||||||
.pest-tags {
|
.pest-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -283,41 +231,15 @@ t-swiper {
|
|||||||
color: #DC2626;
|
color: #DC2626;
|
||||||
padding: 12rpx 24rpx;
|
padding: 12rpx 24rpx;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
font-size: 26rpx;
|
font-size: 24rpx;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
}
|
|
||||||
|
|
||||||
.care-tips-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-item {
|
|
||||||
display: flex;
|
|
||||||
gap: 16rpx;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-dot {
|
|
||||||
width: 12rpx;
|
|
||||||
height: 12rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #558B2F;
|
|
||||||
margin-top: 14rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #4B5563;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======== Loading ======== */
|
||||||
.wiki-detail-loading {
|
.wiki-detail-loading {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: #F9FAFB;
|
background: #F4F6F0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
.identify-page {
|
.identify-page {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: #F5F7F5;
|
background: #F4F6F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== Shared State Container ========== */
|
/* ========== Shared State Container ========== */
|
||||||
|
|||||||
+4
-1
@@ -150,7 +150,10 @@ Page({
|
|||||||
goToDetail(e) {
|
goToDetail(e) {
|
||||||
const item = e.currentTarget.dataset.item;
|
const item = e.currentTarget.dataset.item;
|
||||||
wx.navigateTo({
|
wx.navigateTo({
|
||||||
url: `/pages/wiki/detail/index?id=${item.id}`
|
url: `/pages/wiki/detail/index?id=${item.id}`,
|
||||||
|
success: (res) => {
|
||||||
|
res.eventChannel.emit('acceptDataFromOpenerPage', { data: item.raw });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"t-search": "tdesign-miniprogram/search/search",
|
"t-search": "tdesign-miniprogram/search/search",
|
||||||
"t-tag": "tdesign-miniprogram/tag/tag",
|
"t-tag": "tdesign-miniprogram/tag/tag",
|
||||||
"t-image": "tdesign-miniprogram/image/image",
|
"t-image": "tdesign-miniprogram/image/image",
|
||||||
"t-fab": "tdesign-miniprogram/fab/fab",
|
|
||||||
"t-popup": "tdesign-miniprogram/popup/popup",
|
"t-popup": "tdesign-miniprogram/popup/popup",
|
||||||
"t-cell": "tdesign-miniprogram/cell/cell",
|
"t-cell": "tdesign-miniprogram/cell/cell",
|
||||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||||
|
|||||||
@@ -113,7 +113,10 @@
|
|||||||
<view style="height: 160rpx;"></view>
|
<view style="height: 160rpx;"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<t-fab icon="scan" text="植物识别" bind:click="openIdentifyModal" aria-label="植物识别"></t-fab>
|
<view class="floating-add-btn" bindtap="openIdentifyModal">
|
||||||
|
<t-icon name="scan" size="40rpx" color="#FFF" />
|
||||||
|
<text>植物识别</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- Identify Popup -->
|
<!-- Identify Popup -->
|
||||||
<t-popup visible="{{showIdentifyModal}}" bind:visible-change="onPopupVisibleChange" placement="bottom">
|
<t-popup visible="{{showIdentifyModal}}" bind:visible-change="onPopupVisibleChange" placement="bottom">
|
||||||
|
|||||||
+26
-2
@@ -3,7 +3,7 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #F9FAFB;
|
background-color: #F4F6F0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,30 @@
|
|||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Floating Action Button */
|
||||||
|
.floating-add-btn {
|
||||||
|
position: fixed;
|
||||||
|
right: 40rpx;
|
||||||
|
bottom: 60rpx;
|
||||||
|
background: #558B2F;
|
||||||
|
color: white;
|
||||||
|
padding: 24rpx 40rpx;
|
||||||
|
border-radius: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
|
||||||
|
z-index: 1000;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-add-btn:active {
|
||||||
|
transform: scale(0.92);
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(85, 139, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
/* Popup Styles */
|
/* Popup Styles */
|
||||||
.popup-content {
|
.popup-content {
|
||||||
background: white;
|
background: white;
|
||||||
@@ -144,7 +168,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20rpx;
|
gap: 20rpx;
|
||||||
background: #F9FAFB;
|
background: #F4F6F0;
|
||||||
border-radius: 32rpx;
|
border-radius: 32rpx;
|
||||||
padding: 40rpx 24rpx;
|
padding: 40rpx 24rpx;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|||||||
+6
-6
@@ -72,12 +72,12 @@ class WxRequest {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle non-200 HTTP errors
|
// Handle non-200 HTTP errors
|
||||||
this.handleError({ errMsg: `HTTP Error: ${statusCode}`, ...res });
|
this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
|
||||||
reject(res);
|
reject(res);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
this.handleError({ errMsg: 'Network Error', ...err });
|
this.handleError({ ...err, errMsg: 'Network Error' });
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -154,12 +154,12 @@ class WxRequest {
|
|||||||
reject(finalData);
|
reject(finalData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.handleError({ errMsg: `HTTP Error: ${statusCode}`, ...res });
|
this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
|
||||||
reject(res);
|
reject(res);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
this.handleError({ errMsg: 'Upload Network Error', ...err });
|
this.handleError({ ...err, errMsg: 'Upload Network Error' });
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -215,12 +215,12 @@ class WxRequest {
|
|||||||
reject(finalData);
|
reject(finalData);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.handleError({ errMsg: `HTTP Error: ${statusCode}`, ...res });
|
this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
|
||||||
reject(res);
|
reject(res);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
this.handleError({ errMsg: 'Upload Network Error', ...err });
|
this.handleError({ ...err, errMsg: 'Upload Network Error' });
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user