12 changed files with 1931 additions and 731 deletions
-
3app.json
-
9pages/home/home.js
-
9pages/home/home.wxml
-
12pages/home/home.wxss
-
BINpagesB/images/hf.png
-
346pagesB/pages/forumlist/forumlist.js
-
3pagesB/pages/forumlist/forumlist.json
-
202pagesB/pages/forumlist/forumlist.wxml
-
442pagesB/pages/forumlist/forumlist.wxss
-
538pagesB/pages/onlineAsk/onlineAsk.js
-
244pagesB/pages/onlineAsk/onlineAsk.wxml
-
838pagesB/pages/onlineAsk/onlineAsk.wxss
|
After Width: 200 | Height: 200 | Size: 3.8 KiB |
@ -0,0 +1,346 @@ |
|||
Page({ |
|||
data: { |
|||
posts: [], |
|||
loading: false, |
|||
loadingMore: false, |
|||
refreshing: false, |
|||
hasMore: true, |
|||
page: 1, |
|||
pageSize: 10, |
|||
currentFilter: 'all', |
|||
searchKeyword: '', |
|||
currentUser: '当前用户' |
|||
}, |
|||
|
|||
onLoad: function() { |
|||
this.loadPosts(); |
|||
|
|||
// 监听页面显示,用于刷新数据
|
|||
wx.onAppShow(() => { |
|||
this.refreshData(); |
|||
}); |
|||
}, |
|||
|
|||
onShow: function() { |
|||
this.refreshData(); |
|||
}, |
|||
|
|||
// 加载帖子列表
|
|||
loadPosts: function(reset = false) { |
|||
if (reset) { |
|||
this.setData({ |
|||
page: 1, |
|||
hasMore: true, |
|||
posts: [], |
|||
loading: true |
|||
}); |
|||
} else if (this.data.loadingMore) { |
|||
return; |
|||
} |
|||
|
|||
const params = { |
|||
page: this.data.page, |
|||
pageSize: this.data.pageSize, |
|||
filter: this.data.currentFilter, |
|||
search: this.data.searchKeyword |
|||
}; |
|||
|
|||
this.setData({ |
|||
loading: reset || this.data.page === 1, |
|||
loadingMore: !reset && this.data.page > 1 |
|||
}); |
|||
|
|||
// 模拟API请求
|
|||
setTimeout(() => { |
|||
const mockPosts = this.generateMockPosts(params); |
|||
|
|||
if (reset) { |
|||
this.setData({ |
|||
posts: mockPosts, |
|||
loading: false, |
|||
hasMore: mockPosts.length === params.pageSize |
|||
}); |
|||
} else { |
|||
this.setData({ |
|||
posts: [...this.data.posts, ...mockPosts], |
|||
loading: false, |
|||
loadingMore: false, |
|||
hasMore: mockPosts.length === params.pageSize |
|||
}); |
|||
} |
|||
|
|||
if (this.data.refreshing) { |
|||
wx.stopPullDownRefresh(); |
|||
this.setData({ refreshing: false }); |
|||
} |
|||
}, 800); |
|||
}, |
|||
|
|||
// 生成模拟数据
|
|||
generateMockPosts: function(params) { |
|||
const posts = []; |
|||
const currentUser = this.data.currentUser; |
|||
const baseId = (params.page - 1) * params.pageSize; |
|||
|
|||
for (let i = 0; i < params.pageSize; i++) { |
|||
const id = baseId + i + 1; |
|||
const solved = i % 4 === 0; |
|||
const hot = i % 3 === 0 && i % 2 === 0; |
|||
const isMine = i % 5 === 0; |
|||
|
|||
// 根据筛选条件过滤
|
|||
if (params.filter === 'solved' && !solved) continue; |
|||
if (params.filter === 'unsolved' && solved) continue; |
|||
if (params.filter === 'mine' && !isMine) continue; |
|||
|
|||
const tags = this.getRandomTags(); |
|||
const post = { |
|||
id: id, |
|||
title: this.getRandomTitle(id), |
|||
summary: this.getRandomSummary(id), |
|||
username: isMine ? currentUser : this.getRandomUsername(), |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: this.getRandomTime(), |
|||
likeCount: Math.floor(Math.random() * 50), |
|||
replyCount: Math.floor(Math.random() * 30), |
|||
viewCount: Math.floor(Math.random() * 300), |
|||
solved: solved, |
|||
hot: hot, |
|||
tags: tags, |
|||
lastReply: Math.random() > 0.3 ? { |
|||
username: this.getRandomUsername(), |
|||
time: this.getRandomTime('short') |
|||
} : null |
|||
}; |
|||
|
|||
// 搜索过滤
|
|||
if (params.search) { |
|||
const keyword = params.search.toLowerCase(); |
|||
const titleMatch = post.title.toLowerCase().includes(keyword); |
|||
const summaryMatch = post.summary.toLowerCase().includes(keyword); |
|||
const tagMatch = post.tags.some(tag => tag.toLowerCase().includes(keyword)); |
|||
|
|||
if (!titleMatch && !summaryMatch && !tagMatch) { |
|||
continue; |
|||
} |
|||
} |
|||
|
|||
posts.push(post); |
|||
} |
|||
|
|||
// 热门排序
|
|||
if (params.filter === 'hot') { |
|||
posts.sort((a, b) => { |
|||
const aScore = a.likeCount * 2 + a.replyCount * 3 + a.viewCount; |
|||
const bScore = b.likeCount * 2 + b.replyCount * 3 + b.viewCount; |
|||
return bScore - aScore; |
|||
}); |
|||
} |
|||
|
|||
return posts; |
|||
}, |
|||
|
|||
// 随机生成标题
|
|||
getRandomTitle: function(id) { |
|||
const titles = [ |
|||
'微信小程序如何实现图片上传和预览功能?', |
|||
'uni-app开发中如何处理不同平台的兼容性问题?', |
|||
'JavaScript闭包的使用场景有哪些?', |
|||
'Vue3组合式API和选项式API该如何选择?', |
|||
'React Hooks在项目中的最佳实践', |
|||
'Node.js高并发场景下的性能优化方案', |
|||
'TypeScript在大型项目中的类型设计经验分享', |
|||
'微信小程序云开发数据库查询性能优化', |
|||
'前端工程化建设:从零搭建Webpack配置', |
|||
'移动端H5页面适配的最佳方案是什么?', |
|||
'如何优雅地处理前端错误监控和上报?', |
|||
'微前端架构在实际项目中的应用经验', |
|||
'Webpack5 Module Federation实战分享', |
|||
'前端代码质量保证:ESLint + Prettier + Husky', |
|||
'跨端开发框架选型:Flutter vs React Native vs uni-app' |
|||
]; |
|||
return titles[id % titles.length] || titles[0]; |
|||
}, |
|||
|
|||
// 随机生成摘要
|
|||
getRandomSummary: function(id) { |
|||
const summaries = [ |
|||
'我正在开发一个微信小程序,需要实现图片上传功能,并且能够在上传前预览图片。请问有什么好的实现方案吗?上传的图片大小限制和格式有什么建议?', |
|||
'最近在做一个uni-app项目,需要同时兼容微信小程序和H5,遇到了一些样式和API兼容性问题,大家有什么好的解决方案吗?', |
|||
'在实际项目中经常使用闭包,但对其原理和应用场景理解还不够深入,想请教一下大家在项目中都是如何使用闭包的?', |
|||
'公司新项目准备使用Vue3,对于组合式API和选项式API的选择有些纠结,大家有什么建议吗?各自的使用场景是什么?', |
|||
'React Hooks确实很方便,但在大型项目中如何合理组织Hooks,避免过度使用导致代码难以维护?', |
|||
'我们的Node.js服务在高并发场景下性能表现不佳,有哪些常见的性能优化方案可以参考?', |
|||
'项目准备从JavaScript迁移到TypeScript,在类型设计方面有什么经验可以分享吗?如何设计合理的泛型和接口?' |
|||
]; |
|||
return summaries[id % summaries.length] || summaries[0]; |
|||
}, |
|||
|
|||
// 随机生成用户名
|
|||
getRandomUsername: function() { |
|||
const usernames = [ |
|||
'前端工程师', '技术爱好者', '小程序开发', '全栈程序员', |
|||
'架构师老王', '代码艺术家', '算法工程师', '产品经理', |
|||
'UI设计师', '测试工程师', '运维小哥', '数据分析师' |
|||
]; |
|||
return usernames[Math.floor(Math.random() * usernames.length)]; |
|||
}, |
|||
|
|||
// 随机生成时间
|
|||
getRandomTime: function(type = 'normal') { |
|||
const times = type === 'short' |
|||
? ['5分钟前', '10分钟前', '半小时前', '1小时前'] |
|||
: ['2小时前', '5小时前', '昨天', '2天前', '3天前', '一周前']; |
|||
return times[Math.floor(Math.random() * times.length)]; |
|||
}, |
|||
|
|||
// 随机生成标签
|
|||
getRandomTags: function() { |
|||
const allTags = [ |
|||
'微信小程序', '前端开发', 'JavaScript', 'Vue.js', 'React', |
|||
'Node.js', 'TypeScript', 'uni-app', '性能优化', '工程化', |
|||
'移动端', 'H5', 'CSS', 'Webpack', 'Git' |
|||
]; |
|||
|
|||
const count = Math.floor(Math.random() * 3) + 1; |
|||
const tags = []; |
|||
const usedIndices = new Set(); |
|||
|
|||
for (let i = 0; i < count; i++) { |
|||
let index; |
|||
do { |
|||
index = Math.floor(Math.random() * allTags.length); |
|||
} while (usedIndices.has(index)); |
|||
|
|||
usedIndices.add(index); |
|||
tags.push(allTags[index]); |
|||
} |
|||
|
|||
return tags; |
|||
}, |
|||
|
|||
// 下拉刷新
|
|||
onRefresh: function() { |
|||
this.setData({ refreshing: true }); |
|||
this.loadPosts(true); |
|||
}, |
|||
|
|||
// 滚动到底部加载更多
|
|||
loadMore: function() { |
|||
if (!this.data.hasMore || this.data.loadingMore) return; |
|||
|
|||
this.setData({ |
|||
page: this.data.page + 1 |
|||
}, () => { |
|||
this.loadPosts(); |
|||
}); |
|||
}, |
|||
|
|||
// 筛选切换
|
|||
changeFilter: function(e) { |
|||
const filterType = e.currentTarget.dataset.type; |
|||
if (this.data.currentFilter === filterType) return; |
|||
|
|||
this.setData({ |
|||
currentFilter: filterType, |
|||
searchKeyword: '' // 切换筛选时清空搜索
|
|||
}, () => { |
|||
this.loadPosts(true); |
|||
}); |
|||
}, |
|||
|
|||
// 搜索输入
|
|||
onSearchInput: function(e) { |
|||
this.setData({ searchKeyword: e.detail.value }); |
|||
|
|||
// 防抖搜索
|
|||
clearTimeout(this.searchTimer); |
|||
this.searchTimer = setTimeout(() => { |
|||
if (e.detail.value.trim()) { |
|||
this.loadPosts(true); |
|||
} |
|||
}, 300); |
|||
}, |
|||
|
|||
// 搜索确认
|
|||
onSearchConfirm: function(e) { |
|||
const keyword = e.detail.value.trim(); |
|||
if (keyword) { |
|||
this.setData({ searchKeyword: keyword }); |
|||
this.loadPosts(true); |
|||
} |
|||
}, |
|||
|
|||
// 清空搜索
|
|||
clearSearch: function() { |
|||
this.setData({ |
|||
searchKeyword: '', |
|||
currentFilter: 'all' |
|||
}, () => { |
|||
this.loadPosts(true); |
|||
}); |
|||
}, |
|||
|
|||
// 跳转到详情页
|
|||
goToDetail: function(e) { |
|||
const postId = e.currentTarget.dataset.id; |
|||
wx.navigateTo({ |
|||
url: `/pages/forum/detail/detail?id=${postId}`, |
|||
success: () => { |
|||
// 记录浏览历史
|
|||
this.recordViewHistory(postId); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 创建新帖子
|
|||
createPost: function() { |
|||
wx.navigateTo({ |
|||
url: '/pages/forum/create/create' |
|||
}); |
|||
}, |
|||
|
|||
// 记录浏览历史
|
|||
recordViewHistory: function(postId) { |
|||
try { |
|||
const history = wx.getStorageSync('forumViewHistory') || []; |
|||
const index = history.findIndex(item => item.id === postId); |
|||
|
|||
if (index !== -1) { |
|||
history.splice(index, 1); |
|||
} |
|||
|
|||
history.unshift({ |
|||
id: postId, |
|||
timestamp: Date.now() |
|||
}); |
|||
|
|||
// 只保留最近50条记录
|
|||
if (history.length > 50) { |
|||
history.pop(); |
|||
} |
|||
|
|||
wx.setStorageSync('forumViewHistory', history); |
|||
} catch (error) { |
|||
console.error('记录浏览历史失败:', error); |
|||
} |
|||
}, |
|||
|
|||
// 刷新数据
|
|||
refreshData: function() { |
|||
// 这里可以检查是否有新数据需要刷新
|
|||
// 例如:从详情页返回时刷新点赞状态等
|
|||
}, |
|||
|
|||
onPullDownRefresh: function() { |
|||
this.onRefresh(); |
|||
}, |
|||
|
|||
onReachBottom: function() { |
|||
this.loadMore(); |
|||
}, |
|||
|
|||
onPageScroll: function(e) { |
|||
// 可以在这里处理页面滚动时的效果
|
|||
} |
|||
}); |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"usingComponents": {} |
|||
} |
|||
@ -0,0 +1,202 @@ |
|||
<view class="forum-list-page"> |
|||
|
|||
<!-- 顶部栏 --> |
|||
<view class="header"> |
|||
<view class="title">问答论坛</view> |
|||
<button class="add-btn" bindtap="createPost">提问</button> |
|||
</view> |
|||
|
|||
<!-- 筛选栏 --> |
|||
<view class="filter-bar"> |
|||
<scroll-view class="filter-scroll" scroll-x> |
|||
<view class="filter-list"> |
|||
<view |
|||
class="filter-item {{currentFilter === 'all' ? 'active' : ''}}" |
|||
bindtap="changeFilter" |
|||
data-type="all" |
|||
> |
|||
<text>全部</text> |
|||
</view> |
|||
<view |
|||
class="filter-item {{currentFilter === 'unsolved' ? 'active' : ''}}" |
|||
bindtap="changeFilter" |
|||
data-type="unsolved" |
|||
> |
|||
<text>待解决</text> |
|||
</view> |
|||
<view |
|||
class="filter-item {{currentFilter === 'solved' ? 'active' : ''}}" |
|||
bindtap="changeFilter" |
|||
data-type="solved" |
|||
> |
|||
<text>已解决</text> |
|||
</view> |
|||
<view |
|||
class="filter-item {{currentFilter === 'hot' ? 'active' : ''}}" |
|||
bindtap="changeFilter" |
|||
data-type="hot" |
|||
> |
|||
<text>热门</text> |
|||
</view> |
|||
<view |
|||
class="filter-item {{currentFilter === 'mine' ? 'active' : ''}}" |
|||
bindtap="changeFilter" |
|||
data-type="mine" |
|||
> |
|||
<text>我的帖子</text> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
|
|||
<!-- 搜索框 --> |
|||
<view class="search-container"> |
|||
<view class="search-input-wrapper"> |
|||
<image class="search-icon" src="/images/search.png" mode="aspectFit"></image> |
|||
<input |
|||
class="search-input" |
|||
placeholder="搜索问题或关键词..." |
|||
value="{{searchKeyword}}" |
|||
bindinput="onSearchInput" |
|||
bindconfirm="onSearchConfirm" |
|||
confirm-type="search" |
|||
/> |
|||
<view class="search-clear" wx:if="{{searchKeyword}}" bindtap="clearSearch"> |
|||
× |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 帖子列表 --> |
|||
<scroll-view |
|||
class="post-list-container" |
|||
scroll-y |
|||
enable-back-to-top |
|||
bindscrolltolower="loadMore" |
|||
refresher-enabled="{{true}}" |
|||
refresher-triggered="{{refreshing}}" |
|||
bindrefresherrefresh="onRefresh" |
|||
> |
|||
|
|||
<!-- 空状态 --> |
|||
<view class="empty-state" wx:if="{{!loading && posts.length === 0}}"> |
|||
<image class="empty-image" src="/images/empty-forum.png" mode="aspectFit"></image> |
|||
<view class="empty-text" wx:if="{{searchKeyword}}"> |
|||
没有找到"{{searchKeyword}}"相关的问题 |
|||
</view> |
|||
<view class="empty-text" wx:else> |
|||
暂无帖子,快来发布第一个问题吧! |
|||
</view> |
|||
<button class="empty-btn" bindtap="createPost" wx:if="{{!searchKeyword}}">发布问题</button> |
|||
<button class="empty-btn" bindtap="clearSearch" wx:else>清空搜索</button> |
|||
</view> |
|||
|
|||
<!-- 帖子列表 --> |
|||
<view class="post-list" wx:if="{{posts.length > 0}}"> |
|||
<block wx:for="{{posts}}" wx:key="id"> |
|||
<view class="post-item" data-id="{{item.id}}" bindtap="goToDetail"> |
|||
|
|||
<!-- 左侧状态栏 --> |
|||
<view class="post-status-side"> |
|||
<view class="vote-count"> |
|||
<view class="vote-number">{{item.likeCount}}</view> |
|||
<view class="vote-label">点赞</view> |
|||
</view> |
|||
<view class="reply-count"> |
|||
<view class="reply-number">{{item.replyCount}}</view> |
|||
<view class="reply-label">回答</view> |
|||
</view> |
|||
<view class="view-count"> |
|||
<view class="view-number">{{item.viewCount}}</view> |
|||
<view class="view-label">浏览</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 右侧内容区 --> |
|||
<view class="post-content-main"> |
|||
|
|||
<!-- 帖子标题和状态 --> |
|||
<view class="post-header"> |
|||
<view class="post-title">{{item.title}}</view> |
|||
<view class="status-badge" wx:if="{{item.solved}}"> |
|||
<text class="solved-badge">已解决</text> |
|||
</view> |
|||
<view class="status-badge" wx:if="{{item.hot}}"> |
|||
<text class="hot-badge">热门</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 帖子内容摘要 --> |
|||
<view class="post-summary">{{item.summary}}</view> |
|||
|
|||
<!-- 帖子元信息 --> |
|||
<view class="post-meta"> |
|||
|
|||
<!-- 用户信息 --> |
|||
<view class="user-info"> |
|||
<image class="user-avatar" src="{{item.avatar}}" mode="aspectFill"></image> |
|||
<text class="username">{{item.username}}</text> |
|||
<text class="separator">·</text> |
|||
<text class="post-time">{{item.time}}</text> |
|||
</view> |
|||
|
|||
<!-- 标签 --> |
|||
<view class="post-tags" wx:if="{{item.tags && item.tags.length > 0}}"> |
|||
<block wx:for="{{item.tags.slice(0, 2)}}" wx:key="index"> |
|||
<text class="tag">{{item}}</text> |
|||
</block> |
|||
<text class="more-tags" wx:if="{{item.tags.length > 2}}">+{{item.tags.length - 2}}</text> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<!-- 最后回复信息 --> |
|||
<view class="last-reply" wx:if="{{item.lastReply}}"> |
|||
<text class="last-reply-label">最后回复:</text> |
|||
<text class="last-reply-user">{{item.lastReply.username}}</text> |
|||
<text class="separator">·</text> |
|||
<text class="last-reply-time">{{item.lastReply.time}}</text> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
</view> |
|||
</block> |
|||
|
|||
<!-- 加载更多 --> |
|||
<view class="load-more" wx:if="{{hasMore && posts.length > 0}}"> |
|||
<view class="loading-text" wx:if="{{!loadingMore}}"> |
|||
上拉加载更多 |
|||
</view> |
|||
<view class="loading-more" wx:else> |
|||
<image class="loading-icon" src="/images/loading.png" mode="aspectFit"></image> |
|||
加载中... |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 没有更多了 --> |
|||
<view class="no-more" wx:if="{{!hasMore && posts.length > 0}}"> |
|||
<text>没有更多了</text> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<!-- 底部占位 --> |
|||
<view class="bottom-placeholder"></view> |
|||
|
|||
</scroll-view> |
|||
|
|||
<!-- 加载提示 --> |
|||
<view class="loading" wx:if="{{loading && posts.length === 0}}"> |
|||
<image class="loading-icon" src="/images/loading.png" mode="aspectFit"></image> |
|||
加载中... |
|||
</view> |
|||
|
|||
<!-- 创建帖子浮动按钮 --> |
|||
<view class="fab-container" wx:if="{{!searchKeyword}}"> |
|||
<view class="fab" bindtap="createPost"> |
|||
<image class="fab-icon" src="/images/add.png" mode="aspectFit"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
@ -0,0 +1,442 @@ |
|||
.forum-list-page { |
|||
min-height: 100vh; |
|||
background-color: #f8f8f8; |
|||
position: relative; |
|||
} |
|||
|
|||
/* 顶部栏 */ |
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 60rpx 30rpx 20rpx; |
|||
background-color: #fff; |
|||
position: sticky; |
|||
top: 0; |
|||
z-index: 100; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.title { |
|||
font-size: 40rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.add-btn { |
|||
background-color: #07c160; |
|||
color: white; |
|||
font-size: 28rpx; |
|||
padding: 12rpx 30rpx; |
|||
line-height: normal; |
|||
border-radius: 40rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 筛选栏 */ |
|||
.filter-bar { |
|||
background-color: #fff; |
|||
padding: 20rpx 30rpx; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
position: sticky; |
|||
top: 120rpx; |
|||
z-index: 99; |
|||
} |
|||
|
|||
.filter-scroll { |
|||
white-space: nowrap; |
|||
height: 60rpx; |
|||
} |
|||
|
|||
.filter-list { |
|||
display: inline-flex; |
|||
gap: 30rpx; |
|||
} |
|||
|
|||
.filter-item { |
|||
padding: 0 20rpx; |
|||
height: 60rpx; |
|||
line-height: 60rpx; |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
border-radius: 30rpx; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.filter-item.active { |
|||
background-color: rgba(7, 193, 96, 0.1); |
|||
color: #07c160; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 搜索框 */ |
|||
.search-container { |
|||
padding: 20rpx 30rpx; |
|||
background-color: #fff; |
|||
position: sticky; |
|||
top: 200rpx; |
|||
z-index: 98; |
|||
} |
|||
|
|||
.search-input-wrapper { |
|||
display: flex; |
|||
align-items: center; |
|||
background-color: #f8f8f8; |
|||
border-radius: 40rpx; |
|||
padding: 0 30rpx; |
|||
height: 80rpx; |
|||
} |
|||
|
|||
.search-icon { |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
margin-right: 20rpx; |
|||
opacity: 0.5; |
|||
} |
|||
|
|||
.search-input { |
|||
flex: 1; |
|||
font-size: 28rpx; |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
} |
|||
|
|||
.search-clear { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
line-height: 40rpx; |
|||
text-align: center; |
|||
font-size: 36rpx; |
|||
color: #999; |
|||
border-radius: 50%; |
|||
background-color: #e0e0e0; |
|||
} |
|||
|
|||
/* 帖子列表容器 */ |
|||
.post-list-container { |
|||
height: calc(100vh - 300rpx); |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
/* 空状态 */ |
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 120rpx 30rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.empty-image { |
|||
width: 300rpx; |
|||
height: 300rpx; |
|||
margin-bottom: 40rpx; |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 30rpx; |
|||
color: #999; |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
.empty-btn { |
|||
background-color: #07c160; |
|||
color: white; |
|||
font-size: 28rpx; |
|||
padding: 20rpx 50rpx; |
|||
line-height: normal; |
|||
border-radius: 40rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
/* 帖子列表 */ |
|||
.post-list { |
|||
padding: 0 30rpx; |
|||
} |
|||
|
|||
.post-item { |
|||
display: flex; |
|||
background-color: white; |
|||
border-radius: 16rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
transition: transform 0.3s, box-shadow 0.3s; |
|||
} |
|||
|
|||
.post-item:active { |
|||
transform: translateY(-2rpx); |
|||
box-shadow: 0 6rpx 30rpx rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
/* 左侧状态栏 */ |
|||
.post-status-side { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
margin-right: 30rpx; |
|||
min-width: 120rpx; |
|||
} |
|||
|
|||
.vote-count, |
|||
.reply-count, |
|||
.view-count { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.vote-count .vote-number, |
|||
.reply-count .reply-number, |
|||
.view-count .view-number { |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.vote-count .vote-label, |
|||
.reply-count .reply-label, |
|||
.view-count .view-label { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
/* 右侧内容区 */ |
|||
.post-content-main { |
|||
flex: 1; |
|||
min-width: 0; |
|||
} |
|||
|
|||
/* 帖子标题和状态 */ |
|||
.post-header { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
flex-wrap: wrap; |
|||
gap: 12rpx; |
|||
} |
|||
|
|||
.post-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
line-height: 1.4; |
|||
flex: 1; |
|||
min-width: 0; |
|||
word-break: break-all; |
|||
} |
|||
|
|||
.status-badge { |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.solved-badge { |
|||
background-color: #07c160; |
|||
color: white; |
|||
font-size: 22rpx; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
.hot-badge { |
|||
background-color: #ff6b6b; |
|||
color: white; |
|||
font-size: 22rpx; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
/* 帖子内容摘要 */ |
|||
.post-summary { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
line-height: 1.5; |
|||
margin-bottom: 20rpx; |
|||
display: -webkit-box; |
|||
-webkit-box-orient: vertical; |
|||
-webkit-line-clamp: 2; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
/* 帖子元信息 */ |
|||
.post-meta { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 15rpx; |
|||
flex-wrap: wrap; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.user-info { |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.user-avatar { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
border-radius: 50%; |
|||
margin-right: 10rpx; |
|||
} |
|||
|
|||
.username { |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.separator { |
|||
margin: 0 8rpx; |
|||
} |
|||
|
|||
.post-time { |
|||
color: #999; |
|||
} |
|||
|
|||
/* 标签 */ |
|||
.post-tags { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 10rpx; |
|||
} |
|||
|
|||
.tag { |
|||
font-size: 22rpx; |
|||
color: #07c160; |
|||
background-color: rgba(7, 193, 96, 0.1); |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 20rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.more-tags { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
margin-left: 4rpx; |
|||
} |
|||
|
|||
/* 最后回复信息 */ |
|||
.last-reply { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
padding-top: 15rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.last-reply-label { |
|||
color: #666; |
|||
} |
|||
|
|||
.last-reply-user { |
|||
color: #07c160; |
|||
margin: 0 5rpx; |
|||
} |
|||
|
|||
/* 加载更多 */ |
|||
.load-more { |
|||
padding: 40rpx 0; |
|||
text-align: center; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.loading-text { |
|||
color: #999; |
|||
} |
|||
|
|||
.loading-more { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
color: #999; |
|||
} |
|||
|
|||
.loading-more .loading-icon { |
|||
width: 50rpx; |
|||
height: 50rpx; |
|||
margin-bottom: 10rpx; |
|||
} |
|||
|
|||
/* 没有更多了 */ |
|||
.no-more { |
|||
padding: 40rpx 0; |
|||
text-align: center; |
|||
color: #999; |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
/* 底部占位 */ |
|||
.bottom-placeholder { |
|||
height: 100rpx; |
|||
} |
|||
|
|||
/* 加载提示 */ |
|||
.loading { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.loading-icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
margin-bottom: 20rpx; |
|||
animation: rotate 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes rotate { |
|||
0% { transform: rotate(0deg); } |
|||
100% { transform: rotate(360deg); } |
|||
} |
|||
|
|||
/* 浮动按钮 */ |
|||
.fab-container { |
|||
position: fixed; |
|||
bottom: 60rpx; |
|||
right: 40rpx; |
|||
z-index: 1000; |
|||
} |
|||
|
|||
.fab { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
background-color: #07c160; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
box-shadow: 0 8rpx 30rpx rgba(7, 193, 96, 0.3); |
|||
transition: transform 0.3s, box-shadow 0.3s; |
|||
} |
|||
|
|||
.fab:active { |
|||
transform: scale(0.95); |
|||
box-shadow: 0 4rpx 20rpx rgba(7, 193, 96, 0.2); |
|||
} |
|||
|
|||
.fab-icon { |
|||
width: 44rpx; |
|||
height: 44rpx; |
|||
} |
|||
|
|||
/* 移除按钮边框 */ |
|||
button::after { |
|||
border: none; |
|||
} |
|||
|
|||
/* 滚动条样式 */ |
|||
::-webkit-scrollbar { |
|||
width: 0; |
|||
height: 0; |
|||
color: transparent; |
|||
} |
|||
@ -1,305 +1,407 @@ |
|||
// pages/forum/forum.js
|
|||
Page({ |
|||
data: { |
|||
// 帖子列表数据
|
|||
posts: [], |
|||
// 是否显示发帖弹窗
|
|||
showPostModal: false, |
|||
// 发帖标题
|
|||
postTitle: '', |
|||
// 发帖内容
|
|||
postContent: '', |
|||
// 标签输入
|
|||
tagInput: '', |
|||
// 已选标签
|
|||
selectedTags: [], |
|||
// 当前回复的帖子索引
|
|||
activeReplyIndex: -1, |
|||
// 回复内容
|
|||
post: null, |
|||
replyContent: '', |
|||
// 加载状态
|
|||
replyTarget: { |
|||
type: '', |
|||
username: '', |
|||
replyId: '', |
|||
replyIndex: null, |
|||
commentId: '', |
|||
commentIndex: null |
|||
}, |
|||
replyPlaceholder: '输入您的回复...', |
|||
isInputFocused: false, |
|||
inputTransformY: '0', |
|||
isSubmitting: false, |
|||
showPreview: false, |
|||
previewImages: [], |
|||
previewIndex: 0, |
|||
loading: false, |
|||
scrollToId: '', |
|||
currentUser: '当前用户', |
|||
keyboardHeight: 0 |
|||
}, |
|||
|
|||
onLoad: function() { |
|||
// 页面加载时获取帖子数据
|
|||
this.loadPosts(); |
|||
}, |
|||
onLoad: function(options) { |
|||
const postId = options.id || '1'; |
|||
this.loadPostDetail(postId); |
|||
|
|||
onPullDownRefresh: function() { |
|||
// 下拉刷新
|
|||
this.loadPosts(); |
|||
wx.stopPullDownRefresh(); |
|||
// 监听键盘高度变化
|
|||
wx.onKeyboardHeightChange(res => { |
|||
if (res.height > 0) { |
|||
this.setData({ keyboardHeight: res.height }); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 加载帖子数据
|
|||
loadPosts: function() { |
|||
// 加载帖子详情
|
|||
loadPostDetail: function(postId) { |
|||
this.setData({ loading: true }); |
|||
|
|||
// 模拟网络请求
|
|||
// 模拟API请求
|
|||
setTimeout(() => { |
|||
// 模拟数据
|
|||
const mockPosts = [ |
|||
{ |
|||
id: 1, |
|||
username: '技术爱好者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '2小时前', |
|||
title: '微信小程序如何实现图片上传和预览功能?', |
|||
content: '我正在开发一个微信小程序,需要实现图片上传功能,并且能够在上传前预览图片。请问有什么好的实现方案吗?上传的图片大小限制和格式有什么建议?', |
|||
tags: ['微信小程序', '图片上传', '前端开发'], |
|||
likeCount: 12, |
|||
replyCount: 5, |
|||
viewCount: 156, |
|||
liked: false, |
|||
solved: false, |
|||
showAllReplies: false, |
|||
replies: [ |
|||
{ |
|||
username: '前端开发工程师', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '1小时前', |
|||
content: '可以使用wx.chooseImage选择图片,然后使用wx.uploadFile上传到服务器。预览功能可以使用wx.previewImage实现。' |
|||
}, |
|||
{ |
|||
username: '小程序开发者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '45分钟前', |
|||
content: '建议将图片大小限制在2MB以内,支持JPG、PNG格式。可以使用云开发存储功能简化上传流程。' |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
id: 2, |
|||
username: '产品经理小王', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '昨天 14:30', |
|||
title: '如何设计一个用户友好的注册流程?', |
|||
content: '我们正在设计一个新产品的注册流程,希望既保证安全性又尽量简化步骤。大家有什么好的设计建议或参考案例吗?', |
|||
tags: ['产品设计', '用户体验', '注册流程'], |
|||
likeCount: 8, |
|||
replyCount: 3, |
|||
viewCount: 89, |
|||
liked: true, |
|||
solved: true, |
|||
showAllReplies: false, |
|||
replies: [ |
|||
{ |
|||
username: 'UX设计师', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '昨天 16:45', |
|||
content: '建议采用手机号验证码注册,配合第三方登录选项。关键是将必填信息减到最少,其他信息可以后续引导补充。' |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
id: 3, |
|||
username: '后端开发', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '3天前', |
|||
title: 'RESTful API设计的最佳实践有哪些?', |
|||
content: '我正在设计一组RESTful API,想了解一些最佳实践,比如URL命名规范、状态码使用、版本控制等方面有什么推荐的做法?', |
|||
tags: ['后端开发', 'API设计', 'RESTful'], |
|||
likeCount: 15, |
|||
replyCount: 7, |
|||
viewCount: 234, |
|||
liked: false, |
|||
solved: false, |
|||
showAllReplies: false, |
|||
replies: [ |
|||
{ |
|||
username: '架构师', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '2天前', |
|||
content: '建议使用名词复数形式作为资源端点,合理使用HTTP状态码,API版本可以通过URL路径或请求头来管理。' |
|||
}, |
|||
{ |
|||
username: '全栈工程师', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '1天前', |
|||
content: '不要忘记实现合适的错误处理机制,返回清晰的错误信息。同时考虑API限流和身份验证机制。' |
|||
}, |
|||
{ |
|||
username: '资深开发者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '1天前', |
|||
content: '推荐使用Swagger或OpenAPI规范来文档化你的API,这对前后端协作非常有帮助。' |
|||
} |
|||
] |
|||
} |
|||
]; |
|||
const mockPost = { |
|||
id: 1, |
|||
username: '技术爱好者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '2小时前', |
|||
title: '微信小程序如何实现图片上传和预览功能?', |
|||
content: '我正在开发一个微信小程序,需要实现图片上传功能,并且能够在上传前预览图片。请问有什么好的实现方案吗?上传的图片大小限制和格式有什么建议?', |
|||
images: [ |
|||
'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
'https://img.yzcdn.cn/vant/cat.jpeg' |
|||
], |
|||
tags: ['微信小程序', '图片上传', '前端开发'], |
|||
likeCount: 12, |
|||
replyCount: 5, |
|||
viewCount: 156, |
|||
liked: false, |
|||
solved: false, |
|||
replies: [ |
|||
{ |
|||
replyId: 'r1_1', |
|||
username: '前端开发工程师', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '1小时前', |
|||
content: '可以使用wx.chooseImage选择图片,然后使用wx.uploadFile上传到服务器。预览功能可以使用wx.previewImage实现。', |
|||
likeCount: 3, |
|||
liked: false, |
|||
comments: [ |
|||
{ |
|||
commentId: 'c1_1', |
|||
username: '学习者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
toUsername: '前端开发工程师', |
|||
time: '30分钟前', |
|||
content: '感谢分享,请问有具体的代码示例吗?' |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
replyId: 'r1_2', |
|||
username: '小程序开发者', |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '45分钟前', |
|||
content: '建议将图片大小限制在2MB以内,支持JPG、PNG格式。可以使用云开发存储功能简化上传流程。', |
|||
likeCount: 2, |
|||
liked: true, |
|||
comments: [] |
|||
} |
|||
] |
|||
}; |
|||
|
|||
this.setData({ |
|||
posts: mockPosts, |
|||
post: mockPost, |
|||
loading: false |
|||
}); |
|||
}, 800); |
|||
}, 500); |
|||
}, |
|||
|
|||
// 显示发帖弹窗
|
|||
showPostModal: function() { |
|||
this.setData({ |
|||
showPostModal: true |
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
// 滚动监听
|
|||
onScroll: function(e) { |
|||
// 可以在这里实现滚动相关逻辑
|
|||
}, |
|||
|
|||
// 隐藏发帖弹窗
|
|||
hidePostModal: function() { |
|||
// 输入框获取焦点
|
|||
onInputFocus: function(e) { |
|||
this.setData({ |
|||
showPostModal: false, |
|||
postTitle: '', |
|||
postContent: '', |
|||
selectedTags: [], |
|||
tagInput: '' |
|||
isInputFocused: true, |
|||
inputTransformY: `-${e.detail.height}px` |
|||
}); |
|||
}, |
|||
|
|||
// 防止弹窗内点击事件冒泡
|
|||
preventTap: function() { |
|||
// 阻止事件冒泡
|
|||
// 输入框失去焦点
|
|||
onInputBlur: function() { |
|||
this.setData({ |
|||
isInputFocused: false, |
|||
inputTransformY: '0' |
|||
}); |
|||
}, |
|||
|
|||
// 发帖标题输入
|
|||
onPostTitleInput: function(e) { |
|||
// 回复输入
|
|||
onReplyInput: function(e) { |
|||
this.setData({ |
|||
postTitle: e.detail.value |
|||
replyContent: e.detail.value |
|||
}); |
|||
}, |
|||
|
|||
// 发帖内容输入
|
|||
onPostContentInput: function(e) { |
|||
// 聚焦到输入框
|
|||
focusReplyInput: function() { |
|||
this.setData({ |
|||
postContent: e.detail.value |
|||
replyTarget: { |
|||
type: 'post', |
|||
username: this.data.post.username |
|||
}, |
|||
replyPlaceholder: `回复 ${this.data.post.username}...`, |
|||
replyContent: '', |
|||
isInputFocused: true |
|||
}); |
|||
}, |
|||
|
|||
// 回复用户
|
|||
replyToUser: function(e) { |
|||
const { type, index, replyIndex, commentIndex, username } = e.currentTarget.dataset; |
|||
|
|||
const replyTarget = { |
|||
type: type, |
|||
username: username |
|||
}; |
|||
|
|||
if (type === 'reply') { |
|||
replyTarget.replyIndex = parseInt(index); |
|||
replyTarget.replyId = this.data.post.replies[index].replyId; |
|||
} else if (type === 'comment') { |
|||
replyTarget.replyIndex = parseInt(replyIndex); |
|||
replyTarget.commentIndex = parseInt(commentIndex); |
|||
replyTarget.commentId = this.data.post.replies[replyIndex].comments[commentIndex].commentId; |
|||
replyTarget.replyId = this.data.post.replies[replyIndex].replyId; |
|||
} |
|||
|
|||
// 提交帖子
|
|||
submitPost: function() { |
|||
const { postTitle, postContent, selectedTags } = this.data; |
|||
this.setData({ |
|||
replyTarget: replyTarget, |
|||
replyPlaceholder: `回复 @${username}...`, |
|||
replyContent: '', |
|||
isInputFocused: true |
|||
}); |
|||
|
|||
if (!postTitle.trim()) { |
|||
wx.showToast({ |
|||
title: '请输入标题', |
|||
icon: 'none' |
|||
// 滚动到输入框位置
|
|||
setTimeout(() => { |
|||
this.setData({ |
|||
scrollToId: type === 'reply' ? `reply-${replyTarget.replyId}` : '' |
|||
}); |
|||
}, 100); |
|||
}, |
|||
|
|||
// 清除回复目标
|
|||
clearReplyTarget: function() { |
|||
this.setData({ |
|||
replyTarget: { |
|||
type: '', |
|||
username: '', |
|||
replyId: '', |
|||
replyIndex: null, |
|||
commentId: '', |
|||
commentIndex: null |
|||
}, |
|||
replyPlaceholder: '输入您的回复...', |
|||
replyContent: '' |
|||
}); |
|||
}, |
|||
|
|||
// 提交回复
|
|||
submitReply: function() { |
|||
const { replyContent, replyTarget, post } = this.data; |
|||
const content = replyContent.trim(); |
|||
|
|||
if (!content) { |
|||
wx.showToast({ title: '请输入内容', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
if (!postContent.trim()) { |
|||
wx.showToast({ |
|||
title: '请输入内容', |
|||
icon: 'none' |
|||
}); |
|||
if (content.length > 500) { |
|||
wx.showToast({ title: '回复内容不能超过500字', icon: 'none' }); |
|||
return; |
|||
} |
|||
|
|||
// 创建新帖子
|
|||
const newPost = { |
|||
id: Date.now(), |
|||
username: '当前用户', |
|||
this.setData({ isSubmitting: true }); |
|||
|
|||
// 模拟网络请求
|
|||
setTimeout(() => { |
|||
if (replyTarget.type === 'post') { |
|||
// 回复帖子
|
|||
this.replyToPost(content); |
|||
} else if (replyTarget.type === 'reply') { |
|||
// 回复评论
|
|||
this.replyToComment(content, replyTarget); |
|||
} else if (replyTarget.type === 'comment') { |
|||
// 回复评论的回复
|
|||
this.replyToCommentReply(content, replyTarget); |
|||
} else { |
|||
// 普通回复(直接回复帖子)
|
|||
this.replyToPost(content); |
|||
} |
|||
|
|||
this.setData({ isSubmitting: false }); |
|||
}, 500); |
|||
}, |
|||
|
|||
// 回复帖子
|
|||
replyToPost: function(content) { |
|||
const post = this.data.post; |
|||
const replyId = `r${post.id}_${Date.now()}`; |
|||
|
|||
const newReply = { |
|||
replyId: replyId, |
|||
username: this.data.currentUser, |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
time: '刚刚', |
|||
title: postTitle, |
|||
content: postContent, |
|||
tags: selectedTags, |
|||
content: content, |
|||
likeCount: 0, |
|||
replyCount: 0, |
|||
viewCount: 0, |
|||
liked: false, |
|||
solved: false, |
|||
showAllReplies: false, |
|||
replies: [] |
|||
comments: [] |
|||
}; |
|||
|
|||
// 添加到帖子列表顶部
|
|||
const posts = this.data.posts; |
|||
posts.unshift(newPost); |
|||
post.replies.push(newReply); |
|||
post.replyCount += 1; |
|||
|
|||
this.setData({ |
|||
posts: posts, |
|||
showPostModal: false, |
|||
postTitle: '', |
|||
postContent: '', |
|||
selectedTags: [], |
|||
tagInput: '' |
|||
post: post, |
|||
replyContent: '', |
|||
replyTarget: { |
|||
type: '', |
|||
username: '', |
|||
replyId: '', |
|||
replyIndex: null, |
|||
commentId: '', |
|||
commentIndex: null |
|||
}, |
|||
replyPlaceholder: '输入您的回复...', |
|||
isInputFocused: false |
|||
}); |
|||
|
|||
wx.showToast({ |
|||
title: '发布成功', |
|||
icon: 'success' |
|||
title: '回复成功', |
|||
icon: 'success', |
|||
duration: 1500 |
|||
}); |
|||
|
|||
// 滚动到最新回复
|
|||
setTimeout(() => { |
|||
this.setData({ |
|||
scrollToId: `reply-${replyId}` |
|||
}); |
|||
}, 800); |
|||
}, |
|||
|
|||
// 回复评论
|
|||
replyToComment: function(content, target) { |
|||
const post = this.data.post; |
|||
const replyIndex = target.replyIndex; |
|||
const reply = post.replies[replyIndex]; |
|||
const commentId = `c${reply.replyId}_${Date.now()}`; |
|||
|
|||
const newComment = { |
|||
commentId: commentId, |
|||
username: this.data.currentUser, |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
toUsername: target.username, |
|||
time: '刚刚', |
|||
content: content |
|||
}; |
|||
|
|||
if (!reply.comments) { |
|||
reply.comments = []; |
|||
} |
|||
reply.comments.push(newComment); |
|||
|
|||
// 显示回复输入框
|
|||
showReplyInput: function(e) { |
|||
const index = e.currentTarget.dataset.index; |
|||
this.setData({ |
|||
activeReplyIndex: index, |
|||
replyContent: '' |
|||
post: post, |
|||
replyContent: '', |
|||
replyTarget: { |
|||
type: '', |
|||
username: '', |
|||
replyId: '', |
|||
replyIndex: null, |
|||
commentId: '', |
|||
commentIndex: null |
|||
}, |
|||
replyPlaceholder: '输入您的回复...', |
|||
isInputFocused: false |
|||
}); |
|||
}, |
|||
|
|||
// 回复内容输入
|
|||
onReplyInput: function(e) { |
|||
this.setData({ |
|||
replyContent: e.detail.value |
|||
wx.showToast({ |
|||
title: '回复成功', |
|||
icon: 'success', |
|||
duration: 1500 |
|||
}); |
|||
}, |
|||
|
|||
// 提交回复
|
|||
submitReply: function(e) { |
|||
const index = e.currentTarget.dataset.index; |
|||
const replyContent = this.data.replyContent.trim(); |
|||
|
|||
if (!replyContent) { |
|||
wx.showToast({ |
|||
title: '请输入回复内容', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
const posts = this.data.posts; |
|||
const post = posts[index]; |
|||
// 回复评论的回复
|
|||
replyToCommentReply: function(content, target) { |
|||
const post = this.data.post; |
|||
const reply = post.replies[target.replyIndex]; |
|||
const originalComment = reply.comments[target.commentIndex]; |
|||
const commentId = `cc${originalComment.commentId}_${Date.now()}`; |
|||
|
|||
// 创建新回复
|
|||
const newReply = { |
|||
username: '当前用户', |
|||
const newComment = { |
|||
commentId: commentId, |
|||
username: this.data.currentUser, |
|||
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg', |
|||
toUsername: target.username, |
|||
time: '刚刚', |
|||
content: replyContent |
|||
content: content |
|||
}; |
|||
|
|||
post.replies.push(newReply); |
|||
post.replyCount += 1; |
|||
reply.comments.push(newComment); |
|||
|
|||
this.setData({ |
|||
posts: posts, |
|||
activeReplyIndex: -1, |
|||
replyContent: '' |
|||
post: post, |
|||
replyContent: '', |
|||
replyTarget: { |
|||
type: '', |
|||
username: '', |
|||
replyId: '', |
|||
replyIndex: null, |
|||
commentId: '', |
|||
commentIndex: null |
|||
}, |
|||
replyPlaceholder: '输入您的回复...', |
|||
isInputFocused: false |
|||
}); |
|||
|
|||
wx.showToast({ |
|||
title: '回复成功', |
|||
icon: 'success' |
|||
icon: 'success', |
|||
duration: 1500 |
|||
}); |
|||
}, |
|||
|
|||
// 切换显示全部回复
|
|||
toggleReplies: function(e) { |
|||
const index = e.currentTarget.dataset.index; |
|||
const posts = this.data.posts; |
|||
const post = posts[index]; |
|||
// 图片预览
|
|||
previewImage: function(e) { |
|||
const imgIndex = e.currentTarget.dataset.imgIndex; |
|||
const images = this.data.post.images; |
|||
|
|||
post.showAllReplies = !post.showAllReplies; |
|||
this.setData({ |
|||
showPreview: true, |
|||
previewImages: images, |
|||
previewIndex: imgIndex |
|||
}); |
|||
}, |
|||
|
|||
// 图片预览滑动
|
|||
onSwiperChange: function(e) { |
|||
this.setData({ |
|||
posts: posts |
|||
previewIndex: e.detail.current |
|||
}); |
|||
}, |
|||
|
|||
// 隐藏预览
|
|||
hidePreview: function() { |
|||
this.setData({ showPreview: false }); |
|||
}, |
|||
|
|||
// 下拉刷新
|
|||
onPullDownRefresh: function() { |
|||
if (this.data.post) { |
|||
this.loadPostDetail(this.data.post.id); |
|||
} |
|||
wx.stopPullDownRefresh(); |
|||
}, |
|||
|
|||
// 页面上拉触底
|
|||
onReachBottom: function() { |
|||
// 这里可以实现加载更多回复的逻辑
|
|||
console.log('加载更多回复'); |
|||
}, |
|||
|
|||
// 页面卸载
|
|||
onUnload: function() { |
|||
// 移除键盘高度监听
|
|||
wx.offKeyboardHeightChange(); |
|||
} |
|||
}); |
|||
@ -1,386 +1,454 @@ |
|||
.forum-page { |
|||
min-height: 100vh; |
|||
padding-bottom: 40rpx; |
|||
} |
|||
|
|||
/* 顶部栏 */ |
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx 30rpx 20rpx; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #eee; |
|||
position: sticky; |
|||
top: 0; |
|||
z-index: 10; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.add-btn { |
|||
background-color: #007AFF; |
|||
color: white; |
|||
font-size: 26rpx; |
|||
padding: 10rpx 20rpx; |
|||
line-height: normal; |
|||
border-radius: 30rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
.add-btn:after { |
|||
border: none; |
|||
} |
|||
|
|||
/* 帖子项 */ |
|||
.post-list { |
|||
padding: 20rpx 30rpx; |
|||
} |
|||
|
|||
.post-item { |
|||
background-color: white; |
|||
border-radius: 16rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.post-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.user-info { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.avatar { |
|||
width: 70rpx; |
|||
height: 70rpx; |
|||
border-radius: 50%; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.user-detail { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.username { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 6rpx; |
|||
} |
|||
|
|||
.post-time { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
|
|||
|
|||
/* 帖子内容 */ |
|||
.post-content { |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.post-title { |
|||
font-size: 34rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.post-desc { |
|||
font-size: 28rpx; |
|||
color: #555; |
|||
line-height: 1.6; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
/* 回复列表 */ |
|||
.reply-list { |
|||
margin-top: 24rpx; |
|||
} |
|||
|
|||
.reply-title { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
margin-bottom: 20rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.reply-item { |
|||
padding: 24rpx 0; |
|||
border-bottom: 1rpx solid #f8f8f8; |
|||
} |
|||
|
|||
.reply-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.reply-user { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.reply-avatar { |
|||
width: 56rpx; |
|||
height: 56rpx; |
|||
border-radius: 50%; |
|||
margin-right: 16rpx; |
|||
} |
|||
|
|||
.reply-user-info { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.reply-username { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.reply-time { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.reply-content { |
|||
font-size: 28rpx; |
|||
color: #444; |
|||
line-height: 1.5; |
|||
margin-left: 72rpx; |
|||
} |
|||
|
|||
.view-more-replies { |
|||
font-size: 26rpx; |
|||
color: #007AFF; |
|||
text-align: center; |
|||
padding: 20rpx 0; |
|||
margin-top: 10rpx; |
|||
} |
|||
|
|||
/* 回复输入区域 */ |
|||
.reply-input-container { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-top: 20rpx; |
|||
padding-top: 20rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.reply-input { |
|||
flex: 1; |
|||
background-color: #f8f8f8; |
|||
border-radius: 30rpx; |
|||
padding: 16rpx 24rpx; |
|||
font-size: 28rpx; |
|||
margin-right: 16rpx; |
|||
} |
|||
|
|||
.send-reply-btn { |
|||
background-color: #007AFF; |
|||
color: white; |
|||
font-size: 26rpx; |
|||
padding: 12rpx 24rpx; |
|||
line-height: normal; |
|||
border-radius: 30rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
.send-reply-btn:after { |
|||
border: none; |
|||
} |
|||
|
|||
/* 空状态 */ |
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 30rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.empty-image { |
|||
width: 300rpx; |
|||
height: 300rpx; |
|||
margin-bottom: 40rpx; |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 30rpx; |
|||
color: #999; |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
.empty-btn { |
|||
background-color: #007AFF; |
|||
color: white; |
|||
font-size: 30rpx; |
|||
padding: 20rpx 40rpx; |
|||
line-height: normal; |
|||
border-radius: 40rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
.empty-btn:after { |
|||
border: none; |
|||
} |
|||
|
|||
/* 弹窗样式 */ |
|||
.modal-overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 100; |
|||
} |
|||
|
|||
.modal-content { |
|||
background-color: white; |
|||
border-radius: 20rpx; |
|||
width: 85%; |
|||
max-height: 80vh; |
|||
overflow-y: auto; |
|||
padding: 40rpx; |
|||
} |
|||
|
|||
.modal-header { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 40rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.form-item { |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
.form-input { |
|||
background-color: #f8f8f8; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.form-textarea { |
|||
background-color: #f8f8f8; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
font-size: 28rpx; |
|||
height: 200rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.textarea-counter { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
text-align: right; |
|||
margin-top: 10rpx; |
|||
} |
|||
|
|||
.selected-tags { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.selected-tag { |
|||
font-size: 24rpx; |
|||
color: #007AFF; |
|||
background-color: #e6f2ff; |
|||
padding: 8rpx 20rpx; |
|||
border-radius: 20rpx; |
|||
margin-right: 12rpx; |
|||
margin-bottom: 12rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.remove-tag { |
|||
font-size: 30rpx; |
|||
margin-left: 8rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.modal-buttons { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
margin-top: 40rpx; |
|||
} |
|||
|
|||
.modal-btn { |
|||
flex: 1; |
|||
font-size: 30rpx; |
|||
border-radius: 50px; |
|||
margin: 0 10rpx; |
|||
} |
|||
|
|||
.modal-btn::after{ |
|||
border: none; |
|||
} |
|||
|
|||
.modal-btn.cancel { |
|||
background-color: #f0f0f0; |
|||
color: #666; |
|||
} |
|||
|
|||
.modal-btn.submit { |
|||
background-color: #007AFF; |
|||
color: white; |
|||
} |
|||
|
|||
.modal-btn.submit[disabled] { |
|||
background-color: #cccccc; |
|||
color: white; |
|||
} |
|||
|
|||
/* 加载提示 */ |
|||
.loading { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60rpx 0; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.loading-icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
margin-bottom: 20rpx; |
|||
animation: rotate 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes rotate { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
min-height: 100vh; |
|||
background-color: #f8f8f8; |
|||
position: relative; |
|||
} |
|||
|
|||
|
|||
/* 帖子详情容器 */ |
|||
.post-detail-container { |
|||
height: 100vh; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
/* 帖子主体 */ |
|||
.post-main { |
|||
background-color: white; |
|||
padding: 40rpx 30rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.post-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.user-info { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.avatar { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
border-radius: 50%; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.user-detail { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.username { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.post-time { |
|||
font-size: 26rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.post-status .solved-badge { |
|||
background-color: #07c160; |
|||
color: white; |
|||
font-size: 24rpx; |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
/* 帖子内容 */ |
|||
.post-content { |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.post-title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 24rpx; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.post-desc { |
|||
font-size: 30rpx; |
|||
color: #555; |
|||
line-height: 1.6; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
/* 帖子图片 */ |
|||
.post-images { |
|||
margin: 30rpx 0; |
|||
} |
|||
|
|||
.images-container { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.post-image { |
|||
width: 220rpx; |
|||
height: 220rpx; |
|||
border-radius: 12rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
/* 回复区域 */ |
|||
.replies-section { |
|||
background-color: white; |
|||
border-radius: 20rpx 20rpx 0 0; |
|||
padding: 40rpx 30rpx; |
|||
margin: 0; |
|||
} |
|||
|
|||
.section-title { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 30rpx; |
|||
padding-bottom: 20rpx; |
|||
border-bottom: 2rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.title-text { |
|||
font-size: 34rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.reply-count { |
|||
font-size: 28rpx; |
|||
color: #07c160; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
/* 回复列表 */ |
|||
.reply-list { |
|||
margin-top: 10rpx; |
|||
} |
|||
|
|||
.reply-item { |
|||
padding: 30rpx 0; |
|||
border-bottom: 1rpx solid #f8f8f8; |
|||
} |
|||
|
|||
.reply-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.reply-user { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.reply-avatar { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
border-radius: 50%; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.reply-user-info { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.reply-username { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 6rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.reply-time { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.reply-content { |
|||
font-size: 30rpx; |
|||
color: #444; |
|||
line-height: 1.5; |
|||
margin-left: 84rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
/* 回复操作 */ |
|||
.reply-actions { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-left: 84rpx; |
|||
margin-top: 20rpx; |
|||
gap: 40rpx; |
|||
} |
|||
|
|||
.reply-action { |
|||
display: flex; |
|||
align-items: center; |
|||
color: #999; |
|||
font-size: 24rpx; |
|||
padding: 4rpx 8rpx; |
|||
} |
|||
|
|||
.reply-action-icon { |
|||
width: 28rpx; |
|||
height: 28rpx; |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.reply-action-text { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
/* 评论列表 */ |
|||
.comment-list { |
|||
margin-left: 84rpx; |
|||
margin-top: 20rpx; |
|||
background-color: #fafafa; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.comment-item { |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
.comment-item:not(:last-child) { |
|||
border-bottom: 1rpx solid #eee; |
|||
} |
|||
|
|||
.comment-user { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.comment-avatar { |
|||
width: 48rpx; |
|||
height: 48rpx; |
|||
border-radius: 50%; |
|||
margin-right: 15rpx; |
|||
} |
|||
|
|||
.comment-user-info { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.comment-username { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.comment-time { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.comment-content { |
|||
font-size: 28rpx; |
|||
color: #555; |
|||
line-height: 1.4; |
|||
margin-left: 63rpx; |
|||
} |
|||
|
|||
.comment-to { |
|||
color: #07c160; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
/* 评论操作 */ |
|||
.comment-actions { |
|||
margin-left: 63rpx; |
|||
margin-top: 12rpx; |
|||
} |
|||
|
|||
.comment-action { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
padding: 4rpx 8rpx; |
|||
} |
|||
|
|||
/* 空回复状态 */ |
|||
.empty-replies { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
text-align: center; |
|||
} |
|||
|
|||
|
|||
|
|||
.empty-reply-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.bottom-placeholder { |
|||
height: 160rpx; |
|||
} |
|||
|
|||
/* 底部输入栏 */ |
|||
.bottom-input-container { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background-color: white; |
|||
border-top: 1rpx solid #eee; |
|||
padding: 20rpx 30rpx; |
|||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
z-index: 100; |
|||
transition: transform 0.3s ease; |
|||
} |
|||
|
|||
.input-wrapper { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.reply-input { |
|||
flex: 1; |
|||
background-color: #f8f8f8; |
|||
border-radius: 40rpx; |
|||
padding: 10rpx 30rpx; |
|||
font-size: 30rpx; |
|||
} |
|||
|
|||
.send-btn { |
|||
background-color: #07c160; |
|||
color: white; |
|||
font-size: 28rpx; |
|||
padding: 20rpx 40rpx; |
|||
line-height: normal; |
|||
border-radius: 40rpx; |
|||
margin: 0; |
|||
min-width: 120rpx; |
|||
transition: background-color 0.3s; |
|||
} |
|||
|
|||
.send-btn[disabled] { |
|||
background-color: #cccccc; |
|||
color: white; |
|||
} |
|||
|
|||
/* 回复对象显示 */ |
|||
.reply-target { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background-color: #f0f0f0; |
|||
border-radius: 20rpx; |
|||
padding: 16rpx 24rpx; |
|||
margin-top: 15rpx; |
|||
animation: slideIn 0.3s ease; |
|||
} |
|||
|
|||
@keyframes slideIn { |
|||
from { |
|||
opacity: 0; |
|||
transform: translateY(-10rpx); |
|||
} |
|||
to { |
|||
opacity: 1; |
|||
transform: translateY(0); |
|||
} |
|||
} |
|||
|
|||
.target-text { |
|||
font-size: 26rpx; |
|||
color: #07c160; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.clear-target { |
|||
font-size: 36rpx; |
|||
color: #999; |
|||
padding: 0 10rpx 4rpx 20rpx; |
|||
} |
|||
|
|||
/* 图片预览 */ |
|||
.preview-overlay { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.95); |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 1000; |
|||
} |
|||
|
|||
.preview-swiper { |
|||
width: 100%; |
|||
height: 70vh; |
|||
} |
|||
|
|||
.preview-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.preview-indicator { |
|||
position: absolute; |
|||
bottom: 60rpx; |
|||
color: white; |
|||
font-size: 28rpx; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
padding: 10rpx 24rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
/* 加载提示 */ |
|||
.loading { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60rpx 0; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.loading-icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
margin-bottom: 20rpx; |
|||
animation: rotate 1s linear infinite; |
|||
} |
|||
|
|||
@keyframes rotate { |
|||
0% { transform: rotate(0deg); } |
|||
100% { transform: rotate(360deg); } |
|||
} |
|||
|
|||
/* 移除按钮边框 */ |
|||
button::after { |
|||
border: none; |
|||
} |
|||
|
|||
/* 滚动条样式 */ |
|||
::-webkit-scrollbar { |
|||
width: 0; |
|||
height: 0; |
|||
color: transparent; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue