init: initial commit
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
// pages/community/create/index.js
|
||||
import { MOCK_POSTS } from '../../../utils/mockData';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
content: '',
|
||||
images: [],
|
||||
canPublish: false,
|
||||
autoFocus: true,
|
||||
location: '',
|
||||
selectedTopics: [],
|
||||
suggestedTopics: ['植物养护', '多肉日记', '绿植分享', '花卉美照', '阳台花园', '新手入门'],
|
||||
hasDraft: false,
|
||||
showImageSheet: false,
|
||||
imageSheetItems: [
|
||||
{ label: '拍照', value: 'camera' },
|
||||
{ label: '从相册选择', value: 'album' }
|
||||
]
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// Check for saved draft
|
||||
this.loadDraft();
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
// Save draft if there's content
|
||||
this.saveDraft();
|
||||
},
|
||||
|
||||
loadDraft() {
|
||||
try {
|
||||
const draft = wx.getStorageSync('post_draft');
|
||||
if (draft && (draft.content || draft.images.length > 0)) {
|
||||
this.setData({
|
||||
content: draft.content || '',
|
||||
images: draft.images || [],
|
||||
selectedTopics: draft.selectedTopics || [],
|
||||
canPublish: draft.content && draft.content.trim().length > 0,
|
||||
hasDraft: true
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('No draft found');
|
||||
}
|
||||
},
|
||||
|
||||
saveDraft() {
|
||||
if (this.data.content || this.data.images.length > 0) {
|
||||
try {
|
||||
wx.setStorageSync('post_draft', {
|
||||
content: this.data.content,
|
||||
images: this.data.images,
|
||||
selectedTopics: this.data.selectedTopics
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Failed to save draft');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearDraft() {
|
||||
try {
|
||||
wx.removeStorageSync('post_draft');
|
||||
} catch (e) {
|
||||
console.log('Failed to clear draft');
|
||||
}
|
||||
},
|
||||
|
||||
onContentInput(e) {
|
||||
const content = e.detail.value;
|
||||
this.setData({
|
||||
content,
|
||||
canPublish: content.trim().length > 0,
|
||||
hasDraft: false
|
||||
});
|
||||
},
|
||||
|
||||
showImageSourceSheet() {
|
||||
this.setData({ showImageSheet: true });
|
||||
},
|
||||
|
||||
hideImageSheet() {
|
||||
this.setData({ showImageSheet: false });
|
||||
},
|
||||
|
||||
onImageSheetSelect(e) {
|
||||
const { value } = e.detail.selected;
|
||||
this.setData({ showImageSheet: false });
|
||||
|
||||
if (value === 'camera') {
|
||||
this.takePhoto();
|
||||
} else {
|
||||
this.chooseImage();
|
||||
}
|
||||
},
|
||||
|
||||
chooseImage() {
|
||||
const remaining = 9 - this.data.images.length;
|
||||
if (remaining <= 0) {
|
||||
wx.showToast({ title: '最多9张图片', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.chooseMedia({
|
||||
count: remaining,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album'],
|
||||
success: (res) => {
|
||||
const newImages = res.tempFiles.map(f => f.tempFilePath);
|
||||
this.setData({
|
||||
images: [...this.data.images, ...newImages],
|
||||
hasDraft: false
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
takePhoto() {
|
||||
if (this.data.images.length >= 9) {
|
||||
wx.showToast({ title: '最多9张图片', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.chooseMedia({
|
||||
count: 1,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['camera'],
|
||||
camera: 'back',
|
||||
success: (res) => {
|
||||
const newImage = res.tempFiles[0].tempFilePath;
|
||||
this.setData({
|
||||
images: [...this.data.images, newImage],
|
||||
hasDraft: false
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
removeImage(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
const images = [...this.data.images];
|
||||
images.splice(index, 1);
|
||||
this.setData({ images });
|
||||
},
|
||||
|
||||
showImageMenu(e) {
|
||||
const index = e.currentTarget.dataset.index;
|
||||
wx.showActionSheet({
|
||||
itemList: ['设为封面', '删除'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
// Move to first position
|
||||
const images = [...this.data.images];
|
||||
const [img] = images.splice(index, 1);
|
||||
images.unshift(img);
|
||||
this.setData({ images });
|
||||
wx.showToast({ title: '已设为封面', icon: 'success' });
|
||||
} else if (res.tapIndex === 1) {
|
||||
this.removeImage({ currentTarget: { dataset: { index } } });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
chooseLocation() {
|
||||
wx.chooseLocation({
|
||||
success: (res) => {
|
||||
this.setData({ location: res.name || res.address });
|
||||
},
|
||||
fail: () => {
|
||||
// User cancelled or no permission
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
toggleTopic(e) {
|
||||
const topic = e.currentTarget.dataset.topic;
|
||||
const hashtag = `#${topic} `;
|
||||
let { content, selectedTopics } = this.data;
|
||||
|
||||
if (selectedTopics.includes(topic)) {
|
||||
// Remove topic and hashtag from content
|
||||
selectedTopics = selectedTopics.filter(t => t !== topic);
|
||||
content = content.replace(hashtag, '');
|
||||
} else {
|
||||
if (selectedTopics.length >= 3) {
|
||||
wx.showToast({ title: '最多选择3个话题', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
// Add topic and insert hashtag into content
|
||||
selectedTopics.push(topic);
|
||||
content = content + hashtag;
|
||||
}
|
||||
|
||||
this.setData({
|
||||
selectedTopics,
|
||||
content,
|
||||
canPublish: content.trim().length > 0
|
||||
});
|
||||
},
|
||||
|
||||
insertEmoji() {
|
||||
// Simple emoji picker simulation
|
||||
const emojis = ['🌱', '🌿', '🍀', '🌵', '🌻', '🌺', '🌸', '🌼', '🪴', '🌲'];
|
||||
wx.showActionSheet({
|
||||
itemList: emojis,
|
||||
success: (res) => {
|
||||
const emoji = emojis[res.tapIndex];
|
||||
this.setData({
|
||||
content: this.data.content + emoji,
|
||||
canPublish: true
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleCancel() {
|
||||
if (this.data.content || this.data.images.length > 0) {
|
||||
wx.showModal({
|
||||
title: '保存草稿',
|
||||
content: '是否保存当前内容为草稿?',
|
||||
cancelText: '不保存',
|
||||
confirmText: '保存',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.saveDraft();
|
||||
wx.showToast({ title: '已保存草稿', icon: 'success' });
|
||||
setTimeout(() => wx.navigateBack(), 500);
|
||||
} else {
|
||||
this.clearDraft();
|
||||
wx.navigateBack();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
wx.navigateBack();
|
||||
}
|
||||
},
|
||||
|
||||
handlePublish() {
|
||||
if (!this.data.canPublish) {
|
||||
wx.showToast({ title: '请输入内容', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Content already includes topics as hashtags
|
||||
const finalContent = this.data.content.trim();
|
||||
|
||||
// Create new post
|
||||
const newPost = {
|
||||
id: Date.now().toString(),
|
||||
user: '我的花园',
|
||||
content: finalContent,
|
||||
images: this.data.images,
|
||||
time: '刚刚',
|
||||
likes: [],
|
||||
comments: []
|
||||
};
|
||||
|
||||
// Add to global mock data (at the beginning)
|
||||
MOCK_POSTS.unshift(newPost);
|
||||
|
||||
// Clear draft
|
||||
this.clearDraft();
|
||||
|
||||
wx.showToast({ title: '发布成功', icon: 'success' });
|
||||
|
||||
setTimeout(() => {
|
||||
wx.navigateBack();
|
||||
}, 1000);
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"navigationBarTitleText": "发布动态",
|
||||
"navigationStyle": "custom",
|
||||
"usingComponents": {
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-image": "tdesign-miniprogram/image/image",
|
||||
"t-action-sheet": "tdesign-miniprogram/action-sheet/action-sheet"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<view class="create-post-page">
|
||||
<!-- Content Area -->
|
||||
<scroll-view scroll-y class="create-content" enhanced="{{true}}" show-scrollbar="{{false}}">
|
||||
<!-- Action Row: Cancel + Publish -->
|
||||
<view class="action-row">
|
||||
<view class="cancel-btn" bindtap="handleCancel">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
<view class="publish-btn {{canPublish ? 'active' : ''}}" bindtap="handlePublish">
|
||||
<text>发布</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Text Input -->
|
||||
<textarea
|
||||
class="post-textarea"
|
||||
placeholder="分享你的植物养护心得..."
|
||||
placeholder-class="textarea-placeholder"
|
||||
value="{{content}}"
|
||||
bindinput="onContentInput"
|
||||
maxlength="500"
|
||||
auto-height
|
||||
focus="{{autoFocus}}"
|
||||
/>
|
||||
|
||||
<!-- Character Counter -->
|
||||
<view class="char-counter" wx:if="{{content.length > 0}}">
|
||||
<text class="{{content.length >= 450 ? 'warning' : ''}}">{{content.length}}</text>
|
||||
<text class="total">/500</text>
|
||||
</view>
|
||||
|
||||
<!-- Image Preview Grid -->
|
||||
<view wx:if="{{images.length > 0}}" class="image-section">
|
||||
<view class="image-preview-grid">
|
||||
<view wx:for="{{images}}" wx:key="*this" class="preview-item" bindlongpress="showImageMenu" data-index="{{index}}">
|
||||
<t-image src="{{item}}" mode="aspectFill" width="100%" height="100%" />
|
||||
<view class="remove-btn" catchtap="removeImage" data-index="{{index}}">
|
||||
<t-icon name="close" size="24rpx" color="#fff" />
|
||||
</view>
|
||||
<view class="image-index">{{index + 1}}</view>
|
||||
</view>
|
||||
|
||||
<!-- Add More Button -->
|
||||
<view wx:if="{{images.length < 9}}" class="add-image-btn" bindtap="showImageSourceSheet">
|
||||
<t-icon name="add" size="48rpx" color="#999" />
|
||||
<text class="add-count">{{images.length}}/9</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Add Image Area (when no images) -->
|
||||
<view wx:else class="add-first-image" bindtap="showImageSourceSheet">
|
||||
<view class="image-upload-box">
|
||||
<view class="upload-icon">
|
||||
<t-icon name="photo" size="64rpx" color="#558B2F" />
|
||||
</view>
|
||||
<text class="upload-text">添加图片</text>
|
||||
<text class="upload-hint">分享你的植物美照,最多9张</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Location Tag (Optional Feature) -->
|
||||
<view class="location-section" bindtap="chooseLocation">
|
||||
<view class="location-left">
|
||||
<t-icon name="location" size="40rpx" color="#558B2F" />
|
||||
<text class="location-text">{{location || '添加位置'}}</text>
|
||||
</view>
|
||||
<t-icon name="chevron-right" size="36rpx" color="#ccc" />
|
||||
</view>
|
||||
|
||||
<!-- Topic Tags -->
|
||||
<view class="topic-section">
|
||||
<view class="section-title">
|
||||
<t-icon name="hashtag" size="36rpx" color="#558B2F" />
|
||||
<text>添加话题</text>
|
||||
</view>
|
||||
<view class="topic-list">
|
||||
<view class="topic-tag {{selectedTopics.includes(item) ? 'selected' : ''}}"
|
||||
wx:for="{{suggestedTopics}}"
|
||||
wx:key="*this"
|
||||
bindtap="toggleTopic"
|
||||
data-topic="{{item}}">
|
||||
<text>#{{item}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Bottom Spacer -->
|
||||
<view style="height: 60rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- Image Source Action Sheet -->
|
||||
<t-action-sheet
|
||||
visible="{{showImageSheet}}"
|
||||
items="{{imageSheetItems}}"
|
||||
bind:selected="onImageSheetSelect"
|
||||
bind:cancel="hideImageSheet"
|
||||
show-cancel
|
||||
/>
|
||||
</view>
|
||||
@@ -0,0 +1,316 @@
|
||||
/** pages/community/create/index.wxss **/
|
||||
page {
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.create-post-page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
.create-content {
|
||||
flex: 1;
|
||||
padding: 0 32rpx;
|
||||
padding-top: calc(env(safe-area-inset-top) + 100rpx);
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.create-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Action Row */
|
||||
.action-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 0;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* Cancel Button */
|
||||
.cancel-btn {
|
||||
font-size: 30rpx;
|
||||
color: #666;
|
||||
padding: 12rpx 0;
|
||||
}
|
||||
|
||||
.cancel-btn:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Publish Button */
|
||||
.publish-btn {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
background: #f0f0f0;
|
||||
padding: 16rpx 40rpx;
|
||||
border-radius: 32rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.publish-btn.active {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #689F38, #558B2F);
|
||||
box-shadow: 0 8rpx 24rpx rgba(85, 139, 47, 0.3);
|
||||
}
|
||||
|
||||
.publish-btn.active:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Textarea */
|
||||
.post-textarea {
|
||||
width: 100%;
|
||||
min-height: 200rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 1.7;
|
||||
color: #333;
|
||||
padding: 32rpx 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.textarea-placeholder {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* Character Counter */
|
||||
.char-counter {
|
||||
text-align: right;
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.char-counter text {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.char-counter .warning {
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
.char-counter .total {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/* Image Section */
|
||||
.image-section {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
/* Image Preview Grid */
|
||||
.image-preview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.preview-item {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.remove-btn:active {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.image-index {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
left: 8rpx;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.add-image-btn {
|
||||
aspect-ratio: 1;
|
||||
border: 2rpx dashed #ddd;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.add-image-btn:active {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.add-count {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Add First Image */
|
||||
.add-first-image {
|
||||
margin: 32rpx 0;
|
||||
}
|
||||
|
||||
.image-upload-box {
|
||||
padding: 64rpx 48rpx;
|
||||
border: 2rpx dashed #C8E6C9;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: linear-gradient(180deg, #F8FCF8, #fff);
|
||||
}
|
||||
|
||||
.image-upload-box:active {
|
||||
background: #F0F7F0;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: #E8F5E9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 32rpx;
|
||||
color: #558B2F;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
font-size: 26rpx;
|
||||
color: #90A4AE;
|
||||
}
|
||||
|
||||
/* Location Section */
|
||||
.location-section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 28rpx 0;
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
border-bottom: 2rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.location-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.location-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Topic Section */
|
||||
.topic-section {
|
||||
padding: 28rpx 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.topic-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.topic-tag {
|
||||
padding: 12rpx 24rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.topic-tag.selected {
|
||||
background: #E8F5E9;
|
||||
color: #558B2F;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.topic-tag:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Bottom Toolbar */
|
||||
.create-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16rpx 32rpx;
|
||||
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toolbar-btn:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.draft-hint {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// pages/community/index.js
|
||||
import { MOCK_POSTS } from '../../utils/mockData';
|
||||
|
||||
Page({
|
||||
data: {
|
||||
posts: [],
|
||||
displayedPosts: [],
|
||||
activePostId: null, // For showing action popup
|
||||
showCommentBar: false,
|
||||
commentingPostId: null,
|
||||
commentText: ''
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({ posts: MOCK_POSTS });
|
||||
this.updateDisplayedPosts();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({ selected: 2 });
|
||||
}
|
||||
// Refresh posts in case new ones were added
|
||||
this.setData({ posts: MOCK_POSTS });
|
||||
this.updateDisplayedPosts();
|
||||
},
|
||||
|
||||
updateDisplayedPosts() {
|
||||
const { posts } = this.data;
|
||||
// Show all posts with likedByMe flag
|
||||
const displayed = posts.map(post => ({
|
||||
...post,
|
||||
likedByMe: post.likes.includes('我的花园')
|
||||
}));
|
||||
this.setData({ displayedPosts: displayed });
|
||||
},
|
||||
|
||||
// Preview image in full screen
|
||||
previewImage(e) {
|
||||
const { url, urls } = e.currentTarget.dataset;
|
||||
const resolvedUrls = urls.map(img => {
|
||||
if (img.indexOf('http') === 0 || img.indexOf('wxfile') === 0) {
|
||||
return img;
|
||||
}
|
||||
return `/assets/${img}`;
|
||||
});
|
||||
|
||||
wx.previewImage({
|
||||
current: url,
|
||||
urls: resolvedUrls
|
||||
});
|
||||
},
|
||||
|
||||
// Toggle action popup (WeChat style)
|
||||
toggleActionPopup(e) {
|
||||
const postId = e.currentTarget.dataset.id;
|
||||
if (this.data.activePostId === postId) {
|
||||
this.setData({ activePostId: null });
|
||||
} else {
|
||||
this.setData({ activePostId: postId });
|
||||
}
|
||||
},
|
||||
|
||||
hideActionPopup() {
|
||||
if (this.data.activePostId) {
|
||||
this.setData({ activePostId: null });
|
||||
}
|
||||
},
|
||||
|
||||
stopPropagation() {
|
||||
// Just stop event propagation, do nothing
|
||||
},
|
||||
|
||||
// Like post
|
||||
likePost(e) {
|
||||
const postId = e.currentTarget.dataset.id;
|
||||
const posts = this.data.posts.map(post => {
|
||||
if (post.id === postId) {
|
||||
const likes = [...post.likes];
|
||||
const myName = '我的花园';
|
||||
if (likes.includes(myName)) {
|
||||
// Unlike
|
||||
const idx = likes.indexOf(myName);
|
||||
likes.splice(idx, 1);
|
||||
} else {
|
||||
// Like
|
||||
likes.push(myName);
|
||||
}
|
||||
return { ...post, likes };
|
||||
}
|
||||
return post;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
posts,
|
||||
activePostId: null // Hide popup after action
|
||||
}, () => {
|
||||
this.updateDisplayedPosts();
|
||||
});
|
||||
},
|
||||
|
||||
// Show comment input bar
|
||||
showCommentInput(e) {
|
||||
const postId = e.currentTarget.dataset.id;
|
||||
this.setData({
|
||||
showCommentBar: true,
|
||||
commentingPostId: postId,
|
||||
commentText: '',
|
||||
activePostId: null // Hide popup
|
||||
});
|
||||
},
|
||||
|
||||
hideCommentBar() {
|
||||
this.setData({
|
||||
showCommentBar: false,
|
||||
commentingPostId: null,
|
||||
commentText: ''
|
||||
});
|
||||
},
|
||||
|
||||
onCommentInput(e) {
|
||||
this.setData({ commentText: e.detail.value });
|
||||
},
|
||||
|
||||
submitComment() {
|
||||
const { commentText, commentingPostId } = this.data;
|
||||
|
||||
if (!commentText.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const posts = this.data.posts.map(post => {
|
||||
if (post.id === commentingPostId) {
|
||||
const comments = [...post.comments, {
|
||||
id: Date.now().toString(),
|
||||
user: '我的花园',
|
||||
content: commentText.trim()
|
||||
}];
|
||||
return { ...post, comments };
|
||||
}
|
||||
return post;
|
||||
});
|
||||
|
||||
this.setData({
|
||||
posts,
|
||||
showCommentBar: false,
|
||||
commentingPostId: null,
|
||||
commentText: ''
|
||||
}, () => {
|
||||
this.updateDisplayedPosts();
|
||||
wx.showToast({ title: '评论成功', icon: 'success' });
|
||||
});
|
||||
},
|
||||
|
||||
goToCreatePost() {
|
||||
wx.navigateTo({
|
||||
url: '/pages/community/create/index'
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"navigationBarTitleText": "植友动态",
|
||||
"usingComponents": {
|
||||
"t-avatar": "tdesign-miniprogram/avatar/avatar",
|
||||
"t-image": "tdesign-miniprogram/image/image",
|
||||
"t-tabs": "tdesign-miniprogram/tabs/tabs",
|
||||
"t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel",
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-button": "tdesign-miniprogram/button/button",
|
||||
"t-loading": "tdesign-miniprogram/loading/loading"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<wxs src="../../utils/tools.wxs" module="tools" />
|
||||
<view class="community-page">
|
||||
<!-- Header with User Info -->
|
||||
<view class="community-header">
|
||||
<view class="user-info">
|
||||
<view class="user-avatar">
|
||||
<text>我</text>
|
||||
</view>
|
||||
<text class="user-name">我的花园</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Posts Feed -->
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="moments-feed"
|
||||
enhanced="{{true}}"
|
||||
show-scrollbar="{{false}}"
|
||||
bindtap="hideActionPopup"
|
||||
>
|
||||
<block wx:if="{{displayedPosts.length > 0}}">
|
||||
<view wx:for="{{displayedPosts}}" wx:key="id" class="moment-post">
|
||||
<!-- Avatar -->
|
||||
<view class="post-avatar">
|
||||
<view class="avatar-square">
|
||||
<text>{{item.user[0]}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Content -->
|
||||
<view class="post-content">
|
||||
<text class="post-user-name">{{item.user}}</text>
|
||||
<text class="post-text">{{item.content}}</text>
|
||||
|
||||
<!-- Image Grid -->
|
||||
<view wx:if="{{item.images.length > 0}}" class="post-images-grid grid-{{item.images.length === 1 ? '1' : (item.images.length <= 4 ? '2' : '3')}}">
|
||||
<view wx:for="{{item.images}}" wx:for-item="img" wx:key="*this" class="post-image-item" catchtap="previewImage" data-url="{{tools.resolvePath(img)}}" data-urls="{{item.images}}">
|
||||
<t-image
|
||||
src="{{tools.resolvePath(img)}}"
|
||||
mode="aspectFill"
|
||||
width="100%"
|
||||
height="100%"
|
||||
lazy-load
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Meta: Time + Action -->
|
||||
<view class="post-meta">
|
||||
<text class="post-time">{{item.time}}</text>
|
||||
|
||||
<!-- Action Button & Popup Container -->
|
||||
<view class="action-container">
|
||||
<!-- WeChat Style Popup -->
|
||||
<view class="action-popup {{activePostId === item.id ? 'show' : ''}}" catchtap="stopPropagation">
|
||||
<view class="popup-btn like-btn" catchtap="likePost" data-id="{{item.id}}">
|
||||
<t-icon name="heart" size="32rpx" color="#fff" />
|
||||
<text>{{item.likedByMe ? '取消' : '赞'}}</text>
|
||||
</view>
|
||||
<view class="popup-divider"></view>
|
||||
<view class="popup-btn comment-btn" catchtap="showCommentInput" data-id="{{item.id}}">
|
||||
<t-icon name="chat" size="32rpx" color="#fff" />
|
||||
<text>评论</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Trigger Button -->
|
||||
<view class="action-btn" catchtap="toggleActionPopup" data-id="{{item.id}}">
|
||||
<view class="action-dot"></view>
|
||||
<view class="action-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Likes & Comments Box -->
|
||||
<view wx:if="{{item.likes.length > 0 || item.comments.length > 0}}" class="likes-comments-box">
|
||||
<!-- Likes -->
|
||||
<view wx:if="{{item.likes.length > 0}}" class="likes-section">
|
||||
<t-icon name="heart-filled" size="28rpx" color="#576b95" />
|
||||
<view class="likes-list">
|
||||
<text wx:for="{{item.likes}}" wx:for-item="liker" wx:key="*this" class="like-name">{{liker}}{{index < item.likes.length - 1 ? ',' : ''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Divider -->
|
||||
<view wx:if="{{item.likes.length > 0 && item.comments.length > 0}}" class="divider"></view>
|
||||
|
||||
<!-- Comments -->
|
||||
<view wx:if="{{item.comments.length > 0}}" class="comments-section">
|
||||
<view wx:for="{{item.comments}}" wx:for-item="comment" wx:key="id" class="comment-item">
|
||||
<text class="comment-user">{{comment.user}}:</text>
|
||||
<text class="comment-content">{{comment.content}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- Empty State -->
|
||||
<view wx:else class="empty-feed">
|
||||
<view class="empty-icon">
|
||||
<t-icon name="chat" size="80rpx" color="#ccc" />
|
||||
</view>
|
||||
<text class="empty-text">暂无相关动态</text>
|
||||
<text class="empty-hint">快来发布第一条动态吧</text>
|
||||
</view>
|
||||
|
||||
<!-- Bottom Spacer -->
|
||||
<view style="height: 160rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- Floating Action Button -->
|
||||
<view class="fab" bindtap="goToCreatePost">
|
||||
<t-icon name="add" size="48rpx" color="#fff" />
|
||||
</view>
|
||||
|
||||
<!-- Comment Input Bar (WeChat Style) -->
|
||||
<view class="comment-input-bar {{showCommentBar ? 'show' : ''}}">
|
||||
<view class="comment-input-mask" bindtap="hideCommentBar"></view>
|
||||
<view class="comment-input-content">
|
||||
<input
|
||||
class="comment-input"
|
||||
placeholder="评论..."
|
||||
value="{{commentText}}"
|
||||
bindinput="onCommentInput"
|
||||
bindconfirm="submitComment"
|
||||
focus="{{showCommentBar}}"
|
||||
confirm-type="send"
|
||||
adjust-position="{{true}}"
|
||||
/>
|
||||
<view class="send-btn {{commentText ? 'active' : ''}}" bindtap="submitComment">
|
||||
<text>发送</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,431 @@
|
||||
/** pages/community/index.wxss **/
|
||||
page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.community-page {
|
||||
background-color: #fff;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Header with User Info */
|
||||
.community-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 40rpx;
|
||||
padding-top: calc(24rpx + env(safe-area-inset-top));
|
||||
background: white;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 2rpx solid rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.community-header .user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.community-header .user-avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
||||
color: #558B2F;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.community-header .user-name {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Feed Scroll Area */
|
||||
.moments-feed {
|
||||
flex: 1;
|
||||
padding: 0 40rpx;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.moments-feed::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* Post Card */
|
||||
.moment-post {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin-top: 40rpx;
|
||||
padding-bottom: 40rpx;
|
||||
border-bottom: 2rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.moment-post:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Avatar - WeChat Style Rounded Square */
|
||||
.post-avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-square {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
|
||||
color: #558B2F;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
/* Post Content */
|
||||
.post-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.post-user-name {
|
||||
color: #576b95;
|
||||
font-weight: 600;
|
||||
font-size: 30rpx;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.post-text {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Image Grid - CSS Grid Layout */
|
||||
.post-images-grid {
|
||||
display: grid;
|
||||
gap: 8rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-image-item {
|
||||
position: relative;
|
||||
background: #f0f0f0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Single Image */
|
||||
.grid-1 {
|
||||
display: flex;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.grid-1 .post-image-item {
|
||||
width: 100%;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.grid-1 .post-image-item t-image {
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
/* 2-4 Images (2 columns) */
|
||||
.grid-2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.grid-2 .post-image-item {
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
/* 5+ Images (3 columns) */
|
||||
.grid-3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.grid-3 .post-image-item {
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
/* Post Meta */
|
||||
.post-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.post-time {
|
||||
font-size: 24rpx;
|
||||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: #f7f7f7;
|
||||
border-radius: 8rpx;
|
||||
width: 72rpx;
|
||||
height: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
background: #e8e8e8;
|
||||
}
|
||||
|
||||
.action-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
background: #576b95;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Likes & Comments Box */
|
||||
.likes-comments-box {
|
||||
background: #f7f7f7;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
position: relative;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.likes-comments-box::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
left: 24rpx;
|
||||
border-width: 0 12rpx 12rpx;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent #f7f7f7;
|
||||
}
|
||||
|
||||
.likes-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12rpx;
|
||||
color: #576b95;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.likes-list {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.like-name {
|
||||
color: #576b95;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 2rpx;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
margin: 16rpx 0;
|
||||
}
|
||||
|
||||
.comments-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.comment-user {
|
||||
color: #576b95;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-feed {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* Floating Action Button */
|
||||
.fab {
|
||||
position: fixed;
|
||||
right: 40rpx;
|
||||
bottom: 200rpx;
|
||||
width: 112rpx;
|
||||
height: 112rpx;
|
||||
background: linear-gradient(135deg, #689F38, #558B2F);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 12rpx 32rpx rgba(85, 139, 47, 0.4);
|
||||
z-index: 100;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.fab:active {
|
||||
transform: scale(0.92);
|
||||
box-shadow: 0 6rpx 16rpx rgba(85, 139, 47, 0.3);
|
||||
}
|
||||
|
||||
/* WeChat Style Action Container */
|
||||
.action-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* WeChat Style Action Popup */
|
||||
.action-popup {
|
||||
position: absolute;
|
||||
right: 80rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) scaleX(0);
|
||||
transform-origin: right center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #4c4c4c;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease-out;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.action-popup.show {
|
||||
transform: translateY(-50%) scaleX(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popup-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.popup-btn:active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.popup-divider {
|
||||
width: 2rpx;
|
||||
height: 32rpx;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Comment Input Bar */
|
||||
.comment-input-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
.comment-input-bar.show {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.comment-input-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.comment-input-content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx 32rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
background: #f5f5f5;
|
||||
border-top: 2rpx solid #e5e5e5;
|
||||
}
|
||||
|
||||
.comment-input {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 36rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
padding: 16rpx 32rpx;
|
||||
background: #e0e0e0;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.send-btn.active {
|
||||
background: #558B2F;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.send-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
Reference in New Issue
Block a user