init: initial commit
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// pages/profile/index.js
|
||||
import { MOCK_FAVORITES, MOCK_BADGES, MOCK_POSTS } from '../../utils/mockData';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
view: 'profile', // profile, favorites, posts, badges
|
||||
favTab: 'all', // all, plant, article
|
||||
postsTab: 'published', // published, drafts
|
||||
|
||||
favorites: [],
|
||||
filteredFavorites: [],
|
||||
myPublishedPosts: [],
|
||||
myDrafts: [],
|
||||
badges: []
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.setData({
|
||||
favorites: MOCK_FAVORITES,
|
||||
badges: MOCK_BADGES
|
||||
});
|
||||
this.filterFavorites();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' &&
|
||||
this.getTabBar()) {
|
||||
this.getTabBar().setData({
|
||||
selected: 4 // Index 4 is Profile
|
||||
})
|
||||
}
|
||||
|
||||
// Refresh posts data
|
||||
this.loadMyPosts();
|
||||
this.loadDrafts();
|
||||
},
|
||||
|
||||
loadMyPosts() {
|
||||
// Get published posts by current user
|
||||
const myPosts = MOCK_POSTS.filter(p => p.user === '我的花园');
|
||||
this.setData({ myPublishedPosts: myPosts });
|
||||
},
|
||||
|
||||
loadDrafts() {
|
||||
// Load drafts from storage
|
||||
try {
|
||||
const draft = wx.getStorageSync('post_draft');
|
||||
if (draft && (draft.content || (draft.images && draft.images.length > 0))) {
|
||||
// Convert single draft to array for consistency
|
||||
this.setData({
|
||||
myDrafts: [{
|
||||
id: 'draft_1',
|
||||
content: draft.content || '',
|
||||
images: draft.images || [],
|
||||
selectedTopics: draft.selectedTopics || []
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
this.setData({ myDrafts: [] });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setData({ myDrafts: [] });
|
||||
}
|
||||
},
|
||||
|
||||
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) {
|
||||
const tab = 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) {
|
||||
const postId = e.currentTarget.dataset.id;
|
||||
wx.showModal({
|
||||
title: '删除动态',
|
||||
content: '确定要删除这条动态吗?',
|
||||
confirmColor: '#EF5350',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// Remove from MOCK_POSTS
|
||||
const idx = MOCK_POSTS.findIndex(p => p.id === postId);
|
||||
if (idx > -1) {
|
||||
MOCK_POSTS.splice(idx, 1);
|
||||
}
|
||||
this.loadMyPosts();
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Edit a draft
|
||||
editDraft(e) {
|
||||
// Navigate to create page, which will load the draft
|
||||
wx.navigateTo({
|
||||
url: '/pages/community/create/index'
|
||||
});
|
||||
},
|
||||
|
||||
// Delete a draft
|
||||
deleteDraft(e) {
|
||||
wx.showModal({
|
||||
title: '删除草稿',
|
||||
content: '确定要删除这份草稿吗?',
|
||||
confirmColor: '#EF5350',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
wx.removeStorageSync('post_draft');
|
||||
} catch (e) { }
|
||||
this.setData({ myDrafts: [] });
|
||||
wx.showToast({ title: '已删除', icon: 'success' });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"usingComponents": {
|
||||
"t-grid": "tdesign-miniprogram/grid/grid",
|
||||
"t-grid-item": "tdesign-miniprogram/grid-item/grid-item",
|
||||
"t-cell": "tdesign-miniprogram/cell/cell",
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||
"t-avatar": "tdesign-miniprogram/avatar/avatar",
|
||||
"t-image": "tdesign-miniprogram/image/image",
|
||||
"t-tag": "tdesign-miniprogram/tag/tag",
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-badge": "tdesign-miniprogram/badge/badge",
|
||||
"t-progress": "tdesign-miniprogram/progress/progress"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<wxs src="../../utils/tools.wxs" module="tools" />
|
||||
<view class="profile-page">
|
||||
<!-- Sub-views handled by conditional rendering to match prototype single-page feel -->
|
||||
|
||||
<!-- FAVORITES VIEW -->
|
||||
<view wx:if="{{view === 'favorites'}}" class="favorites-page info-view-anim">
|
||||
<view class="back-nav sticky-nav">
|
||||
<t-button variant="text" icon="arrow-left" bind:tap="setView" data-view="profile">我的收藏</t-button>
|
||||
</view>
|
||||
|
||||
<t-tabs value="{{favTab}}" bind:change="onFavTabChange" theme="card">
|
||||
<t-tab-panel label="全部" value="all" />
|
||||
<t-tab-panel label="植物" value="plant" />
|
||||
<t-tab-panel label="文章" value="article" />
|
||||
</t-tabs>
|
||||
|
||||
<view class="tab-content">
|
||||
<view class="fav-grid">
|
||||
<block wx:if="{{filteredFavorites.length > 0}}">
|
||||
<view wx:for="{{filteredFavorites}}" wx:key="id" class="fav-card">
|
||||
<t-image src="{{tools.resolvePath(item.image)}}" class="fav-img" mode="aspectFill" width="100%" height="240rpx" />
|
||||
<view class="fav-info">
|
||||
<text class="fav-name">{{item.name}}</text>
|
||||
<view class="fav-meta-row">
|
||||
<t-icon name="{{item.type === 'plant' ? 'heart' : 'book'}}" size="32rpx" color="#90A4AE" />
|
||||
<text class="fav-type">{{item.meta}}</text>
|
||||
</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>
|
||||
|
||||
<!-- POSTS VIEW -->
|
||||
<view wx:elif="{{view === 'posts'}}" class="posts-page-detail info-view-anim">
|
||||
<view class="back-nav sticky-nav">
|
||||
<t-button variant="text" icon="arrow-left" bind:tap="setView" data-view="profile">我的发布</t-button>
|
||||
</view>
|
||||
|
||||
<!-- Tabs for Published / Drafts -->
|
||||
<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 class="my-post-time">{{item.time}}</view>
|
||||
<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="{{tools.resolvePath(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"><t-icon name="heart" size="32rpx" /> <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 delete-btn" bindtap="deletePost" data-id="{{item.id}}"><t-icon name="delete" size="32rpx" color="#EF5350" /></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>
|
||||
|
||||
<!-- Draft Posts -->
|
||||
<view wx:if="{{postsTab === 'drafts'}}" class="my-posts-list">
|
||||
<block wx:if="{{myDrafts.length > 0}}">
|
||||
<view wx:for="{{myDrafts}}" wx:key="id" class="my-post-card draft-card">
|
||||
<view class="draft-badge">草稿</view>
|
||||
<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>
|
||||
|
||||
<!-- BADGES VIEW -->
|
||||
<view wx:elif="{{view === 'badges'}}" class="badges-page info-view-anim">
|
||||
<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-header">
|
||||
<view class="level-info-large">
|
||||
<text class="level-label">当前等级</text>
|
||||
<text class="level-value">Lv.4 资深植人</text>
|
||||
</view>
|
||||
<t-icon name="trophy" size="80rpx" color="#FFD700" />
|
||||
</view>
|
||||
|
||||
<view class="level-progress-section">
|
||||
<view class="progress-text">
|
||||
<text>经验值</text>
|
||||
<text>350 / 500</text>
|
||||
</view>
|
||||
<t-progress percentage="70" theme="plump" color="#FFD700" track-color="rgba(255,255,255,0.3)" />
|
||||
<text class="next-level-tip">距离 Lv.5 园艺大师 还需 150 经验</text>
|
||||
</view>
|
||||
</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'}}" />
|
||||
<!-- TDesign grid item image might need full path or use slot for svg if needed, using png for now -->
|
||||
</t-grid>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- MAIN PROFILE VIEW -->
|
||||
<view wx:else class="main-profile-view">
|
||||
<view class="profile-header">
|
||||
<view class="user-main">
|
||||
<view class="user-avatar">
|
||||
<t-avatar image="https://api.dicebear.com/7.x/avataaars/svg?seed=Lucky" size="large" />
|
||||
</view>
|
||||
<view class="user-text">
|
||||
<text class="user-name">布偶猫园长</text>
|
||||
<t-tag theme="warning" variant="light" size="small">Lv.4 资深植人</t-tag>
|
||||
</view>
|
||||
</view>
|
||||
<t-icon name="setting" size="48rpx" />
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="profile-content">
|
||||
<view class="stats-grid">
|
||||
<view class="stat-col">
|
||||
<text class="stat-num">12</text>
|
||||
<text class="stat-label">植物</text>
|
||||
</view>
|
||||
<view class="stat-col">
|
||||
<text class="stat-num">328</text>
|
||||
<text class="stat-label">养护</text>
|
||||
</view>
|
||||
<view class="stat-col">
|
||||
<text class="stat-num">15</text>
|
||||
<text class="stat-label">关注</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="profile-menu">
|
||||
<t-cell-group title="常用功能" theme="card">
|
||||
<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>
|
||||
|
||||
<!-- Spacer -->
|
||||
<view style="height: 40rpx;"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,149 @@
|
||||
/** pages/profile/index.wxss **/
|
||||
.profile-page {
|
||||
background-color: #F4F6F0;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.info-view-anim {
|
||||
animation: slideInRight 0.3s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.sticky-nav {
|
||||
position: sticky; top: 0; z-index: 100; background: white;
|
||||
border-bottom: 2rpx solid #f0f0f0;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.tab-content { padding: 32rpx; }
|
||||
|
||||
/* Favorites Grid */
|
||||
.fav-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 24rpx;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.fav-card {
|
||||
background: white;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
.fav-img { width: 100%; display: block; 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 */
|
||||
.my-posts-list { padding: 40rpx; }
|
||||
.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 {
|
||||
border-left: 4rpx solid #FFC107;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Action Buttons */
|
||||
.edit-btn, .delete-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-btn:active, .delete-btn:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Badges View */
|
||||
.badges-content { padding: 40rpx; background: white; height: 100%; }
|
||||
|
||||
.level-card-large {
|
||||
background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);
|
||||
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;
|
||||
margin: 24rpx 40rpx;
|
||||
background: white;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
|
||||
.stat-col { display: flex; flex-direction: column; align-items: center; gap: 4rpx; }
|
||||
.stat-num { font-size: 36rpx; font-weight: 800; color: var(--text-main); }
|
||||
.stat-label { font-size: 22rpx; color: #90A4AE; }
|
||||
|
||||
.profile-menu {
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user