Browse Source

Merge remote-tracking branch 'origin/wr_202303' into wr_202303

# Conflicts:
#	lib/im/chat_details_page.dart
wr_202303
zsw 4 months ago
parent
commit
0761defe24
  1. 2
      lib/im/SocketClient.dart
  2. 120
      lib/im/chat_details_page.dart
  3. 52
      lib/im/chat_friend_group.dart
  4. 70
      lib/im/database/hx_database.dart
  5. 10
      lib/im/im_search.dart
  6. 4
      lib/im/im_view/im_page.dart
  7. 20
      lib/im/im_view/time_formatter.dart
  8. 4
      lib/main.dart
  9. 1
      lib/main_page.dart
  10. 18
      lib/utils/flutter_utils.dart

2
lib/im/SocketClient.dart

@ -85,8 +85,8 @@ class SocketClient {
dispose() { dispose() {
if (_socket != null) { if (_socket != null) {
_socket = null;
_socket.close(); _socket.close();
_socket = null;
} }
} }

120
lib/im/chat_details_page.dart

@ -1,13 +1,14 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:ui'; import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/out/message.pb.dart';
import 'package:huixiang/main.dart'; import 'package:huixiang/main.dart';
import 'package:huixiang/retrofit/data/im_user.dart'; import 'package:huixiang/retrofit/data/im_user.dart';
import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/retrofit/retrofit_api.dart';
@ -21,6 +22,9 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../community/release_dynamic.dart'; import '../../community/release_dynamic.dart';
import '../../generated/l10n.dart'; import '../../generated/l10n.dart';
import '../../utils/font_weight.dart'; import '../../utils/font_weight.dart';
import '../retrofit/data/base_data.dart';
import '../retrofit/data/user_info.dart';
import '../utils/flutter_utils.dart';
import '../view_widget/custom_image.dart'; import '../view_widget/custom_image.dart';
import 'im_view/on_chat_message.dart'; import 'im_view/on_chat_message.dart';
import 'im_view/on_chat_msg_instance.dart'; import 'im_view/on_chat_msg_instance.dart';
@ -54,6 +58,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
bool needHideMore = false; bool needHideMore = false;
var commentFocus = FocusNode(); var commentFocus = FocusNode();
String hintText = "输入消息内容..."; String hintText = "输入消息内容...";
UserInfo userInfo;
final OnChatMessage _tempOnChatMessage = final OnChatMessage _tempOnChatMessage =
OnChatMsgInstance.instance.onChatMessage; OnChatMsgInstance.instance.onChatMessage;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
@ -86,12 +91,29 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
messages.insert(0, message); messages.insert(0, message);
refreshState(); refreshState();
// if (scrollController.position != null) {
// double offset = scrollController.position.maxScrollExtent;
// debugPrint("offset: $offset");
// Future.delayed(const Duration(milliseconds: 500), () {
// scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
// });
// }
if (scrollController.position != null) { if (scrollController.position != null) {
double offset = scrollController.position.maxScrollExtent; double maxScrollExtent = scrollController.position.maxScrollExtent;
debugPrint("offset: $offset"); double viewportDimensions = scrollController.position.viewportDimension;
Future.delayed(const Duration(milliseconds: 500), () {
scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn); //
}); if (maxScrollExtent > viewportDimensions) {
double offset = maxScrollExtent;
debugPrint("offset: $offset");
Future.delayed(const Duration(milliseconds: 500), () {
scrollController.animateTo(
offset + 108,
duration: const Duration(milliseconds: 500),
curve: Curves.easeIn
);
});
}
} }
}); });
@ -114,6 +136,28 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
return; return;
} }
///
queryUser() async {
final SharedPreferences value = await SharedPreferences.getInstance();
if (value.containsKey('user') &&
value.getString('user') != null &&
value.getString('user') != "") {
userInfo = UserInfo.fromJson(jsonDecode(value.getString('user')));
}
if(apiService == null)
apiService = ApiService(Dio(), context: context, token: value.getString("token"));
BaseData<UserInfo> baseData =
await apiService.queryInfo().catchError((onError) {});
if (baseData != null && baseData.isSuccess) {
setState(() {
userInfo = baseData.data;
});
SharedPreferences.getInstance().then((value) => {
value.setString('user', jsonEncode(baseData.data)),
});
}
}
refreshState() { refreshState() {
if (mounted) setState(() {}); if (mounted) setState(() {});
} }
@ -127,6 +171,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
commentFocus.addListener(_focusNodeListener); commentFocus.addListener(_focusNodeListener);
queryUser();
loadMessageList(); loadMessageList();
} }
@ -309,25 +354,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
child: SmartRefresher( child: SingleChildScrollView(
enablePullDown: true,
enablePullUp: false,
header: MyHeader(),
physics: BouncingScrollPhysics(),
footer: CustomFooter(
loadStyle: LoadStyle.ShowWhenLoading,
builder: (BuildContext context, LoadStatus mode) {
return (messages.length == 0) ? Container() : MyFooter(mode);
},
),
controller: refreshController,
onRefresh: () {
refresh().then((){
refreshState();
});
},
onLoading: () {},
child: SingleChildScrollView(
physics: BouncingScrollPhysics(), physics: BouncingScrollPhysics(),
controller: scrollController, controller: scrollController,
child: Column( child: Column(
@ -348,7 +375,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
], ],
), ),
), ),
),
flex: 1, flex: 1,
), ),
input() input()
@ -362,7 +388,6 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
Widget chatDetailsList() { Widget chatDetailsList() {
return Container( return Container(
margin: EdgeInsets.only(bottom: 48.h), margin: EdgeInsets.only(bottom: 48.h),
alignment: Alignment.bottomCenter,
child: ListView.builder( child: ListView.builder(
itemCount: messages.length, itemCount: messages.length,
shrinkWrap: true, shrinkWrap: true,
@ -393,7 +418,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
child: Column( child: Column(
children: [ children: [
Text( Text(
"${DateTime.fromMillisecondsSinceEpoch(int.parse(message.time))}", AppUtils.timeFormatter(DateTime.fromMillisecondsSinceEpoch(int.parse(message.time))),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
color: Color(0xFFA29E9E), color: Color(0xFFA29E9E),
@ -728,7 +753,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
width: 12.w, width: 12.w,
), ),
MImage( MImage(
_toUser.avatar, userInfo?.headimg??"",
isCircle: true, isCircle: true,
height: 44.h, height: 44.h,
width: 44.h, width: 44.h,
@ -863,13 +888,38 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
chatController.clear(); chatController.clear();
if (mounted) setState(() {}); if (mounted) setState(() {});
if (scrollController.position != null) { // if (scrollController.position != null) {
double offset = scrollController.position.maxScrollExtent; // double offset = scrollController.position.maxScrollExtent;
debugPrint("offset: $offset"); // debugPrint("offset: $offset");
Future.delayed(const Duration(milliseconds: 500), () { // Future.delayed(const Duration(milliseconds: 500), () {
scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // scrollController.animateTo(offset + 108, duration: const Duration(milliseconds: 500), curve: Curves.easeIn);
}); // });
// }
if (scrollController.position.maxScrollExtent > scrollController.offset) {
//
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.easeOut,
);
} }
// if (scrollController.position != null) {
// double maxScrollExtent = scrollController.position.maxScrollExtent;
// double viewportDimensions = scrollController.position.viewportDimension;
//
// //
// if (maxScrollExtent > viewportDimensions) {
// // double offset = maxScrollExtent;
// // debugPrint("offset: $offset");
// Future.delayed(const Duration(milliseconds: 500), () {
// scrollController.animateTo(
// maxScrollExtent+viewportDimensions,
// duration: const Duration(milliseconds: 500),
// curve: Curves.easeIn
// );
// });
// }
// }
}); });
}, },

52
lib/im/chat_friend_group.dart

@ -84,32 +84,32 @@ class _ChatFriendGroup extends State<ChatFriendGroup>
leading: true, leading: true,
leadingColor: Colors.black, leadingColor: Colors.black,
background: Color(0xFFFFFFFF), background: Color(0xFFFFFFFF),
action: GestureDetector( // action: GestureDetector(
behavior: HitTestBehavior.opaque, // behavior: HitTestBehavior.opaque,
onTap: () { // onTap: () {
Navigator.of(context).pushNamed('/router/add_friend'); // Navigator.of(context).pushNamed('/router/add_friend');
}, // },
child: Container( // child: Container(
padding: EdgeInsets.all(12), // padding: EdgeInsets.all(12),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Color(0xFFFFFFFF), // color: Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(20.r), // borderRadius: BorderRadius.circular(20.r),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: Color(0xFF000000).withAlpha(25), // color: Color(0xFF000000).withAlpha(25),
offset: Offset(0, 0), // offset: Offset(0, 0),
blurRadius: 4, // blurRadius: 4,
spreadRadius: 0, // spreadRadius: 0,
) // )
], // ],
), // ),
child: Image.asset( // child: Image.asset(
"assets/image/add_friend.webp", // "assets/image/add_friend.webp",
fit: BoxFit.fill, // fit: BoxFit.fill,
height: 14.h,width: 14.h, // height: 14.h,width: 14.h,
), // ),
), // ),
), // ),
), ),
body: Container( body: Container(
child: Column( child: Column(

70
lib/im/database/hx_database.dart

@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:huixiang/constant.dart'; import 'package:huixiang/constant.dart';
import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/database/migration.dart'; import 'package:huixiang/im/database/migration.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import '../../retrofit/data/im_user.dart'; import '../../retrofit/data/im_user.dart';
@ -37,10 +38,15 @@ class HxDatabase {
db.close(); db.close();
} }
Future<Message> lastMessage(String userId) async { _dbIsOpen() async {
if (db == null || !db.isOpen) { if (db == null || !db.isOpen) {
return Future.value(); var sp = await SharedPreferences.getInstance();
open(key: sp.getString("userId"));
} }
}
Future<Message> lastMessage(String userId) async {
await _dbIsOpen();
String sql = String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1'; 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages = List<Message> messages =
@ -55,10 +61,8 @@ class HxDatabase {
return (messages?.isNotEmpty ?? false) ? messages.first : null; return (messages?.isNotEmpty ?? false) ? messages.first : null;
} }
Future<List<Message>> queryList(userId) { Future<List<Message>> queryList(userId) async{
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(<Message>[]);
}
String sql = String sql =
'SELECT * FROM (SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC) mm GROUP BY mm.toId,mm.fromId'; 'SELECT * FROM (SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC) mm GROUP BY mm.toId,mm.fromId';
return db.rawQuery(sql, [userId, userId]).then((value) { return db.rawQuery(sql, [userId, userId]).then((value) {
@ -71,10 +75,8 @@ class HxDatabase {
}); });
} }
Future<List<Message>> queryUList(userId, {int page = 1, int pageSize = 10}) { Future<List<Message>> queryUList(userId, {int page = 1, int pageSize = 10}) async{
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(<Message>[]);
}
int start = (page - 1) * pageSize; int start = (page - 1) * pageSize;
String sql = String sql =
'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT ?, ?'; 'SELECT * FROM Message WHERE toId = ? OR fromId = ? ORDER BY time DESC LIMIT ?, ?';
@ -86,9 +88,7 @@ class HxDatabase {
} }
Future<Map<String, int>> messageUnreadCount(List<String> userIds, String selfUserId) async { Future<Map<String, int>> messageUnreadCount(List<String> userIds, String selfUserId) async {
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value({});
}
String userStr = userIds.join(","); String userStr = userIds.join(",");
debugPrint("userStr: $userStr"); debugPrint("userStr: $userStr");
List<Message> messages = await db.rawQuery( List<Message> messages = await db.rawQuery(
@ -98,13 +98,11 @@ class HxDatabase {
}, onError: (error) { }, onError: (error) {
debugPrint("Message-error: $error"); debugPrint("Message-error: $error");
}); });
return messages.lGroupBy((p)=> p.fromId).mGroupCount; return (messages??[]).lGroupBy((p) => p.fromId).mGroupCount;
} }
Future<List<Map>> queryListAll() { Future<List<Map>> queryListAll() async{
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value();
}
String sql = 'SELECT * FROM Message ORDER BY time DESC'; String sql = 'SELECT * FROM Message ORDER BY time DESC';
return db.rawQuery(sql); return db.rawQuery(sql);
} }
@ -113,35 +111,29 @@ class HxDatabase {
return db.delete("Message"); return db.delete("Message");
} }
update(Map<dynamic, dynamic> message) { update(Map<dynamic, dynamic> message) async{
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(0);
}
debugPrint("Message_insert: $message"); debugPrint("Message_insert: $message");
return db.update("Message", message, return db.update("Message", message,
where: 'id = ?', whereArgs: [message['id']]); where: 'id = ?', whereArgs: [message['id']]);
} }
Future<int> insert(Map message) async { Future<int> insert(Map message) async {
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(0);
}
debugPrint("Message_insert: $message"); debugPrint("Message_insert: $message");
return db.insert("Message", message); return db.insert("Message", message);
} }
/// update message read state /// update message read state
readMessage(String selfUserId, String userId) { readMessage(String selfUserId, String userId) async{
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(<Message>[]); db.update("Message", {"state": 1},
} where: "fromId = ? AND toId = ? AND state = 0 AND isDelete = 0",
db.update("Message", {"state": 1}, where: "fromId = ? AND toId = ? AND state = 0 AND isDelete = 0", whereArgs: [userId, selfUserId]); whereArgs: [userId, selfUserId]);
} }
Future<int> insertOrUpdateImUser(Map imUserMap) async { Future<int> insertOrUpdateImUser(Map imUserMap) async {
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(0);
}
debugPrint("imUser_insert: $imUserMap"); debugPrint("imUser_insert: $imUserMap");
if ((await queryImUserById(imUserMap['mid'])) == null) if ((await queryImUserById(imUserMap['mid'])) == null)
return db.insert("ImUser", imUserMap); return db.insert("ImUser", imUserMap);
@ -151,10 +143,9 @@ class HxDatabase {
} }
Future<List<ImUser>> queryImUser(List<String> userIds) async { Future<List<ImUser>> queryImUser(List<String> userIds) async {
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value(<ImUser>[]); String query =
} 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
String query = 'SELECT * FROM ImUser WHERE mid IN (${userIds.map((mid) => "'$mid'").join(',')})';
return db.rawQuery(query).then((value) { return db.rawQuery(query).then((value) {
return value.map((e) => ImUser.fromJson(e)).toList(); return value.map((e) => ImUser.fromJson(e)).toList();
}, onError: (error) { }, onError: (error) {
@ -163,9 +154,7 @@ class HxDatabase {
} }
Future<ImUser> queryImUserById(String userId) async { Future<ImUser> queryImUserById(String userId) async {
if (db == null || !db.isOpen) { await _dbIsOpen();
return Future.value();
}
List<ImUser> imUser = await db.query("ImUser", List<ImUser> imUser = await db.query("ImUser",
distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) { distinct: true, where: "mid = ?", whereArgs: [userId]).then((value) {
return value.map((e) => ImUser.fromJson(e)).toList(); return value.map((e) => ImUser.fromJson(e)).toList();
@ -206,5 +195,4 @@ class HxDatabase {
await migration.migrate(migrationDatabase); await migration.migrate(migrationDatabase);
} }
} }
} }

10
lib/im/im_search.dart

@ -3,7 +3,6 @@ import 'dart:ui';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -59,9 +58,12 @@ class _ImSearch extends State<ImSearch> {
await apiService.memberSearch(keyword).catchError((onError) {}); await apiService.memberSearch(keyword).catchError((onError) {});
if (baseData != null && baseData.isSuccess) { if (baseData != null && baseData.isSuccess) {
searchUser.clear(); searchUser.clear();
searchUser.addAll(baseData.data); baseData.data.forEach((element) {
if(element.phone != "" && element.nickname != ""){
searchUser.add(element);
}
});
searchState = 1; searchState = 1;
if(baseData.data.length == 0){ if(baseData.data.length == 0){
searchState = 2; searchState = 2;
} }
@ -214,7 +216,7 @@ class _ImSearch extends State<ImSearch> {
return Container( return Container(
padding: EdgeInsets.only(left:10.w,right:16.w,bottom:15.h), padding: EdgeInsets.only(left:10.w,right:16.w,bottom:15.h),
child: Text( child: Text(
textType == 1 ?(searchUser?.phone ?? searchUser?.nickname?? "") : (searchUser?.nickname ?? searchUser?.phone ?? ""), textType == 1 ?(searchUser?.phone?? "") : (searchUser?.nickname ?? ""),
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: Color(0xFF32A060), color: Color(0xFF32A060),

4
lib/im/im_view/im_page.dart

@ -4,7 +4,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:huixiang/constant.dart'; import 'package:huixiang/constant.dart';
import 'package:huixiang/generated/l10n.dart'; import 'package:huixiang/generated/l10n.dart';
import 'package:huixiang/im/database/message.dart'; import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/im/im_view/time_formatter.dart';
import 'package:huixiang/main.dart'; import 'package:huixiang/main.dart';
import 'package:huixiang/retrofit/data/base_data.dart'; import 'package:huixiang/retrofit/data/base_data.dart';
import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/retrofit/retrofit_api.dart';
@ -183,6 +182,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
} }
refreshState() { refreshState() {
if(_refreshController.isRefresh)_refreshController.refreshCompleted();
if (mounted) setState(() {}); if (mounted) setState(() {});
} }
@ -489,7 +489,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
), ),
Text( Text(
lastMessageMap[userId]?.time != null lastMessageMap[userId]?.time != null
? TimeFormatter.formatTime( ? AppUtils.timeFormatter(
DateTime.fromMillisecondsSinceEpoch(num.parse( DateTime.fromMillisecondsSinceEpoch(num.parse(
lastMessageMap[userId]?.time ?? ""))) lastMessageMap[userId]?.time ?? "")))
: "", : "",

20
lib/im/im_view/time_formatter.dart

@ -1,20 +0,0 @@
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); //
}
}
}

4
lib/main.dart

@ -224,6 +224,10 @@ initDatabase(String userId) async {
hxDatabase.open(key: userId); hxDatabase.open(key: userId);
} }
closeDatabase() async {
hxDatabase.close();
}
final SocketClient socketClient = new SocketClient(); final SocketClient socketClient = new SocketClient();
EventBus eventBus = EventBus(sync: true); EventBus eventBus = EventBus(sync: true);

1
lib/main_page.dart

@ -70,6 +70,7 @@ class _MainPage extends State<MainPage> with WidgetsBindingObserver {
void dispose() { void dispose() {
super.dispose(); super.dispose();
socketClient.dispose(); socketClient.dispose();
closeDatabase();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
} }

18
lib/utils/flutter_utils.dart

@ -288,4 +288,22 @@ class AppUtils {
return true; return true;
} }
} }
///im列表时间显示
static String timeFormatter(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); //
}
}
} }

Loading…
Cancel
Save