Browse Source

在线问答列表,详情页

master
ZhaoYang 3 weeks ago
parent
commit
9bb0f42a1a
  1. 3
      app.json
  2. 9
      pages/home/home.js
  3. 9
      pages/home/home.wxml
  4. 12
      pages/home/home.wxss
  5. BIN
      pagesB/images/hf.png
  6. 346
      pagesB/pages/forumlist/forumlist.js
  7. 3
      pagesB/pages/forumlist/forumlist.json
  8. 202
      pagesB/pages/forumlist/forumlist.wxml
  9. 442
      pagesB/pages/forumlist/forumlist.wxss
  10. 482
      pagesB/pages/onlineAsk/onlineAsk.js
  11. 192
      pagesB/pages/onlineAsk/onlineAsk.wxml
  12. 468
      pagesB/pages/onlineAsk/onlineAsk.wxss

3
app.json

@ -30,7 +30,8 @@
"pages/onlineAsk/onlineAsk",
"pages/administrativeDivision/administrativeDivision",
"pages/wzDetails/wzDetails",
"pages/spDetails/spDetails"
"pages/spDetails/spDetails",
"pages/forumlist/forumlist"
],
"independent": true
}

9
pages/home/home.js

@ -107,7 +107,14 @@ Page({
})
},
// 用户提问
//问答列表
bindwdlist(){
wx.navigateTo({
url: '/pagesB/pages/forumlist/forumlist',
})
},
// 问答详情
bindTw() {
wx.navigateTo({
url: '/pagesB/pages/onlineAsk/onlineAsk',

9
pages/home/home.wxml

@ -106,11 +106,11 @@
</view>
<!-- 用户提问板块 -->
<view class="user-question-section" bind:tap="bindTw">
<view class="user-question-section">
<!-- 标题区域 -->
<view class="question-header">
<view class="question-header" bind:tap="bindwdlist">
<view class="question-title">
<view class="title-text">用户提问</view>
<view class="title-text">在线问答</view>
<view class="title-sub">看看其他养殖户遇到的问题</view>
</view>
<view class="view-all" bindtap="viewAllQuestions">
@ -120,10 +120,9 @@
<!-- 问题列表 -->
<view class="question-card placeholder">
<view class="question-card placeholder" bind:tap="bindTw">
<view class="question-meta">
<view class="question-tag">
<text class="tag-icon">【羊】</text>
<text class="tag-text">发烧、流鼻涕、越来越瘦是什么原因?应该怎么治疗?</text>
</view>
</view>

12
pages/home/home.wxss

@ -489,17 +489,6 @@
gap: 12rpx;
}
.tag-icon {
font-size: 28rpx;
font-weight: bold;
color: #60C0B9;
background: rgba(96, 192, 185, 0.1);
padding: 6rpx 16rpx;
border-radius: 12rpx;
flex-shrink: 0;
line-height: 1.4;
}
.tag-text {
font-size: 30rpx;
color: #333;
@ -512,7 +501,6 @@
}
/* 用户信息 */
.question-info {
display: flex;

BIN
pagesB/images/hf.png

After

Width: 200  |  Height: 200  |  Size: 3.8 KiB

346
pagesB/pages/forumlist/forumlist.js

@ -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) {
// 可以在这里处理页面滚动时的效果
}
});

3
pagesB/pages/forumlist/forumlist.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

202
pagesB/pages/forumlist/forumlist.wxml

@ -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>

442
pagesB/pages/forumlist/forumlist.wxss

@ -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;
}

482
pagesB/pages/onlineAsk/onlineAsk.js

@ -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 = [
{
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,
showAllReplies: false,
replies: [
{
replyId: 'r1_1',
username: '前端开发工程师',
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
time: '1小时前',
content: '可以使用wx.chooseImage选择图片,然后使用wx.uploadFile上传到服务器。预览功能可以使用wx.previewImage实现。'
},
content: '可以使用wx.chooseImage选择图片,然后使用wx.uploadFile上传到服务器。预览功能可以使用wx.previewImage实现。',
likeCount: 3,
liked: false,
comments: [
{
username: '小程序开发者',
commentId: 'c1_1',
username: '学习者',
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
time: '45分钟前',
content: '建议将图片大小限制在2MB以内,支持JPG、PNG格式。可以使用云开发存储功能简化上传流程。'
toUsername: '前端开发工程师',
time: '30分钟前',
content: '感谢分享,请问有具体的代码示例吗?'
}
]
},
{
id: 2,
username: '产品经理小王',
replyId: 'r1_2',
username: '小程序开发者',
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
time: '昨天 14:30',
title: '如何设计一个用户友好的注册流程?',
content: '我们正在设计一个新产品的注册流程,希望既保证安全性又尽量简化步骤。大家有什么好的设计建议或参考案例吗?',
tags: ['产品设计', '用户体验', '注册流程'],
likeCount: 8,
replyCount: 3,
viewCount: 89,
time: '45分钟前',
content: '建议将图片大小限制在2MB以内,支持JPG、PNG格式。可以使用云开发存储功能简化上传流程。',
likeCount: 2,
liked: true,
solved: true,
showAllReplies: false,
replies: [
{
username: 'UX设计师',
avatar: 'https://img.yzcdn.cn/vant/cat.jpeg',
time: '昨天 16:45',
content: '建议采用手机号验证码注册,配合第三方登录选项。关键是将必填信息减到最少,其他信息可以后续引导补充。'
comments: []
}
]
},
{
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,这对前后端协作非常有帮助。'
}
]
}
];
};
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
});
},
// 显示回复输入框
showReplyInput: function(e) {
const index = e.currentTarget.dataset.index;
// 滚动到最新回复
setTimeout(() => {
this.setData({
activeReplyIndex: index,
replyContent: ''
scrollToId: `reply-${replyId}`
});
}, 800);
},
// 回复内容输入
onReplyInput: function(e) {
// 回复评论
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);
this.setData({
replyContent: e.detail.value
});
post: post,
replyContent: '',
replyTarget: {
type: '',
username: '',
replyId: '',
replyIndex: null,
commentId: '',
commentIndex: null
},
replyPlaceholder: '输入您的回复...',
isInputFocused: false
});
// 提交回复
submitReply: function(e) {
const index = e.currentTarget.dataset.index;
const replyContent = this.data.replyContent.trim();
if (!replyContent) {
wx.showToast({
title: '请输入回复内容',
icon: 'none'
title: '回复成功',
icon: 'success',
duration: 1500
});
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();
}
});

192
pagesB/pages/onlineAsk/onlineAsk.wxml

@ -1,40 +1,65 @@
<view class="forum-page">
<!-- 顶部栏 -->
<view class="header">
<view class="title">问答论坛</view>
<button class="add-btn" bindtap="showPostModal">发帖提问</button>
</view>
<!-- 帖子列表 -->
<view class="post-list" wx:if="{{posts.length > 0}}">
<block wx:for="{{posts}}" wx:key="id">
<view class="post-item" data-index="{{index}}">
<!-- 帖子详情 -->
<scroll-view
class="post-detail-container"
scroll-y
scroll-with-animation
scroll-into-view="{{scrollToId}}"
bindscroll="onScroll"
>
<!-- 帖子主体 -->
<view class="post-main" id="post-main">
<!-- 帖子头部 -->
<view class="post-header">
<view class="user-info">
<image class="avatar" src="{{item.avatar}}" mode="aspectFill"></image>
<image class="avatar" src="{{post.avatar}}" mode="aspectFill"></image>
<view class="user-detail">
<view class="username">{{item.username}}</view>
<view class="post-time">{{item.time}}</view>
<view class="username">{{post.username}}</view>
<view class="post-time">{{post.time}}</view>
</view>
</view>
<view class="post-status" wx:if="{{post.solved}}">
<text class="solved-badge">已解决</text>
</view>
</view>
<!-- 帖子内容 -->
<view class="post-content">
<view class="post-title">{{item.title}}</view>
<view class="post-desc">{{item.content}}</view>
<view class="post-title">{{post.title}}</view>
<view class="post-desc">{{post.content}}</view>
<!-- 帖子图片 -->
<view class="post-images" wx:if="{{post.images && post.images.length > 0}}">
<view class="images-container">
<block wx:for="{{post.images}}" wx:for-item="image" wx:for-index="imgIndex" wx:key="index">
<image
class="post-image"
src="{{image}}"
mode="aspectFill"
data-img-index="{{imgIndex}}"
bindtap="previewImage"
></image>
</block>
</view>
</view>
</view>
</view>
<!-- 回复列表 -->
<view class="reply-list" wx:if="{{item.replies.length > 0}}">
<view class="reply-title">共{{item.replies.length}}条回复</view>
<block wx:for="{{item.replies}}" wx:key="index">
<view class="reply-item" wx:if="{{index < 2 || item.showAllReplies}}">
<view class="replies-section" id="replies-section">
<view class="section-title">
<text class="title-text">全部回复</text>
<text class="reply-count">{{post.replyCount}}条</text>
</view>
<view class="reply-list" wx:if="{{post.replies && post.replies.length > 0}}">
<block wx:for="{{post.replies}}" wx:key="replyId" wx:for-index="replyIndex">
<view class="reply-item" id="reply-{{item.replyId}}">
<view class="reply-user">
<image class="reply-avatar" src="{{item.avatar}}" mode="aspectFill"></image>
<view class="reply-user-info">
@ -43,83 +68,100 @@
</view>
</view>
<view class="reply-content">{{item.content}}</view>
</view>
</block>
<!-- 查看更多回复 -->
<view class="view-more-replies" wx:if="{{item.replies.length > 2 && !item.showAllReplies}}" bindtap="toggleReplies" data-index="{{index}}">
查看全部{{item.replies.length}}条回复
<!-- 回复操作 -->
<view class="reply-actions">
<view class="reply-action" bindtap="replyToUser" data-type="reply" data-index="{{replyIndex}}" data-username="{{item.username}}">
<image class="reply-action-icon" src="/pagesB/images/hf.png" mode="aspectFit"></image>
<text class="reply-action-text">回复</text>
</view>
</view>
<!-- 收起回复 -->
<view class="view-more-replies" wx:if="{{item.replies.length > 2 && item.showAllReplies}}" bindtap="toggleReplies" data-index="{{index}}">
收起回复
<!-- 评论列表 -->
<view class="comment-list" wx:if="{{item.comments && item.comments.length > 0}}">
<block wx:for="{{item.comments}}" wx:key="commentId">
<view class="comment-item">
<view class="comment-user">
<image class="comment-avatar" src="{{item.avatar}}" mode="aspectFill"></image>
<view class="comment-user-info">
<view class="comment-username">{{item.username}}</view>
<view class="comment-time">{{item.time}}</view>
</view>
</view>
<view class="comment-content">
<text class="comment-to" wx:if="{{item.toUsername}}">@{{item.toUsername}} </text>
{{item.content}}
</view>
<!-- 回复输入区域(当前激活的帖子) -->
<view class="reply-input-container" wx:if="{{activeReplyIndex === index}}">
<input
class="reply-input"
placeholder="输入您的回复..."
value="{{replyContent}}"
bindinput="onReplyInput"
focus="{{activeReplyIndex === index}}"
/>
<button class="send-reply-btn" bindtap="submitReply" data-index="{{index}}">发送</button>
<!-- 评论操作 -->
<view class="comment-actions">
<view class="comment-action" bindtap="replyToUser"
data-type="comment"
data-reply-index="{{replyIndex}}"
data-comment-index="{{index}}"
data-username="{{item.username}}">
<text>回复</text>
</view>
</view>
</view>
</block>
</view>
</view>
</block>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:else>
<image class="empty-image" src="/images/empty.png" mode="aspectFit"></image>
<view class="empty-text">暂无帖子,快来发布第一个问题吧!</view>
<button class="empty-btn" bindtap="showPostModal">发布问题</button>
<!-- 空回复状态 -->
<view class="empty-replies" wx:else>
<view class="empty-reply-text">还没有回复,快来第一个回复吧!</view>
</view>
</view>
<!-- 发帖弹窗 -->
<view class="modal-overlay" wx:if="{{showPostModal}}" bindtap="hidePostModal">
<view class="modal-content" catchtap="preventTap">
<view class="modal-header">发布新问题</view>
<!-- 底部占位 -->
<view class="bottom-placeholder" style="height: 160rpx;"></view>
</scroll-view>
<view class="form-item">
<!-- 底部回复输入栏 -->
<view class="bottom-input-container" style="transform: translateY({{inputTransformY}});">
<view class="input-wrapper">
<input
class="form-input"
placeholder="请输入问题标题"
value="{{postTitle}}"
bindinput="onPostTitleInput"
/>
</view>
<view class="form-item">
<textarea
class="form-textarea"
placeholder="请详细描述您的问题..."
value="{{postContent}}"
bindinput="onPostContentInput"
class="reply-input"
placeholder="{{replyPlaceholder}}"
value="{{replyContent}}"
bindinput="onReplyInput"
bindfocus="onInputFocus"
bindblur="onInputBlur"
bindconfirm="submitReply"
adjust-position="{{false}}"
focus="{{isInputFocused}}"
maxlength="500"
/>
<view class="textarea-counter">{{postContent.length}}/500</view>
<button
class="send-btn"
bindtap="submitReply"
disabled="{{!replyContent}}"
>
{{isSubmitting ? '发送中' : '发送'}}
</button>
</view>
<view class="form-item">
<view class="selected-tags" wx:if="{{selectedTags.length > 0}}">
<block wx:for="{{selectedTags}}" wx:key="index">
<view class="selected-tag">
{{item}}
<text class="remove-tag" data-index="{{index}}" bindtap="removeTag">×</text>
</view>
</block>
<!-- 回复对象显示 -->
<view class="reply-target" wx:if="{{replyTarget.username}}">
<text class="target-text">回复 {{replyTarget.username}}</text>
<text class="clear-target" bindtap="clearReplyTarget">×</text>
</view>
</view>
<view class="modal-buttons">
<button class="modal-btn cancel" bindtap="hidePostModal">取消</button>
<button class="modal-btn submit" bindtap="submitPost" disabled="{{!postTitle || !postContent}}">发布</button>
</view>
</view>
<!-- 图片预览模态框 -->
<view class="preview-overlay" wx:if="{{showPreview}}" catchtap="hidePreview">
<swiper class="preview-swiper" current="{{previewIndex}}" bindchange="onSwiperChange">
<block wx:for="{{previewImages}}" wx:key="index">
<swiper-item>
<image class="preview-image" src="{{item}}" mode="aspectFit"></image>
</swiper-item>
</block>
</swiper>
<view class="preview-indicator">{{previewIndex + 1}}/{{previewImages.length}}</view>
</view>
<!-- 加载提示 -->

468
pagesB/pages/onlineAsk/onlineAsk.wxss

@ -1,59 +1,28 @@
.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;
background-color: #f8f8f8;
position: relative;
}
.add-btn:after {
border: none;
}
/* 帖子项 */
.post-list {
padding: 20rpx 30rpx;
/* 帖子详情容器 */
.post-detail-container {
height: 100vh;
box-sizing: border-box;
}
.post-item {
/* 帖子主体 */
.post-main {
background-color: white;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
.post-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
margin-bottom: 30rpx;
}
.user-info {
@ -62,8 +31,8 @@
}
.avatar {
width: 70rpx;
height: 70rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
}
@ -74,56 +43,102 @@
}
.username {
font-size: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 6rpx;
margin-bottom: 8rpx;
}
.post-time {
font-size: 24rpx;
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: 24rpx;
margin-bottom: 30rpx;
}
.post-title {
font-size: 34rpx;
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
margin-bottom: 24rpx;
line-height: 1.4;
}
.post-desc {
font-size: 28rpx;
font-size: 30rpx;
color: #555;
line-height: 1.6;
margin-bottom: 20rpx;
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;
}
/* 回复列表 */
.reply-list {
margin-top: 24rpx;
/* 回复区域 */
.replies-section {
background-color: white;
border-radius: 20rpx 20rpx 0 0;
padding: 40rpx 30rpx;
margin: 0;
}
.reply-title {
font-size: 26rpx;
color: #666;
margin-bottom: 20rpx;
.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: 24rpx 0;
padding: 30rpx 0;
border-bottom: 1rpx solid #f8f8f8;
}
@ -134,14 +149,14 @@
.reply-user {
display: flex;
align-items: center;
margin-bottom: 16rpx;
margin-bottom: 20rpx;
}
.reply-avatar {
width: 56rpx;
height: 56rpx;
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
margin-right: 20rpx;
}
.reply-user-info {
@ -150,212 +165,257 @@
}
.reply-username {
font-size: 26rpx;
font-size: 28rpx;
color: #333;
margin-bottom: 4rpx;
margin-bottom: 6rpx;
font-weight: 500;
}
.reply-time {
font-size: 22rpx;
font-size: 24rpx;
color: #999;
}
.reply-content {
font-size: 28rpx;
font-size: 30rpx;
color: #444;
line-height: 1.5;
margin-left: 72rpx;
margin-left: 84rpx;
margin-bottom: 20rpx;
}
.view-more-replies {
font-size: 26rpx;
color: #007AFF;
text-align: center;
padding: 20rpx 0;
margin-top: 10rpx;
/* 回复操作 */
.reply-actions {
display: flex;
align-items: center;
margin-left: 84rpx;
margin-top: 20rpx;
gap: 40rpx;
}
/* 回复输入区域 */
.reply-input-container {
.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;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
background-color: #fafafa;
border-radius: 12rpx;
padding: 20rpx;
}
.reply-input {
flex: 1;
background-color: #f8f8f8;
border-radius: 30rpx;
padding: 16rpx 24rpx;
font-size: 28rpx;
margin-right: 16rpx;
.comment-item {
padding: 20rpx 0;
}
.send-reply-btn {
background-color: #007AFF;
color: white;
font-size: 26rpx;
padding: 12rpx 24rpx;
line-height: normal;
border-radius: 30rpx;
margin: 0;
.comment-item:not(:last-child) {
border-bottom: 1rpx solid #eee;
}
.send-reply-btn:after {
border: none;
.comment-user {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
/* 空状态 */
.empty-state {
.comment-avatar {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 15rpx;
}
.comment-user-info {
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;
.comment-username {
font-size: 26rpx;
color: #666;
margin-bottom: 4rpx;
}
.empty-text {
font-size: 30rpx;
.comment-time {
font-size: 22rpx;
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;
.comment-content {
font-size: 28rpx;
color: #555;
line-height: 1.4;
margin-left: 63rpx;
}
.empty-btn:after {
border: none;
.comment-to {
color: #07c160;
font-weight: 500;
}
/* 弹窗样式 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
/* 评论操作 */
.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;
z-index: 100;
padding: 100rpx 0;
text-align: center;
}
.modal-content {
background-color: white;
border-radius: 20rpx;
width: 85%;
max-height: 80vh;
overflow-y: auto;
padding: 40rpx;
.empty-reply-text {
font-size: 28rpx;
color: #999;
}
.modal-header {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 40rpx;
text-align: center;
.bottom-placeholder {
height: 160rpx;
}
.form-item {
margin-bottom: 40rpx;
/* 底部输入栏 */
.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;
}
.form-input {
background-color: #f8f8f8;
border-radius: 12rpx;
padding: 24rpx;
font-size: 28rpx;
.input-wrapper {
display: flex;
align-items: center;
gap: 20rpx;
}
.form-textarea {
.reply-input {
flex: 1;
background-color: #f8f8f8;
border-radius: 12rpx;
padding: 24rpx;
font-size: 28rpx;
height: 200rpx;
width: 100%;
box-sizing: border-box;
border-radius: 40rpx;
padding: 10rpx 30rpx;
font-size: 30rpx;
}
.textarea-counter {
font-size: 24rpx;
color: #999;
text-align: right;
margin-top: 10rpx;
.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;
}
.selected-tags {
display: flex;
flex-wrap: wrap;
.send-btn[disabled] {
background-color: #cccccc;
color: white;
}
.selected-tag {
font-size: 24rpx;
color: #007AFF;
background-color: #e6f2ff;
padding: 8rpx 20rpx;
border-radius: 20rpx;
margin-right: 12rpx;
margin-bottom: 12rpx;
/* 回复对象显示 */
.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;
}
.remove-tag {
font-size: 30rpx;
margin-left: 8rpx;
color: #999;
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal-buttons {
display: flex;
justify-content: space-between;
margin-top: 40rpx;
.target-text {
font-size: 26rpx;
color: #07c160;
font-weight: 500;
}
.modal-btn {
flex: 1;
font-size: 30rpx;
border-radius: 50px;
margin: 0 10rpx;
.clear-target {
font-size: 36rpx;
color: #999;
padding: 0 10rpx 4rpx 20rpx;
}
.modal-btn::after{
border: none;
/* 图片预览 */
.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;
}
.modal-btn.cancel {
background-color: #f0f0f0;
color: #666;
.preview-swiper {
width: 100%;
height: 70vh;
}
.modal-btn.submit {
background-color: #007AFF;
color: white;
.preview-image {
width: 100%;
height: 100%;
}
.modal-btn.submit[disabled] {
background-color: #cccccc;
.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;
}
/* 加载提示 */
@ -377,10 +437,18 @@
}
@keyframes rotate {
0% {
transform: rotate(0deg);
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
100% {
transform: rotate(360deg);
/* 移除按钮边框 */
button::after {
border: none;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
Loading…
Cancel
Save