Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
1a959e7c3b
@ -13,31 +13,42 @@ class ChatController extends GetxController {
|
|||||||
final chatList = <ConversationViewModel>[].obs;
|
final chatList = <ConversationViewModel>[].obs;
|
||||||
|
|
||||||
void initChatData() {
|
void initChatData() {
|
||||||
chatList.value = <ConversationViewModel>[];
|
// chatList.value = <ConversationViewModel>[];
|
||||||
|
chatList.clear();
|
||||||
nextSeq.value = '0';
|
nextSeq.value = '0';
|
||||||
isFinished.value = false;
|
isFinished.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有会话列表
|
// 获取所有会话列表
|
||||||
void getConversationList() async {
|
Future<void> getConversationList() async {
|
||||||
|
logger.e('开始获取会话列表数据');
|
||||||
if (isFinished.value) {
|
if (isFinished.value) {
|
||||||
// 拉取完数据了,直接结束
|
// 拉取完数据了,直接结束
|
||||||
|
logger.e('会话触底无数据');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final res = await ImService.instance.getConversationList(nextSeq.value, count.value);
|
try {
|
||||||
|
final res = await ImService.instance.getConversationList(nextSeq.value, count.value);
|
||||||
|
|
||||||
if (!res.success || res.data == null) return;
|
if (!res.success || res.data == null) {
|
||||||
|
logger.e('获取会话失败::${res.desc}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final List<ConversationViewModel> convList = res.data;
|
final List<ConversationViewModel> convList = res.data;
|
||||||
for (var conv in convList) {
|
for (var conv in convList) {
|
||||||
logger.i('基本会话: ${conv.conversation.toJson()}, 会话ID: ${conv.conversation.conversationID}');
|
logger.w('基本会话: ${conv.conversation.toLogString()}');
|
||||||
}
|
}
|
||||||
|
|
||||||
chatList.addAll(convList);
|
chatList.addAll(convList);
|
||||||
// 不包含noFriend才执行加载数据逻辑,分页加载时候过滤
|
// 不包含noFriend才执行加载数据逻辑,分页加载时候过滤
|
||||||
final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false);
|
final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false);
|
||||||
if (!hasNoFriend) {
|
logger.e('开始构建陌生人入口是否包含noFriend?:$hasNoFriend');
|
||||||
getNoFriendData();
|
if (!hasNoFriend) {
|
||||||
|
getNoFriendData();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('获取会话异常::$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +56,7 @@ class ChatController extends GetxController {
|
|||||||
void getNoFriendData({V2TimConversation? csion}) async {
|
void getNoFriendData({V2TimConversation? csion}) async {
|
||||||
// 检测会话列表是否已有陌生人消息菜单
|
// 检测会话列表是否已有陌生人消息菜单
|
||||||
final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false);
|
final hasNoFriend = chatList.any((item) => item.conversation.conversationGroupList?.contains(myConversationType.ConversationType.noFriend.name) ?? false);
|
||||||
|
logger.w('检测是否存在nofriend入口:$hasNoFriend');
|
||||||
if (hasNoFriend) {
|
if (hasNoFriend) {
|
||||||
// 已经有了入口
|
// 已经有了入口
|
||||||
final ConversationViewModel matchItem = chatList.firstWhere(
|
final ConversationViewModel matchItem = chatList.firstWhere(
|
||||||
@ -60,49 +72,49 @@ class ChatController extends GetxController {
|
|||||||
matchItem.conversation.lastMessage = csion!.lastMessage;
|
matchItem.conversation.lastMessage = csion!.lastMessage;
|
||||||
matchItem.conversation.unreadCount = unreadTotal.data;
|
matchItem.conversation.unreadCount = unreadTotal.data;
|
||||||
chatList.refresh();
|
chatList.refresh();
|
||||||
return;
|
} else {
|
||||||
}
|
// 没有则执行创建逻辑
|
||||||
// 没有则执行创建逻辑
|
final res = await ImService.instance.getConversationListByFilter(
|
||||||
final res = await ImService.instance.getConversationListByFilter(
|
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
|
||||||
filter: V2TimConversationFilter(conversationGroup: myConversationType.ConversationType.noFriend.name),
|
nextSeq: 0,
|
||||||
nextSeq: 0,
|
count: 1,
|
||||||
count: 1,
|
);
|
||||||
);
|
if (res.success && res.data != null) {
|
||||||
if (res.success && res.data != null) {
|
final convList = res.data!.conversationList ?? [];
|
||||||
final convList = res.data!.conversationList ?? [];
|
if (convList.isNotEmpty) {
|
||||||
if (convList.isNotEmpty) {
|
// logger.i(res.data!.toJson());
|
||||||
// logger.i(res.data!.toJson());
|
// 有陌生人消息,1.获取未读数,2.组装converstaionviewmodel
|
||||||
// 有陌生人消息,1.获取未读数,2.组装converstaionviewmodel
|
final unread = await ImService.instance.getUnreadMessageCountByFilter(
|
||||||
final unread = await ImService.instance.getUnreadMessageCountByFilter(
|
filter: V2TimConversationFilter(
|
||||||
filter: V2TimConversationFilter(
|
conversationGroup: myConversationType.ConversationType.noFriend.name,
|
||||||
conversationGroup: myConversationType.ConversationType.noFriend.name,
|
hasUnreadCount: true,
|
||||||
hasUnreadCount: true,
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
if (unread.success) {
|
|
||||||
final conv = convList.first;
|
|
||||||
final faceUrl = 'assets/images/notify/msr.png';
|
|
||||||
conv.showName = '陌生人消息';
|
|
||||||
conv.unreadCount = unread.data;
|
|
||||||
final createItem = ConversationViewModel(
|
|
||||||
conversation: conv,
|
|
||||||
faceUrl: faceUrl,
|
|
||||||
);
|
);
|
||||||
final newList = List<ConversationViewModel>.from(chatList);
|
if (unread.success) {
|
||||||
newList.add(createItem);
|
final conv = convList.first;
|
||||||
newList.sort((a, b) {
|
final faceUrl = 'assets/images/notify/msr.png';
|
||||||
final atime = a.conversation.lastMessage?.timestamp ?? 0;
|
conv.showName = '陌生人消息';
|
||||||
final btime = b.conversation.lastMessage?.timestamp ?? 0;
|
conv.unreadCount = unread.data;
|
||||||
return btime.compareTo(atime); // 降序
|
final createItem = ConversationViewModel(
|
||||||
});
|
conversation: conv,
|
||||||
chatList.value = newList;
|
faceUrl: faceUrl,
|
||||||
|
);
|
||||||
|
final newList = List<ConversationViewModel>.from(chatList);
|
||||||
|
newList.add(createItem);
|
||||||
|
newList.sort((a, b) {
|
||||||
|
final atime = a.conversation.lastMessage?.timestamp ?? 0;
|
||||||
|
final btime = b.conversation.lastMessage?.timestamp ?? 0;
|
||||||
|
return btime.compareTo(atime); // 降序
|
||||||
|
});
|
||||||
|
chatList.value = newList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 按会话分组查询 getConversationListByFilter
|
/// 按会话分组查询 getConversationListByFilter
|
||||||
// void getConversationList() async {
|
// void filterConversationList() async {
|
||||||
// final res = await ImService.instance.getConversationListByFilter(
|
// final res = await ImService.instance.getConversationListByFilter(
|
||||||
// filter: V2TimConversationFilter(conversationGroup: null),
|
// filter: V2TimConversationFilter(conversationGroup: null),
|
||||||
// nextSeq: nextSeq.value,
|
// nextSeq: nextSeq.value,
|
||||||
|
@ -8,8 +8,7 @@ class ImUserInfoController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
refreshUserInfo();
|
logger.i('开始IM用户信息初始化');
|
||||||
logger.i('IM用户信息初始化');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
V2TimUserFullInfo? rawUserInfo;
|
V2TimUserFullInfo? rawUserInfo;
|
||||||
@ -52,14 +51,14 @@ class ImUserInfoController extends GetxController {
|
|||||||
birthday.value = userInfo.birthday ?? 0;
|
birthday.value = userInfo.birthday ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshUserInfo() async {
|
Future<void> refreshUserInfo() async {
|
||||||
try {
|
try {
|
||||||
final updatedUserInfo = await ImService.instance.selfInfo();
|
final updatedUserInfo = await ImService.instance.selfInfo();
|
||||||
if (updatedUserInfo.success) {
|
if (updatedUserInfo.success) {
|
||||||
init(updatedUserInfo.data);
|
init(updatedUserInfo.data);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('刷新用户信息失败: $e');
|
logger.e('刷新用户信息失败: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ class GlobalBadge extends GetxController {
|
|||||||
/// 监听器对象(用于 add/remove)
|
/// 监听器对象(用于 add/remove)
|
||||||
late final V2TimConversationListener _listener;
|
late final V2TimConversationListener _listener;
|
||||||
|
|
||||||
|
void rest() {
|
||||||
|
totalUnread.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@ -34,12 +38,16 @@ class GlobalBadge extends GetxController {
|
|||||||
onConversationChanged: (List<V2TimConversation> conversationList) async {
|
onConversationChanged: (List<V2TimConversation> conversationList) async {
|
||||||
logger.w('会话变更:会话分组:${conversationList.first.conversationGroupList},会话内容${conversationList.first.toLogString()}');
|
logger.w('会话变更:会话分组:${conversationList.first.conversationGroupList},会话内容${conversationList.first.toLogString()}');
|
||||||
final ctl = Get.find<ChatController>();
|
final ctl = Get.find<ChatController>();
|
||||||
|
logger.w('当前会话列表内容:${ctl.chatList.length}');
|
||||||
|
|
||||||
final updatedIds = conversationList.map((e) => e.conversationID).toSet();
|
final updatedIds = conversationList.map((e) => e.conversationID).toSet();
|
||||||
logger.w('要变更的会话id:$updatedIds');
|
logger.w('要变更的会话id:$updatedIds');
|
||||||
for (int i = 0; i < ctl.chatList.length; i++) {
|
for (int i = 0; i < ctl.chatList.length; i++) {
|
||||||
final chatItem = ctl.chatList[i];
|
final chatItem = ctl.chatList[i];
|
||||||
logger.w('需要更新的ID:${chatItem.conversation.conversationID}');
|
logger.w('需要更新的ID:${chatItem.conversation.conversationID}');
|
||||||
if (updatedIds.contains(chatItem.conversation.conversationID)) {
|
if (updatedIds.contains(chatItem.conversation.conversationID)) {
|
||||||
|
logger.w('找到的chatList的ID:${chatItem.conversation.conversationID}');
|
||||||
|
|
||||||
final updatedConv = conversationList.firstWhere(
|
final updatedConv = conversationList.firstWhere(
|
||||||
(c) => c.conversationID == chatItem.conversation.conversationID,
|
(c) => c.conversationID == chatItem.conversation.conversationID,
|
||||||
orElse: () => V2TimConversation(conversationID: ''),
|
orElse: () => V2TimConversation(conversationID: ''),
|
||||||
@ -57,10 +65,20 @@ class GlobalBadge extends GetxController {
|
|||||||
chatItem.conversation.unreadCount = unread.data; // 获取陌生人未读总数
|
chatItem.conversation.unreadCount = unread.data; // 获取陌生人未读总数
|
||||||
} else {
|
} else {
|
||||||
// 其他类型统一更新处理
|
// 其他类型统一更新处理
|
||||||
|
logger.w('不需要分组的会话,正常更新');
|
||||||
chatItem.conversation = updatedConv;
|
chatItem.conversation = updatedConv;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 如果没当前会话列表为空
|
||||||
|
if (ctl.chatList.isEmpty) {
|
||||||
|
// 重新获取一次
|
||||||
|
logger.w('重新获取会话');
|
||||||
|
ctl.initChatData();
|
||||||
|
ctl.getConversationList();
|
||||||
|
}
|
||||||
|
|
||||||
//重新排序
|
//重新排序
|
||||||
ctl.chatList.sort((a, b) {
|
ctl.chatList.sort((a, b) {
|
||||||
final atime = a.conversation.lastMessage?.timestamp ?? 0;
|
final atime = a.conversation.lastMessage?.timestamp ?? 0;
|
||||||
@ -70,8 +88,8 @@ class GlobalBadge extends GetxController {
|
|||||||
ctl.chatList.refresh();
|
ctl.chatList.refresh();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final ctl = Get.find<ChatController>();
|
// final ctl = Get.find<ChatController>();
|
||||||
ctl.getConversationList();
|
// ctl.getConversationList();
|
||||||
_initUnreadCount();
|
_initUnreadCount();
|
||||||
_addListener();
|
_addListener();
|
||||||
}
|
}
|
||||||
@ -117,7 +135,7 @@ class GlobalBadge extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.w('不需要进行分组');
|
logger.w('新会话不需分组');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,9 +147,21 @@ class GlobalBadge extends GetxController {
|
|||||||
Get.find<TabBarController>().setBadge(TabType.chat, totalUnread.value);
|
Get.find<TabBarController>().setBadge(TabType.chat, totalUnread.value);
|
||||||
final to = res.data;
|
final to = res.data;
|
||||||
logger.i('初始化未读消息数$to');
|
logger.i('初始化未读消息数$to');
|
||||||
|
} else {
|
||||||
|
//处理安卓端重新登录后获取未读数量失败的问题
|
||||||
|
logger.e('获取初始化未读数失败:${res.desc},重新补偿获取');
|
||||||
|
Future.delayed(Duration(seconds: 1), handAndroid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 处理安卓端异常的问题
|
||||||
|
handAndroid() {
|
||||||
|
_initUnreadCount();
|
||||||
|
final ctl = Get.find<ChatController>();
|
||||||
|
ctl.initChatData();
|
||||||
|
ctl.getConversationList();
|
||||||
|
}
|
||||||
|
|
||||||
/// 添加会话未读数监听器
|
/// 添加会话未读数监听器
|
||||||
void _addListener() {
|
void _addListener() {
|
||||||
TencentImSDKPlugin.v2TIMManager.getConversationManager().addConversationListener(listener: _listener);
|
TencentImSDKPlugin.v2TIMManager.getConversationManager().addConversationListener(listener: _listener);
|
||||||
@ -146,6 +176,8 @@ class GlobalBadge extends GetxController {
|
|||||||
/// 移除监听器,防止重复注册
|
/// 移除监听器,防止重复注册
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
|
logger.i(_listener);
|
||||||
|
logger.i('移除global未读监听器');
|
||||||
TencentImSDKPlugin.v2TIMManager.getConversationManager().removeConversationListener(listener: _listener);
|
TencentImSDKPlugin.v2TIMManager.getConversationManager().removeConversationListener(listener: _listener);
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/controller/video_module_controller.dart';
|
||||||
|
import 'package:loopin/utils/common.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||||
@ -9,6 +14,18 @@ final logger = Logger();
|
|||||||
class ImCore {
|
class ImCore {
|
||||||
static bool _isInitialized = false;
|
static bool _isInitialized = false;
|
||||||
|
|
||||||
|
static Future<void> handleLogout() async {
|
||||||
|
final loginRes = await ImService.instance.logout();
|
||||||
|
if (loginRes.success) {
|
||||||
|
// 清除存储信息
|
||||||
|
Common.logout();
|
||||||
|
// 初始化视频
|
||||||
|
final videoController = Get.find<VideoModuleController>();
|
||||||
|
videoController.init();
|
||||||
|
Get.toNamed('/login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<bool> init({required int sdkAppId}) async {
|
static Future<bool> init({required int sdkAppId}) async {
|
||||||
if (_isInitialized) return true;
|
if (_isInitialized) return true;
|
||||||
|
|
||||||
@ -20,7 +37,18 @@ class ImCore {
|
|||||||
logger.i("IM连接成功");
|
logger.i("IM连接成功");
|
||||||
},
|
},
|
||||||
onConnectFailed: (code, error) => logger.e("IM连接失败: $code $error"),
|
onConnectFailed: (code, error) => logger.e("IM连接失败: $code $error"),
|
||||||
onKickedOffline: () => logger.w("IM被踢下线"),
|
onKickedOffline: () {
|
||||||
|
logger.w("IM被踢下线");
|
||||||
|
Get.snackbar(
|
||||||
|
'提示',
|
||||||
|
'您的帐号已在其他处登录',
|
||||||
|
duration: Duration(seconds: 10),
|
||||||
|
backgroundColor: Colors.red.withAlpha(230),
|
||||||
|
colorText: Colors.white,
|
||||||
|
icon: const Icon(Icons.error_outline, color: Colors.white),
|
||||||
|
);
|
||||||
|
handleLogout();
|
||||||
|
},
|
||||||
onUserSigExpired: () => logger.w("UserSig 过期"),
|
onUserSigExpired: () => logger.w("UserSig 过期"),
|
||||||
onSelfInfoUpdated: (V2TimUserFullInfo info) {
|
onSelfInfoUpdated: (V2TimUserFullInfo info) {
|
||||||
logger.i("用户信息更新: ${info.toJson()}");
|
logger.i("用户信息更新: ${info.toJson()}");
|
||||||
|
@ -29,6 +29,8 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart';
|
|||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_info_result.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_info_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
|
||||||
@ -61,16 +63,22 @@ class ImService {
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
logger.i("IM 登录成功:$userID");
|
logger.i("IM 登录成功:$userID");
|
||||||
// 初始化push服务
|
|
||||||
PushService().initPush(
|
// 初始化会话数据
|
||||||
sdkAppId: 1600080789,
|
final ctl = Get.find<ChatController>();
|
||||||
appKey: 'vkFpe55aYqfV7Sk5uGaoxhEstJ3tcI9dquk7JwG1GloDSLD2HeMWeQweWWXgNlhC',
|
await ctl.getConversationList();
|
||||||
);
|
|
||||||
// 初始化微信 SDK
|
/// 初始化微信 SDK
|
||||||
await Wxsdk.init();
|
await Wxsdk.init();
|
||||||
|
|
||||||
// 注册用户信息(基本信息+自定义信息)
|
// 注册用户信息(基本信息+自定义信息)
|
||||||
Get.put(ImUserInfoController(), permanent: true);
|
if (!Get.isRegistered<ImUserInfoController>()) {
|
||||||
|
final imInfo = Get.put(ImUserInfoController(), permanent: true);
|
||||||
|
await imInfo.refreshUserInfo();
|
||||||
|
} else {
|
||||||
|
await Get.find<ImUserInfoController>().refreshUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
// 登录成功后注册高级消息监听器
|
// 登录成功后注册高级消息监听器
|
||||||
final messageService = ImMessageListenerService();
|
final messageService = ImMessageListenerService();
|
||||||
Get.put<ImMessageListenerService>(messageService, permanent: true);
|
Get.put<ImMessageListenerService>(messageService, permanent: true);
|
||||||
@ -84,6 +92,12 @@ class ImService {
|
|||||||
|
|
||||||
/// 注册消息未读数监听器
|
/// 注册消息未读数监听器
|
||||||
Get.put(GlobalBadge(), permanent: true);
|
Get.put(GlobalBadge(), permanent: true);
|
||||||
|
|
||||||
|
// 初始化push服务
|
||||||
|
PushService().initPush(
|
||||||
|
sdkAppId: 1600080789,
|
||||||
|
appKey: 'vkFpe55aYqfV7Sk5uGaoxhEstJ3tcI9dquk7JwG1GloDSLD2HeMWeQweWWXgNlhC',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.i("IM 登录失败:${result.code} - ${result.desc}");
|
logger.i("IM 登录失败:${result.code} - ${result.desc}");
|
||||||
Get.snackbar(
|
Get.snackbar(
|
||||||
@ -101,16 +115,12 @@ class ImService {
|
|||||||
Future<ImResult> logout() async {
|
Future<ImResult> logout() async {
|
||||||
final res = await TencentImSDKPlugin.v2TIMManager.logout();
|
final res = await TencentImSDKPlugin.v2TIMManager.logout();
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
/// 清理用户信息
|
|
||||||
Get.delete<ImUserInfoController>(force: true);
|
|
||||||
|
|
||||||
/// 移出消息监听器
|
/// 移出消息监听器
|
||||||
Get.find<ImMessageListenerService>().onClose();
|
await Get.delete<ImMessageListenerService>(force: true);
|
||||||
Get.delete<ImMessageListenerService>(force: true);
|
|
||||||
|
|
||||||
/// 移出关系链监听器
|
/// 移出关系链监听器
|
||||||
Get.find<ImFriendListeners>().unregister();
|
Get.find<ImFriendListeners>().unregister();
|
||||||
Get.delete<ImFriendListeners>(force: true);
|
await Get.delete<ImFriendListeners>(force: true);
|
||||||
|
|
||||||
/// 清理tabbar
|
/// 清理tabbar
|
||||||
Get.find<TabBarController>().badgeMap.clear();
|
Get.find<TabBarController>().badgeMap.clear();
|
||||||
@ -120,13 +130,13 @@ class ImService {
|
|||||||
|
|
||||||
/// 移出未读消息监听器
|
/// 移出未读消息监听器
|
||||||
Get.find<GlobalBadge>().onClose();
|
Get.find<GlobalBadge>().onClose();
|
||||||
Get.delete<GlobalBadge>(force: true);
|
await Get.delete<GlobalBadge>(force: true);
|
||||||
|
|
||||||
/// 移除推送服务
|
/// 移除推送服务
|
||||||
PushService.unInitPush();
|
await PushService.unInitPush();
|
||||||
|
|
||||||
/// 反初始化
|
/// 反初始化
|
||||||
ImCore.unInit();
|
await ImCore.unInit();
|
||||||
}
|
}
|
||||||
return ImResult.wrapNoData(res);
|
return ImResult.wrapNoData(res);
|
||||||
}
|
}
|
||||||
@ -231,26 +241,26 @@ class ImService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final convList = res.data?.conversationList ?? [];
|
final convList = res.data?.conversationList ?? [];
|
||||||
|
logger.w('未过滤前到会话数据:$convList');
|
||||||
|
|
||||||
final userIDList = <String>[];
|
final userIDList = <String>[];
|
||||||
final groupIDList = <String>[];
|
final groupIDList = <String>[];
|
||||||
|
|
||||||
// 提前收集所有需要批量查询的 userID 和 groupID
|
// 提前收集所有需要批量查询的 userID 和 groupID
|
||||||
for (var conv in convList) {
|
for (var conv in convList) {
|
||||||
if ((conv.faceUrl == null || conv.faceUrl!.isEmpty)) {
|
if (conv.userID != null) {
|
||||||
if (conv.userID != null) {
|
userIDList.add(conv.userID!);
|
||||||
userIDList.add(conv.userID!);
|
} else if (conv.groupID != null) {
|
||||||
} else if (conv.groupID != null) {
|
groupIDList.add(conv.groupID!);
|
||||||
groupIDList.add(conv.groupID!);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String?> userFaceUrlMap = {};
|
Map<String, String?> userFaceUrlMap = {};
|
||||||
Map<String, String?> groupFaceUrlMap = {};
|
Map<String, String?> groupFaceUrlMap = {};
|
||||||
String isCustomAdmin = '0';
|
Map<String, String> isCustomAdmin = {};
|
||||||
|
|
||||||
if (userIDList.isNotEmpty) {
|
if (userIDList.isNotEmpty) {
|
||||||
final userRes = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: userIDList);
|
final userRes = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: userIDList);
|
||||||
|
|
||||||
if (userRes.code == 0) {
|
if (userRes.code == 0) {
|
||||||
for (var user in userRes.data!) {
|
for (var user in userRes.data!) {
|
||||||
final userId = user.userID ?? '';
|
final userId = user.userID ?? '';
|
||||||
@ -258,8 +268,9 @@ class ImService {
|
|||||||
|
|
||||||
// 读取管理员标识
|
// 读取管理员标识
|
||||||
final customInfo = user.customInfo;
|
final customInfo = user.customInfo;
|
||||||
|
|
||||||
if (customInfo != null) {
|
if (customInfo != null) {
|
||||||
isCustomAdmin = customInfo['admin'] ?? '0';
|
isCustomAdmin[userId] = customInfo['admin'] ?? '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,7 +291,6 @@ class ImService {
|
|||||||
final viewList = convList.map((conv) {
|
final viewList = convList.map((conv) {
|
||||||
String? faceUrl = conv.faceUrl;
|
String? faceUrl = conv.faceUrl;
|
||||||
|
|
||||||
// 如果 faceUrl 本身为空,尝试从查到的 map 中取值
|
|
||||||
if (faceUrl == null || faceUrl.isEmpty) {
|
if (faceUrl == null || faceUrl.isEmpty) {
|
||||||
if (conv.userID != null) {
|
if (conv.userID != null) {
|
||||||
faceUrl = userFaceUrlMap[conv.userID!];
|
faceUrl = userFaceUrlMap[conv.userID!];
|
||||||
@ -289,7 +299,11 @@ class ImService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConversationViewModel(conversation: conv, faceUrl: faceUrl, isCustomAdmin: isCustomAdmin);
|
return ConversationViewModel(
|
||||||
|
conversation: conv,
|
||||||
|
faceUrl: faceUrl,
|
||||||
|
isCustomAdmin: isCustomAdmin[conv.userID],
|
||||||
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
// 筛选数据,过滤掉陌生人消息
|
// 筛选数据,过滤掉陌生人消息
|
||||||
@ -337,9 +351,6 @@ class ImService {
|
|||||||
///获取所有会话数据
|
///获取所有会话数据
|
||||||
Future<ImResult<V2TimConversationResult>> getConvData(String nextSeq, int count) async {
|
Future<ImResult<V2TimConversationResult>> getConvData(String nextSeq, int count) async {
|
||||||
final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getConversationList(nextSeq: nextSeq, count: count);
|
final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getConversationList(nextSeq: nextSeq, count: count);
|
||||||
// for (var element in res.data!.conversationList) {
|
|
||||||
// logger.e('所有的会话数据:${element.toJson()}');
|
|
||||||
// }
|
|
||||||
return ImResult.wrap(res);
|
return ImResult.wrap(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +368,33 @@ class ImService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 搜索本地消息
|
||||||
|
Future<ImResult<V2TimMessageSearchResult>> searchLocalMessages({
|
||||||
|
required String page,
|
||||||
|
required String conversationID,
|
||||||
|
|
||||||
|
/// 关键词匹配机制or=0,and=1,
|
||||||
|
int type = 1,
|
||||||
|
|
||||||
|
/// ['你好','周末']
|
||||||
|
required List<String> keywordList,
|
||||||
|
|
||||||
|
/// 默认自定义消息
|
||||||
|
List<int> messageTypeList = const [1, 2],
|
||||||
|
}) async {
|
||||||
|
final searchParam = V2TimMessageSearchParam(
|
||||||
|
type: type,
|
||||||
|
conversationID: conversationID,
|
||||||
|
keywordList: keywordList,
|
||||||
|
messageTypeList: messageTypeList,
|
||||||
|
pageSize: 100,
|
||||||
|
// pageIndex: page,
|
||||||
|
searchCursor: page,
|
||||||
|
);
|
||||||
|
final V2TimValueCallback<V2TimMessageSearchResult> res = await TIMMessageManager.instance.searchLocalMessages(searchParam: searchParam);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取消息
|
/// 获取消息
|
||||||
Future<ImResult<List<V2TimMessage>>> findMessages({
|
Future<ImResult<List<V2TimMessage>>> findMessages({
|
||||||
required List<String> messageIDList,
|
required List<String> messageIDList,
|
||||||
|
@ -135,7 +135,7 @@ class PushService {
|
|||||||
logger.w(router);
|
logger.w(router);
|
||||||
if (router == null || router != '') {
|
if (router == null || router != '') {
|
||||||
// 聊天
|
// 聊天
|
||||||
if (data['userID'] != null) {
|
if (data['userID'] != null || data['userID'] != '') {
|
||||||
logger.w('有userID');
|
logger.w('有userID');
|
||||||
// 单聊,获取会话
|
// 单聊,获取会话
|
||||||
final covRes = await ImService.instance.getConversation(conversationID: 'c2c_${data['userID']}');
|
final covRes = await ImService.instance.getConversation(conversationID: 'c2c_${data['userID']}');
|
||||||
|
@ -6,8 +6,8 @@ class ShopApi {
|
|||||||
/// [nameLike] 商品名称
|
/// [nameLike] 商品名称
|
||||||
static const String shopList = '/app/product/page'; // 商品列表
|
static const String shopList = '/app/product/page'; // 商品列表
|
||||||
|
|
||||||
/// [showStatus]1=显示 [nameLike]分类名称
|
/// [showStatus]1=显示 [nameLike]分类名称 [level]1
|
||||||
static const String shopCategory = '/app/productCategory/page'; // 商品分类
|
static const String shopCategory = '/app/product/category/tree'; // 商品分类
|
||||||
/// []
|
/// []
|
||||||
static const String shopSwiperList = '/app/article/carousel'; // 商品首页轮播图
|
static const String shopSwiperList = '/app/article/carousel'; // 商品首页轮播图
|
||||||
|
|
||||||
|
@ -26,6 +26,18 @@ class NetworkOrAssetImage extends StatelessWidget {
|
|||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
fit: fit,
|
fit: fit,
|
||||||
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
|
if (loadingProgress == null) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
// 显示占位
|
||||||
|
return Image.asset(
|
||||||
|
placeholderAsset,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
fit: fit,
|
||||||
|
);
|
||||||
|
},
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
return Image.asset(
|
return Image.asset(
|
||||||
placeholderAsset,
|
placeholderAsset,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/im_service.dart';
|
|
||||||
import 'package:loopin/api/shop_api.dart';
|
import 'package:loopin/api/shop_api.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
RxInt currentTabIndex = 0.obs;
|
RxInt currentTabIndex = 0.obs;
|
||||||
|
|
||||||
/// 初始化 Tab 分类
|
/// 初始化 Tab 分类
|
||||||
void initTabs() async {
|
void initTabs({required TickerProvider vsync}) async {
|
||||||
// 释放旧的 ScrollController
|
// 释放旧的 ScrollController
|
||||||
tabs.forEach((_, state) => state.scrollController.dispose());
|
tabs.forEach((_, state) => state.scrollController.dispose());
|
||||||
tabs.clear();
|
tabs.clear();
|
||||||
@ -50,10 +49,10 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
|
|
||||||
// 赋值 tab 数据
|
// 赋值 tab 数据
|
||||||
final res = await Http.post(ShopApi.shopCategory, data: {
|
final res = await Http.post(ShopApi.shopCategory, data: {
|
||||||
'showStatus': 1,
|
'level': 1,
|
||||||
});
|
});
|
||||||
final data = res['data']['records'] as List<dynamic>;
|
final data = res['data'] as List<dynamic>;
|
||||||
logger.w(data);
|
// logger.w(data);
|
||||||
tabList.addAll(data);
|
tabList.addAll(data);
|
||||||
|
|
||||||
// 初始化每个 tab 的状态
|
// 初始化每个 tab 的状态
|
||||||
@ -70,8 +69,9 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建新的 TabController
|
// 创建新的 TabController
|
||||||
tabController = TabController(length: tabList.length, vsync: this);
|
tabController = TabController(length: tabList.length, vsync: vsync);
|
||||||
tabController!.addListener(_tabListener);
|
|
||||||
|
tabController?.addListener(_tabListener);
|
||||||
// 初始化第一个 tab 的数据
|
// 初始化第一个 tab 的数据
|
||||||
if (tabList.isNotEmpty) {
|
if (tabList.isNotEmpty) {
|
||||||
loadSwiperData();
|
loadSwiperData();
|
||||||
@ -118,7 +118,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
|
|
||||||
final data = res['data']['records'];
|
final data = res['data']['records'];
|
||||||
tab.dataList.addAll(data);
|
tab.dataList.addAll(data);
|
||||||
logger.w(res);
|
// logger.w(res);
|
||||||
|
|
||||||
tab.currentPage.value += 1;
|
tab.currentPage.value += 1;
|
||||||
tab.isLoading.value = false;
|
tab.isLoading.value = false;
|
||||||
@ -131,14 +131,13 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
'type': 1,
|
'type': 1,
|
||||||
});
|
});
|
||||||
final data = res['data'];
|
final data = res['data'];
|
||||||
logger.w(res);
|
// logger.w(res);
|
||||||
swiperData.assignAll(data);
|
swiperData.assignAll(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
tabController?.removeListener(_tabListener);
|
tabController?.removeListener(_tabListener);
|
||||||
tabController?.dispose();
|
|
||||||
tabs.forEach((_, state) => state.scrollController.dispose());
|
tabs.forEach((_, state) => state.scrollController.dispose());
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ library;
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
|
||||||
import 'package:loopin/IM/controller/tab_bar_controller.dart';
|
import 'package:loopin/IM/controller/tab_bar_controller.dart';
|
||||||
import 'package:loopin/models/tab_type.dart';
|
import 'package:loopin/models/tab_type.dart';
|
||||||
import 'package:loopin/pages/video/module/recommend.dart';
|
import 'package:loopin/pages/video/module/recommend.dart';
|
||||||
@ -222,10 +221,11 @@ class _LayoutState extends State<Layout> {
|
|||||||
}
|
}
|
||||||
if (index == 3) {
|
if (index == 3) {
|
||||||
// 更新会话列表
|
// 更新会话列表
|
||||||
final ctl = Get.find<ChatController>();
|
// final ctl = Get.find<ChatController>();
|
||||||
if (ctl.chatList.isEmpty) {
|
// logger.e(ctl.chatList.toJson());
|
||||||
Get.find<ChatController>().getConversationList();
|
// if (ctl.chatList.isEmpty) {
|
||||||
}
|
// Get.find<ChatController>().getConversationList();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if (index == 4) {
|
if (index == 4) {
|
||||||
myPageKey.currentState?.refreshData();
|
myPageKey.currentState?.refreshData();
|
||||||
|
@ -10,6 +10,7 @@ import 'package:loopin/IM/controller/chat_controller.dart';
|
|||||||
import 'package:loopin/IM/controller/tab_bar_controller.dart';
|
import 'package:loopin/IM/controller/tab_bar_controller.dart';
|
||||||
import 'package:loopin/IM/im_core.dart' as im_core;
|
import 'package:loopin/IM/im_core.dart' as im_core;
|
||||||
import 'package:loopin/IM/im_service.dart';
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/controller/shop_index_controller.dart';
|
||||||
import 'package:loopin/service/http_config.dart';
|
import 'package:loopin/service/http_config.dart';
|
||||||
import 'package:loopin/utils/common.dart';
|
import 'package:loopin/utils/common.dart';
|
||||||
import 'package:loopin/utils/lifecycle_handler.dart';
|
import 'package:loopin/utils/lifecycle_handler.dart';
|
||||||
@ -21,13 +22,14 @@ import 'package:shirne_dialog/shirne_dialog.dart';
|
|||||||
import 'layouts/index.dart';
|
import 'layouts/index.dart';
|
||||||
// 引入路由配置
|
// 引入路由配置
|
||||||
import 'router/index.dart';
|
import 'router/index.dart';
|
||||||
// import 'utils/common.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// 注入tabbar
|
// 注入tabbar
|
||||||
Get.put(TabBarController());
|
Get.put(TabBarController(), permanent: true);
|
||||||
// 注入会话列表
|
// 注入会话列表
|
||||||
Get.put(ChatController());
|
Get.put(ChatController(), permanent: true);
|
||||||
|
// 注入底导菜单易选的
|
||||||
|
Get.put(ShopIndexController(), permanent: true);
|
||||||
|
|
||||||
// 监听app前后台状态
|
// 监听app前后台状态
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/// 枚举定义:所有的会话类型分组,用于一级消息分类
|
/// 枚举定义:所有的会话类型分组,用于一级消息分类和路由地址
|
||||||
enum ConversationType {
|
enum ConversationType {
|
||||||
noFriend, // 陌生人消息
|
noFriend, // 陌生人消息
|
||||||
system, //系统消息
|
system, //系统消息
|
||||||
|
@ -4,7 +4,7 @@ enum NotifyMessageType {
|
|||||||
systemNotify, // 系统->通知
|
systemNotify, // 系统->通知
|
||||||
systemReport, // 系统->举报下架(视频,视频评论)
|
systemReport, // 系统->举报下架(视频,视频评论)
|
||||||
systemCheck, // 系统->审核结果(复审,驳回 ,通过)
|
systemCheck, // 系统->审核结果(复审,驳回 ,通过)
|
||||||
systemPush, //系统->推广
|
systemPush, //系统->推广类的
|
||||||
interactionComment, //互动->评论
|
interactionComment, //互动->评论
|
||||||
interactionAt, //互动->视频评论中的@
|
interactionAt, //互动->视频评论中的@
|
||||||
interactionLike, //互动->点赞
|
interactionLike, //互动->点赞
|
||||||
@ -18,6 +18,26 @@ enum NotifyMessageType {
|
|||||||
groupNotifyLeaveUp, // 群通知->群升级为达人群通知
|
groupNotifyLeaveUp, // 群通知->群升级为达人群通知
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 常量映射
|
||||||
|
class NotifyMessageTypeConstants {
|
||||||
|
static const String newFoucs = 'newFoucs';
|
||||||
|
static const String systemNotify = 'systemNotify';
|
||||||
|
static const String systemReport = 'systemReport';
|
||||||
|
static const String systemCheck = 'systemCheck';
|
||||||
|
static const String systemPush = 'systemPush';
|
||||||
|
static const String interactionComment = 'interactionComment';
|
||||||
|
static const String interactionAt = 'interactionAt';
|
||||||
|
static const String interactionLike = 'interactionLike';
|
||||||
|
static const String interactionReply = 'interactionReply';
|
||||||
|
static const String orderRecharge = 'orderRecharge';
|
||||||
|
static const String orderPay = 'orderPay';
|
||||||
|
static const String orderRefund = 'orderRefund';
|
||||||
|
static const String groupNotifyCheck = 'groupNotifyCheck';
|
||||||
|
static const String groupNotifyAccpet = 'groupNotifyAccpet';
|
||||||
|
static const String groupNotifyFail = 'groupNotifyFail';
|
||||||
|
static const String groupNotifyLeaveUp = 'groupNotifyLeaveUp';
|
||||||
|
}
|
||||||
|
|
||||||
extension NotifyMessageTypeExtension on NotifyMessageType {
|
extension NotifyMessageTypeExtension on NotifyMessageType {
|
||||||
String get name {
|
String get name {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
13
lib/models/share_type.dart
Normal file
13
lib/models/share_type.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/// 枚举定义:所有分享相关的
|
||||||
|
enum ShareType { video, shop }
|
||||||
|
|
||||||
|
extension ShareTypeExtension on ShareType {
|
||||||
|
String get name {
|
||||||
|
switch (this) {
|
||||||
|
case ShareType.video:
|
||||||
|
return 'https://wuzhongjie.com.cn/video';
|
||||||
|
case ShareType.shop:
|
||||||
|
return 'https://wuzhongjie.com.cn/shop';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -72,36 +72,38 @@ class _LoginState extends State<Login> {
|
|||||||
String userId = obj['userId'];
|
String userId = obj['userId'];
|
||||||
String userSig = obj['userSig'];
|
String userSig = obj['userSig'];
|
||||||
// 初始化im_sdk
|
// 初始化im_sdk
|
||||||
await im_core.ImCore.init(sdkAppId: 1600080789);
|
final initRes = await im_core.ImCore.init(sdkAppId: 1600080789);
|
||||||
|
if (initRes) {
|
||||||
|
// String userId = '1940667704585248769'; //13212279365
|
||||||
|
// String userId = '1943510443312078850'; //18832510385
|
||||||
|
// String userSig =
|
||||||
|
// 'eJwtjcEKgkAURf9l1iFPm*e8EdoYYUWFURAtg5nk5VRiEln0703q8p57Ofcj9qtd8LS1SEQUgBh1mY29NXzmDodaQhwrBRIJI0kq1sPsYcpTVbERSRgDAIEi3Tf2VXFtPUfEyFc9bfj6ZwrH4J1Ig4UL-6LX0ihyS7U5bi-Wzd8LzrK8TFs6TJ1sZwWGxlGas71PxPcHwH4y9Q__';
|
||||||
|
// 'eJwtzLEKwjAUheF3ySwlNzXNbcHFxSIOaqTWUUgsF1FDG2tEfHdj2-F8P5wPO2x00tuWFUwknM2GTcbePV1oYEBMhQSeopxyZ65n58iwAjLOOXKF*VhscNTa6FJKEdOonm5-UxJQpZhN2lET3599Xllbv9ZBH2uHuDfvst5tG6FX0EFYVhpOpZ973z8W7PsDmYwyIw__';
|
||||||
|
|
||||||
// String userId = '1940667704585248769'; //13212279365
|
try {
|
||||||
// String userId = '1943510443312078850'; //18832510385
|
final loginRes = await ImService.instance.login(userID: userId, userSig: userSig);
|
||||||
// String userSig =
|
|
||||||
// 'eJwtjcEKgkAURf9l1iFPm*e8EdoYYUWFURAtg5nk5VRiEln0703q8p57Ofcj9qtd8LS1SEQUgBh1mY29NXzmDodaQhwrBRIJI0kq1sPsYcpTVbERSRgDAIEi3Tf2VXFtPUfEyFc9bfj6ZwrH4J1Ig4UL-6LX0ihyS7U5bi-Wzd8LzrK8TFs6TJ1sZwWGxlGas71PxPcHwH4y9Q__';
|
|
||||||
// 'eJwtzLEKwjAUheF3ySwlNzXNbcHFxSIOaqTWUUgsF1FDG2tEfHdj2-F8P5wPO2x00tuWFUwknM2GTcbePV1oYEBMhQSeopxyZ65n58iwAjLOOXKF*VhscNTa6FJKEdOonm5-UxJQpZhN2lET3599Xllbv9ZBH2uHuDfvst5tG6FX0EFYVhpOpZ973z8W7PsDmYwyIw__';
|
|
||||||
|
|
||||||
try {
|
if (loginRes.success) {
|
||||||
final loginRes = await ImService.instance.login(userID: userId, userSig: userSig);
|
// 存储登录信息
|
||||||
|
Storage.write('hasLogged', true);
|
||||||
|
Storage.write('userSig', userSig);
|
||||||
|
Storage.write('userId', userId);
|
||||||
|
Storage.write('token', obj['access_token']);
|
||||||
|
// 获取用户账户信息
|
||||||
|
// final accountRes = await Http.get(CommonApi.accountInfo);
|
||||||
|
// logger.e(accountRes);
|
||||||
|
// 刷新短视频列表
|
||||||
|
final videoController = Get.find<VideoModuleController>();
|
||||||
|
videoController.markNeedRefresh();
|
||||||
|
dialogController.close();
|
||||||
|
|
||||||
logger.i(loginRes);
|
Get.back();
|
||||||
if (loginRes.success) {
|
}
|
||||||
// 存储登录信息
|
} catch (e) {
|
||||||
Storage.write('hasLogged', true);
|
logger.i(e);
|
||||||
Storage.write('userSig', userSig);
|
|
||||||
Storage.write('userId', userId);
|
|
||||||
Storage.write('token', obj['access_token']);
|
|
||||||
// 获取用户账户信息
|
|
||||||
final accountRes = await Http.get('${CommonApi.accountInfo}');
|
|
||||||
logger.i(accountRes);
|
|
||||||
// 刷新短视频列表
|
|
||||||
final videoController = Get.find<VideoModuleController>();
|
|
||||||
videoController.markNeedRefresh();
|
|
||||||
dialogController.close();
|
|
||||||
|
|
||||||
Get.back();
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
logger.i(e);
|
logger.e('登陆异常:im初始化失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,9 @@ 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/global_badge.dart';
|
import 'package:loopin/IM/global_badge.dart';
|
||||||
import 'package:loopin/IM/im_service.dart';
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/components/scan_util.dart';
|
import 'package:loopin/components/scan_util.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
import 'package:loopin/models/conversation_view_model.dart';
|
import 'package:loopin/models/conversation_view_model.dart';
|
||||||
import 'package:loopin/utils/parse_message_summary.dart';
|
import 'package:loopin/utils/parse_message_summary.dart';
|
||||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||||
@ -323,6 +325,10 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
physics: BouncingScrollPhysics(),
|
physics: BouncingScrollPhysics(),
|
||||||
itemCount: chatList.length,
|
itemCount: chatList.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
final isNoFriend = chatList[index].conversation.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false;
|
||||||
|
final isAdmin = chatList[index].isCustomAdmin != null && chatList[index].isCustomAdmin != '0';
|
||||||
|
|
||||||
|
logger.e(chatList[index].isCustomAdmin);
|
||||||
return Ink(
|
return Ink(
|
||||||
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
@ -334,35 +340,10 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// 头图
|
// 头图
|
||||||
ClipOval(
|
ClipOval(
|
||||||
child: Builder(
|
child: NetworkOrAssetImage(
|
||||||
builder: (context) {
|
imageUrl: chatList[index].faceUrl,
|
||||||
final faceUrl = chatList[index].faceUrl;
|
width: 50,
|
||||||
final isNetwork =
|
height: 50,
|
||||||
faceUrl != null && faceUrl.isNotEmpty && (faceUrl.startsWith('http://') || faceUrl.startsWith('https://'));
|
|
||||||
if (isNetwork) {
|
|
||||||
return Image.network(
|
|
||||||
faceUrl,
|
|
||||||
height: 50.0,
|
|
||||||
width: 50.0,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder: (context, error, stackTrace) {
|
|
||||||
return Image.asset(
|
|
||||||
'assets/images/pic1.jpg',
|
|
||||||
height: 50.0,
|
|
||||||
width: 50.0,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Image.asset(
|
|
||||||
(faceUrl != null && faceUrl.isNotEmpty) ? faceUrl : 'assets/images/pic1.jpg',
|
|
||||||
height: 50.0,
|
|
||||||
width: 50.0,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -371,17 +352,11 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// Text(
|
|
||||||
// chatList[index].conversation.showName ?? '',
|
|
||||||
// style: const TextStyle(fontSize: 16.0),
|
|
||||||
// ),
|
|
||||||
Text(
|
Text(
|
||||||
chatList[index].conversation.showName ?? '',
|
chatList[index].conversation.showName ?? '未知',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: (chatList[index].conversation.conversationGroupList?.isNotEmpty ?? false) ? 20 : 16,
|
fontSize: (isAdmin || isNoFriend) ? 20 : 16,
|
||||||
fontWeight:
|
fontWeight: (isAdmin || isNoFriend) ? FontWeight.bold : FontWeight.normal),
|
||||||
(chatList[index].conversation.conversationGroupList?.isNotEmpty ?? false) ? FontWeight.bold : FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2.0),
|
const SizedBox(height: 2.0),
|
||||||
Text(
|
Text(
|
||||||
@ -400,7 +375,7 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: chatList[index].conversation.lastMessage?.timestamp != null,
|
visible: !(isAdmin || isNoFriend),
|
||||||
child: Text(
|
child: Text(
|
||||||
// 转成日期字符串显示
|
// 转成日期字符串显示
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
@ -418,19 +393,25 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: chatList[index].isCustomAdmin != '0',
|
visible: (isAdmin || isNoFriend),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
color: Colors.grey,
|
color: Colors.blueGrey,
|
||||||
size: 12.0,
|
size: 14.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (chatList[index].isCustomAdmin != '0') {
|
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) {
|
||||||
// 跳转系统消息级别页面
|
// 跳转对应的通知消息页
|
||||||
|
logger.e(chatList[index].isCustomAdmin);
|
||||||
|
Get.toNamed('/${chatList[index].isCustomAdmin}', arguments: chatList[index].conversation.lastMessage);
|
||||||
|
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
|
||||||
|
// 跳转陌生人消息页面
|
||||||
|
logger.e(chatList[index].conversation.conversationGroupList);
|
||||||
|
Get.toNamed('/noFriend');
|
||||||
} else {
|
} else {
|
||||||
// 会话id查询会话详情
|
// 会话id查询会话详情
|
||||||
Get.toNamed('/chat', arguments: chatList[index].conversation);
|
Get.toNamed('/chat', arguments: chatList[index].conversation);
|
||||||
|
0
lib/pages/chat/notify/groupNotify.dart
Normal file
0
lib/pages/chat/notify/groupNotify.dart
Normal file
368
lib/pages/chat/notify/interaction.dart
Normal file
368
lib/pages/chat/notify/interaction.dart
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
/// 聊天首页模板
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
|
import 'package:loopin/models/notify_message.type.dart';
|
||||||
|
import 'package:loopin/styles/index.dart';
|
||||||
|
import 'package:loopin/utils/index.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
|
|
||||||
|
// interactionComment, //互动->评论
|
||||||
|
// interactionAt, //互动->视频评论中的@
|
||||||
|
// interactionLike, //互动->点赞
|
||||||
|
// interactionReply, //互动->评论回复
|
||||||
|
|
||||||
|
class Interaction extends StatefulWidget {
|
||||||
|
const Interaction({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Interaction> createState() => InteractionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class InteractionState extends State<Interaction> with SingleTickerProviderStateMixin {
|
||||||
|
bool isLoading = false; // 是否在加载中
|
||||||
|
bool hasMore = true; // 是否还有更多数据
|
||||||
|
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||||
|
final ScrollController chatController = ScrollController();
|
||||||
|
String page = '';
|
||||||
|
|
||||||
|
///-------------------
|
||||||
|
V2TimConversation? conv;
|
||||||
|
RxList<V2TimMessage> msgList = <V2TimMessage>[].obs;
|
||||||
|
final RxString selectedMessageType = '全部'.obs;
|
||||||
|
final List<Map<String, dynamic>> messageFilters = [
|
||||||
|
{'value': 'all', 'label': '全部', 'icon': Icons.all_inclusive},
|
||||||
|
{'value': NotifyMessageTypeConstants.interactionLike, 'label': '点赞', 'icon': Icons.favorite_border},
|
||||||
|
{'value': NotifyMessageTypeConstants.interactionComment, 'label': '评论', 'icon': Icons.comment},
|
||||||
|
{'value': NotifyMessageTypeConstants.interactionAt, 'label': '@我', 'icon': Icons.alternate_email},
|
||||||
|
{'value': NotifyMessageTypeConstants.interactionReply, 'label': '回复', 'icon': Icons.reply},
|
||||||
|
];
|
||||||
|
RxList demoList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (Get.arguments != null && Get.arguments is V2TimConversation) {
|
||||||
|
// 如果有参数
|
||||||
|
conv = Get.arguments as V2TimConversation;
|
||||||
|
final lastmsg = conv?.lastMessage;
|
||||||
|
if (lastmsg != null) {
|
||||||
|
msgList.add(lastmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chatController.addListener(() {
|
||||||
|
if (_throttleFlag.value) return;
|
||||||
|
if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
|
||||||
|
_throttleFlag.value = true;
|
||||||
|
getMsgData().then((_) {
|
||||||
|
// 解锁
|
||||||
|
Future.delayed(Duration(milliseconds: 1000), () {
|
||||||
|
_throttleFlag.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
chatController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页获取全部数据
|
||||||
|
Future<void> getMsgData() async {
|
||||||
|
// 获取最旧一条消息作为游标
|
||||||
|
V2TimMessage? lastRealMsg;
|
||||||
|
lastRealMsg = msgList.last;
|
||||||
|
final res = await ImService.instance.getHistoryMessageList(
|
||||||
|
userID: ConversationType.interaction.name, // userID为固定的interaction
|
||||||
|
lastMsg: lastRealMsg,
|
||||||
|
);
|
||||||
|
if (res.success && res.data != null) {
|
||||||
|
msgList.addAll(res.data!);
|
||||||
|
if (res.data!.isEmpty) {
|
||||||
|
hasMore = false;
|
||||||
|
}
|
||||||
|
logger.i('聊天数据加载成功');
|
||||||
|
} else {
|
||||||
|
logger.e('聊天数据加载失败:${res.desc}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
Future<void> handleRefresh() async {
|
||||||
|
await Future.delayed(Duration(seconds: 5));
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取不同的消息分类数据
|
||||||
|
void _filterMessages(String filterType) async {
|
||||||
|
logger.e(filterType);
|
||||||
|
final res = await ImService.instance.searchLocalMessages(
|
||||||
|
page: page,
|
||||||
|
conversationID: 'c2c_${ConversationType.interaction.name}',
|
||||||
|
keywordList: ['action', filterType],
|
||||||
|
);
|
||||||
|
logger.e(res.data!.toLogString());
|
||||||
|
if (res.success && res.data != null) {
|
||||||
|
final resultList = res.data?.messageSearchResultItems ?? [];
|
||||||
|
if (resultList.isNotEmpty) {
|
||||||
|
for (var item in resultList) {
|
||||||
|
logger.e(item.toJson());
|
||||||
|
msgList.addAll(item.messageList ?? []);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.e('数据为空${res.desc}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉选择
|
||||||
|
void _showFilterMenu(BuildContext context) {
|
||||||
|
final double screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
||||||
|
|
||||||
|
showMenu(
|
||||||
|
context: context,
|
||||||
|
position: RelativeRect.fromLTRB(
|
||||||
|
0,
|
||||||
|
kToolbarHeight + statusBarHeight + 1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
elevation: 8,
|
||||||
|
color: Colors.white,
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: screenWidth,
|
||||||
|
maxWidth: screenWidth,
|
||||||
|
),
|
||||||
|
useRootNavigator: true, //移除默认的内边距
|
||||||
|
items: messageFilters.map((filter) {
|
||||||
|
return PopupMenuItem<String>(
|
||||||
|
value: filter['value'],
|
||||||
|
padding: EdgeInsets.zero, // 移除菜单项的内边距
|
||||||
|
child: Container(
|
||||||
|
width: screenWidth,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: filter['value'] != messageFilters.last['value'] ? BorderSide(color: Colors.grey[200]!) : BorderSide.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(filter['icon'], size: 22, color: Colors.grey[700]),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
filter['label'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: selectedMessageType.value == filter['label'] ? Colors.orange : Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (selectedMessageType.value == filter['label']) Icon(Icons.check, size: 20, color: Colors.orange),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
).then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
final selectedFilter = messageFilters.firstWhere((f) => f['value'] == value);
|
||||||
|
selectedMessageType.value = selectedFilter['label'];
|
||||||
|
_filterMessages(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey[50],
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
forceMaterialTransparency: true,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(1.0),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Obx(
|
||||||
|
() => GestureDetector(
|
||||||
|
onTap: () => _showFilterMenu(context),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
selectedMessageType.value,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_drop_down, color: Colors.black, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
body: ScrollConfiguration(
|
||||||
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
color: Color(0xFFFF5000),
|
||||||
|
displacement: 10.0,
|
||||||
|
onRefresh: handleRefresh,
|
||||||
|
child: Obx(() {
|
||||||
|
return ListView.builder(
|
||||||
|
controller: chatController,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
// itemCount: msgList.length,
|
||||||
|
itemCount: demoList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
//检测cloudCustomData
|
||||||
|
// interactionComment, //互动->评论
|
||||||
|
// interactionAt, //互动->视频评论中的@
|
||||||
|
// interactionLike, //互动->点赞
|
||||||
|
// interactionReply, //互动->评论回复
|
||||||
|
|
||||||
|
//----
|
||||||
|
// final element =msgList[index].customElem!;
|
||||||
|
// final cloudCustomData = msgList[index].cloudCustomData;
|
||||||
|
// final desc = msgList[index].customElem!.desc!;
|
||||||
|
// final jsonData = msgList[index].customElem!.data;
|
||||||
|
|
||||||
|
// ----测试数据
|
||||||
|
final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||||
|
final item = jsonDecode(jsonData); // 数据
|
||||||
|
final desc = '测试desc';
|
||||||
|
final cloudCustomData = 'interactionLike';
|
||||||
|
V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
spacing: 10.0,
|
||||||
|
children: <Widget>[
|
||||||
|
// 头像
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
// 点击头像转到对方主页
|
||||||
|
// 先获取视频详情
|
||||||
|
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||||
|
//
|
||||||
|
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||||
|
// Get.toNamed('/vloger', arguments: res['data']);
|
||||||
|
Get.toNamed('/vloger');
|
||||||
|
},
|
||||||
|
child: ClipOval(
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: item['faceUrl'],
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 消息
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// 点击头像转到对方主页
|
||||||
|
// 先获取视频详情
|
||||||
|
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||||
|
//
|
||||||
|
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||||
|
// Get.toNamed('/vloger', arguments: res['data']);
|
||||||
|
Get.toNamed('/vloger');
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
// 昵称
|
||||||
|
Text(
|
||||||
|
item['nickName'] ?? '未知',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2.0),
|
||||||
|
// 描述内容
|
||||||
|
Text(
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
|
// desc,
|
||||||
|
'很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
Utils.formatTime(element.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 右侧
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Visibility(
|
||||||
|
visible: true,
|
||||||
|
// 视频首图
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: item['firstFrameImg'],
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
width: 40,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5.0),
|
||||||
|
// 角标
|
||||||
|
Visibility(
|
||||||
|
visible: !(element.isRead ?? true),
|
||||||
|
child: FStyle.badge(0, isdot: true),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
0
lib/pages/chat/notify/newFoucs.dart
Normal file
0
lib/pages/chat/notify/newFoucs.dart
Normal file
235
lib/pages/chat/notify/noFriend.dart
Normal file
235
lib/pages/chat/notify/noFriend.dart
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/// 聊天首页模板
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
|
import 'package:loopin/styles/index.dart';
|
||||||
|
import 'package:loopin/utils/index.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_filter.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
|
|
||||||
|
// noFriend, //陌生人消息
|
||||||
|
|
||||||
|
class Nofriend extends StatefulWidget {
|
||||||
|
const Nofriend({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Nofriend> createState() => NofriendState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class NofriendState extends State<Nofriend> with SingleTickerProviderStateMixin {
|
||||||
|
bool isLoading = false; // 是否在加载中
|
||||||
|
bool hasMore = true; // 是否还有更多数据
|
||||||
|
final RxBool _throttleFlag = false.obs; // 滚动节流锁
|
||||||
|
final ScrollController chatController = ScrollController();
|
||||||
|
int page = 1;
|
||||||
|
|
||||||
|
///-------------------
|
||||||
|
RxList<V2TimConversation> convList = <V2TimConversation>[].obs;
|
||||||
|
|
||||||
|
RxList demoList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getMsgData();
|
||||||
|
chatController.addListener(() {
|
||||||
|
if (_throttleFlag.value) return;
|
||||||
|
if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
|
||||||
|
_throttleFlag.value = true;
|
||||||
|
getMsgData().then((_) {
|
||||||
|
// 解锁
|
||||||
|
Future.delayed(Duration(milliseconds: 1000), () {
|
||||||
|
_throttleFlag.value = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
chatController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页获取全部数据
|
||||||
|
Future<void> getMsgData() async {
|
||||||
|
final res = await ImService.instance.getConversationListByFilter(
|
||||||
|
filter: V2TimConversationFilter(conversationGroup: ConversationType.noFriend.name),
|
||||||
|
nextSeq: page,
|
||||||
|
);
|
||||||
|
logger.i('获取会话数据成功:${res.data!.toJson()}');
|
||||||
|
if (res.success && res.data != null) {
|
||||||
|
final newList = res.data!.conversationList ?? [];
|
||||||
|
final isFinished = res.data!.isFinished ?? true;
|
||||||
|
convList.addAll(newList);
|
||||||
|
if (isFinished) {
|
||||||
|
hasMore = false;
|
||||||
|
// 加载没数据了
|
||||||
|
page = int.parse(res.data!.nextSeq ?? '0');
|
||||||
|
} else {
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
logger.i('获取会话数据成功:$newList');
|
||||||
|
} else {
|
||||||
|
logger.e('获取会话数据失败:${res.data!.isFinished}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
Future<void> handleRefresh() async {
|
||||||
|
await Future.delayed(Duration(seconds: 5));
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey[50],
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
forceMaterialTransparency: true,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: Size.fromHeight(1.0),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'陌生人消息',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
body: ScrollConfiguration(
|
||||||
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
color: Color(0xFFFF5000),
|
||||||
|
displacement: 10.0,
|
||||||
|
onRefresh: handleRefresh,
|
||||||
|
child: Obx(() {
|
||||||
|
return ListView.builder(
|
||||||
|
controller: chatController,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
// itemCount: convList.length,
|
||||||
|
itemCount: demoList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
// ----测试数据
|
||||||
|
final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||||
|
final item = jsonDecode(jsonData); // 数据
|
||||||
|
final desc = '测试desc';
|
||||||
|
final cloudCustomData = 'interactionLike';
|
||||||
|
V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
spacing: 10.0,
|
||||||
|
children: <Widget>[
|
||||||
|
// 头像
|
||||||
|
InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
// 点击头像转到对方主页
|
||||||
|
// 先获取视频详情
|
||||||
|
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||||
|
//
|
||||||
|
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||||
|
// Get.toNamed('/vloger', arguments: res['data']);
|
||||||
|
Get.toNamed('/vloger');
|
||||||
|
},
|
||||||
|
child: ClipOval(
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: item['faceUrl'],
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 消息
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// 点击头像转到对应的聊天
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
// 昵称
|
||||||
|
Text(
|
||||||
|
item['nickName'] ?? '未知',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2.0),
|
||||||
|
// 描述内容
|
||||||
|
Text(
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
|
// desc,
|
||||||
|
'很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
Utils.formatTime(element.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 右侧
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Visibility(
|
||||||
|
visible: true,
|
||||||
|
// 视频首图
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: item['firstFrameImg'],
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
width: 40,
|
||||||
|
height: 60,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5.0),
|
||||||
|
// 角标
|
||||||
|
Visibility(
|
||||||
|
visible: !(element.isRead ?? true),
|
||||||
|
child: FStyle.badge(0, isdot: true),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
0
lib/pages/chat/notify/order.dart
Normal file
0
lib/pages/chat/notify/order.dart
Normal file
444
lib/pages/chat/notify/system.dart
Normal file
444
lib/pages/chat/notify/system.dart
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
/// 聊天首页模板
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/global_badge.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/components/scan_util.dart';
|
||||||
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
|
import 'package:loopin/models/conversation_view_model.dart';
|
||||||
|
import 'package:loopin/styles/index.dart';
|
||||||
|
import 'package:loopin/utils/parse_message_summary.dart';
|
||||||
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||||
|
|
||||||
|
// systemNotify, // 系统->通知
|
||||||
|
// systemReport, // 系统->举报下架(视频,视频评论)
|
||||||
|
// systemCheck, // 系统->审核结果(复审,驳回 ,通过)
|
||||||
|
// systemPush, //系统->推广类的
|
||||||
|
|
||||||
|
class System extends StatefulWidget {
|
||||||
|
const System({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<System> createState() => SystemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class SystemState extends State<System> {
|
||||||
|
late final ChatController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller = Get.find<ChatController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deletConv(context, ConversationViewModel item) async {
|
||||||
|
final res = await ImService.instance.deleteConversation(conversationID: item.conversation.conversationID);
|
||||||
|
if (res.success) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
controller.chatList.remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 长按坐标点
|
||||||
|
double posDX = 0.0;
|
||||||
|
double posDY = 0.0;
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
Future<void> handleRefresh() async {
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 长按菜单
|
||||||
|
void showContextMenu(BuildContext context, ConversationViewModel item) {
|
||||||
|
bool isLeft = posDX > MediaQuery.of(context).size.width / 2 ? false : true;
|
||||||
|
bool isTop = posDY > MediaQuery.of(context).size.height / 2 ? false : true;
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierColor: Colors.black12, // 遮罩透明
|
||||||
|
builder: (context) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: isTop ? posDY : posDY - 135,
|
||||||
|
left: isLeft ? posDX : posDX - 135,
|
||||||
|
width: 135,
|
||||||
|
child: Material(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||||
|
color: Colors.white,
|
||||||
|
elevation: 2.0,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'设为免打扰',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'置顶消息',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'不显示该消息',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'删除',
|
||||||
|
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
onTap: () {
|
||||||
|
deletConv(context, item);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey[50],
|
||||||
|
appBar: AppBar(
|
||||||
|
forceMaterialTransparency: true,
|
||||||
|
title: Row(
|
||||||
|
spacing: 8.0,
|
||||||
|
children: [
|
||||||
|
Text('消息'),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 4.0),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.0), boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withAlpha(20),
|
||||||
|
offset: Offset(0.0, 1.0),
|
||||||
|
blurRadius: 2.0,
|
||||||
|
spreadRadius: 0.0,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
if (Get.find<GlobalBadge>().totalUnread > 0) {
|
||||||
|
final res = await ImService.instance.clearConversationUnreadCount(conversationID: '');
|
||||||
|
if (res.success) {
|
||||||
|
MyDialog.toast('操作成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||||
|
} else {
|
||||||
|
MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
spacing: 3.0,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.cleaning_services_sharp,
|
||||||
|
size: 14.0,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'清除未读',
|
||||||
|
style: TextStyle(fontSize: 12.0),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
/// 先不做搜索功能了后面再说
|
||||||
|
// IconButton(
|
||||||
|
// icon: const Icon(Icons.search),
|
||||||
|
// onPressed: () {},
|
||||||
|
// ),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add_circle_outline),
|
||||||
|
onPressed: () async {
|
||||||
|
final paddingTop = MediaQuery.of(Get.context!).padding.top;
|
||||||
|
|
||||||
|
final selected = await showMenu(
|
||||||
|
context: Get.context!,
|
||||||
|
position: RelativeRect.fromLTRB(
|
||||||
|
double.infinity,
|
||||||
|
kToolbarHeight + paddingTop - 12,
|
||||||
|
8,
|
||||||
|
double.infinity,
|
||||||
|
),
|
||||||
|
color: FStyle.primaryColor,
|
||||||
|
elevation: 8,
|
||||||
|
items: [
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'group',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.group, color: Colors.white, size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'发起群聊',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'friend',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.person_add, color: Colors.white, size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'添加朋友',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'scan',
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'扫一扫',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected != null) {
|
||||||
|
switch (selected) {
|
||||||
|
case 'group':
|
||||||
|
print('点击了发起群聊');
|
||||||
|
break;
|
||||||
|
case 'friend':
|
||||||
|
print('点击了添加朋友');
|
||||||
|
break;
|
||||||
|
case 'scan':
|
||||||
|
print('点击了扫一扫');
|
||||||
|
ScanUtil.openScanner(onResult: (code) {
|
||||||
|
print('扫码结果:$code');
|
||||||
|
Get.snackbar('扫码成功', code);
|
||||||
|
// 在这里继续你的业务逻辑,比如跳转页面、请求接口等
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: ScrollConfiguration(
|
||||||
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withAlpha(10),
|
||||||
|
offset: Offset(0.0, 1.0),
|
||||||
|
blurRadius: 2.0,
|
||||||
|
spreadRadius: 0.0,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'assets/images/svg/order.svg',
|
||||||
|
height: 36.0,
|
||||||
|
width: 36.0,
|
||||||
|
),
|
||||||
|
Text('群聊'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'assets/images/svg/kefu.svg',
|
||||||
|
height: 36.0,
|
||||||
|
width: 36.0,
|
||||||
|
),
|
||||||
|
Text('互关'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'assets/images/svg/comment.svg',
|
||||||
|
height: 36.0,
|
||||||
|
width: 36.0,
|
||||||
|
),
|
||||||
|
Text('粉丝'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'assets/images/svg/comment.svg',
|
||||||
|
height: 36.0,
|
||||||
|
width: 36.0,
|
||||||
|
),
|
||||||
|
Text('关注'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
color: Color(0xFFFF5000),
|
||||||
|
displacement: 10.0,
|
||||||
|
onRefresh: handleRefresh,
|
||||||
|
child: Obx(() {
|
||||||
|
final chatList = controller.chatList;
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
itemCount: chatList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return Ink(
|
||||||
|
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
||||||
|
child: InkWell(
|
||||||
|
splashColor: Colors.grey[200],
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
spacing: 10.0,
|
||||||
|
children: <Widget>[
|
||||||
|
// 头图
|
||||||
|
ClipOval(
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: chatList[index].faceUrl,
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 消息
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
chatList[index].conversation.showName ?? '未知',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) ||
|
||||||
|
chatList[index].isCustomAdmin != '0'
|
||||||
|
? 20
|
||||||
|
: 16,
|
||||||
|
fontWeight: (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) ||
|
||||||
|
chatList[index].isCustomAdmin != '0'
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2.0),
|
||||||
|
Text(
|
||||||
|
chatList[index].conversation.lastMessage != null
|
||||||
|
? parseMessageSummary(chatList[index].conversation.lastMessage!)
|
||||||
|
: '',
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 13.0),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 右侧
|
||||||
|
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: <Widget>[
|
||||||
|
Visibility(
|
||||||
|
visible: !(chatList[index].isCustomAdmin != '0' ||
|
||||||
|
chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)),
|
||||||
|
child: Text(
|
||||||
|
// 转成日期字符串显示
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
(chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
|
||||||
|
).toLocal().toString().substring(0, 16), // 简单截取年月日时分
|
||||||
|
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5.0),
|
||||||
|
// 数字角标
|
||||||
|
Visibility(
|
||||||
|
visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
|
||||||
|
child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: chatList[index].isCustomAdmin != '0' ||
|
||||||
|
chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
color: Colors.blueGrey,
|
||||||
|
size: 14.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (conversationTypeFromString(chatList[index].isCustomAdmin)) {
|
||||||
|
// 跳转对应的通知消息页
|
||||||
|
logger.e(chatList[index].isCustomAdmin);
|
||||||
|
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
|
||||||
|
// 跳转陌生人消息页面
|
||||||
|
logger.e(chatList[index].conversation.conversationGroupList);
|
||||||
|
} else {
|
||||||
|
// 会话id查询会话详情
|
||||||
|
Get.toNamed('/chat', arguments: chatList[index].conversation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTapDown: (TapDownDetails details) {
|
||||||
|
posDX = details.globalPosition.dx;
|
||||||
|
posDY = details.globalPosition.dy;
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
showContextMenu(context, chatList[index]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import 'package:loopin/IM/im_service.dart';
|
|||||||
import 'package:loopin/api/shop_api.dart';
|
import 'package:loopin/api/shop_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';
|
||||||
|
import 'package:loopin/models/share_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/index.dart';
|
import 'package:loopin/utils/index.dart';
|
||||||
@ -80,10 +81,10 @@ class _GoodsState extends State<Goods> {
|
|||||||
final description = shopObj['describe']; // 商品描述
|
final description = shopObj['describe']; // 商品描述
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
// 好友
|
// 好友
|
||||||
Wxsdk.shareToFriend(title: '快看看我分享的商品', description: description, webpageUrl: 'https://baidu.com');
|
Wxsdk.shareToFriend(title: '快看看我分享的商品', description: description, webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
|
||||||
} else if (index == 2) {
|
} else if (index == 2) {
|
||||||
// 朋友圈
|
// 朋友圈
|
||||||
Wxsdk.shareToTimeline(title: '快看看我分享的商品', webpageUrl: 'https://baidu.com');
|
Wxsdk.shareToTimeline(title: '快看看我分享的商品', webpageUrl: '${ShareType.shop.name}?id=${shopObj['id']}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,9 +552,9 @@ class _GoodsState extends State<Goods> {
|
|||||||
// ),
|
// ),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
// 可以在这里打开聊天、拨打电话等
|
// 可以在这里打开聊天
|
||||||
logger.i('联系客服');
|
logger.i('联系客服');
|
||||||
final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['shoperId']}');
|
final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
|
||||||
V2TimConversation conversation = res.data;
|
V2TimConversation conversation = res.data;
|
||||||
logger.i(conversation.toLogString());
|
logger.i(conversation.toLogString());
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
@ -619,9 +620,16 @@ class _GoodsState extends State<Goods> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
color: Color(0xFFFF5000),
|
color: Color(0xFFFF5000),
|
||||||
child: Text(
|
child: GestureDetector(
|
||||||
'立即购买',
|
onTap: () {
|
||||||
style: TextStyle(color: Colors.white, fontSize: 14.0),
|
// 这里走生成预支付订单,拿到orderId
|
||||||
|
String orderId = '1958380183857659904'; //测试数据
|
||||||
|
Get.toNamed('/order/detail', arguments: orderId);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
'立即购买',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 14.0),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/components/backtop.dart';
|
import 'package:loopin/components/backtop.dart';
|
||||||
import 'package:loopin/components/loading.dart';
|
import 'package:loopin/components/loading.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/controller/shop_index_controller.dart';
|
import 'package:loopin/controller/shop_index_controller.dart';
|
||||||
import 'package:loopin/utils/index.dart';
|
import 'package:loopin/utils/index.dart';
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
// }
|
// }
|
||||||
// ];
|
// ];
|
||||||
final ScrollController pageScrollController = ScrollController();
|
final ScrollController pageScrollController = ScrollController();
|
||||||
final ShopIndexController controller = Get.put(ShopIndexController());
|
late ShopIndexController controller;
|
||||||
|
|
||||||
// 瀑布流卡片
|
// 瀑布流卡片
|
||||||
Widget cardList(item) {
|
Widget cardList(item) {
|
||||||
@ -55,7 +56,12 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
]),
|
]),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Image.network('${item['pic']}'),
|
// Image.network(),
|
||||||
|
NetworkOrAssetImage(
|
||||||
|
imageUrl: '${item['pic']}',
|
||||||
|
width: double.infinity,
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -106,109 +112,121 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
controller.initTabs();
|
controller = Get.find<ShopIndexController>();
|
||||||
|
controller.initTabs(vsync: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() {
|
return Scaffold(
|
||||||
final tabIndex = controller.currentTabIndex.value;
|
backgroundColor: Colors.grey[50],
|
||||||
final currentTab = controller.tabs[tabIndex];
|
body: Column(
|
||||||
|
children: [
|
||||||
|
_buildTopSection(),
|
||||||
|
// 内容区域
|
||||||
|
Expanded(
|
||||||
|
child: controller.tabController == null
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: TabBarView(
|
||||||
|
controller: controller.tabController,
|
||||||
|
children: List.generate(
|
||||||
|
controller.tabList.length,
|
||||||
|
(index) => _buildTabContent(index),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButton: Obx(() {
|
||||||
|
final tabIndex = controller.currentTabIndex.value;
|
||||||
|
final currentTab = controller.tabs[tabIndex];
|
||||||
|
if (currentTab == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return Scaffold(
|
return Backtop(
|
||||||
backgroundColor: Colors.grey[50],
|
controller: currentTab.scrollController,
|
||||||
body: Column(
|
offset: currentTab.scrollOffset.value,
|
||||||
children: [
|
);
|
||||||
// 顶部固定区域(轮播图 + TabBar)
|
}),
|
||||||
_buildTopSection(),
|
);
|
||||||
|
|
||||||
// 内容区域
|
|
||||||
Expanded(
|
|
||||||
child: TabBarView(
|
|
||||||
controller: controller.tabController,
|
|
||||||
children: controller.tabList.asMap().entries.map((entry) {
|
|
||||||
final index = entry.key;
|
|
||||||
return _buildTabContent(index);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
floatingActionButton: currentTab != null
|
|
||||||
? Backtop(
|
|
||||||
controller: currentTab.scrollController,
|
|
||||||
offset: currentTab.scrollOffset.value,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建顶部固定区域
|
// 构建顶部固定区域
|
||||||
Widget _buildTopSection() {
|
Widget _buildTopSection() {
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
if (controller.tabController == null) {
|
||||||
int tabCount = controller.tabList.length;
|
return SizedBox();
|
||||||
// 每个 Tab 的最小宽度
|
}
|
||||||
double minTabWidth = 80;
|
|
||||||
// 是否可滚动
|
|
||||||
bool isScrollable = tabCount * minTabWidth > screenWidth;
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// 轮播图
|
// 轮播图
|
||||||
Container(
|
Obx(() {
|
||||||
width: double.infinity,
|
if (controller.swiperData.isEmpty) return const SizedBox.shrink();
|
||||||
height: 240,
|
if (controller.swiperData.length == 1) {
|
||||||
decoration: const BoxDecoration(
|
final imageUrl = controller.swiperData.first['images'] ?? '';
|
||||||
gradient: LinearGradient(
|
return Image.network(imageUrl, fit: BoxFit.fill, width: double.infinity, height: 240);
|
||||||
begin: Alignment.topLeft,
|
}
|
||||||
end: Alignment.bottomRight,
|
return SizedBox(
|
||||||
colors: [Color(0xFFFF5000), Color(0xFFfcaec4)],
|
width: double.infinity,
|
||||||
),
|
height: 240,
|
||||||
),
|
child: Swiper(
|
||||||
child: controller.swiperData.length <= 1
|
itemCount: controller.swiperData.length,
|
||||||
? (controller.swiperData.isNotEmpty
|
autoplay: true,
|
||||||
? Image.network(
|
loop: true,
|
||||||
controller.swiperData.first['images'] ?? '',
|
pagination: const SwiperPagination(
|
||||||
fit: BoxFit.fill,
|
builder: DotSwiperPaginationBuilder(
|
||||||
)
|
color: Colors.white70,
|
||||||
: const SizedBox.shrink())
|
activeColor: Colors.white,
|
||||||
: Swiper(
|
|
||||||
itemCount: controller.swiperData.length,
|
|
||||||
autoplay: true,
|
|
||||||
loop: true,
|
|
||||||
pagination: SwiperPagination(
|
|
||||||
builder: DotSwiperPaginationBuilder(
|
|
||||||
color: Colors.white70,
|
|
||||||
activeColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final imageUrl = controller.swiperData[index]['images'] ?? '';
|
|
||||||
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final imageUrl = controller.swiperData[index]['images'] ?? '';
|
||||||
|
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
// TabBar
|
// TabBar
|
||||||
Container(
|
Obx(() {
|
||||||
color: Colors.white,
|
int tabCount = controller.tabList.length;
|
||||||
child: TabBar(
|
double screenWidth = MediaQuery.of(context).size.width;
|
||||||
controller: controller.tabController,
|
double minTabWidth = 60;
|
||||||
tabs: controller.tabList.map((item) {
|
bool isScrollable = tabCount * minTabWidth > screenWidth;
|
||||||
return Tab(
|
|
||||||
child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
|
return Container(
|
||||||
);
|
color: Colors.white,
|
||||||
}).toList(),
|
child: TabBar(
|
||||||
isScrollable: isScrollable,
|
controller: controller.tabController,
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
tabs: controller.tabList.map((item) {
|
||||||
unselectedLabelColor: Colors.black87,
|
return Tab(
|
||||||
labelColor: Color.fromARGB(255, 236, 108, 49),
|
child: Text(
|
||||||
indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0)),
|
item['name'] ?? '',
|
||||||
unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold),
|
overflow: TextOverflow.ellipsis,
|
||||||
dividerHeight: 0,
|
softWrap: false,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
}).toList(),
|
||||||
|
isScrollable: isScrollable,
|
||||||
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
|
unselectedLabelColor: Colors.black87,
|
||||||
|
labelColor: const Color.fromARGB(255, 236, 108, 49),
|
||||||
|
indicator: const UnderlineTabIndicator(
|
||||||
|
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
|
||||||
|
),
|
||||||
|
unselectedLabelStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontFamily: 'Microsoft YaHei',
|
||||||
|
),
|
||||||
|
labelStyle: const TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
fontFamily: 'Microsoft YaHei',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
dividerHeight: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
/// 首页模板
|
/// 首页模板
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:card_swiper/card_swiper.dart';
|
import 'package:card_swiper/card_swiper.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
@ -118,7 +116,7 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
controller.initTabs();
|
controller.initTabs(vsync: this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -21,7 +21,7 @@ class PageParams {
|
|||||||
int page;
|
int page;
|
||||||
int pageSize;
|
int pageSize;
|
||||||
bool isLoading;
|
bool isLoading;
|
||||||
bool hasMore;
|
RxBool hasMore;
|
||||||
int total;
|
int total;
|
||||||
bool isInitLoading;
|
bool isInitLoading;
|
||||||
|
|
||||||
@ -29,16 +29,16 @@ class PageParams {
|
|||||||
this.page = 1,
|
this.page = 1,
|
||||||
this.pageSize = 10,
|
this.pageSize = 10,
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
this.hasMore = true,
|
bool hasMore = true,
|
||||||
this.total = 0,
|
this.total = 0,
|
||||||
this.isInitLoading = true,
|
this.isInitLoading = true,
|
||||||
});
|
}) : hasMore = hasMore.obs;
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
page = 1;
|
page = 1;
|
||||||
pageSize = 10;
|
pageSize = 10;
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
hasMore = true;
|
hasMore.value = true;
|
||||||
total = 0;
|
total = 0;
|
||||||
isInitLoading = true;
|
isInitLoading = true;
|
||||||
}
|
}
|
||||||
@ -86,7 +86,6 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
initControllers();
|
initControllers();
|
||||||
|
|
||||||
scrollListener = () {
|
scrollListener = () {
|
||||||
@ -95,9 +94,9 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
if (!isNearBottom) return;
|
if (!isNearBottom) return;
|
||||||
|
|
||||||
if (currentTabIndex.value == 0 && !itemsParams.isLoading && itemsParams.hasMore) {
|
if (currentTabIndex.value == 0 && !itemsParams.isLoading && itemsParams.hasMore.value) {
|
||||||
loadData(0);
|
loadData(0);
|
||||||
} else if (currentTabIndex.value == 1 && !favoriteParams.isLoading && favoriteParams.hasMore) {
|
} else if (currentTabIndex.value == 1 && !favoriteParams.isLoading && favoriteParams.hasMore.value) {
|
||||||
loadData(1);
|
loadData(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -145,62 +144,58 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
Future<void> loadData([int? tabIndex]) async {
|
Future<void> loadData([int? tabIndex]) async {
|
||||||
final index = tabIndex ?? currentTabIndex.value;
|
final index = tabIndex ?? currentTabIndex.value;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
if (itemsParams.isLoading || !itemsParams.hasMore) return;
|
if (itemsParams.isLoading || !itemsParams.hasMore.value) return;
|
||||||
|
|
||||||
itemsParams.isLoading = true;
|
itemsParams.isLoading = true;
|
||||||
// itemsParams.isInitLoading = true;
|
// itemsParams.isInitLoading = true;
|
||||||
|
|
||||||
try {
|
final res = await Http.post(VideoApi.myPublicList, data: {
|
||||||
final res = await Http.post(VideoApi.myPublicList, data: {
|
"userId": imUserInfoController?.userID.value,
|
||||||
"userId": imUserInfoController?.userID.value,
|
"yesOrNo": 0,
|
||||||
"yesOrNo": 0,
|
"current": itemsParams.page,
|
||||||
"current": itemsParams.page,
|
"size": itemsParams.pageSize,
|
||||||
"size": itemsParams.pageSize,
|
});
|
||||||
});
|
final obj = res['data'];
|
||||||
final obj = res['data'];
|
final total = obj['total'];
|
||||||
final total = obj['total'];
|
final row = obj['records'] ?? [];
|
||||||
final row = obj['rows'];
|
logger.i(res['data']);
|
||||||
logger.i(res['data']);
|
// 判断是否还有更多数据
|
||||||
// 判断是否还有更多数据
|
logger.e(items.length);
|
||||||
if (items.length >= total) {
|
// 添加新数据,触发响应式更新
|
||||||
itemsParams.hasMore = false;
|
items.addAll(row);
|
||||||
}
|
logger.e(obj);
|
||||||
// 添加新数据,触发响应式更新
|
if (items.length >= total) {
|
||||||
items.addAll(row);
|
itemsParams.hasMore.value = false;
|
||||||
|
|
||||||
// 页码加一
|
|
||||||
itemsParams.page++;
|
|
||||||
} finally {
|
|
||||||
itemsParams.isLoading = false;
|
|
||||||
itemsParams.isInitLoading = false;
|
|
||||||
}
|
}
|
||||||
|
// 页码加一
|
||||||
|
itemsParams.page++;
|
||||||
|
//
|
||||||
|
itemsParams.isLoading = false;
|
||||||
|
itemsParams.isInitLoading = false;
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
if (favoriteParams.isLoading || !favoriteParams.hasMore) return;
|
if (favoriteParams.isLoading || !favoriteParams.hasMore.value) return;
|
||||||
|
|
||||||
favoriteParams.isLoading = true;
|
favoriteParams.isLoading = true;
|
||||||
// favoriteParams.isInitLoading = true;
|
// favoriteParams.isInitLoading = true;
|
||||||
|
|
||||||
try {
|
final res = await Http.post(VideoApi.myLikedList, data: {
|
||||||
final res = await Http.post(VideoApi.myPublicList, data: {
|
"userId": imUserInfoController?.userID.value,
|
||||||
"userId": imUserInfoController?.userID.value,
|
"yesOrNo": 0,
|
||||||
"yesOrNo": 0,
|
"current": favoriteParams.page,
|
||||||
"current": itemsParams.page,
|
"size": favoriteParams.pageSize,
|
||||||
"size": itemsParams.pageSize,
|
});
|
||||||
});
|
final obj = res['data'];
|
||||||
final obj = res['data'];
|
final total = obj['total'];
|
||||||
final total = obj['total'];
|
final row = obj['records'] ?? [];
|
||||||
final row = obj['rows'];
|
favoriteItems.addAll(row);
|
||||||
|
|
||||||
if (favoriteItems.length >= total) {
|
if (favoriteItems.length >= total) {
|
||||||
itemsParams.hasMore = false;
|
favoriteParams.hasMore.value = false;
|
||||||
}
|
|
||||||
|
|
||||||
favoriteItems.addAll(row);
|
|
||||||
favoriteParams.page++;
|
|
||||||
} finally {
|
|
||||||
favoriteParams.isLoading = false;
|
|
||||||
favoriteParams.isInitLoading = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
favoriteParams.page++;
|
||||||
|
favoriteParams.isLoading = false;
|
||||||
|
favoriteParams.isInitLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,7 +232,11 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
// 获取当前登录用户基本信息
|
// 获取当前登录用户基本信息
|
||||||
void selfInfo() async {
|
void selfInfo() async {
|
||||||
// imUserInfoController = Get.find<ImUserInfoController>();
|
// imUserInfoController = Get.find<ImUserInfoController>();
|
||||||
final res = await ImService.instance.getUserFollowInfo(userIDList: [imUserInfoController!.userID.value]);
|
if (!Get.isRegistered<ImUserInfoController>()) {
|
||||||
|
logger.e('用户信息controller未注册');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final res = await ImService.instance.getUserFollowInfo(userIDList: [imUserInfoController?.userID.value ?? '']);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
//这里少个点赞,从服务端获取
|
//这里少个点赞,从服务端获取
|
||||||
// followersCount粉丝,多少人关注了我,mutualFollowersCount互关,followingCount我关注了多少人
|
// followersCount粉丝,多少人关注了我,mutualFollowersCount互关,followingCount我关注了多少人
|
||||||
@ -330,108 +329,106 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
} else {
|
} else {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFFAF6F9),
|
backgroundColor: const Color(0xFFFAF6F9),
|
||||||
body: Obx(() {
|
body: NestedScrollViewPlus(
|
||||||
return NestedScrollViewPlus(
|
controller: scrollController,
|
||||||
controller: scrollController,
|
physics: shouldFixHeader.value
|
||||||
physics: shouldFixHeader.value
|
? OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics())
|
||||||
? OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics())
|
: isPinned.value
|
||||||
: isPinned.value
|
? NeverScrollableScrollPhysics()
|
||||||
? NeverScrollableScrollPhysics()
|
: AlwaysScrollableScrollPhysics(),
|
||||||
: AlwaysScrollableScrollPhysics(),
|
// physics: shouldFixHeader.value ? OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics()) : AlwaysScrollableScrollPhysics(),
|
||||||
// physics: shouldFixHeader.value ? OnlyDownScrollPhysics(parent: AlwaysScrollableScrollPhysics()) : AlwaysScrollableScrollPhysics(),
|
overscrollBehavior: OverscrollBehavior.outer,
|
||||||
overscrollBehavior: OverscrollBehavior.outer,
|
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
return [
|
||||||
return [
|
SliverAppBar(
|
||||||
SliverAppBar(
|
backgroundColor: Colors.transparent,
|
||||||
backgroundColor: Colors.transparent,
|
surfaceTintColor: Colors.transparent,
|
||||||
surfaceTintColor: Colors.transparent,
|
expandedHeight: 180.0,
|
||||||
expandedHeight: 180.0,
|
collapsedHeight: 120.0,
|
||||||
collapsedHeight: 120.0,
|
pinned: true,
|
||||||
pinned: true,
|
stretch: true,
|
||||||
stretch: true,
|
onStretchTrigger: () async {
|
||||||
onStretchTrigger: () async {
|
logger.i('触发 stretch 拉伸');
|
||||||
logger.i('触发 stretch 拉伸');
|
// 加载刷新逻辑
|
||||||
// 加载刷新逻辑
|
},
|
||||||
},
|
actions: [
|
||||||
actions: [
|
// _buildIcon('assets/images/svg/service.svg', () {
|
||||||
// _buildIcon('assets/images/svg/service.svg', () {
|
// logger.i('点击客服按钮');
|
||||||
// logger.i('点击客服按钮');
|
// }),
|
||||||
// }),
|
const SizedBox(width: 8.0),
|
||||||
const SizedBox(width: 8.0),
|
_buildIcon('assets/images/svg/setting.svg', () {
|
||||||
_buildIcon('assets/images/svg/setting.svg', () {
|
logger.i('点击设置按钮');
|
||||||
logger.i('点击设置按钮');
|
Get.toNamed('/setting');
|
||||||
Get.toNamed('/setting');
|
}),
|
||||||
}),
|
const SizedBox(width: 10.0),
|
||||||
const SizedBox(width: 10.0),
|
],
|
||||||
],
|
flexibleSpace: _buildFlexibleSpace(),
|
||||||
flexibleSpace: _buildFlexibleSpace(),
|
),
|
||||||
),
|
SliverToBoxAdapter(
|
||||||
SliverToBoxAdapter(
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(10.0),
|
||||||
padding: const EdgeInsets.all(10.0),
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
Obx(() => _buildStatsCard()),
|
||||||
Obx(() => _buildStatsCard()),
|
const SizedBox(height: 10.0),
|
||||||
const SizedBox(height: 10.0),
|
_buildOrderCard(context),
|
||||||
_buildOrderCard(context),
|
const SizedBox(height: 10.0),
|
||||||
const SizedBox(height: 10.0),
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPersistentHeader(
|
),
|
||||||
pinned: true,
|
SliverPersistentHeader(
|
||||||
delegate: CustomStickyHeader(
|
pinned: true,
|
||||||
isPinned: isPinned,
|
delegate: CustomStickyHeader(
|
||||||
positions: positions,
|
isPinned: isPinned,
|
||||||
child: PreferredSize(
|
positions: positions,
|
||||||
preferredSize: const Size.fromHeight(48.0),
|
child: PreferredSize(
|
||||||
child: Container(
|
preferredSize: const Size.fromHeight(48.0),
|
||||||
color: Colors.white,
|
child: Container(
|
||||||
child: TabBar(
|
color: Colors.white,
|
||||||
controller: tabController,
|
child: TabBar(
|
||||||
tabs: tabList.map((item) {
|
controller: tabController,
|
||||||
return Tab(
|
tabs: tabList.map((item) {
|
||||||
child: Badge.count(
|
return Tab(
|
||||||
backgroundColor: Colors.red,
|
child: Badge.count(
|
||||||
count: item['badge'] ?? 0,
|
backgroundColor: Colors.red,
|
||||||
isLabelVisible: item['badge'] != null,
|
count: item['badge'] ?? 0,
|
||||||
alignment: Alignment.topRight,
|
isLabelVisible: item['badge'] != null,
|
||||||
offset: const Offset(14, -6),
|
alignment: Alignment.topRight,
|
||||||
child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
|
offset: const Offset(14, -6),
|
||||||
),
|
child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
);
|
),
|
||||||
}).toList(),
|
);
|
||||||
isScrollable: false,
|
}).toList(),
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
isScrollable: false,
|
||||||
unselectedLabelColor: Colors.black87,
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
labelColor: const Color(0xFFFF5000),
|
unselectedLabelColor: Colors.black87,
|
||||||
indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0)),
|
labelColor: const Color(0xFFFF5000),
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
indicator: const UnderlineTabIndicator(borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0)),
|
||||||
unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold),
|
unselectedLabelStyle: const TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
||||||
dividerHeight: 0,
|
labelStyle: const TextStyle(fontSize: 18.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.bold),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
dividerHeight: 0,
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 15.0),
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
),
|
labelPadding: const EdgeInsets.symmetric(horizontal: 15.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
),
|
||||||
},
|
];
|
||||||
body: TabBarView(
|
},
|
||||||
controller: tabController,
|
body: TabBarView(
|
||||||
children: [
|
controller: tabController,
|
||||||
// Tab 1:
|
children: [
|
||||||
_buildGridTab(0),
|
// Tab 1:
|
||||||
|
_buildGridTab(0),
|
||||||
|
|
||||||
// Tab 2:
|
// Tab 2:
|
||||||
_buildGridTab(1)
|
_buildGridTab(1)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -510,7 +507,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: params.hasMore ? const CircularProgressIndicator() : const Text('没有更多数据了'),
|
child: params.hasMore.value ? CircularProgressIndicator() : Text('没有更多数据了'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -612,86 +609,108 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
Widget _buildFlexibleSpace() {
|
Widget _buildFlexibleSpace() {
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (context, constraints) {
|
||||||
final double maxHeight = 180.0;
|
final double maxHeight = 180;
|
||||||
final double minHeight = 120.0;
|
final double minHeight = 120;
|
||||||
final double currentHeight = constraints.maxHeight;
|
final currentHeight = constraints.maxHeight;
|
||||||
double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
|
double ratio = ((currentHeight - minHeight) / (maxHeight - minHeight)).clamp(0.0, 1.0);
|
||||||
ratio = ratio.clamp(0.0, 1.0);
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
// 背景图 Obx
|
||||||
child: Opacity(
|
Obx(() {
|
||||||
opacity: 1.0,
|
final bgUrl = imUserInfoController?.customInfo['coverBg'] ?? '';
|
||||||
child: NetworkOrAssetImage(imageUrl: imUserInfoController?.customInfo['coverBg'], placeholderAsset: 'assets/images/bk.jpg'),
|
return NetworkOrAssetImage(
|
||||||
),
|
imageUrl: bgUrl,
|
||||||
),
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 15.0,
|
left: 15,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
right: 15.0,
|
right: 15,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: <Widget>[
|
children: [
|
||||||
ClipOval(
|
// 头像 Obx
|
||||||
child: Obx(() {
|
Obx(() {
|
||||||
final faceUrl = imUserInfoController?.faceUrl.value;
|
final faceUrl = imUserInfoController?.faceUrl.value ?? '';
|
||||||
return ClipRRect(
|
return ClipOval(
|
||||||
borderRadius: BorderRadius.circular(30),
|
child: NetworkOrAssetImage(
|
||||||
child: NetworkOrAssetImage(
|
imageUrl: faceUrl,
|
||||||
imageUrl: faceUrl,
|
width: 80,
|
||||||
width: 80,
|
height: 80,
|
||||||
height: 80,
|
),
|
||||||
),
|
);
|
||||||
);
|
}),
|
||||||
}),
|
const SizedBox(width: 15),
|
||||||
),
|
|
||||||
const SizedBox(width: 15.0),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
// 昵称 Obx
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
Obx(() {
|
||||||
decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
final nickname = imUserInfoController?.nickname.value ?? '';
|
||||||
child: Obx(
|
return Container(
|
||||||
() => Text(
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
imUserInfoController!.nickname.value.isNotEmpty == true ? imUserInfoController!.nickname.value : '昵称',
|
decoration: BoxDecoration(
|
||||||
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, fontFamily: 'Arial', color: Colors.white),
|
color: Colors.black.withAlpha(76),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
nickname.isNotEmpty ? nickname : '昵称',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
const SizedBox(width: 8.0),
|
);
|
||||||
|
}),
|
||||||
|
const SizedBox(width: 8),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () => qrcodeAlertDialog(context),
|
||||||
qrcodeAlertDialog(context);
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
decoration: BoxDecoration(
|
||||||
child: const Icon(Icons.qr_code_outlined, size: 18.0, color: Colors.white),
|
color: Colors.black.withAlpha(76),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: const Icon(Icons.qr_code_outlined, size: 18, color: Colors.white),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8.0),
|
const SizedBox(height: 8),
|
||||||
Container(
|
// 用户ID Obx
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
Obx(() {
|
||||||
decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
final userId = imUserInfoController?.userID.value ?? '';
|
||||||
child: InkWell(
|
return Container(
|
||||||
onTap: () {
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
logger.i('点击id');
|
decoration: BoxDecoration(
|
||||||
Clipboard.setData(ClipboardData(text: imUserInfoController!.userID.value));
|
color: Colors.black.withAlpha(76),
|
||||||
MyDialog.toast('ID已复制', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
borderRadius: BorderRadius.circular(20),
|
||||||
},
|
),
|
||||||
child: Text('ID:${imUserInfoController!.userID.value}', style: TextStyle(fontSize: 12.0, color: Colors.white)),
|
child: InkWell(
|
||||||
),
|
onTap: () {
|
||||||
),
|
Clipboard.setData(ClipboardData(text: userId));
|
||||||
|
MyDialog.toast(
|
||||||
|
'ID已复制',
|
||||||
|
icon: const Icon(Icons.check_circle),
|
||||||
|
style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text('ID:$userId', style: const TextStyle(fontSize: 12, color: Colors.white)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -705,6 +724,104 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Widget _buildFlexibleSpace() {
|
||||||
|
// return LayoutBuilder(
|
||||||
|
// builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
// final double maxHeight = 180.0;
|
||||||
|
// final double minHeight = 120.0;
|
||||||
|
// final double currentHeight = constraints.maxHeight;
|
||||||
|
// double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
|
||||||
|
// ratio = ratio.clamp(0.0, 1.0);
|
||||||
|
// final faceUrl = imUserInfoController?.faceUrl.value ?? '';
|
||||||
|
|
||||||
|
// return Obx(
|
||||||
|
// () => Stack(
|
||||||
|
// fit: StackFit.expand,
|
||||||
|
// children: [
|
||||||
|
// Positioned.fill(
|
||||||
|
// child: Opacity(
|
||||||
|
// opacity: 1.0,
|
||||||
|
// child: NetworkOrAssetImage(imageUrl: imUserInfoController?.customInfo['coverBg'] ?? '', placeholderAsset: 'assets/images/bk.jpg'),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Positioned(
|
||||||
|
// left: 15.0,
|
||||||
|
// bottom: 0,
|
||||||
|
// right: 15.0,
|
||||||
|
// child: Container(
|
||||||
|
// padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
// child: Row(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
// children: <Widget>[
|
||||||
|
// ClipOval(
|
||||||
|
// child: ClipRRect(
|
||||||
|
// borderRadius: BorderRadius.circular(30),
|
||||||
|
// child: NetworkOrAssetImage(
|
||||||
|
// imageUrl: faceUrl,
|
||||||
|
// width: 80,
|
||||||
|
// height: 80,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const SizedBox(width: 15.0),
|
||||||
|
// Expanded(
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
// children: [
|
||||||
|
// Row(
|
||||||
|
// children: [
|
||||||
|
// Container(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||||
|
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
||||||
|
// child: Obx(
|
||||||
|
// () => Text(
|
||||||
|
// imUserInfoController?.nickname.value.isNotEmpty ?? false == true
|
||||||
|
// ? imUserInfoController?.nickname.value ?? imUserInfoController!.nickname.value
|
||||||
|
// : '昵称',
|
||||||
|
// style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, fontFamily: 'Arial', color: Colors.white),
|
||||||
|
// ),
|
||||||
|
// )),
|
||||||
|
// const SizedBox(width: 8.0),
|
||||||
|
// InkWell(
|
||||||
|
// onTap: () {
|
||||||
|
// qrcodeAlertDialog(context);
|
||||||
|
// },
|
||||||
|
// child: Container(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
|
||||||
|
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
||||||
|
// child: const Icon(Icons.qr_code_outlined, size: 18.0, color: Colors.white),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// const SizedBox(height: 8.0),
|
||||||
|
// Container(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||||
|
// decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
|
||||||
|
// child: InkWell(
|
||||||
|
// onTap: () {
|
||||||
|
// logger.i('点击id');
|
||||||
|
// Clipboard.setData(ClipboardData(text: imUserInfoController?.userID.value ?? ''));
|
||||||
|
// MyDialog.toast('ID已复制',
|
||||||
|
// icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||||
|
// },
|
||||||
|
// child: Text('ID:${imUserInfoController?.userID.value ?? ''}', style: TextStyle(fontSize: 12.0, color: Colors.white)),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
Widget _buildStatsCard() {
|
Widget _buildStatsCard() {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -783,7 +900,9 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
_buildOrderIcon('assets/images/ico_sh.png', '提现vloger', () {
|
_buildOrderIcon('assets/images/ico_sh.png', '提现vloger', () {
|
||||||
Get.toNamed('/vloger');
|
Get.toNamed('/vloger');
|
||||||
}),
|
}),
|
||||||
_buildOrderIcon('assets/images/ico_tgm.png', '推广码', () {}),
|
_buildOrderIcon('assets/images/ico_tgm.png', '推广码', () {
|
||||||
|
logger.e('推广码');
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -388,18 +388,18 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
|||||||
double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
|
double ratio = (currentHeight - minHeight) / (maxHeight - minHeight);
|
||||||
ratio = ratio.clamp(0.0, 1.0);
|
ratio = ratio.clamp(0.0, 1.0);
|
||||||
String coverBg = userInfo.value.customInfo?['coverBg'] ?? '';
|
String coverBg = userInfo.value.customInfo?['coverBg'] ?? '';
|
||||||
coverBg = coverBg.isEmpty ? 'assets/images/pic2.jpg' : coverBg;
|
|
||||||
logger.w(coverBg);
|
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: 1.0,
|
opacity: 1.0,
|
||||||
child: Image.asset(
|
child: NetworkOrAssetImage(
|
||||||
coverBg,
|
imageUrl: coverBg,
|
||||||
fit: BoxFit.cover,
|
width: double.infinity,
|
||||||
))),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 15.0,
|
left: 15.0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
@ -411,20 +411,6 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ClipOval(
|
ClipOval(
|
||||||
child: NetworkOrAssetImage(imageUrl: userInfo.value.faceUrl),
|
child: NetworkOrAssetImage(imageUrl: userInfo.value.faceUrl),
|
||||||
// child: Image.asset(
|
|
||||||
// userInfo.value.faceUrl ?? 'assets/images/pic1.jpg',
|
|
||||||
// height: 60.0,
|
|
||||||
// width: 60.0,
|
|
||||||
// fit: BoxFit.cover,
|
|
||||||
// errorBuilder: (context, error, stackTrace) {
|
|
||||||
// return Image.asset(
|
|
||||||
// 'assets/images/pic1.jpg',
|
|
||||||
// height: 60.0,
|
|
||||||
// width: 60.0,
|
|
||||||
// fit: BoxFit.cover,
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15.0),
|
const SizedBox(width: 15.0),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||||
|
import 'package:timer_count_down/timer_count_down.dart';
|
||||||
|
|
||||||
import '../../behavior/custom_scroll_behavior.dart';
|
import '../../behavior/custom_scroll_behavior.dart';
|
||||||
|
|
||||||
@ -13,6 +15,30 @@ class OrderDetail extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStateMixin {
|
class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStateMixin {
|
||||||
|
late String _orderId;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_orderId = Get.arguments;
|
||||||
|
getOrderDetail(orderId: _orderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取订单详情信息,包含商品参数
|
||||||
|
void getOrderDetail({required String orderId}) async {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
// 计算时间
|
||||||
|
int handleTime() {
|
||||||
|
String dbTime = '2025-08-21 15:07:31.293';
|
||||||
|
DateTime orderTime = DateTime.parse(dbTime);
|
||||||
|
// 计算过期时间 = 下单时间 + 15分钟
|
||||||
|
DateTime expireTime = orderTime.add(const Duration(minutes: 15));
|
||||||
|
// 剩余秒数
|
||||||
|
int remainSeconds = expireTime.difference(DateTime.now()).inSeconds;
|
||||||
|
if (remainSeconds < 0) remainSeconds = 0;
|
||||||
|
return remainSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
Widget emptyTip() {
|
Widget emptyTip() {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -39,12 +65,12 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
|||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
title: Text('订单详情'),
|
title: Text('订单详情'),
|
||||||
titleSpacing: 1.0,
|
titleSpacing: 1.0,
|
||||||
actions: [
|
// actions: [
|
||||||
IconButton(
|
// IconButton(
|
||||||
icon: Icon(Icons.help, size: 18.0),
|
// icon: Icon(Icons.help, size: 18.0),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
),
|
),
|
||||||
body: ScrollConfiguration(
|
body: ScrollConfiguration(
|
||||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
@ -64,9 +90,31 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
|||||||
)),
|
)),
|
||||||
TextSpan(text: ' 待支付, '),
|
TextSpan(text: ' 待支付, '),
|
||||||
TextSpan(text: ' 剩余 '),
|
TextSpan(text: ' 剩余 '),
|
||||||
TextSpan(
|
// TextSpan(
|
||||||
text: '00 : 29 : 55',
|
// text: '00 : 29 : 55',
|
||||||
style: TextStyle(color: Colors.red),
|
// style: TextStyle(color: Colors.red),
|
||||||
|
// ),
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: Countdown(
|
||||||
|
// createtime
|
||||||
|
// seconds: 15 * 60, // 15分钟
|
||||||
|
seconds: handleTime(),
|
||||||
|
build: (_, double time) {
|
||||||
|
int m = ((time % 3600) ~/ 60).toInt();
|
||||||
|
int s = (time % 60).toInt();
|
||||||
|
String formatted = "${m.toString().padLeft(2, '0')} : ${s.toString().padLeft(2, '0')}";
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
formatted,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
interval: const Duration(seconds: 1),
|
||||||
|
onFinished: () {
|
||||||
|
print("倒计时结束");
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
@ -9,12 +9,12 @@ import '../../behavior/custom_scroll_behavior.dart';
|
|||||||
import '../../components/keepalive_wrapper.dart';
|
import '../../components/keepalive_wrapper.dart';
|
||||||
import '../../controller/video_module_controller.dart';
|
import '../../controller/video_module_controller.dart';
|
||||||
import './module/attention.dart';
|
import './module/attention.dart';
|
||||||
import './module/recommend.dart';
|
|
||||||
// import './module/browse.dart';
|
// import './module/browse.dart';
|
||||||
// import './module/buying.dart';
|
// import './module/buying.dart';
|
||||||
// import './module/drama.dart';
|
// import './module/drama.dart';
|
||||||
// import './module/live.dart';
|
// import './module/live.dart';
|
||||||
import './module/friend.dart';
|
import './module/friend.dart';
|
||||||
|
import './module/recommend.dart';
|
||||||
// 引入tab内容模块
|
// 引入tab内容模块
|
||||||
// import './module/subscribe.dart';
|
// import './module/subscribe.dart';
|
||||||
|
|
||||||
@ -138,19 +138,19 @@ class _VideoPageState extends State<VideoPage> with SingleTickerProviderStateMix
|
|||||||
child: PageView(
|
child: PageView(
|
||||||
controller: pageController,
|
controller: pageController,
|
||||||
onPageChanged: (index) {
|
onPageChanged: (index) {
|
||||||
logger.i('$index');
|
logger.i('$index');
|
||||||
// 根据当前 tab 控制对应播放器播放,其它暂停
|
// 根据当前 tab 控制对应播放器播放,其它暂停
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
AttentionModule.playVideo();
|
AttentionModule.playVideo();
|
||||||
// FriendModule.pauseVideo();
|
// FriendModule.pauseVideo();
|
||||||
RecommendModule.pauseVideo();
|
RecommendModule.pauseVideo();
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
AttentionModule.pauseVideo();
|
AttentionModule.pauseVideo();
|
||||||
// FriendModule.playVideo();
|
// FriendModule.playVideo();
|
||||||
RecommendModule.pauseVideo();
|
RecommendModule.pauseVideo();
|
||||||
} else if (index == 2) {
|
} else if (index == 2) {
|
||||||
AttentionModule.pauseVideo();
|
AttentionModule.pauseVideo();
|
||||||
// FriendModule.pauseVideo();
|
// FriendModule.pauseVideo();
|
||||||
RecommendModule.playVideo();
|
RecommendModule.playVideo();
|
||||||
}
|
}
|
||||||
videoModuleController.updateVideoTabIndex(index);
|
videoModuleController.updateVideoTabIndex(index);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
/// 精选推荐模块
|
/// 精选推荐模块
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
@ -16,9 +17,9 @@ 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';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/wxsdk.dart';
|
|
||||||
import 'package:loopin/utils/permissions.dart';
|
|
||||||
import 'package:loopin/utils/download_video.dart';
|
import 'package:loopin/utils/download_video.dart';
|
||||||
|
import 'package:loopin/utils/permissions.dart';
|
||||||
|
import 'package:loopin/utils/wxsdk.dart';
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
@ -48,6 +49,7 @@ class RecommendModule extends StatefulWidget {
|
|||||||
@override
|
@override
|
||||||
State<RecommendModule> createState() => _RecommendModuleState();
|
State<RecommendModule> createState() => _RecommendModuleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommentBottomSheet extends StatefulWidget {
|
class CommentBottomSheet extends StatefulWidget {
|
||||||
final String videoId;
|
final String videoId;
|
||||||
final Function(int) onCommentCountChanged; // 新增回调函数
|
final Function(int) onCommentCountChanged; // 新增回调函数
|
||||||
@ -72,7 +74,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
String replyingCommentId = '';
|
String replyingCommentId = '';
|
||||||
String replyingCommentUser = '';
|
String replyingCommentUser = '';
|
||||||
|
|
||||||
// 新增:子评论相关状态
|
// 新增:子评论相关状态
|
||||||
Map<String, dynamic> expandedReplies = {}; // 存储已展开的回复 {commentId: true/false}
|
Map<String, dynamic> expandedReplies = {}; // 存储已展开的回复 {commentId: true/false}
|
||||||
Map<String, List<Map<String, dynamic>>> replyData = {}; // 存储子评论数据 {commentId: [replies]}
|
Map<String, List<Map<String, dynamic>>> replyData = {}; // 存储子评论数据 {commentId: [replies]}
|
||||||
Map<String, int> replyPage = {}; // 子评论分页
|
Map<String, int> replyPage = {}; // 子评论分页
|
||||||
@ -97,7 +99,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
hasMoreComments = true;
|
hasMoreComments = true;
|
||||||
commentList.clear();
|
commentList.clear();
|
||||||
}
|
}
|
||||||
logger.d('入参vlogId-------------------->: ${widget.videoId}');
|
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,
|
||||||
@ -109,8 +111,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
|
|
||||||
if (res['code'] == 200 && res['data'] != null) {
|
if (res['code'] == 200 && res['data'] != null) {
|
||||||
final data = res['data'];
|
final data = res['data'];
|
||||||
final List<Map<String, dynamic>> newComments =
|
final List<Map<String, dynamic>> newComments = List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||||
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
|
||||||
final int total = data['total'] ?? 0;
|
final int total = data['total'] ?? 0;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -134,91 +135,87 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> postComment(String content, {String parentCommentId = ''}) 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.isNotEmpty ? parentCommentId : null,
|
'fatherCommentId': parentCommentId.isNotEmpty ? parentCommentId : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res['code'] == 200) {
|
if (res['code'] == 200) {
|
||||||
// 如果是回复,刷新对应的子评论
|
// 如果是回复,刷新对应的子评论
|
||||||
if (parentCommentId.isNotEmpty) {
|
if (parentCommentId.isNotEmpty) {
|
||||||
fetchReplies(parentCommentId, false);
|
fetchReplies(parentCommentId, false);
|
||||||
// 更新主评论的子评论数量
|
// 更新主评论的子评论数量
|
||||||
setState(() {
|
setState(() {
|
||||||
final comment = commentList.firstWhere(
|
final comment = commentList.firstWhere((c) => c['id'] == parentCommentId, orElse: () => <String, dynamic>{});
|
||||||
(c) => c['id'] == parentCommentId,
|
if (comment.isNotEmpty) {
|
||||||
orElse: () => <String, dynamic>{}
|
comment['childCount'] = (comment['childCount'] ?? 0) + 1;
|
||||||
);
|
}
|
||||||
if (comment != null && comment.isNotEmpty) {
|
});
|
||||||
comment['childCount'] = (comment['childCount'] ?? 0) + 1;
|
} else {
|
||||||
|
// 如果是新评论,刷新整个列表
|
||||||
|
fetchComments(false);
|
||||||
}
|
}
|
||||||
});
|
widget.onCommentCountChanged(commentList.length + 1);
|
||||||
} else {
|
MyToast().tip(
|
||||||
// 如果是新评论,刷新整个列表
|
title: '评论成功',
|
||||||
fetchComments(false);
|
position: 'center',
|
||||||
|
type: 'success',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
widget.onCommentCountChanged(commentList.length + 1);
|
} catch (e) {
|
||||||
|
logger.i('发布评论失败: $e');
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '评论成功',
|
title: '评论失败',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'success',
|
type: 'error',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
logger.i('发布评论失败: $e');
|
|
||||||
MyToast().tip(
|
|
||||||
title: '评论失败',
|
|
||||||
position: 'center',
|
|
||||||
type: 'error',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 获取二级评论,复用一级评论的接口,修改传参
|
// 获取二级评论,复用一级评论的接口,修改传参
|
||||||
Future<void> fetchReplies(String commentId, bool loadMore) async {
|
Future<void> fetchReplies(String commentId, bool loadMore) async {
|
||||||
if (isLoadingReplies[commentId] == true && !loadMore) return;
|
if (isLoadingReplies[commentId] == true && !loadMore) return;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoadingReplies[commentId] = true;
|
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) {
|
if (!loadMore) {
|
||||||
final data = res['data'];
|
replyPage[commentId] = 1;
|
||||||
final List<Map<String, dynamic>> newReplies =
|
replyData[commentId] = [];
|
||||||
List<Map<String, dynamic>>.from(data['records'] ?? []);
|
}
|
||||||
|
|
||||||
|
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(() {
|
setState(() {
|
||||||
if (loadMore) {
|
isLoadingReplies[commentId] = false;
|
||||||
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) {
|
||||||
@ -234,7 +231,7 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
/* Expanded(
|
/* Expanded(
|
||||||
child: Text.rich(TextSpan(children: [
|
child: Text.rich(TextSpan(children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '大家都在搜: ',
|
text: '大家都在搜: ',
|
||||||
@ -270,9 +267,7 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (ScrollNotification scrollInfo) {
|
onNotification: (ScrollNotification scrollInfo) {
|
||||||
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent &&
|
if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && !isLoadingMoreComments && hasMoreComments) {
|
||||||
!isLoadingMoreComments &&
|
|
||||||
hasMoreComments) {
|
|
||||||
fetchComments(true);
|
fetchComments(true);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -292,9 +287,7 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(16.0),
|
padding: EdgeInsets.all(16.0),
|
||||||
child: isLoadingMoreComments
|
child: isLoadingMoreComments ? CircularProgressIndicator() : Text('没有更多评论了'),
|
||||||
? CircularProgressIndicator()
|
|
||||||
: Text('没有更多评论了'),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -388,7 +381,7 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${comment['createTime']?.toString().substring(0, 10) ?? ''}',
|
comment['createTime']?.toString().substring(0, 10) ?? '',
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -402,67 +395,65 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
...replies.map((reply) => ListTile(
|
...replies.map((reply) => ListTile(
|
||||||
leading: ClipRRect(
|
leading: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(25.0),
|
borderRadius: BorderRadius.circular(25.0),
|
||||||
child: NetworkOrAssetImage(
|
child: NetworkOrAssetImage(
|
||||||
imageUrl: reply['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
imageUrl: reply['commentUserFace'] ?? 'assets/images/avatar/default.png',
|
||||||
width: 25.0,
|
width: 25.0,
|
||||||
height: 25.0,
|
height: 25.0,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
reply['commentUserNickname'] ?? '未知用户',
|
reply['commentUserNickname'] ?? '未知用户',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
fontSize: 11.0,
|
fontSize: 11.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
replyingCommentId = comment['id'];
|
||||||
|
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.reply,
|
||||||
|
color: Colors.black54,
|
||||||
|
size: 14.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
GestureDetector(
|
subtitle: Column(
|
||||||
onTap: () {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
setState(() {
|
children: [
|
||||||
replyingCommentId = comment['id'];
|
Container(
|
||||||
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
|
margin: EdgeInsets.symmetric(vertical: 3.0),
|
||||||
});
|
child: Text(
|
||||||
},
|
reply['content'] ?? '',
|
||||||
child: Icon(
|
style: TextStyle(fontSize: 13.0),
|
||||||
Icons.reply,
|
),
|
||||||
color: Colors.black54,
|
),
|
||||||
size: 14.0,
|
Text(
|
||||||
),
|
reply['createTime']?.toString().substring(0, 10) ?? '',
|
||||||
|
style: TextStyle(color: Colors.grey, fontSize: 11.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'])
|
if (replies.length < comment['childCount'])
|
||||||
Center(
|
Center(
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () => fetchReplies(comment['id'], true),
|
onPressed: () => fetchReplies(comment['id'], true),
|
||||||
child: isLoadingReplies[comment['id']] == true
|
child: isLoadingReplies[comment['id']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
|
||||||
? CircularProgressIndicator()
|
|
||||||
: Text('加载更多回复'),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -474,8 +465,8 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
// 在回复输入区域显示更详细的信息
|
// 在回复输入区域显示更详细的信息
|
||||||
if (replyingCommentId.isNotEmpty)
|
if (replyingCommentId.isNotEmpty)
|
||||||
Container(
|
Container(
|
||||||
@ -502,54 +493,58 @@ Future<void> postComment(String content, {String parentCommentId = ''}) async {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
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.isNotEmpty ? '回复 @$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.isNotEmpty ? '回复 @$replyingCommentUser' : '说点什么...',
|
||||||
|
onChanged: (value) {
|
||||||
|
debugPrint('评论内容: $value');
|
||||||
|
},
|
||||||
|
onSubmitted: (value) {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
postComment(value, parentCommentId: replyingCommentId);
|
||||||
|
setState(() {
|
||||||
|
replyingCommentId = '';
|
||||||
|
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>();
|
||||||
@ -731,12 +726,10 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
Future<void> doUnLikeVideo(item) async {
|
Future<void> doUnLikeVideo(item) async {
|
||||||
logger.d('点击了点赞按钮$item');
|
logger.d('点击了点赞按钮$item');
|
||||||
try {
|
try {
|
||||||
final res = await Http.post(VideoApi.unlike, data:{
|
final res = await Http.post(VideoApi.unlike, data: {'vlogId': item['id']});
|
||||||
'vlogId': item['id']
|
|
||||||
});
|
|
||||||
final resCode = res['code'];
|
final resCode = res['code'];
|
||||||
if (resCode == 200) {
|
if (resCode == 200) {
|
||||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.i('点击取消喜欢按钮报错: $e');
|
logger.i('点击取消喜欢按钮报错: $e');
|
||||||
@ -746,9 +739,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
// 点击喜欢视频
|
// 点击喜欢视频
|
||||||
Future<void> doLikeVideo(item) async {
|
Future<void> doLikeVideo(item) async {
|
||||||
try {
|
try {
|
||||||
final res = await Http.post(VideoApi.like, data:{
|
final res = await Http.post(VideoApi.like, data: {'vlogId': item['id']});
|
||||||
'vlogId': item['id']
|
|
||||||
});
|
|
||||||
final resCode = res['code'];
|
final resCode = res['code'];
|
||||||
if (resCode == 200) {
|
if (resCode == 200) {
|
||||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||||
@ -760,35 +751,34 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 评论弹框
|
// 评论弹框
|
||||||
void handleComment(index) {
|
void handleComment(index) {
|
||||||
final videoId = videoList[index]['id'];
|
final videoId = videoList[index]['id'];
|
||||||
logger.i('点击了评论按钮$videoId');
|
logger.i('点击了评论按钮$videoId');
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))),
|
||||||
showDragHandle: false,
|
showDragHandle: false,
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxHeight: MediaQuery.of(context).size.height * 3 / 4,
|
maxHeight: MediaQuery.of(context).size.height * 3 / 4,
|
||||||
),
|
),
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return CommentBottomSheet(
|
return CommentBottomSheet(
|
||||||
videoId: videoId,
|
videoId: videoId,
|
||||||
onCommentCountChanged: (newCount) {
|
onCommentCountChanged: (newCount) {
|
||||||
// 更新对应视频的评论数量
|
// 更新对应视频的评论数量
|
||||||
setState(() {
|
setState(() {
|
||||||
if (index < videoList.length) {
|
if (index < videoList.length) {
|
||||||
videoList[index]['commentsCounts'] = newCount;
|
videoList[index]['commentsCounts'] = newCount;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 分享弹框
|
// 分享弹框
|
||||||
void handleShare(index) {
|
void handleShare(index) {
|
||||||
@ -843,40 +833,41 @@ void handleComment(index) {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// 会话列表
|
// 会话列表
|
||||||
SizedBox(
|
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
|
||||||
height: 110,
|
SizedBox(
|
||||||
child: ListView.builder(
|
height: 110,
|
||||||
scrollDirection: Axis.horizontal,
|
child: ListView.builder(
|
||||||
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
scrollDirection: Axis.horizontal,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
|
||||||
itemBuilder: (context, index) {
|
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
|
||||||
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
itemBuilder: (context, index) {
|
||||||
return GestureDetector(
|
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
|
||||||
onTap: () => handlCoverClick(filteredList[index].conversation),
|
return GestureDetector(
|
||||||
child: Container(
|
onTap: () => handlCoverClick(filteredList[index].conversation),
|
||||||
width: 64,
|
child: Container(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 8.0),
|
width: 64,
|
||||||
child: Column(
|
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
NetworkOrAssetImage(
|
children: [
|
||||||
imageUrl: filteredList[index].faceUrl,
|
NetworkOrAssetImage(
|
||||||
width: 48.0,
|
imageUrl: filteredList[index].faceUrl,
|
||||||
height: 48.0,
|
width: 48.0,
|
||||||
),
|
height: 48.0,
|
||||||
SizedBox(height: 5),
|
),
|
||||||
Text(
|
const SizedBox(height: 5),
|
||||||
'${filteredList[index].conversation.showName}',
|
Text(
|
||||||
style: TextStyle(fontSize: 12.0),
|
filteredList[index].conversation.showName ?? '',
|
||||||
overflow: TextOverflow.ellipsis,
|
style: const TextStyle(fontSize: 12.0),
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
SafeArea(
|
SafeArea(
|
||||||
@ -909,36 +900,37 @@ void handleComment(index) {
|
|||||||
final videoUrl = videoList[videoModuleController.videoPlayIndex.value]['url'];
|
final videoUrl = videoList[videoModuleController.videoPlayIndex.value]['url'];
|
||||||
final description = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '快来看看这个视频';
|
final description = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '快来看看这个视频';
|
||||||
var httpPrefix = 'http://43.143.227.203/adv';
|
var httpPrefix = 'http://43.143.227.203/adv';
|
||||||
logger.i('分享链接地址----------------: ${httpPrefix}/goods-detail?id=${videoId}');
|
logger.i('分享链接地址----------------: $httpPrefix/goods-detail?id=$videoId');
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
// 分享好友
|
// 分享好友
|
||||||
Wxsdk.shareToFriend(title: '快来看看这个视频', description: description, webpageUrl: '${httpPrefix}/video-detail?id=${videoId}');
|
Wxsdk.shareToFriend(title: '快来看看这个视频', description: description, webpageUrl: '$httpPrefix/video-detail?id=$videoId');
|
||||||
} else if (index == 1) {
|
} else if (index == 1) {
|
||||||
// 分享到朋友圈
|
// 分享到朋友圈
|
||||||
Wxsdk.shareToTimeline(title: '快来看看这个视频', webpageUrl: '${httpPrefix}/goods-detail?id=${videoId}');
|
Wxsdk.shareToTimeline(title: '快来看看这个视频', webpageUrl: '$httpPrefix/goods-detail?id=$videoId');
|
||||||
} else if (index == 2) {
|
} else if (index == 2) {
|
||||||
// 复制链接到剪切板
|
// 复制链接到剪切板
|
||||||
copyToClipboard(videoUrl);
|
copyToClipboard(videoUrl);
|
||||||
} else if (index == 3) {
|
} else if (index == 3) {
|
||||||
// 下载视频到本地
|
// 下载视频到本地
|
||||||
_downloadVideoWithDio(videoUrl,description);
|
_downloadVideoWithDio(videoUrl, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 复制链接到剪贴板
|
|
||||||
void copyToClipboard(String text) async {
|
// 复制链接到剪贴板
|
||||||
|
void copyToClipboard(String text) async {
|
||||||
try {
|
try {
|
||||||
await Clipboard.setData(ClipboardData(text: text));
|
await Clipboard.setData(ClipboardData(text: text));
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '链接已复制到剪贴板',
|
title: '链接已复制到剪贴板',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '复制失败',
|
title: '复制失败',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -946,14 +938,14 @@ void handleComment(index) {
|
|||||||
Future<void> _downloadVideoWithDio(String videoUrl, String fileName) async {
|
Future<void> _downloadVideoWithDio(String videoUrl, String fileName) async {
|
||||||
try {
|
try {
|
||||||
// 请求存储权限
|
// 请求存储权限
|
||||||
String? toastId; // 用于存储toast的ID,以便后续关闭
|
String? toastId; // 用于存储toast的ID,以便后续关闭
|
||||||
var status = await Permissions.requestStoragePermission();
|
var status = await Permissions.requestStoragePermission();
|
||||||
if (!status) {
|
if (!status) {
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '需要存储权限才能下载视频',
|
title: '需要存储权限才能下载视频',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await DownloadManager.downloadFile(
|
await DownloadManager.downloadFile(
|
||||||
@ -964,14 +956,14 @@ void handleComment(index) {
|
|||||||
// 显示进度组件
|
// 显示进度组件
|
||||||
},
|
},
|
||||||
onComplete: (filePath) {
|
onComplete: (filePath) {
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '下载完成',
|
title: '下载完成',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'success',
|
type: 'success',
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
MyToast().tip(
|
MyToast().tip(
|
||||||
title: '下载失败: $error',
|
title: '下载失败: $error',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@ -1080,7 +1072,6 @@ void handleComment(index) {
|
|||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
StreamBuilder(
|
StreamBuilder(
|
||||||
stream: player.stream.playing,
|
stream: player.stream.playing,
|
||||||
builder: (context, playing) {
|
builder: (context, playing) {
|
||||||
@ -1123,20 +1114,16 @@ void handleComment(index) {
|
|||||||
height: 55.0,
|
height: 55.0,
|
||||||
width: 48.0,
|
width: 48.0,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: ()async {
|
onTap: () async {
|
||||||
player.pause();
|
player.pause();
|
||||||
// 跳转到 Vloger 页面并等待返回结果
|
// 跳转到 Vloger 页面并等待返回结果
|
||||||
final result = await Get.toNamed(
|
final result = await Get.toNamed('/vloger', arguments: videoList[videoModuleController.videoPlayIndex.value]);
|
||||||
'/vloger',
|
|
||||||
arguments: videoList[videoModuleController
|
|
||||||
.videoPlayIndex.value]);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
// 处理返回的参数
|
// 处理返回的参数
|
||||||
print('返回的数据: ${result['followStatus']}');
|
print('返回的数据: ${result['followStatus']}');
|
||||||
player.play();
|
player.play();
|
||||||
videoList[index]['doIFollowVloger'] = result['followStatus'];
|
videoList[index]['doIFollowVloger'] = result['followStatus'];
|
||||||
|
}
|
||||||
};
|
|
||||||
},
|
},
|
||||||
child: UnconstrainedBox(
|
child: UnconstrainedBox(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
@ -1174,17 +1161,17 @@ void handleComment(index) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final vlogerId = videoList[index]['memberId'];
|
final vlogerId = videoList[index]['memberId'];
|
||||||
final doIFollowVloger = videoList[index]['doIFollowVloger'];
|
final doIFollowVloger = videoList[index]['doIFollowVloger'];
|
||||||
// 未关注点击才去关注
|
// 未关注点击才去关注
|
||||||
if(doIFollowVloger == false){
|
if (doIFollowVloger == false) {
|
||||||
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
|
final res = await ImService.instance.followUser(userIDList: [vlogerId]);
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setState(() {
|
setState(() {
|
||||||
videoList[index]['doIFollowVloger'] = !videoList[index]['doIFollowVloger'];
|
videoList[index]['doIFollowVloger'] = !videoList[index]['doIFollowVloger'];
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1207,10 +1194,10 @@ void handleComment(index) {
|
|||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||||
if(videoList[index]['doILikeThisVlog'] == true){
|
if (videoList[index]['doILikeThisVlog'] == true) {
|
||||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||||
doUnLikeVideo(videoList[index]);
|
doUnLikeVideo(videoList[index]);
|
||||||
}else{
|
} else {
|
||||||
doLikeVideo(videoList[index]);
|
doLikeVideo(videoList[index]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,9 @@ import 'package:loopin/bings/chat_binding.dart';
|
|||||||
import 'package:loopin/pages/chat/chat.dart';
|
import 'package:loopin/pages/chat/chat.dart';
|
||||||
import 'package:loopin/pages/chat/chat_group.dart';
|
import 'package:loopin/pages/chat/chat_group.dart';
|
||||||
import 'package:loopin/pages/chat/chat_no_friend.dart';
|
import 'package:loopin/pages/chat/chat_no_friend.dart';
|
||||||
|
import 'package:loopin/pages/chat/notify/interaction.dart';
|
||||||
|
import 'package:loopin/pages/chat/notify/noFriend.dart';
|
||||||
|
import 'package:loopin/pages/chat/notify/system.dart';
|
||||||
import 'package:loopin/pages/my/des.dart';
|
import 'package:loopin/pages/my/des.dart';
|
||||||
import 'package:loopin/pages/my/nick_name.dart';
|
import 'package:loopin/pages/my/nick_name.dart';
|
||||||
import 'package:loopin/pages/my/setting.dart';
|
import 'package:loopin/pages/my/setting.dart';
|
||||||
@ -29,7 +32,6 @@ import '../utils/common.dart';
|
|||||||
final Map<String, Widget> routes = {
|
final Map<String, Widget> routes = {
|
||||||
'/': const Layout(),
|
'/': const Layout(),
|
||||||
'/goods': const Goods(),
|
'/goods': const Goods(),
|
||||||
// '/chat': const Chat(),
|
|
||||||
// '/chatNoFriend': const ChatNoFriend(),
|
// '/chatNoFriend': const ChatNoFriend(),
|
||||||
// '/chatGroup': const ChatGroup(),
|
// '/chatGroup': const ChatGroup(),
|
||||||
'/order': const Order(),
|
'/order': const Order(),
|
||||||
@ -44,6 +46,10 @@ final Map<String, Widget> routes = {
|
|||||||
'/about': const Setting(),
|
'/about': const Setting(),
|
||||||
'/des': const Des(),
|
'/des': const Des(),
|
||||||
'/nickName': const NickName(),
|
'/nickName': const NickName(),
|
||||||
|
//通知相关
|
||||||
|
'/noFriend': const Nofriend(),
|
||||||
|
'/system': const System(),
|
||||||
|
'/interaction': const Interaction(),
|
||||||
};
|
};
|
||||||
|
|
||||||
final List<GetPage> routeList = routes.entries
|
final List<GetPage> routeList = routes.entries
|
||||||
|
@ -8,7 +8,9 @@ class HttpConfig {
|
|||||||
// baseUrl: 'http://43.143.227.203:8099',
|
// baseUrl: 'http://43.143.227.203:8099',
|
||||||
// baseUrl: 'http://111.62.22.190:8080',
|
// baseUrl: 'http://111.62.22.190:8080',
|
||||||
// baseUrl: 'http://cjh.wuzhongjie.com.cn',
|
// baseUrl: 'http://cjh.wuzhongjie.com.cn',
|
||||||
baseUrl: 'http://82.156.121.2:8880',
|
// baseUrl: 'http://82.156.121.2:8880',
|
||||||
|
baseUrl: 'http://192.168.1.65:8880',
|
||||||
|
|
||||||
// connectTimeout: Duration(seconds: 30),
|
// connectTimeout: Duration(seconds: 30),
|
||||||
// receiveTimeout: Duration(seconds: 30),
|
// receiveTimeout: Duration(seconds: 30),
|
||||||
));
|
));
|
||||||
|
@ -134,6 +134,7 @@ class Utils {
|
|||||||
return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), '');
|
return number.toStringAsFixed(1).replaceAll(RegExp(r'\.0$'), '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理聊天消息时间
|
||||||
String formatChatTime(int timestamp) {
|
String formatChatTime(int timestamp) {
|
||||||
// timestamp 是秒级时间戳,转成 DateTime
|
// timestamp 是秒级时间戳,转成 DateTime
|
||||||
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||||
@ -157,6 +158,23 @@ class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理IM消息时间显示
|
||||||
|
static String formatTime(int timestamp) {
|
||||||
|
// 获取消息时间
|
||||||
|
final messageTime = DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
timestamp * 1000,
|
||||||
|
).toLocal();
|
||||||
|
// 获取当前时间
|
||||||
|
final now = DateTime.now();
|
||||||
|
if (messageTime.year == now.year) {
|
||||||
|
// 同一年:显示月-日
|
||||||
|
return '${messageTime.month.toString().padLeft(2, '0')}-${messageTime.day.toString().padLeft(2, '0')}';
|
||||||
|
} else {
|
||||||
|
// 不同年:显示年-月-日 时:分
|
||||||
|
return '${messageTime.year}-${messageTime.month.toString().padLeft(2, '0')}-${messageTime.day.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _formatHourMinute(DateTime dt) {
|
String _formatHourMinute(DateTime dt) {
|
||||||
final hour = dt.hour.toString().padLeft(2, '0');
|
final hour = dt.hour.toString().padLeft(2, '0');
|
||||||
final minute = dt.minute.toString().padLeft(2, '0');
|
final minute = dt.minute.toString().padLeft(2, '0');
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:loopin/models/notify_message.type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
@ -31,9 +32,26 @@ String parseMessageSummary(V2TimMessage msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newFoucs, //新的关注
|
||||||
|
// systemNotify, // 系统->通知
|
||||||
|
// systemReport, // 系统->举报下架(视频,视频评论)
|
||||||
|
// systemCheck, // 系统->审核结果(复审,驳回 ,通过)
|
||||||
|
// systemPush, //系统->推广类的
|
||||||
|
// interactionComment, //互动->评论
|
||||||
|
// interactionAt, //互动->视频评论中的@
|
||||||
|
// interactionLike, //互动->点赞
|
||||||
|
// interactionReply, //互动->评论回复
|
||||||
|
// orderRecharge, //订单->充值 online
|
||||||
|
// orderPay, //订单->订单交易成功通知 online
|
||||||
|
// orderRefund, //订单->退款结果通知
|
||||||
|
// groupNotifyCheck, //群通知->进群申请 online
|
||||||
|
// groupNotifyAccpet, // 群通知->进群审核审核通过 online
|
||||||
|
// groupNotifyFail, // 群通知->进群审核审核拒绝 online
|
||||||
|
// groupNotifyLeaveUp, // 群通知->群升级为达人群通知
|
||||||
String _parseCustomMessage(V2TimMessage? msg) {
|
String _parseCustomMessage(V2TimMessage? msg) {
|
||||||
if (msg == null) return '[null]';
|
if (msg == null) return '[null]';
|
||||||
final sum = msg.cloudCustomData;
|
final sum = msg.cloudCustomData;
|
||||||
|
final elment = msg.customElem; // 所有服务端发送的通知消息都走【自定义消息类型】
|
||||||
try {
|
try {
|
||||||
switch (sum) {
|
switch (sum) {
|
||||||
case SummaryType.hongbao:
|
case SummaryType.hongbao:
|
||||||
@ -43,6 +61,115 @@ String _parseCustomMessage(V2TimMessage? msg) {
|
|||||||
return '[分享商品]';
|
return '[分享商品]';
|
||||||
case SummaryType.shareVideo:
|
case SummaryType.shareVideo:
|
||||||
return '[分享视频]';
|
return '[分享视频]';
|
||||||
|
// 解析服务端通知类的
|
||||||
|
/// [des]显示的文本内容
|
||||||
|
/// [data] json数据
|
||||||
|
/// 名称firstFrameImg:先取cover,如果没有在取firstFrameImg
|
||||||
|
case NotifyMessageTypeConstants.newFoucs:
|
||||||
|
// 关注
|
||||||
|
|
||||||
|
///发起关注人的[userID],
|
||||||
|
///发起关注人的昵称[nickName],
|
||||||
|
///发起关注人的头像地址[faceUrl],
|
||||||
|
///------
|
||||||
|
///客户端检测发起关注人与本人的关注关系
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.systemNotify:
|
||||||
|
|
||||||
|
///系统级别的没有就先不做
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.systemReport:
|
||||||
|
// 视频举报
|
||||||
|
|
||||||
|
///先只处理视频,评论的先不管
|
||||||
|
///被举报的视频[vlogID],
|
||||||
|
///被举报的视频标题[title],
|
||||||
|
///被举报的视频首帧图[firstFrameImg]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.systemCheck:
|
||||||
|
// 视频审核
|
||||||
|
|
||||||
|
///视频审核结果通知 通过还是驳回
|
||||||
|
///被审核的视频[vlogID],
|
||||||
|
///被审核的视频标题[title],
|
||||||
|
///被审核的视频首帧图[firstFrameImg]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.systemPush:
|
||||||
|
|
||||||
|
///系统推广类的先不管了
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.interactionComment:
|
||||||
|
// 评论视频
|
||||||
|
|
||||||
|
/// 发起评论人的[userID]
|
||||||
|
/// 发起评论人的头像[faceUrl]
|
||||||
|
/// 发起评论人的[nickName]
|
||||||
|
/// 评论的内容[comment]
|
||||||
|
/// 评论的id[commentID]
|
||||||
|
/// 被评论的视频[vlogID]
|
||||||
|
/// 被评论的视频首帧图[firstFrameImg]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.interactionAt:
|
||||||
|
// 视频评论中@互关好友
|
||||||
|
|
||||||
|
/// 发起@人的[userID]
|
||||||
|
/// 发起@人的[nickName]
|
||||||
|
/// 发起@人的头像[faceUrl]
|
||||||
|
/// 关联视频的首帧图[firstFrameImg]
|
||||||
|
/// 关联视频的[vlogID]
|
||||||
|
/// 评论的id[commentID]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.interactionLike:
|
||||||
|
// 点赞
|
||||||
|
|
||||||
|
/// 发起点赞人的[userID]
|
||||||
|
/// 发起点赞人的头像[faceUrl]
|
||||||
|
/// 发起点赞人的[nickName]
|
||||||
|
/// 被点赞视频的[vlogID]
|
||||||
|
/// 被点赞视频的封面图[firstFrameImg]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.interactionReply:
|
||||||
|
// 回复评论
|
||||||
|
/// 回复人的[userID]
|
||||||
|
/// 回复人的[faceUrl]
|
||||||
|
/// 回复人的[nickName]
|
||||||
|
/// 关联视频的[vlogID]
|
||||||
|
/// 关联视频的首帧图[firstFrameImg]
|
||||||
|
/// 被回复评论的[commentID]
|
||||||
|
/// 回复的评论内容[comment]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.orderRecharge:
|
||||||
|
// 充值成功通知
|
||||||
|
/// 订单编号[orderID]
|
||||||
|
/// 充值金额[amount]
|
||||||
|
/// 充值后帐户余额[totalAmount]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.orderPay:
|
||||||
|
// 订单交易结果通知(商品购买成功后)
|
||||||
|
/// 订单编号[orderID]
|
||||||
|
/// 订单金额[amount]
|
||||||
|
/// 商品名称[name]
|
||||||
|
/// 商品描述[describe]
|
||||||
|
/// 商品价格[price]
|
||||||
|
/// 商品主图[pic]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.orderRefund:
|
||||||
|
// 订单退款通知
|
||||||
|
/// 订单编号[orderID]
|
||||||
|
/// 订单金额[amount]
|
||||||
|
/// 商品名称[name]
|
||||||
|
/// 商品描述[describe]
|
||||||
|
/// 商品价格[price]
|
||||||
|
/// 商品主图[pic]
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.groupNotifyCheck:
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.groupNotifyAccpet:
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.groupNotifyFail:
|
||||||
|
return elment!.desc!;
|
||||||
|
case NotifyMessageTypeConstants.groupNotifyLeaveUp:
|
||||||
|
return elment!.desc!;
|
||||||
default:
|
default:
|
||||||
return '[未知消息类型2]';
|
return '[未知消息类型2]';
|
||||||
}
|
}
|
||||||
|
@ -1274,6 +1274,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.4"
|
version: "0.7.4"
|
||||||
|
timer_count_down:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: timer_count_down
|
||||||
|
sha256: d025d408c2654e497ca0bd4bde014bd7509d4c6397af4ed23a0f9b692bbcf337
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -87,6 +87,7 @@ dependencies:
|
|||||||
record: ^6.0.0 #音频
|
record: ^6.0.0 #音频
|
||||||
audioplayers: ^6.5.0 #音频播放
|
audioplayers: ^6.5.0 #音频播放
|
||||||
flutter_html: ^3.0.0
|
flutter_html: ^3.0.0
|
||||||
|
timer_count_down: ^2.2.2 #倒计时
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
||||||
|
Loading…
x
Reference in New Issue
Block a user