初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/tools_base/debug_log.dart';
import 'package:hgdj/tools_base/global_store/store.dart';
import 'package:hgdj/tools_base/toast.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../../../hj_utils/api_service/group_service.dart';
import '../../../../tools_base/file_upload/file_upload_tool.dart';
import '../../../../tools_base/file_upload/upload_result_model.dart';
import '../../../comment/comment_input_view.dart';
import '../model/im_group_list_model.dart';
import '../model/im_message_resp.dart';
class GroupChatDetailLogic extends GetxController {
final IMGroupItemModel model;
GroupChatDetailLogic(this.model);
int pageNum = 1;
List<IMMessageModel>? msgList;
RefreshController? refreshController;
TextEditingController textEditController = TextEditingController();
Rx<String?> selectedImagePath = Rx<String?>(null);
RxBool isSendingMsg = false.obs;
@override
void onReady() {
refreshData();
super.onReady();
}
void refreshData() {
_loadData();
}
void loadMoreData() {
_loadData(page: pageNum + 1);
}
void _loadData({int page = 1, int size = 20}) async {
try {
final retResp = await GroupService.getMessages(model.groupId, page, size);
pageNum = page;
msgList ??= [];
if (pageNum == 1) {
msgList?.clear();
}
msgList?.addAll(retResp.list ?? []);
retResp.hasNext == true
? refreshController?.loadComplete()
: refreshController?.loadNoData();
} catch (e) {
debugLog(e);
refreshController?.loadComplete();
}
refreshController?.refreshCompleted();
update();
}
Future readySendMsg({bool? isSend}) async {
if (isSend == true &&
(textEditController.text.isNotEmpty ||
selectedImagePath.value?.isNotEmpty == true)) {
// 点击发送按钮有内容,直接发送内容, 否则弹出输入框
sendMsgEvent();
} else {
final result = await Get.bottomSheet(
CommentInputView(
hint: "请输入消息",
textCtr: textEditController,
imgPath: selectedImagePath.value,
onImgChange: (value) => selectedImagePath.value = value,
),
backgroundColor: Colors.transparent,
barrierColor: Colors.transparent,
enableDrag: false,
settings: RouteSettings(name: CommentInputView.routeName),
useRootNavigator: true,
);
if (result == true) {
update();
}
sendMsgEvent();
}
}
Future sendMsgEvent() async {
if (textEditController.text.isEmpty &&
selectedImagePath.value?.isNotEmpty != true) {
showToast("请输入消息内容");
return;
}
if (isSendingMsg.value) return;
isSendingMsg.value = true;
String? sendImagePath;
if (selectedImagePath.value?.isNotEmpty == true) {
try {
ImageUploadResultModel? imgResult =
await FileUploadTool().uploadImage(selectedImagePath.value!);
sendImagePath = imgResult?.coverImg ?? "";
if (sendImagePath.isEmpty) {
isSendingMsg.value = false;
showToast("图片上传失败!");
return;
}
} catch (e) {
debugLog(e);
isSendingMsg.value = false;
showToast("图片上传失败!");
return;
}
}
try {
var ret = await GroupService.sendMessage(
model.groupId, textEditController.text, sendImagePath);
if (ret.isSuccess) {
textEditController.text = "";
selectedImagePath.value = null;
showToast("消息已发送");
if (ret.data is Map && (ret.data['message'] is Map)) {
IMMessageModel sendModel =
IMMessageModel.fromJson(ret.data['message']);
sendModel.portrait = globalStore.meInfo?.portrait;
sendModel.name = globalStore.meInfo?.name;
msgList?.insert(0, sendModel);
update();
}
}
} catch (e) {
showToast("消息发送失败!");
debugLog(e);
}
isSendingMsg.value = false;
}
}
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hgdj/hj_page/comment/widget/comment_foot_view.dart';
import 'package:hgdj/tools_base/refresh/pull_refresh.dart';
import '../model/im_group_list_model.dart';
import 'soul_group_detail_logic.dart';
import 'widget/chat_item_cell.dart';
class GroupChatDetailPage extends StatelessWidget {
final IMGroupItemModel model;
const GroupChatDetailPage(this.model, {super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<GroupChatDetailLogic>(
global: false,
init: GroupChatDetailLogic(model),
builder: (logic) {
return Scaffold(
appBar: AppBar(
title: Text(model.name ?? ""),
),
body: Column(
children: [
Expanded(
child: pullYsRefresh(
onInit: (ctr) => logic.refreshController = ctr,
onRefresh: (ctr) => logic.refreshData(),
onLoading: (ctr) => logic.loadMoreData(),
child: ListView.builder(
itemCount: logic.msgList?.length ?? 0,
reverse: true,
padding: EdgeInsets.only(bottom: 12, top: 12),
itemBuilder: (context, index) {
return ChatItemCell(model: logic.msgList![index]);
},
),
),
),
Obx(
() => CommentFootView(
imgPath: logic.selectedImagePath.value,
isSending: logic.isSendingMsg.value,
onImgChange: (value) => logic.selectedImagePath.value = value,
onComment: (isSend) => logic.readySendMsg(isSend: isSend),
textCtr: logic.textEditController,
),
),
],
),
);
},
);
}
}
@@ -0,0 +1,276 @@
import 'package:flutter/material.dart';
import 'package:hgdj/assets_tool/app_colors.dart';
import 'package:hgdj/routers/jump_router.dart';
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
import '../../../../../tools_base/global_store/store.dart';
import 'package:hgdj/tools_base/widget/image_browser_page.dart';
import '../../model/im_message_resp.dart';
class ChatItemCell extends StatefulWidget {
final IMMessageModel? model;
ChatItemCell({super.key, this.model});
@override
State<StatefulWidget> createState() {
return _ChatItemCellState();
}
}
class _ChatItemCellState extends State<ChatItemCell> {
bool get isImg => widget.model?.image?.isNotEmpty == true;
bool get isMe => globalStore.isMe(widget.model?.uid);
double imageWidth = 173;
@override
void initState() {
super.initState();
}
void _showImageScan(String imageUrl) {
if (imageUrl.isNotEmpty) {
ImageBrowserPage.open([widget.model?.image ?? '']);
}
}
@override
Widget build(BuildContext context) {
if (isMe) {
return _buildRightStyle();
} else {
return _buildLeftStyle();
}
}
Widget _buildLeftStyle() {
return Container(
padding: EdgeInsets.fromLTRB(0, 12, 0, 6),
child: Column(
children: [
_buildTime(),
Container(
padding: EdgeInsets.fromLTRB(16, 0, 50, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
pushToPersonCenter(widget.model?.uid);
},
child: SizedBox(
width: 40,
height: 40,
child: NetworkImageLoader(
imageUrl: widget.model?.portrait ?? "",
width: 40,
height: 40,
borderRadius: 20,
),
),
),
SizedBox(width: 12),
Flexible(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.model?.name ?? "",
style: TextStyle(
color: Colors.white.withValues(alpha: 0.9),
fontSize: 14,
),
),
SizedBox(height: 10),
if (widget.model?.content?.isNotEmpty == true)
Container(
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
margin: EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.05),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
topRight: Radius.circular(12),
topLeft: Radius.circular(2)),
),
child: Text(
widget.model?.content ?? "",
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
if (isImg)
InkWell(
enableFeedback: false,
onTap: () {
_showImageScan(widget.model?.image ?? "");
},
child: Container(
margin: EdgeInsets.only(top: 5),
alignment: Alignment.centerLeft,
constraints: BoxConstraints(maxWidth: 173),
child: _buildImageWidget(isLeft: true),
),
),
],
),
),
],
),
),
],
),
);
}
Widget _buildRightStyle() {
return Container(
padding: EdgeInsets.fromLTRB(0, 12, 0, 6),
child: Column(
children: [
_buildTime(),
Container(
padding: EdgeInsets.fromLTRB(50, 0, 16, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
widget.model?.name ?? "",
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
SizedBox(height: 10),
if (widget.model?.content?.isNotEmpty == true)
Container(
padding: EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
margin: EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Color(0xff1DC194),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
topLeft: Radius.circular(12),
topRight: Radius.circular(4)),
),
child: Text(
widget.model?.content ?? "",
style: TextStyle(
color: Color(0xff141414),
fontSize: 14,
),
),
),
if (isImg)
InkWell(
enableFeedback: false,
onTap: () {
_showImageScan(widget.model?.image ?? "");
},
child: Container(
margin: EdgeInsets.only(top: 5),
alignment: Alignment.centerRight,
constraints: BoxConstraints(maxWidth: 173),
child: _buildImageWidget(isLeft: false),
),
),
],
),
),
SizedBox(width: 12),
GestureDetector(
onTap: () {},
child: SizedBox(
width: 40,
height: 40,
child: NetworkImageLoader(
imageUrl: widget.model?.portrait,
width: 40,
height: 40,
borderRadius: 20,
),
),
),
],
),
),
],
),
);
}
Widget _buildImageWidget({bool isLeft = false}) {
return Stack(
children: [
NetworkImageLoader(
imageUrl: widget.model?.image ?? "",
width: imageWidth,
imgBorderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
topRight: Radius.circular(isLeft ? 12 : 0),
topLeft: Radius.circular(isLeft ? 0 : 12),
),
),
if (isLeft)
Positioned(
bottom: 12,
left: 0,
right: 0,
child: Container(
alignment: Alignment.bottomCenter,
child: Container(
width: 72,
height: 26,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: AppColors.actionRed,
),
child: Text(
'查看原图',
style: TextStyle(fontSize: 12, color: Colors.white),
),
),
),
),
],
);
}
Widget _buildTime() {
// if (widget.model?.isShowTime == true) {
// return Container(
// margin: EdgeInsets.fromLTRB(0, 12, 0, 12),
// padding: EdgeInsets.symmetric(horizontal: 20, vertical: 2),
// // decoration: BoxDecoration(
// // borderRadius: BorderRadius.circular(30),
// // color: Color(0xfff7f7f7).withValues(alpha: 0.42),
// // ),
// child: Text(
// widget.model?.createAtDesc ?? "",
// style: TextStyle(
// color: Color(0xff666666),
// fontSize: 12,
// ),
// ),
// );
// }
return SizedBox();
}
}