16 changed files with 1843 additions and 272 deletions
Split View
Diff Options
-
BINassets/images/like_active.png
-
176lib/controller/home/timeline_controller.dart
-
224lib/controller/home/timeline_info_controller.dart
-
1lib/generated/assets.dart
-
149lib/model/home/post_comment_data.dart
-
103lib/model/home/post_data.dart
-
18lib/network/api_urls.dart
-
35lib/network/home_api.dart
-
214lib/network/home_api.g.dart
-
24lib/pages/home/all_timeline.dart
-
340lib/pages/home/send_timeline.dart
-
654lib/pages/home/timeline_info.dart
-
156lib/pages/home/timeline_item.dart
-
11lib/pages/home/timeline_page.dart
-
4lib/pages/main/main_page.dart
-
6pubspec.yaml
@ -0,0 +1,176 @@ |
|||
import 'package:dating_touchme_app/model/home/marriage_data.dart'; |
|||
import 'package:dating_touchme_app/network/home_api.dart'; |
|||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; |
|||
import 'package:get/get.dart'; |
|||
|
|||
import '../../model/home/post_data.dart'; |
|||
|
|||
class TimelineController extends GetxController { |
|||
// 推荐列表数据 |
|||
final recommendFeed = <MarriageData>[].obs; |
|||
// 同城列表数据 |
|||
final nearbyFeed = <MarriageData>[].obs; |
|||
|
|||
// 推荐列表的加载状态和分页信息 |
|||
final recommendIsLoading = false.obs; |
|||
final recommendPage = 1.obs; |
|||
final recommendHasMore = true.obs; |
|||
|
|||
// 同城列表的加载状态和分页信息 |
|||
final nearbyIsLoading = false.obs; |
|||
final nearbyPage = 1.obs; |
|||
final nearbyHasMore = true.obs; |
|||
|
|||
// 当前标签页索引 |
|||
final selectedTabIndex = 0.obs; |
|||
|
|||
final topTab = 0.obs; |
|||
|
|||
final timelineTab = 0.obs; |
|||
|
|||
// 分页大小 |
|||
final pageSize = 10; |
|||
|
|||
// 从GetX依赖注入中获取HomeApi实例 |
|||
late final HomeApi _homeApi; |
|||
|
|||
final page = 1.obs; |
|||
final size = 10.obs; |
|||
|
|||
final hasMore = true.obs; |
|||
|
|||
final postList = <Records>[].obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
// 从全局依赖中获取HomeApi |
|||
_homeApi = Get.find<HomeApi>(); |
|||
loadPostList(); |
|||
} |
|||
|
|||
loadPostList() async { |
|||
if (recommendIsLoading.value || !recommendHasMore.value) return; |
|||
try{ |
|||
recommendIsLoading.value = true; |
|||
final response = await _homeApi.userPagePost( |
|||
pageNum: page.value, |
|||
pageSize: size.value, |
|||
); |
|||
if (response.data.isSuccess && response.data.data != null) { |
|||
final data = response.data.data?.records ?? []; |
|||
|
|||
postList.addAll(data.toList()); |
|||
if((data.length ?? 0) == size.value){ |
|||
hasMore.value = true; |
|||
} else { |
|||
hasMore.value = false; |
|||
} |
|||
} else { |
|||
|
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message ?? '获取数据失败'); |
|||
} |
|||
} catch(e) { |
|||
print('帖子列表获取失败: $e'); |
|||
SmartDialog.showToast('帖子列表获取失败'); |
|||
rethrow; |
|||
} finally { |
|||
|
|||
recommendIsLoading.value = false; |
|||
} |
|||
} |
|||
|
|||
/// 设置当前标签页 |
|||
void setSelectedTabIndex(int index) { |
|||
print('Setting selected tab index to: $index'); |
|||
selectedTabIndex.value = index; |
|||
// 确保UI能够更新 |
|||
update(); |
|||
} |
|||
|
|||
|
|||
void setTopTab(int index) { |
|||
print('Setting selected tab index to: $index'); |
|||
topTab.value = index; |
|||
// 确保UI能够更新 |
|||
update(); |
|||
} |
|||
|
|||
void setTimelineTab(int index) { |
|||
print('Setting selected tab index to: $index'); |
|||
timelineTab.value = index; |
|||
// 确保UI能够更新 |
|||
update(); |
|||
} |
|||
|
|||
/// 获取当前标签页的列表数据 |
|||
List<MarriageData> getFeedListByTab(int tabIndex) { |
|||
return tabIndex == 0 ? List.from(recommendFeed) : List.from(nearbyFeed); |
|||
} |
|||
|
|||
/// 私有方法:获取婚姻数据(统一的数据获取逻辑) |
|||
/// 返回包含records(数据列表)、current(当前页)、pages(总页数)的Map |
|||
Future<Map<String, dynamic>> _fetchMarriageData({ |
|||
required int pageNum, |
|||
required int type, |
|||
}) async { |
|||
try { |
|||
print('_fetchMarriageData - pageNum: $pageNum, pageSize: $pageSize, type: $type'); |
|||
// 调用API获取数据 |
|||
var response = await _homeApi.getMarriageList( |
|||
pageNum: pageNum, |
|||
pageSize: pageSize, |
|||
type: type, |
|||
); |
|||
|
|||
if (response.data.isSuccess) { |
|||
final paginatedData = response.data.data; |
|||
|
|||
// 检查data是否为空 |
|||
if (paginatedData == null) { |
|||
return { |
|||
'records': <MarriageData>[], |
|||
'current': pageNum, |
|||
'pages': 1, |
|||
'total': 0, |
|||
'size': pageSize, |
|||
}; |
|||
} |
|||
|
|||
// data 是 PaginatedResponse<dynamic>,直接使用其属性 |
|||
// records 中的每个项是 dynamic,需要转换为 MarriageData |
|||
final allRecords = paginatedData.records |
|||
.map((item) => MarriageData.fromJson(item as Map<String, dynamic>)) |
|||
.toList(); |
|||
|
|||
// 过滤掉直播类型的项 |
|||
final records = allRecords.where((item) => !item.isLive).toList(); |
|||
|
|||
print('_fetchMarriageData 返回 - 请求页码: $pageNum, 返回当前页: ${paginatedData.current}, 总页数: ${paginatedData.pages}, 原始记录数: ${allRecords.length}, 过滤后记录数: ${records.length}'); |
|||
|
|||
return { |
|||
'records': records, |
|||
'current': paginatedData.current, |
|||
'pages': paginatedData.pages, |
|||
'total': paginatedData.total, |
|||
'size': paginatedData.size, |
|||
}; |
|||
} else { |
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message); |
|||
} |
|||
} catch (e) { |
|||
// 向上抛出异常,让调用方处理 |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 私有方法:统一的错误处理 |
|||
void _handleError(String logMessage, dynamic error, String toastMessage) { |
|||
// 打印错误日志 |
|||
print('$logMessage: $error'); |
|||
// 显示错误提示 |
|||
SmartDialog.showToast(toastMessage); |
|||
} |
|||
} |
|||
@ -0,0 +1,224 @@ |
|||
import 'package:dating_touchme_app/config/emoji_config.dart'; |
|||
import 'package:dating_touchme_app/model/home/post_data.dart'; |
|||
import 'package:dating_touchme_app/network/home_api.dart'; |
|||
import 'package:easy_refresh/easy_refresh.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; |
|||
import 'package:get/get.dart'; |
|||
import 'package:dating_touchme_app/model/home/post_comment_data.dart' as pcd; |
|||
|
|||
class TimelineInfoController extends GetxController { |
|||
final String id; |
|||
TimelineInfoController({required this.id}); |
|||
|
|||
final showInput = false.obs; |
|||
|
|||
late final HomeApi _homeApi; |
|||
|
|||
final item = Records().obs; |
|||
|
|||
final imgList = <String>[].obs; |
|||
|
|||
|
|||
final commentList = <pcd.Records>[].obs; |
|||
|
|||
final page = 1.obs; |
|||
final size = 10.obs; |
|||
final parentId = "0".obs; |
|||
|
|||
late final EasyRefreshController listRefreshController; |
|||
|
|||
|
|||
final message = ''.obs; |
|||
final messageController = TextEditingController().obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
listRefreshController = EasyRefreshController( |
|||
controlFinishRefresh: true, |
|||
controlFinishLoad: true, |
|||
); |
|||
// 从全局依赖中获取HomeApi |
|||
_homeApi = Get.find<HomeApi>(); |
|||
getPostData(); |
|||
getCommentData(); |
|||
} |
|||
|
|||
getPostData() async { |
|||
try { |
|||
final response = await _homeApi.userPagePostDetail(id: id); |
|||
if (response.data.isSuccess && response.data.data != null) { |
|||
item.value = response.data.data ?? Records(); |
|||
|
|||
if(item.value.mediaUrls != null && item.value.mediaUrls != ""){ |
|||
imgList.value = item.value.mediaUrls!.split(","); |
|||
} |
|||
} else { |
|||
|
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message ?? '获取数据失败'); |
|||
} |
|||
} catch(e){ |
|||
print('详情获取失败: $e'); |
|||
SmartDialog.showToast('详情获取失败'); |
|||
rethrow; |
|||
|
|||
} |
|||
} |
|||
|
|||
getCommentData() async { |
|||
try { |
|||
final response = await _homeApi.userPagePostComment( |
|||
postId: id, |
|||
pageNum: page.value, |
|||
pageSize: size.value |
|||
); |
|||
if (response.data.isSuccess && response.data.data != null) { |
|||
final data = response.data.data?.records ?? []; |
|||
|
|||
commentList.addAll(data); |
|||
|
|||
if((data.length ?? 0) == size.value){ |
|||
|
|||
listRefreshController.finishLoad(IndicatorResult.success); |
|||
} else { |
|||
listRefreshController.finishLoad(IndicatorResult.noMore); |
|||
} |
|||
|
|||
} else { |
|||
|
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message ?? '获取数据失败'); |
|||
} |
|||
} catch(e){ |
|||
print('详情获取失败: $e'); |
|||
SmartDialog.showToast('详情获取失败'); |
|||
rethrow; |
|||
|
|||
} |
|||
} |
|||
|
|||
likePost() async { |
|||
try { |
|||
final response = await _homeApi.userLikePost({ |
|||
"id": id, |
|||
"isLiked": !(item.value.isLiked ?? false), |
|||
}); |
|||
if (response.data.isSuccess) { |
|||
if(item.value.isLiked ?? false){ |
|||
SmartDialog.showToast('取消点赞成功'); |
|||
} else { |
|||
SmartDialog.showToast('点赞成功'); |
|||
} |
|||
getPostData(); |
|||
} else { |
|||
|
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message ?? '获取数据失败'); |
|||
} |
|||
} catch(e){ |
|||
print('帖子发布失败: $e'); |
|||
SmartDialog.showToast('帖子发布失败'); |
|||
rethrow; |
|||
|
|||
} |
|||
} |
|||
|
|||
sendComment() async { |
|||
try { |
|||
if(message.value == ""){ |
|||
SmartDialog.showToast('请输入评论'); |
|||
return; |
|||
} |
|||
final response = await _homeApi.userCreatePostComment({ |
|||
"postId": id, |
|||
"parentId": parentId.value, |
|||
"content": message.value, |
|||
}); |
|||
if (response.data.isSuccess) { |
|||
|
|||
page.value = 1; |
|||
commentList.clear(); |
|||
getPostData(); |
|||
getCommentData(); |
|||
showInput.value = false; |
|||
parentId.value = "0"; |
|||
|
|||
message.value = ""; |
|||
messageController.value.value = TextEditingValue( |
|||
text: message.value, |
|||
selection: TextSelection.fromPosition(TextPosition(offset: message.value.length)), |
|||
); |
|||
} else { |
|||
|
|||
// 响应失败,抛出异常 |
|||
throw Exception(response.data.message ?? '获取数据失败'); |
|||
} |
|||
} catch(e){ |
|||
print('帖子发布失败: $e'); |
|||
SmartDialog.showToast('帖子发布失败'); |
|||
rethrow; |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
/// 构建输入框内容(文本+表情) |
|||
List<Widget> buildInputContentWidgets() { |
|||
final List<Widget> widgets = []; |
|||
final text = item.value.content ?? ""; |
|||
final RegExp emojiRegex = RegExp(r'\[emoji:(\d+)\]'); |
|||
|
|||
int lastMatchEnd = 0; |
|||
final matches = emojiRegex.allMatches(text); |
|||
|
|||
for (final match in matches) { |
|||
// 添加表情之前的文本 |
|||
if (match.start > lastMatchEnd) { |
|||
final textPart = text.substring(lastMatchEnd, match.start); |
|||
widgets.add( |
|||
Text( |
|||
textPart, |
|||
style: TextStyle(fontSize: 14.sp, color: Colors.black), |
|||
), |
|||
); |
|||
} |
|||
|
|||
// 添加表情图片 |
|||
final emojiId = match.group(1); |
|||
if (emojiId != null) { |
|||
final emoji = EmojiConfig.getEmojiById(emojiId); |
|||
if (emoji != null) { |
|||
widgets.add( |
|||
Padding( |
|||
padding: EdgeInsets.symmetric(horizontal: 2.w), |
|||
child: Image.asset( |
|||
emoji.path, |
|||
width: 24.w, |
|||
height: 24.w, |
|||
fit: BoxFit.contain, |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
|
|||
lastMatchEnd = match.end; |
|||
} |
|||
|
|||
// 添加剩余的文本 |
|||
if (lastMatchEnd < text.length) { |
|||
final textPart = text.substring(lastMatchEnd); |
|||
widgets.add( |
|||
Text( |
|||
textPart, |
|||
style: TextStyle(fontSize: 14.sp, color: Colors.black), |
|||
), |
|||
); |
|||
} |
|||
|
|||
return widgets; |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
class PostCommentData { |
|||
List<Records>? records; |
|||
int? total; |
|||
int? size; |
|||
int? current; |
|||
int? pages; |
|||
|
|||
PostCommentData( |
|||
{this.records, this.total, this.size, this.current, this.pages}); |
|||
|
|||
PostCommentData.fromJson(Map<String, dynamic> json) { |
|||
if (json['records'] != null) { |
|||
records = <Records>[]; |
|||
json['records'].forEach((v) { |
|||
records!.add(new Records.fromJson(v)); |
|||
}); |
|||
} |
|||
total = json['total']; |
|||
size = json['size']; |
|||
current = json['current']; |
|||
pages = json['pages']; |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
final Map<String, dynamic> data = new Map<String, dynamic>(); |
|||
if (this.records != null) { |
|||
data['records'] = this.records!.map((v) => v.toJson()).toList(); |
|||
} |
|||
data['total'] = this.total; |
|||
data['size'] = this.size; |
|||
data['current'] = this.current; |
|||
data['pages'] = this.pages; |
|||
return data; |
|||
} |
|||
} |
|||
|
|||
class Records { |
|||
String? id; |
|||
String? userId; |
|||
String? miId; |
|||
String? nickName; |
|||
int? genderCode; |
|||
String? profilePhoto; |
|||
String? parentId; |
|||
String? content; |
|||
String? createTime; |
|||
List<ChildPostCommentList>? childPostCommentList; |
|||
|
|||
Records( |
|||
{this.id, |
|||
this.userId, |
|||
this.miId, |
|||
this.nickName, |
|||
this.genderCode, |
|||
this.profilePhoto, |
|||
this.parentId, |
|||
this.content, |
|||
this.createTime, |
|||
this.childPostCommentList}); |
|||
|
|||
Records.fromJson(Map<String, dynamic> json) { |
|||
id = json['id']; |
|||
userId = json['userId']; |
|||
miId = json['miId']; |
|||
nickName = json['nickName']; |
|||
genderCode = json['genderCode']; |
|||
profilePhoto = json['profilePhoto']; |
|||
parentId = json['parentId']; |
|||
content = json['content']; |
|||
createTime = json['createTime']; |
|||
if (json['childPostCommentList'] != null) { |
|||
childPostCommentList = <ChildPostCommentList>[]; |
|||
json['childPostCommentList'].forEach((v) { |
|||
childPostCommentList!.add(new ChildPostCommentList.fromJson(v)); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
final Map<String, dynamic> data = new Map<String, dynamic>(); |
|||
data['id'] = this.id; |
|||
data['userId'] = this.userId; |
|||
data['miId'] = this.miId; |
|||
data['nickName'] = this.nickName; |
|||
data['genderCode'] = this.genderCode; |
|||
data['profilePhoto'] = this.profilePhoto; |
|||
data['parentId'] = this.parentId; |
|||
data['content'] = this.content; |
|||
data['createTime'] = this.createTime; |
|||
if (this.childPostCommentList != null) { |
|||
data['childPostCommentList'] = |
|||
this.childPostCommentList!.map((v) => v.toJson()).toList(); |
|||
} |
|||
return data; |
|||
} |
|||
} |
|||
|
|||
class ChildPostCommentList { |
|||
String? id; |
|||
String? userId; |
|||
String? miId; |
|||
String? nickName; |
|||
int? genderCode; |
|||
String? profilePhoto; |
|||
String? parentId; |
|||
String? content; |
|||
String? createTime; |
|||
Null? childPostCommentList; |
|||
|
|||
ChildPostCommentList( |
|||
{this.id, |
|||
this.userId, |
|||
this.miId, |
|||
this.nickName, |
|||
this.genderCode, |
|||
this.profilePhoto, |
|||
this.parentId, |
|||
this.content, |
|||
this.createTime, |
|||
this.childPostCommentList}); |
|||
|
|||
ChildPostCommentList.fromJson(Map<String, dynamic> json) { |
|||
id = json['id']; |
|||
userId = json['userId']; |
|||
miId = json['miId']; |
|||
nickName = json['nickName']; |
|||
genderCode = json['genderCode']; |
|||
profilePhoto = json['profilePhoto']; |
|||
parentId = json['parentId']; |
|||
content = json['content']; |
|||
createTime = json['createTime']; |
|||
childPostCommentList = json['childPostCommentList']; |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
final Map<String, dynamic> data = new Map<String, dynamic>(); |
|||
data['id'] = this.id; |
|||
data['userId'] = this.userId; |
|||
data['miId'] = this.miId; |
|||
data['nickName'] = this.nickName; |
|||
data['genderCode'] = this.genderCode; |
|||
data['profilePhoto'] = this.profilePhoto; |
|||
data['parentId'] = this.parentId; |
|||
data['content'] = this.content; |
|||
data['createTime'] = this.createTime; |
|||
data['childPostCommentList'] = this.childPostCommentList; |
|||
return data; |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
class PostData { |
|||
List<Records>? records; |
|||
int? total; |
|||
int? size; |
|||
int? current; |
|||
int? pages; |
|||
|
|||
PostData({this.records, this.total, this.size, this.current, this.pages}); |
|||
|
|||
PostData.fromJson(Map<String, dynamic> json) { |
|||
if (json['records'] != null) { |
|||
records = <Records>[]; |
|||
json['records'].forEach((v) { |
|||
records!.add(new Records.fromJson(v)); |
|||
}); |
|||
} |
|||
total = json['total']; |
|||
size = json['size']; |
|||
current = json['current']; |
|||
pages = json['pages']; |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
final Map<String, dynamic> data = new Map<String, dynamic>(); |
|||
if (this.records != null) { |
|||
data['records'] = this.records!.map((v) => v.toJson()).toList(); |
|||
} |
|||
data['total'] = this.total; |
|||
data['size'] = this.size; |
|||
data['current'] = this.current; |
|||
data['pages'] = this.pages; |
|||
return data; |
|||
} |
|||
} |
|||
|
|||
class Records { |
|||
String? id; |
|||
String? userId; |
|||
String? miId; |
|||
String? nickName; |
|||
int? genderCode; |
|||
String? profilePhoto; |
|||
String? content; |
|||
String? mediaUrls; |
|||
String? topicTags; |
|||
int? status; |
|||
bool? isLiked; |
|||
int? likeCount; |
|||
int? commentCount; |
|||
String? createTime; |
|||
|
|||
Records( |
|||
{this.id, |
|||
this.userId, |
|||
this.miId, |
|||
this.nickName, |
|||
this.genderCode, |
|||
this.profilePhoto, |
|||
this.content, |
|||
this.mediaUrls, |
|||
this.topicTags, |
|||
this.status, |
|||
this.isLiked, |
|||
this.likeCount, |
|||
this.commentCount, |
|||
this.createTime}); |
|||
|
|||
Records.fromJson(Map<String, dynamic> json) { |
|||
id = json['id']; |
|||
userId = json['userId']; |
|||
miId = json['miId']; |
|||
nickName = json['nickName']; |
|||
genderCode = json['genderCode']; |
|||
profilePhoto = json['profilePhoto']; |
|||
content = json['content']; |
|||
mediaUrls = json['mediaUrls']; |
|||
topicTags = json['topicTags']; |
|||
status = json['status']; |
|||
isLiked = json['isLiked']; |
|||
likeCount = json['likeCount']; |
|||
commentCount = json['commentCount']; |
|||
createTime = json['createTime']; |
|||
} |
|||
|
|||
Map<String, dynamic> toJson() { |
|||
final Map<String, dynamic> data = new Map<String, dynamic>(); |
|||
data['id'] = this.id; |
|||
data['userId'] = this.userId; |
|||
data['miId'] = this.miId; |
|||
data['nickName'] = this.nickName; |
|||
data['genderCode'] = this.genderCode; |
|||
data['profilePhoto'] = this.profilePhoto; |
|||
data['content'] = this.content; |
|||
data['mediaUrls'] = this.mediaUrls; |
|||
data['topicTags'] = this.topicTags; |
|||
data['status'] = this.status; |
|||
data['isLiked'] = this.isLiked; |
|||
data['likeCount'] = this.likeCount; |
|||
data['commentCount'] = this.commentCount; |
|||
data['createTime'] = this.createTime; |
|||
return data; |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save