From d221f5e87c1ee032844c37484ecec5bb6f3b30fe Mon Sep 17 00:00:00 2001 From: wurong <953969641@qq.com> Date: Thu, 19 Sep 2024 15:19:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=88=97=E8=A1=A8=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9B=B4=E6=94=B9=EF=BC=9B=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=A5=BD=E5=8F=8B=E5=BC=B9=E7=AA=97=E6=9B=B4=E6=94=B9=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/im/add_friend.dart | 143 ++++++++++++++++++++++++---- lib/im/chat_details_page.dart | 77 +++++++-------- lib/im/database/contact.dart | 33 ------- lib/im/database/hx_database.dart | 97 +++++++++---------- lib/im/im_search.dart | 0 lib/im/im_view/im_page.dart | 98 ++++++++++++------- lib/im/im_view/time_formatter.dart | 20 ++++ lib/retrofit/data/im_user_list.dart | 61 ++++++++++++ lib/retrofit/retrofit_api.dart | 10 ++ lib/retrofit/retrofit_api.g.dart | 48 ++++++++++ 10 files changed, 404 insertions(+), 183 deletions(-) delete mode 100644 lib/im/database/contact.dart create mode 100644 lib/im/im_search.dart create mode 100644 lib/im/im_view/time_formatter.dart create mode 100644 lib/retrofit/data/im_user_list.dart diff --git a/lib/im/add_friend.dart b/lib/im/add_friend.dart index 0d66d634..71ca7307 100644 --- a/lib/im/add_friend.dart +++ b/lib/im/add_friend.dart @@ -96,7 +96,7 @@ class _AddFriend extends State { ); } - /// + ///添加好友列表 Widget addFriendItem() { return Container( margin: EdgeInsets.only(left:16.w,right:16.w,bottom: 24.h), @@ -122,32 +122,133 @@ class _AddFriend extends State { fontWeight: FontWeight.w500), ), ), - Container( - padding: EdgeInsets.symmetric(horizontal:18.w,vertical:6.h), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(26.5.r), - color: Color(0xFF32A060), - boxShadow: [ - BoxShadow( - color: Color(0xFF32A060).withOpacity(0.2), - spreadRadius: 0, - blurRadius: 4, - offset: Offset(0, 4), // changes position of shadow - ), - ], - ), - child: Text( - "添加", - style: TextStyle( - fontSize: 12.sp, - color: Colors.white, - fontWeight:MyFontWeight.regular), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap:(){ + showFriendVerificationDialog(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal:18.w,vertical:6.h), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(26.5.r), + color: Color(0xFF32A060), + boxShadow: [ + BoxShadow( + color: Color(0xFF32A060).withOpacity(0.2), + spreadRadius: 0, + blurRadius: 4, + offset: Offset(0, 4), // changes position of shadow + ), + ], + ), + child: Text( + "添加", + style: TextStyle( + fontSize: 12.sp, + color: Colors.white, + fontWeight:MyFontWeight.regular), + ), ), ) ]), ); } + ///好友验证弹窗 + showFriendVerificationDialog() { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + contentPadding: EdgeInsets.zero, // 移除默认内边距 + content: Container( + // width: MediaQuery.of(context).size.width - 84, + height: 130.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding(padding:EdgeInsets.symmetric(vertical:16.h), + child: Text( + "好友验证", + style: TextStyle( + color: Color(0xFF060606), + fontSize: 16.sp, + fontWeight: MyFontWeight.bold, + ), + ),), + Expanded(child:Text( + "我是秀才8856", + style: TextStyle( + color: Color(0xFF060606), + fontSize: 12.sp, + fontWeight: MyFontWeight.regular, + ), + )), + // Spacer(), + Container( + margin:EdgeInsets.only(top:18.h), + height:1.h, + width: double.infinity, + color:Color(0xFFEDEDED), + ), + Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + // height: 38.h, + child: Text( + "取消", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Color(0xFF060606), + ) + ) + ) + ) + ), + Container( + height:45, + width: 1.w, + color: Color(0xFFEDEDED), + ), + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).pop(); + }, + child: Container( + // height: 38.h, + child: Text( + "确认", + textAlign: TextAlign.center, + style: TextStyle( + color: Color(0xFF060606), + ) + ) + ) + ) + ) + ], + ) + ], + ), + ), + ); + }, + ); + } + /// 搜索框 Widget friendGroupSearch() { return Container( diff --git a/lib/im/chat_details_page.dart b/lib/im/chat_details_page.dart index c1323852..b89bf427 100644 --- a/lib/im/chat_details_page.dart +++ b/lib/im/chat_details_page.dart @@ -17,6 +17,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../../community/release_dynamic.dart'; import '../../generated/l10n.dart'; import '../../utils/font_weight.dart'; +import '../retrofit/data/im_user_list.dart'; +import '../view_widget/custom_image.dart'; import 'im_view/on_chat_message.dart'; import 'im_view/on_chat_msg_instance.dart'; @@ -54,6 +56,8 @@ class _ChatDetailsPage extends State final ScrollController scrollController = ScrollController(); String tex = ""; int copyIndex = 0; + String selfUserId = ""; + ImUserList _toUser; @override void onMessage(txt) { @@ -64,11 +68,9 @@ class _ChatDetailsPage extends State loadMessageList() async { selfUserId = (await SharedPreferences.getInstance()).getString("userId"); + messages = await hxDatabase.queryUList(_toUser.mid); - toUserId = widget.arguments["toId"]; - messages = await hxDatabase.queryUList(toUserId); - - socketClient.addCallback(toUserId, (Message message) { + socketClient.addCallback(_toUser.mid, (Message message) { messages.add(message); refreshState(); @@ -80,12 +82,11 @@ class _ChatDetailsPage extends State if (mounted) setState(() {}); } - String selfUserId = ""; - String toUserId = ""; - @override void initState() { super.initState(); + + _toUser = widget.arguments["toUser"]; OnChatMsgInstance.instance.onChatMessage = this; WidgetsBinding.instance.addObserver(this); commentFocus.addListener(_focusNodeListener); @@ -154,7 +155,7 @@ class _ChatDetailsPage extends State WidgetsBinding.instance.removeObserver(this); commentFocus.removeListener(_focusNodeListener); - socketClient.removeCallback(toUserId); + socketClient.removeCallback(_toUser.mid); } @@ -250,7 +251,7 @@ class _ChatDetailsPage extends State // resizeToAvoidBottomInset: false, backgroundColor: Color(0xFFF6F6F6), appBar: MyAppBar( - title: "哈哈哈哈", + title: _toUser.nickname, titleColor: Color(0xFF0D0D0D), titleSize: 17.sp, leading: true, @@ -367,6 +368,7 @@ class _ChatDetailsPage extends State height: 16.h, ), if (copyIndex == 1) + ///复制聊天消息 Stack( alignment: Alignment.bottomCenter, children: [ @@ -456,19 +458,14 @@ class _ChatDetailsPage extends State padding: EdgeInsets.only(left: 17.w, right: 39.w), child: Row( children: [ - // MImage( - // "", - // isCircle: true, - // width: 44, - // height: 44, - // fit: BoxFit.cover, - // errorSrc: "assets/image/default_user.webp", - // fadeSrc: "assets/image/default_user.webp", - // ), - Image.asset( - "assets/image/fuka_zj.webp", + MImage( + _toUser.avatar, + isCircle: true, height: 44.h, width: 44.h, + fit: BoxFit.cover, + errorSrc: "assets/image/default_1.webp", + fadeSrc: "assets/image/default_1.webp", ), SizedBox( width: 12.w, @@ -658,19 +655,14 @@ class _ChatDetailsPage extends State SizedBox( width: 12.w, ), - // MImage( - // "", - // isCircle: true, - // width: 44, - // height: 44, - // fit: BoxFit.cover, - // errorSrc: "assets/image/default_user.webp", - // fadeSrc: "assets/image/default_user.webp", - // ), - Image.asset( - "assets/image/fuka_zj.webp", - height: 44, - width: 44, + MImage( + "", + isCircle: true, + height: 44.h, + width: 44.h, + fit: BoxFit.cover, + errorSrc: "assets/image/default_1.webp", + fadeSrc: "assets/image/default_1.webp", ), ], ), @@ -682,19 +674,14 @@ class _ChatDetailsPage extends State padding: EdgeInsets.only(left: 17.w, right: 39.w, top: 20.h), child: Row( children: [ - // MImage( - // "", - // isCircle: true, - // width: 44, - // height: 44, - // fit: BoxFit.cover, - // errorSrc: "assets/image/default_user.webp", - // fadeSrc: "assets/image/default_user.webp", - // ), - Image.asset( - "assets/image/fuka_zj.webp", + MImage( + _toUser.avatar, + isCircle: true, height: 44.h, width: 44.h, + fit: BoxFit.cover, + errorSrc: "assets/image/default_1.webp", + fadeSrc: "assets/image/default_1.webp", ), SizedBox( width: 12.w, @@ -786,7 +773,7 @@ class _ChatDetailsPage extends State if (commentText.trim() == "") { return; } - socketClient.sendMessage(toUserId, commentText).then((value) { + socketClient.sendMessage(_toUser.mid, commentText).then((value) { Message message = value; messages.insert(0, message); chatController.clear(); diff --git a/lib/im/database/contact.dart b/lib/im/database/contact.dart deleted file mode 100644 index 559e7b2a..00000000 --- a/lib/im/database/contact.dart +++ /dev/null @@ -1,33 +0,0 @@ -class Contact { - int id; - - String userId; - - String nickName; - - String imageUrl; - - int state; - - int isDelete; - - Contact(this.id, this.userId, this.nickName, this.imageUrl, this.state, - this.isDelete); - - factory Contact.fromJson(Map json) => Contact( - json["id"], - json["userId"], - json["nickName"], - json["imageUrl"], - json["state"], - json["isDelete"]); - - Map toJson() => { - "id": id, - "fromId": userId, - "toId": nickName, - "replyId": imageUrl, - "state": state, - "isDelete": isDelete == null ? 0 : isDelete - }; -} diff --git a/lib/im/database/hx_database.dart b/lib/im/database/hx_database.dart index b74b739a..1e1d59ad 100644 --- a/lib/im/database/hx_database.dart +++ b/lib/im/database/hx_database.dart @@ -1,17 +1,14 @@ - import 'package:flutter/cupertino.dart'; -import 'package:huixiang/im/database/contact.dart'; import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/database/migration.dart'; import 'package:sqflite/sqflite.dart'; +import '../../retrofit/data/im_user_list.dart'; class HxDatabase { - Database db; void open({String key}) async { - // _migrations.add(Migration(1, 2, (Database database) async { // database.execute('ALTER TABLE `Message` ADD COLUMN `replyId` VARCHAR(20) DEFAULT NULL AFTER `toId`'); // })); @@ -20,23 +17,19 @@ class HxDatabase { if (key?.isNotEmpty ?? false) { databaseName = 'hx_$key.db'; } - await openDatabase( - databaseName, - version: 2, + await openDatabase(databaseName, version: 2, onCreate: (Database db, int version) async { - db.execute('CREATE TABLE IF NOT EXISTS `Message` (`id` INTEGER, `fromId` VARCHAR(20), `toId` VARCHAR(20), `replyId` VARCHAR(20), `content` TEXT, `attach` TEXT, `msgType` INTEGER, `time` VARCHAR(20), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))'); - db.execute('CREATE TABLE IF NOT EXISTS `Contact` (`id` INTEGER, `userId` VARCHAR(20), `nickName` VARCHAR(20), `imageUrl` VARCHAR(200), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))'); - }, - onConfigure: (database) async { - await database.execute('PRAGMA foreign_keys = ON'); - }, - onUpgrade: (database, startVersion, endVersion) async { - await runMigrations(database, startVersion, endVersion, _migrations); - }, - onOpen: (Database db) { - this.db = db; - } - ); + db.execute( + 'CREATE TABLE IF NOT EXISTS `Message` (`id` INTEGER, `fromId` VARCHAR(20), `toId` VARCHAR(20), `replyId` VARCHAR(20), `content` TEXT, `attach` TEXT, `msgType` INTEGER, `time` VARCHAR(20), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))'); + db.execute( + 'CREATE TABLE IF NOT EXISTS `ImUser` (`id` INTEGER, `mid` VARCHAR(20), `nickname` VARCHAR(20), `avatar` VARCHAR(200), `phone` VARCHAR(200), `isDelete` INTEGER, PRIMARY KEY (`id`))'); + }, onConfigure: (database) async { + await database.execute('PRAGMA foreign_keys = ON'); + }, onUpgrade: (database, startVersion, endVersion) async { + await runMigrations(database, startVersion, endVersion, _migrations); + }, onOpen: (Database db) { + this.db = db; + }); } void close() { @@ -47,8 +40,10 @@ class HxDatabase { if (db == null) { return Future.value(); } - String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1'; - List messages = await db.rawQuery(sql, [userId, userId]).then((value) { + String sql = + 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1'; + List messages = + await db.rawQuery(sql, [userId, userId]).then((value) { return value.map((e) { debugPrint("Message: ${e}"); return Message.fromJson(e); @@ -63,7 +58,8 @@ class HxDatabase { if (db == null) { return Future.value([]); } - String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? GROUP BY toId,fromId ORDER BY time DESC'; + String sql = + 'SELECT * FROM Message WHERE toId = ? OR fromId = ? GROUP BY toId,fromId ORDER BY time DESC'; return db.rawQuery(sql, [userId, userId]).then((value) { return value.map((e) { debugPrint("Message: ${e}"); @@ -78,7 +74,8 @@ class HxDatabase { if (db == null) { return Future.value([]); } - String sql = 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC'; + String sql = + 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC'; return db.rawQuery(sql, [userId, userId]).then((value) { return value.map((e) => Message.fromJson(e)).toList(); }, onError: (error) { @@ -103,7 +100,8 @@ class HxDatabase { return Future.value(0); } debugPrint("Message_insert: $message"); - return db.update("Message", message, where: 'id = ?', whereArgs: [message['id']]); + return db.update("Message", message, + where: 'id = ?', whereArgs: [message['id']]); } Future insert(Map message) async { @@ -114,37 +112,41 @@ class HxDatabase { return db.insert("Message", message); } - Future insertContact(Map contactMap) async { + Future insertOrUpdateImUser(Map imUserMap) async { if (db == null) { return Future.value(0); } - debugPrint("contact_insert: $contactMap"); - return db.insert("Contact", contactMap); + debugPrint("imUser_insert: $imUserMap"); + if ((await queryImUserById(imUserMap['mid'])) == null) + return db.insert("ImUser", imUserMap); + else + return db.update("ImUser", imUserMap, + where: 'mid = ?', whereArgs: [imUserMap['mid']]); } - Future> queryContact(List userIds) async { + Future> queryImUser(List userIds) async { if (db == null) { - return Future.value([]); + return Future.value([]); } - String sql = 'SELECT * FROM Contact WHERE userId IN (?) AND state = 0 AND isDelete = 0'; - return db.rawQuery(sql, userIds).then((value) { - return value.map((e) => Contact.fromJson(e)).toList(); + String query = 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})'; + return db.rawQuery(query).then((value) { + return value.map((e) => ImUserList.fromJson(e)).toList(); }, onError: (error) { - debugPrint("Contact_error: $error"); + debugPrint("ImUser_error: $error"); }); } - Future queryContactById(String userId) async { + Future queryImUserById(String userId) async { if (db == null) { return Future.value(); } - List contact = await db.query("Contact", distinct: true, where: "userId = ?", whereArgs: [userId]) - .then((value) { - return value.map((e) => Contact.fromJson(e)).toList(); + List imUser = await db.query("ImUser", + distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) { + return value.map((e) => ImUserList.fromJson(e)).toList(); }, onError: (error) { - debugPrint("Contact_error: $error"); + debugPrint("ImUser_error: $error"); }); - return (contact?.isNotEmpty ?? false) ? contact.first : null; + return (imUser?.isNotEmpty ?? false) ? imUser.first : null; } final List _migrations = []; @@ -155,22 +157,22 @@ class HxDatabase { } Future runMigrations( - final Database migrationDatabase, - final int startVersion, - final int endVersion, - final List migrations, - ) async { + final Database migrationDatabase, + final int startVersion, + final int endVersion, + final List migrations, + ) async { final relevantMigrations = migrations .where((migration) => migration.startVersion >= startVersion) .toList() ..sort( - (first, second) => first.startVersion.compareTo(second.startVersion)); + (first, second) => first.startVersion.compareTo(second.startVersion)); if (relevantMigrations.isEmpty || relevantMigrations.last.endVersion != endVersion) { throw StateError( 'There is no migration supplied to update the database to the current version.' - ' Aborting the migration.', + ' Aborting the migration.', ); } @@ -178,5 +180,4 @@ class HxDatabase { await migration.migrate(migrationDatabase); } } - } diff --git a/lib/im/im_search.dart b/lib/im/im_search.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/im/im_view/im_page.dart b/lib/im/im_view/im_page.dart index 1e0b5d22..8f54f17e 100644 --- a/lib/im/im_view/im_page.dart +++ b/lib/im/im_view/im_page.dart @@ -4,9 +4,10 @@ import 'dart:collection'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:huixiang/generated/l10n.dart'; -import 'package:huixiang/im/database/contact.dart'; import 'package:huixiang/im/database/message.dart'; +import 'package:huixiang/im/im_view/time_formatter.dart'; import 'package:huixiang/im/out/message.pb.dart'; import 'package:huixiang/main.dart'; import 'package:huixiang/retrofit/data/base_data.dart'; @@ -17,9 +18,13 @@ import 'package:huixiang/utils/font_weight.dart'; import 'package:huixiang/view_widget/classic_header.dart'; import 'package:huixiang/view_widget/my_footer.dart'; import 'package:huixiang/view_widget/round_button.dart'; +import 'package:intl/intl.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import '../../retrofit/data/im_user_list.dart'; +import '../../utils/flutter_utils.dart'; +import '../../view_widget/custom_image.dart'; import 'on_chat_message.dart'; import 'on_chat_msg_instance.dart'; @@ -36,6 +41,7 @@ class _IMPage extends State implements OnChatMessage { ApiService apiService; int pageNum = 1; List messages = []; + Map imUserList = {}; Map msgNumber = { "1": 0, "2": 0, @@ -67,7 +73,8 @@ class _IMPage extends State implements OnChatMessage { loadMessageList(); SharedPreferences.getInstance().then((value) { - apiService = ApiService(Dio(), token: value.getString("token"), context: context); + apiService = + ApiService(Dio(), token: value.getString("token"), context: context); queryMsgStats(); }); } @@ -79,9 +86,8 @@ class _IMPage extends State implements OnChatMessage { } List userIds = []; - Map contactMap = {}; Map lastMessageMap = {}; - Stream streamSubscription ; + Stream streamSubscription; loadMessageList() async { SharedPreferences shared = await SharedPreferences.getInstance(); @@ -97,9 +103,9 @@ class _IMPage extends State implements OnChatMessage { lastMessageMap[message.fromId] = message; debugPrint("messages_records : ${message.toJson()}"); - if (contactMap[message.fromId] == null) { - /// TODO: message.fromId request Api and setState - } + // if (contactMap[message.fromId] == null) { + // /// TODO: message.fromId request Api and setState + // } if (mounted) { setState(() {}); } @@ -112,23 +118,20 @@ class _IMPage extends State implements OnChatMessage { }); userIds = messages .map((e) => e.toId != userId ? e.toId : e.fromId) - .toSet().where((element) => element != userId) - .toList(); + .toSet() + .where((element) => element != userId) + .toList(); //先处理列表的时间,我搞了一个 + List contacts = (await hxDatabase.queryImUser(userIds)) ?? []; - lastMessageMap = groupBy(messages, (p0) => p0.toId != userId ? p0.toId : p0.fromId); + if (contacts.isNotEmpty) imUserList = groupBy(contacts, (p0) => p0.mid); - List contacts = await hxDatabase.queryContact(userIds); - if (contacts?.isEmpty ?? false) { - /// TODO: userIds request Api - } else { - List queryUserIds = userIds.where((u) => contacts.where((c) => c.userId == u).isEmpty).toList(); - /// TODO: queryUserIds request Api - } - contactMap = groupBy(contacts, (p0) => p0.userId); + lastMessageMap = + groupBy(messages, (p0) => p0.toId != userId ? p0.toId : p0.fromId); if (mounted) { setState(() {}); } + queryMemberInfo(userIds); } void updateLastMessage(String userId) async { @@ -211,6 +214,25 @@ class _IMPage extends State implements OnChatMessage { EasyLoading.dismiss(); } + ///批量查询用户信息 + queryMemberInfo(List mids) async { + BaseData> baseData = await apiService.memberInfoByIds({ + "mids": mids, + }).catchError((error) { + SmartDialog.showToast(AppUtils.dioErrorTypeToString(error.type), + alignment: Alignment.center); + }); + if (baseData != null && baseData.isSuccess) { + baseData.data.forEach((element) async { + await hxDatabase.insertOrUpdateImUser(element.toJson()); + }); + imUserList = groupBy(baseData.data, (p0) => p0.mid); + setState(() {}); + } else { + SmartDialog.showToast(baseData.msg, alignment: Alignment.center); + } + } + RefreshController _refreshController = RefreshController(); @override @@ -280,7 +302,8 @@ class _IMPage extends State implements OnChatMessage { behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context) - .pushNamed('/router/chat_friend_group').then((value) { + .pushNamed('/router/chat_friend_group') + .then((value) { _refresh(); }); }, @@ -375,12 +398,13 @@ class _IMPage extends State implements OnChatMessage { behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pushNamed( - '/router/chat_details_page', + '/router/chat_details_page', arguments: { - "toId": userIds[position], + "toUser": imUserList[userIds[position]], }, ).then((value) { updateLastMessage(userIds[position]); + _refresh(); }); }, child: chatItem(userIds[position]), @@ -395,19 +419,16 @@ class _IMPage extends State implements OnChatMessage { padding: EdgeInsets.only(left: 16.w, right: 17.w, bottom: 18.h), child: Row( children: [ - // MImage( - // "", - // isCircle: true, - // width: 40, - // height: 40, - // fit: BoxFit.cover, - // errorSrc: "assets/image/default_user.webp", - // fadeSrc: "assets/image/default_user.webp", - // ), - Image.asset( - "assets/image/fuka_zj.webp", + MImage( + !imUserList.containsKey(userId) + ? null + : imUserList[userId]?.avatar ?? "", + isCircle: true, height: 54.h, width: 54.h, + fit: BoxFit.cover, + errorSrc: "assets/image/fuka_zj.webp", + fadeSrc: "assets/image/fuka_zj.webp", ), SizedBox( width: 12.w, @@ -420,7 +441,9 @@ class _IMPage extends State implements OnChatMessage { children: [ Expanded( child: Text( - "喽哈 $userId", + !imUserList.containsKey(userId) + ? "" + : imUserList[userId]?.nickname ?? "", // overflow: TextOverflow.fade, maxLines: 1, style: TextStyle( @@ -431,7 +454,11 @@ class _IMPage extends State implements OnChatMessage { ), ), Text( - "2021.03.08 13:22", + lastMessageMap[userId]?.time != null + ? TimeFormatter.formatTime( + DateTime.fromMillisecondsSinceEpoch(num.parse( + lastMessageMap[userId]?.time ?? ""))) + : "", style: TextStyle( fontSize: 12.sp, color: Color(0xFFA29E9E), @@ -447,7 +474,7 @@ class _IMPage extends State implements OnChatMessage { children: [ Expanded( child: Text( - "新开的火锅店好吃得很", + lastMessageMap[userId]?.content ?? "", maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( @@ -482,5 +509,4 @@ class _IMPage extends State implements OnChatMessage { ), ); } - } diff --git a/lib/im/im_view/time_formatter.dart b/lib/im/im_view/time_formatter.dart new file mode 100644 index 00000000..c406c9a5 --- /dev/null +++ b/lib/im/im_view/time_formatter.dart @@ -0,0 +1,20 @@ +import 'package:intl/intl.dart'; + +class TimeFormatter { + static String formatTime(DateTime time) { + final now = DateTime.now(); + final diff = now.difference(time).inHours; + + if (diff < 24) { + return '刚刚'; // 24小时内显示为“刚刚” + } else if (diff < 48) { + return '昨天'; // 昨天 + } else if (diff < 72) { + return '前天'; // 前天 + } else if (time.year == now.year) { + return DateFormat('MM月dd日').format(time); // 今年内的日期 + } else { + return DateFormat('yyyy年MM月dd日').format(time); // 其他年份的日期 + } + } +} \ No newline at end of file diff --git a/lib/retrofit/data/im_user_list.dart b/lib/retrofit/data/im_user_list.dart new file mode 100644 index 00000000..8f59dd6d --- /dev/null +++ b/lib/retrofit/data/im_user_list.dart @@ -0,0 +1,61 @@ +/// mid : "1379254113602109440" +/// nickname : "哈哈" +/// avatar : "https://pos.upload.lotus-wallet.com/admin/2022/11/d501d2cd-ffc0-49f2-967c-2e463462f500.jpeg" +/// phone : "13052919193" +/// isFollow : null +/// createTime : null + +class ImUserList { + ImUserList({ + String mid, + String nickname, + num isDelete, + String avatar, + String phone, }){ + _mid = mid; + _nickname = nickname; + _isDelete = isDelete; + _avatar = avatar; + _phone = phone; +} + + ImUserList.fromJson(dynamic json) { + _mid = json['mid']; + _nickname = json['nickname']; + _isDelete = json['isDelete']; + _avatar = json['avatar']; + _phone = json['phone']; + } + String _mid; + String _nickname; + num _isDelete; + String _avatar; + String _phone; +ImUserList copyWith({ String mid, + String nickname, + num isDelete, + String avatar, + String phone, +}) => ImUserList( mid: mid ?? _mid, + nickname: nickname ?? _nickname, + isDelete: isDelete ?? _isDelete, + avatar: avatar ?? _avatar, + phone: phone ?? _phone, +); + String get mid => _mid; + String get nickname => _nickname; + num get isDelete => _isDelete; + String get avatar => _avatar; + String get phone => _phone; + + Map toJson() { + final map = {}; + map['mid'] = _mid; + map['nickname'] = _nickname; + map['isDelete'] = _isDelete; + map['avatar'] = _avatar; + map['phone'] = _phone; + return map; + } + +} \ No newline at end of file diff --git a/lib/retrofit/retrofit_api.dart b/lib/retrofit/retrofit_api.dart index 970d44c0..ceafabdc 100644 --- a/lib/retrofit/retrofit_api.dart +++ b/lib/retrofit/retrofit_api.dart @@ -39,6 +39,7 @@ import 'data/goods_category.dart'; import 'data/headlines_list.dart'; import 'data/headlines_list_details.dart'; import 'data/home_rank.dart'; +import 'data/im_user_list.dart'; import 'data/invitation_list.dart'; import 'data/invoice_list.dart'; import 'data/invoices_detail_info.dart'; @@ -653,4 +654,13 @@ abstract class ApiService { ///发票详情 @GET("invoice/detail{id}") Future> invoiceDetail(@Path("id") String id); + + ///消息页批量查询用户信息 + @POST("/member/memberInfoByIds") + Future>> memberInfoByIds(@Body() Map param); + + ///Im关键字搜索 + @GET("/member/memberSearch?keyword={keyword}") + Future>> memberSearch( + @Path("keyword") String keyword); } diff --git a/lib/retrofit/retrofit_api.g.dart b/lib/retrofit/retrofit_api.g.dart index 752b8278..15d0a63e 100644 --- a/lib/retrofit/retrofit_api.g.dart +++ b/lib/retrofit/retrofit_api.g.dart @@ -2528,4 +2528,52 @@ class _ApiService implements ApiService { ); return value; } + + @override + Future>> memberInfoByIds(param) async { + ArgumentError.checkNotNull(param, 'param'); + const _extra = {}; + final queryParameters = {}; + final _data = {}; + _data.addAll(param ?? {}); + final _result = await _dio.request>( + '/member/memberInfoByIds', + queryParameters: queryParameters, + options: RequestOptions( + method: 'POST', + headers: {}, + extra: _extra, + baseUrl: baseUrl), + data: _data); + final value = BaseData>.fromJson( + _result.data, + (json) => (json as List) + .map((i) => ImUserList.fromJson(i as Map)) + .toList()); + return value; + } + + @override + Future>> memberSearch(keyword) async { + ArgumentError.checkNotNull(keyword, 'keyword'); + const _extra = {}; + final queryParameters = {}; + final _data = {}; + final _result = await _dio.request>( + '/member/memberSearch?keyword=$keyword', + queryParameters: queryParameters, + options: RequestOptions( + method: 'GET', + headers: {}, + extra: _extra, + baseUrl: baseUrl), + data: _data); + final value = BaseData>.fromJson( + _result.data, + (json) => (json as List) + .map( + (i) => ImUserList.fromJson(i as Map)) + .toList()); + return value; + } }