You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
10 KiB
346 lines
10 KiB
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) {
|
|
// 可以在这里处理页面滚动时的效果
|
|
}
|
|
});
|