推荐视频评论和点赞关注
This commit is contained in:
parent
212fdea5a0
commit
26b8b5abf3
@ -195,7 +195,14 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PopScope(
|
||||||
|
canPop: true,
|
||||||
|
onPopInvokedWithResult: (bool didPop, Object? result) {
|
||||||
|
if (didPop) {
|
||||||
|
print('User navigated back');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child:Scaffold(
|
||||||
backgroundColor: const Color(0xFFFAF6F9),
|
backgroundColor: const Color(0xFFFAF6F9),
|
||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
return NestedScrollViewPlus(
|
return NestedScrollViewPlus(
|
||||||
@ -211,6 +218,16 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
|||||||
collapsedHeight: 120.0,
|
collapsedHeight: 120.0,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
stretch: true,
|
stretch: true,
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
Get.back(result: {
|
||||||
|
'returnTo': '/',
|
||||||
|
'vlogerId': args['memberId'],
|
||||||
|
'followStatus': (followed.value == 1 || followed.value == 3)?true:false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
onStretchTrigger: () async {
|
onStretchTrigger: () async {
|
||||||
logger.i('触发 stretch 拉伸');
|
logger.i('触发 stretch 拉伸');
|
||||||
// 加载刷新逻辑
|
// 加载刷新逻辑
|
||||||
@ -283,6 +300,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
import 'package:loopin/IM/im_core.dart';
|
import 'package:loopin/IM/im_core.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||||
import 'package:loopin/api/video_api.dart';
|
import 'package:loopin/api/video_api.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
@ -46,9 +47,14 @@ class RecommendModule extends StatefulWidget {
|
|||||||
State<RecommendModule> createState() => _RecommendModuleState();
|
State<RecommendModule> createState() => _RecommendModuleState();
|
||||||
}
|
}
|
||||||
class CommentBottomSheet extends StatefulWidget {
|
class CommentBottomSheet extends StatefulWidget {
|
||||||
final int videoId;
|
final String videoId;
|
||||||
|
final Function(int) onCommentCountChanged; // 新增回调函数
|
||||||
|
|
||||||
const CommentBottomSheet({super.key, required this.videoId});
|
const CommentBottomSheet({
|
||||||
|
super.key,
|
||||||
|
required this.videoId,
|
||||||
|
required this.onCommentCountChanged, // 接收回调
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_CommentBottomSheetState createState() => _CommentBottomSheetState();
|
_CommentBottomSheetState createState() => _CommentBottomSheetState();
|
||||||
@ -61,9 +67,15 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
bool isLoadingMoreComments = false;
|
bool isLoadingMoreComments = false;
|
||||||
bool hasMoreComments = true;
|
bool hasMoreComments = true;
|
||||||
List<Map<String, dynamic>> commentList = [];
|
List<Map<String, dynamic>> commentList = [];
|
||||||
int replyingCommentId = 0;
|
String replyingCommentId = '';
|
||||||
String replyingCommentUser = '';
|
String replyingCommentUser = '';
|
||||||
|
|
||||||
|
// 新增:子评论相关状态
|
||||||
|
Map<String, dynamic> expandedReplies = {}; // 存储已展开的回复 {commentId: true/false}
|
||||||
|
Map<String, List<Map<String, dynamic>>> replyData = {}; // 存储子评论数据 {commentId: [replies]}
|
||||||
|
Map<String, int> replyPage = {}; // 子评论分页
|
||||||
|
Map<String, bool> isLoadingReplies = {}; // 子评论加载状态
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -83,7 +95,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
hasMoreComments = true;
|
hasMoreComments = true;
|
||||||
commentList.clear();
|
commentList.clear();
|
||||||
}
|
}
|
||||||
|
logger.d('入参vlogId-------------------->: ${widget.videoId}');
|
||||||
try {
|
try {
|
||||||
final res = await Http.post(VideoApi.videoCommentList, data: {
|
final res = await Http.post(VideoApi.videoCommentList, data: {
|
||||||
'vlogId': widget.videoId,
|
'vlogId': widget.videoId,
|
||||||
@ -91,7 +103,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
'size': commentPageSize,
|
'size': commentPageSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.d('评论接口返回: ${json.encode(res)}');
|
logger.d('评论接口列表返回: ${json.encode(res)}');
|
||||||
|
|
||||||
if (res['code'] == 200 && res['data'] != null) {
|
if (res['code'] == 200 && res['data'] != null) {
|
||||||
final data = res['data'];
|
final data = res['data'];
|
||||||
@ -108,7 +120,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
hasMoreComments = commentList.length < total;
|
hasMoreComments = commentList.length < total;
|
||||||
commentPage++;
|
commentPage++;
|
||||||
});
|
});
|
||||||
|
widget.onCommentCountChanged(total);
|
||||||
logger.d('成功加载 ${newComments.length} 条评论,总共 $total 条');
|
logger.d('成功加载 ${newComments.length} 条评论,总共 $total 条');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -120,32 +132,91 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> postComment(String content, {int parentCommentId = 0}) async {
|
Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
||||||
try {
|
try {
|
||||||
final res = await Http.post(VideoApi.doVideoComment, data: {
|
final res = await Http.post(VideoApi.doVideoComment, data: {
|
||||||
'vlogId': widget.videoId,
|
'vlogId': widget.videoId,
|
||||||
'content': content,
|
'content': content,
|
||||||
'fatherCommentId': parentCommentId,
|
'fatherCommentId': parentCommentId.isNotEmpty ? parentCommentId : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res['code'] == 0) {
|
if (res['code'] == 200) {
|
||||||
// 评论成功,重新加载评论列表
|
// 如果是回复,刷新对应的子评论
|
||||||
|
if (parentCommentId.isNotEmpty) {
|
||||||
|
fetchReplies(parentCommentId, false);
|
||||||
|
// 更新主评论的子评论数量
|
||||||
|
setState(() {
|
||||||
|
final comment = commentList.firstWhere(
|
||||||
|
(c) => c['id'] == parentCommentId,
|
||||||
|
orElse: () => <String, dynamic>{}
|
||||||
|
);
|
||||||
|
if (comment != null && comment.isNotEmpty) {
|
||||||
|
comment['childCount'] = (comment['childCount'] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果是新评论,刷新整个列表
|
||||||
fetchComments(false);
|
fetchComments(false);
|
||||||
MyToast().tip(
|
|
||||||
title: '评论成功',
|
|
||||||
position: 'center',
|
|
||||||
type: 'success',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
widget.onCommentCountChanged(commentList.length + 1);
|
||||||
logger.i('发布评论失败: $e');
|
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '评论失败',
|
title: '评论成功',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'error',
|
type: 'success',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.i('发布评论失败: $e');
|
||||||
|
MyToast().tip(
|
||||||
|
title: '评论失败',
|
||||||
|
position: 'center',
|
||||||
|
type: 'error',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取二级评论,复用一级评论的接口,修改传参
|
||||||
|
Future<void> fetchReplies(String commentId, bool loadMore) async {
|
||||||
|
if (isLoadingReplies[commentId] == true && !loadMore) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
isLoadingReplies[commentId] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!loadMore) {
|
||||||
|
replyPage[commentId] = 1;
|
||||||
|
replyData[commentId] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final res = await Http.post(VideoApi.videoCommentList, data: {
|
||||||
|
'fatherCommentId': commentId,
|
||||||
|
'current': replyPage[commentId],
|
||||||
|
'size': commentPageSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res['code'] == 200 && res['data'] != null) {
|
||||||
|
final data = res['data'];
|
||||||
|
final List<Map<String, dynamic>> newReplies =
|
||||||
|
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (loadMore) {
|
||||||
|
replyData[commentId]!.addAll(newReplies);
|
||||||
|
} else {
|
||||||
|
replyData[commentId] = newReplies;
|
||||||
|
}
|
||||||
|
replyPage[commentId] = replyPage[commentId]! + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('获取子评论异常: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
isLoadingReplies[commentId] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -227,109 +298,200 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final comment = commentList[index];
|
final comment = commentList[index];
|
||||||
return ListTile(
|
final hasReplies = comment['childCount'] > 0;
|
||||||
isThreeLine: true,
|
final isExpanded = expandedReplies[comment['id']] == true;
|
||||||
leading: ClipRRect(
|
final replies = replyData[comment['id']] ?? [];
|
||||||
borderRadius: BorderRadius.circular(50.0),
|
|
||||||
child: NetworkOrAssetImage(
|
return Column(
|
||||||
imageUrl: comment['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
children: [
|
||||||
width: 30.0,
|
// 主评论
|
||||||
height: 30.0,
|
ListTile(
|
||||||
fit: BoxFit.cover,
|
isThreeLine: true,
|
||||||
),
|
leading: ClipRRect(
|
||||||
),
|
borderRadius: BorderRadius.circular(50.0),
|
||||||
title: Row(
|
child: NetworkOrAssetImage(
|
||||||
children: [
|
imageUrl: comment['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
||||||
Expanded(
|
width: 30.0,
|
||||||
child: Text(
|
height: 30.0,
|
||||||
comment['commentUserNickname'] ?? '未知用户',
|
fit: BoxFit.cover,
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.grey,
|
|
||||||
fontSize: 12.0,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 20.0),
|
title: Row(
|
||||||
GestureDetector(
|
children: [
|
||||||
onTap: () {
|
Expanded(
|
||||||
setState(() {
|
child: Text(
|
||||||
replyingCommentId = comment['id'];
|
comment['commentUserNickname'] ?? '未知用户',
|
||||||
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
style: TextStyle(
|
||||||
});
|
color: Colors.grey,
|
||||||
},
|
fontSize: 12.0,
|
||||||
child: Icon(
|
|
||||||
Icons.reply,
|
|
||||||
color: Colors.black54,
|
|
||||||
size: 16.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
subtitle: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.symmetric(vertical: 5.0),
|
|
||||||
child: Text(
|
|
||||||
comment['content'] ?? '',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (comment['childCount'] > 0)
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
replyingCommentId = comment['id'];
|
|
||||||
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.only(right: 15.0),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[100],
|
|
||||||
borderRadius: BorderRadius.circular(20.0),
|
|
||||||
),
|
|
||||||
child: Row(children: [
|
|
||||||
Text(
|
|
||||||
'${comment['childCount']}回复',
|
|
||||||
style: TextStyle(fontSize: 12.0),
|
|
||||||
),
|
),
|
||||||
Icon(
|
),
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 10.0,
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
),
|
SizedBox(width: 20.0),
|
||||||
Text(
|
GestureDetector(
|
||||||
'${comment['createTime']?.toString().substring(0, 10) ?? ''}',
|
onTap: () {
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
setState(() {
|
||||||
|
replyingCommentId = comment['id'];
|
||||||
|
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.reply,
|
||||||
|
color: Colors.black54,
|
||||||
|
size: 16.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
subtitle: Column(
|
||||||
),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: Text(
|
||||||
|
comment['content'] ?? '',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (hasReplies)
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
expandedReplies[comment['id']] = !isExpanded;
|
||||||
|
if (expandedReplies[comment['id']] == true &&
|
||||||
|
(replyData[comment['id']] == null || replyData[comment['id']]!.isEmpty)) {
|
||||||
|
fetchReplies(comment['id'], false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(right: 15.0),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[100],
|
||||||
|
borderRadius: BorderRadius.circular(20.0),
|
||||||
|
),
|
||||||
|
child: Row(children: [
|
||||||
|
Text(
|
||||||
|
'${comment['childCount']}回复',
|
||||||
|
style: TextStyle(fontSize: 12.0),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down,
|
||||||
|
size: 16.0,
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${comment['createTime']?.toString().substring(0, 10) ?? ''}',
|
||||||
|
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 子评论列表
|
||||||
|
if (isExpanded && replies.isNotEmpty)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 40.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...replies.map((reply) => ListTile(
|
||||||
|
leading: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(25.0),
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: reply['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
||||||
|
width: 25.0,
|
||||||
|
height: 25.0,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
reply['commentUserNickname'] ?? '未知用户',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 11.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
replyingCommentId = comment['id'];
|
||||||
|
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.reply,
|
||||||
|
color: Colors.black54,
|
||||||
|
size: 14.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 3.0),
|
||||||
|
child: Text(
|
||||||
|
reply['content'] ?? '',
|
||||||
|
style: TextStyle(fontSize: 13.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${reply['createTime']?.toString().substring(0, 10) ?? ''}',
|
||||||
|
style: TextStyle(color: Colors.grey, fontSize: 11.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)).toList(),
|
||||||
|
|
||||||
|
// 加载更多子评论按钮
|
||||||
|
if (replies.length < comment['childCount'])
|
||||||
|
Center(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () => fetchReplies(comment['id'], true),
|
||||||
|
child: isLoadingReplies[comment['id']] == true
|
||||||
|
? CircularProgressIndicator()
|
||||||
|
: Text('加载更多回复'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Divider(height: 1, color: Colors.grey[200]),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
// 在回复输入区域显示更详细的信息
|
||||||
if (replyingCommentId > 0)
|
if (replyingCommentId.isNotEmpty)
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
'回复 @$replyingCommentUser',
|
child: Text(
|
||||||
style: TextStyle(fontSize: 12.0, color: Colors.blue),
|
'回复 @$replyingCommentUser',
|
||||||
|
style: TextStyle(fontSize: 12.0, color: Colors.blue),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
replyingCommentId = 0;
|
replyingCommentId = '';
|
||||||
replyingCommentUser = '';
|
replyingCommentUser = '';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -338,58 +500,58 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.all(10.0),
|
margin: EdgeInsets.all(10.0),
|
||||||
height: 40.0,
|
height: 40.0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey[100],
|
color: Colors.grey[100],
|
||||||
borderRadius: BorderRadius.circular(30.0),
|
borderRadius: BorderRadius.circular(30.0),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.0),
|
SizedBox(width: 15.0),
|
||||||
Icon(
|
Icon(
|
||||||
Icons.edit_note,
|
Icons.edit_note,
|
||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
size: 16.0,
|
size: 16.0,
|
||||||
),
|
),
|
||||||
SizedBox(width: 5.0),
|
SizedBox(width: 5.0),
|
||||||
Text(
|
Text(
|
||||||
replyingCommentId > 0 ? '回复 @$replyingCommentUser' : '说点什么...',
|
replyingCommentId.isNotEmpty ? '回复 @$replyingCommentUser' : '说点什么...',
|
||||||
style: TextStyle(color: Colors.black54, fontSize: 14.0),
|
style: TextStyle(color: Colors.black54, fontSize: 14.0),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(context, FadeRoute(child: PopupReply(
|
||||||
|
hintText: replyingCommentId.isNotEmpty ? '回复 @$replyingCommentUser' : '说点什么...',
|
||||||
|
onChanged: (value) {
|
||||||
|
debugPrint('评论内容: $value');
|
||||||
|
},
|
||||||
|
onSubmitted: (value) {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
postComment(value, parentCommentId: replyingCommentId);
|
||||||
|
setState(() {
|
||||||
|
replyingCommentId = '';
|
||||||
|
replyingCommentUser = '';
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
onTap: () {
|
],
|
||||||
Navigator.push(context, FadeRoute(child: PopupReply(
|
),
|
||||||
hintText: replyingCommentId > 0 ? '回复 @$replyingCommentUser' : '说点什么...',
|
);
|
||||||
onChanged: (value) {
|
}
|
||||||
debugPrint('评论内容: $value');
|
|
||||||
},
|
|
||||||
onSubmitted: (value) {
|
|
||||||
if (value.isNotEmpty) {
|
|
||||||
postComment(value, parentCommentId: replyingCommentId);
|
|
||||||
setState(() {
|
|
||||||
replyingCommentId = 0;
|
|
||||||
replyingCommentUser = '';
|
|
||||||
});
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
class _RecommendModuleState extends State<RecommendModule> {
|
class _RecommendModuleState extends State<RecommendModule> {
|
||||||
VideoModuleController videoModuleController = Get.put(VideoModuleController());
|
VideoModuleController videoModuleController = Get.put(VideoModuleController());
|
||||||
final ChatController chatController = Get.find<ChatController>();
|
final ChatController chatController = Get.find<ChatController>();
|
||||||
|
final RxMap<String, dynamic> lastReturnData = RxMap({});
|
||||||
// 分页内容
|
// 分页内容
|
||||||
int page = 1;
|
int page = 1;
|
||||||
final int pageSize = 10;
|
final int pageSize = 10;
|
||||||
@ -421,21 +583,15 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
bool hasMoreComments = true;
|
bool hasMoreComments = true;
|
||||||
List<Map<String, dynamic>> commentList = [];
|
List<Map<String, dynamic>> commentList = [];
|
||||||
int currentVideoId = 0;
|
int currentVideoId = 0;
|
||||||
int replyingCommentId = 0; // 正在回复的评论ID
|
String replyingCommentId = ''; // 正在回复的评论ID
|
||||||
String replyingCommentUser = ''; // 正在回复的用户名
|
String replyingCommentUser = ''; // 正在回复的用户名
|
||||||
|
|
||||||
// 分享列表
|
// 分享列表
|
||||||
List shareList = [
|
List shareList = [
|
||||||
{'icon': 'assets/images/share-wx.png', 'label': '好友'},
|
{'icon': 'assets/images/share-wx.png', 'label': '好友'},
|
||||||
{'icon': 'assets/images/share-wx.png', 'label': '微信'},
|
|
||||||
{'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'},
|
{'icon': 'assets/images/share-pyq.png', 'label': '朋友圈'},
|
||||||
{'icon': 'assets/images/share-link.png', 'label': '复制链接'},
|
{'icon': 'assets/images/share-link.png', 'label': '复制链接'},
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载'},
|
{'icon': 'assets/images/share-download.png', 'label': '下载'},
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载'},
|
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载'},
|
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载'},
|
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载下载下载下载下载下载下载下载下载下载下载下载'},
|
|
||||||
{'icon': 'assets/images/share-download.png', 'label': '下载下载下载下载下载下载下载下载下载下载下载下载'},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -545,7 +701,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
if (videos.isNotEmpty) {
|
if (videos.isNotEmpty) {
|
||||||
page++;
|
page++;
|
||||||
}
|
}
|
||||||
logger.i('获取新的视频数据了');
|
logger.i('视频数据列表------------------->$videoList');
|
||||||
|
|
||||||
// 初始化播放器
|
// 初始化播放器
|
||||||
player.open(
|
player.open(
|
||||||
@ -569,91 +725,6 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改 fetchComments 方法
|
|
||||||
Future<void> fetchComments(int videoId, {bool loadMore = false}) async {
|
|
||||||
// 防止重复加载
|
|
||||||
if (isLoadingMoreComments && !loadMore) return;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
isLoadingMoreComments = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
final res = await Http.post(VideoApi.videoCommentList, data: {
|
|
||||||
'vlogId': videoId,
|
|
||||||
'current': loadMore ? commentPage : 1,
|
|
||||||
'size': commentPageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.d('评论接口返回: ${json.encode(res)}');
|
|
||||||
|
|
||||||
if (res['code'] == 200 && res['data'] != null) {
|
|
||||||
final data = res['data'];
|
|
||||||
final List<Map<String, dynamic>> newComments =
|
|
||||||
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
|
||||||
final int total = data['total'] ?? 0;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
if (loadMore) {
|
|
||||||
commentList.addAll(newComments);
|
|
||||||
} else {
|
|
||||||
commentList = newComments;
|
|
||||||
}
|
|
||||||
hasMoreComments = commentList.length < total;
|
|
||||||
commentPage = loadMore ? commentPage + 1 : 2; // 下一页页码
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.d('成功加载 ${newComments.length} 条评论,总共 $total 条');
|
|
||||||
} else {
|
|
||||||
logger.e('获取评论失败: ${res['msg']}');
|
|
||||||
MyToast().tip(
|
|
||||||
title: '获取评论失败',
|
|
||||||
position: 'center',
|
|
||||||
type: 'error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.e('获取评论异常: $e');
|
|
||||||
MyToast().tip(
|
|
||||||
title: '网络异常',
|
|
||||||
position: 'center',
|
|
||||||
type: 'error',
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
isLoadingMoreComments = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发布评论
|
|
||||||
Future<void> postComment(String content, {int parentCommentId = 0}) async {
|
|
||||||
try {
|
|
||||||
final res = await Http.post(VideoApi.doVideoComment, data: {
|
|
||||||
'vlogId': currentVideoId,
|
|
||||||
'content': content,
|
|
||||||
'fatherCommentId': parentCommentId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res['code'] == 0) {
|
|
||||||
// 评论成功,重新加载评论列表
|
|
||||||
fetchComments(currentVideoId, loadMore: false);
|
|
||||||
MyToast().tip(
|
|
||||||
title: '评论成功',
|
|
||||||
position: 'center',
|
|
||||||
type: 'success',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.i('发布评论失败: $e');
|
|
||||||
MyToast().tip(
|
|
||||||
title: '评论失败',
|
|
||||||
position: 'center',
|
|
||||||
type: 'error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消喜欢视频
|
// 取消喜欢视频
|
||||||
Future<void> doUnLikeVideo(item) async {
|
Future<void> doUnLikeVideo(item) async {
|
||||||
logger.d('点击了点赞按钮$item');
|
logger.d('点击了点赞按钮$item');
|
||||||
@ -688,8 +759,7 @@ Future<void> fetchComments(int videoId, {bool loadMore = false}) async {
|
|||||||
|
|
||||||
// 评论弹框
|
// 评论弹框
|
||||||
void handleComment(index) {
|
void handleComment(index) {
|
||||||
final videoIdStr = videoList[index]['id'].toString();
|
final videoId = videoList[index]['id'];
|
||||||
final videoId = int.tryParse(videoIdStr) ?? 0;
|
|
||||||
logger.i('点击了评论按钮$videoId');
|
logger.i('点击了评论按钮$videoId');
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
@ -703,7 +773,17 @@ void handleComment(index) {
|
|||||||
),
|
),
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return CommentBottomSheet(videoId: videoId);
|
return CommentBottomSheet(
|
||||||
|
videoId: videoId,
|
||||||
|
onCommentCountChanged: (newCount) {
|
||||||
|
// 更新对应视频的评论数量
|
||||||
|
setState(() {
|
||||||
|
if (index < videoList.length) {
|
||||||
|
videoList[index]['commentsCounts'] = newCount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -974,9 +1054,20 @@ void handleComment(index) {
|
|||||||
height: 55.0,
|
height: 55.0,
|
||||||
width: 48.0,
|
width: 48.0,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: ()async {
|
||||||
player.pause();
|
player.pause();
|
||||||
Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]);
|
// 跳转到 Vloger 页面并等待返回结果
|
||||||
|
final result = await Get.toNamed(
|
||||||
|
'/vloger',
|
||||||
|
arguments: videoList[videoModuleController
|
||||||
|
.videoPlayIndex.value]);
|
||||||
|
if (result != null) {
|
||||||
|
// 处理返回的参数
|
||||||
|
print('返回的数据: ${result['followStatus']}');
|
||||||
|
player.play();
|
||||||
|
videoList[index]['doIFollowVloger'] = result['followStatus'];
|
||||||
|
|
||||||
|
};
|
||||||
},
|
},
|
||||||
child: UnconstrainedBox(
|
child: UnconstrainedBox(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
@ -1013,10 +1104,18 @@ void handleComment(index) {
|
|||||||
size: 14.0,
|
size: 14.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () async {
|
||||||
setState(() {
|
final vlogerId = videoList[index]['memberId'];
|
||||||
videoList[index]['doIFollowVloger'] = !videoList[index]['doIFollowVloger'];
|
final doIFollowVloger = videoList[index]['doIFollowVloger'];
|
||||||
});
|
// 未关注点击才去关注
|
||||||
|
if(doIFollowVloger == false){
|
||||||
|
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
|
||||||
|
if (res.success) {
|
||||||
|
setState(() {
|
||||||
|
videoList[index]['doIFollowVloger'] = !videoList[index]['doIFollowVloger'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user