123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753 |
- import 'package:cached_network_image/cached_network_image.dart';
- import 'package:dio/dio.dart';
- import 'package:eitc_erm_app/widget/circular_loading.dart';
- import 'package:eitc_erm_app/widget/image_error.dart';
- import 'package:file_picker/file_picker.dart';
- import 'package:flutter/material.dart';
- import 'package:http/http.dart' as http;
- import '../bean/normal_response2.dart';
- import '../image_view.dart';
- import '../online_consultation_detail.dart';
- import '../utils/Component.dart';
- import '../utils/Constants.dart';
- import '../utils/logger.dart';
- import 'chat_disease.dart';
- import 'chat_scroll_behavior.dart';
- import 'chat_scroll_physics.dart';
- import 'socket_core.dart';
- /*void main() {
- runApp(ChatHome());
- }*/
- class ChatHome extends StatefulWidget {
- final String? doctorId;
- final String? doctorName;
- ChatHome(
- {super.key, required String this.doctorId, required this.doctorName});
- @override
- ChatHomeState createState() => ChatHomeState();
- }
- class ChatHomeState extends State<ChatHome> {
- bool _isLoading = false;
- final List<Map<String, dynamic>> _chatRecords = [
- /*{
- "host": true,
- "name": "你",
- "content": "问诊人:刘娟\n问诊资料:之前做过根管,最近有点疼不知道是哪里的问题"
- },*/
- ];
- final ScrollController _scrollController = ScrollController();
- final TextEditingController _textEditingController = TextEditingController();
- final FocusNode _focusNode = FocusNode();
- WebSocketUtility socket = WebSocketUtility();
- String? userId = Global.userId;
- String? doctorId = Global.doctor.data?[Global.selectDoctor].userId.toString();
- bool sendEnable = true;
- late Future<ChatDisease?> _future;
- @override
- void initState() {
- super.initState();
- _scrollController.addListener(scrollListener);
- _focusNode.addListener(textFocusListener);
- if (widget.doctorId != null && widget.doctorId!.isNotEmpty) {
- doctorId = widget.doctorId!;
- }
- socket.initWebSocket(onOpen: () {
- socket.sendMessage(
- "[LOGIN][${DateTime.now().millisecondsSinceEpoch}][${userId}][${doctorId}][2]");
- // socket.initHeartBeat();
- }, onMessage: (data) {
- if (data.toString().contains("SYSTEM")) {
- if (data.toString().contains("已上线")) {
- Component.toast("已连线", 2);
- sendEnable = true;
- } /*else {
- Component.toast("连线失败,请稍后重试!", 0);
- sendEnable = false;
- }*/
- } else {
- if (!data.toString().contains("[$userId][$doctorId]")) {
- if (data.toString().contains("[image]")) {
- _chatRecords.insert(0, {
- "host": false,
- "name": widget.doctorName,
- "content": data.toString().split("[image]")[1].trim(),
- "type": 1
- });
- } else {
- _chatRecords.insert(0, {
- "host": false,
- "name": widget.doctorName,
- "content": data
- .toString()
- .split("-")[1]
- .trim()
- .replaceAll("<br/>", "\n"),
- "type": 0
- });
- }
- } else {
- if (data.toString().contains("[image]")) {
- _chatRecords.insert(0, {
- "host": true,
- "name": widget.doctorName,
- "content": data.toString().split("[image]")[1].trim(),
- "type": 1
- });
- } else if (data.toString().contains("[patient]")) {
- _chatRecords.insert(0, {
- "host": true,
- "name": widget.doctorName,
- "content": data.toString().split("[patient]")[1].trim(),
- "type": 2
- });
- } else {
- _chatRecords.insert(0, {
- "host": true,
- "name": widget.doctorName,
- "content": data
- .toString()
- .split("-")[1]
- .trim()
- .replaceAll("<br/>", "\n"),
- "type": 0
- });
- }
- }
- }
- logd(data);
- setState(() {});
- }, onError: (e) {
- logd(e);
- });
- WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
- if (_scrollController.position.maxScrollExtent == 0) {
- // not scroll content, call load more
- if (_isLoading) return;
- _isLoading = true;
- onLoadMore();
- }
- });
- }
- @override
- void dispose() {
- logd("结束聊天...");
- socket.sendMessage(
- "[LOGOUT][${DateTime.now().millisecondsSinceEpoch}][${userId}][${doctorId}][2]");
- socket.closeSocket();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('问诊',
- style: TextStyle(
- color: Colors.white,
- )),
- centerTitle: true,
- elevation: 0.5,
- backgroundColor: Global.StatusBarColor,
- leading: IconButton(
- tooltip: '返回上一页',
- icon: const Icon(
- Icons.arrow_back_ios,
- color: Colors.white,
- ),
- onPressed: () {
- Navigator.pop(context);
- },
- ),
- ),
- backgroundColor: Global.BackgroundColor,
- body: Column(
- children: <Widget>[
- Container(
- color: Colors.green[300],
- padding: const EdgeInsets.only(left: 10),
- child: Row(children: [
- Text(
- "${Global.patient.data![Global.selectPatient].patientName}的病历",
- style: const TextStyle(fontSize: 16, color: Colors.white),
- ),
- const Spacer(),
- TextButton(
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => OnlineConsultationDetail("")),
- );
- },
- child: const Text(
- "点击查看详情",
- style: TextStyle(color: Colors.white),
- )),
- TextButton(
- onPressed: () {
- onSendMessage(2,
- "[patient]${Global.patient.data![Global.selectPatient].patientId}");
- },
- child: const Text(
- "发送",
- style: TextStyle(color: Colors.white),
- ))
- ]),
- ),
- Expanded(
- child: ScrollConfiguration(
- behavior: ChatScrollBehavior(),
- child: ListView.builder(
- padding: const EdgeInsets.only(bottom: 10),
- itemBuilder: (ctx, index) {
- return index == _chatRecords.length
- ? const Center(
- child: SizedBox(
- height: 0,
- width: 0,
- child: CircularProgressIndicator(
- strokeWidth: 0,
- ),
- ),
- )
- : chatItemWidget(index);
- },
- controller: _scrollController,
- physics: const ChatScrollPhysics(
- parent: AlwaysScrollableScrollPhysics()),
- reverse: true,
- itemCount: _chatRecords.length + 1,
- ),
- ),
- ),
- editMessageWidget(),
- ],
- ),
- /*floatingActionButton: FloatingActionButton(
- onPressed: () {
- // 处理点击事件
- logd('FloatingActionButton was pressed.');
- },
- tooltip: 'Increment',
- child: Icon(Icons.add),
- ),*/
- );
- }
- Widget chatItemWidget(int index) {
- return Column(
- children: <Widget>[
- const SizedBox(
- height: 10,
- ),
- _chatRecords[index]['host']
- ? Row(
- mainAxisAlignment: MainAxisAlignment.end,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- const SizedBox(
- width: 10,
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: <Widget>[
- const Text(
- "你",
- style: TextStyle(
- color: Colors.grey,
- fontSize: 12,
- ),
- ),
- const SizedBox(
- height: 4,
- ),
- Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- color: const Color(0xFF95EC6A),
- borderRadius: BorderRadius.circular(4),
- ),
- alignment: Alignment.centerRight,
- child: ConstrainedBox(
- constraints: const BoxConstraints(
- maxWidth: 220,
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- if (_chatRecords[index]['type'] == 0)
- Text(_chatRecords[index]['content'],
- maxLines: 30,
- style: const TextStyle(
- color: Colors.black,
- )),
- if (_chatRecords[index]['type'] == 1)
- GestureDetector(
- onTap: () async {
- logd(_chatRecords[index]['content']
- .toString());
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- ImagePreviewPage(
- _chatRecords[index]
- ['content']
- .toString()
- .replaceAll(
- "[image]", "")),
- ));
- },
- child: Hero(
- tag: _chatRecords[index]['content']
- .toString()
- .replaceAll("[image]", ""),
- child: CachedNetworkImage(
- imageUrl: _chatRecords[index]
- ['content']
- .toString()
- .replaceAll("[image]", ""),
- width: 80,
- fit: BoxFit.fitWidth,
- progressIndicatorBuilder:
- (ctx, _, progress) => Center(
- child: Circularloading(
- width: 25,
- height: 25,
- value: progress.progress,
- ),
- ),
- errorWidget:
- (context, error, stackTrace) {
- return const ImageError(
- icon: Icons.broken_image_outlined,
- size: 25,
- color: Colors.white,
- ); // 显示一个进度指示器作为错误占位
- },
- )),
- ),
- if (_chatRecords[index]['type'] == 2)
- InkWell(
- onTap: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- OnlineConsultationDetail("")),
- );
- },
- child: Text(
- "${Global.patient.data![Global.selectPatient].patientName}的病历\n【点击查看详情】",
- textAlign: TextAlign.center,
- style: const TextStyle(
- decoration: TextDecoration.underline,
- )),
- ),
- /*if (index == _chatRecords.length - 1)
- InkWell(
- onTap: () {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => OnlineConsultationDetail()),
- );
- },
- child: Text('【点击查看详情】',
- style: TextStyle(
- color: Colors.blue,
- )),
- )*/
- ]),
- ),
- ),
- ],
- ),
- const SizedBox(
- width: 10,
- ),
- Container(
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- color: _chatRecords[index]['host']
- ? Colors.green[400]
- : Colors.blue[400],
- ),
- width: 40,
- height: 40,
- alignment: Alignment.center,
- child: const Text(
- "你",
- style: TextStyle(
- color: Colors.white,
- fontSize: 12,
- ),
- ),
- ),
- const SizedBox(
- width: 10,
- ),
- ],
- )
- : Row(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- const SizedBox(
- width: 10,
- ),
- Container(
- decoration: const BoxDecoration(
- shape: BoxShape.circle,
- color: Colors.blue,
- ),
- width: 40,
- height: 40,
- alignment: Alignment.center,
- child: Text(
- _chatRecords[index]['name'] ?? "医生",
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- style: const TextStyle(
- color: Colors.white,
- fontSize: 12,
- ),
- ),
- ),
- const SizedBox(
- width: 10,
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text(
- _chatRecords[index]['name'] ?? "",
- style: const TextStyle(
- color: Colors.grey,
- fontSize: 12,
- ),
- ),
- const SizedBox(
- height: 4,
- ),
- Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
- alignment: Alignment.centerRight,
- child: ConstrainedBox(
- constraints: const BoxConstraints(
- maxWidth: 220,
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- if (_chatRecords[index]['type'] == 0)
- Text(_chatRecords[index]['content'],
- maxLines: 30,
- style: const TextStyle(
- color: Colors.black,
- )),
- if (_chatRecords[index]['type'] == 1)
- GestureDetector(
- onTap: () async {
- logd(_chatRecords[index]['content']
- .toString());
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- ImagePreviewPage(
- _chatRecords[index]
- ['content']
- .toString()
- .replaceAll(
- "[image]", "")),
- ));
- },
- child: Hero(
- tag: _chatRecords[index]['content']
- .toString()
- .replaceAll("[image]", ""),
- child: CachedNetworkImage(
- imageUrl: _chatRecords[index]
- ['content']
- .toString()
- .replaceAll("[image]", ""),
- width: 80,
- fit: BoxFit.fitWidth,
- progressIndicatorBuilder:
- (ctx, _, progress) =>
- Circularloading(
- value: progress.progress,
- ),
- errorWidget:
- (context, error, stackTrace) {
- return const Center(
- child: Icon(
- Icons.error,
- color: Colors.white,
- ),
- ); // 显示一个进度指示器作为错误占位
- },
- )),
- ),
- /*if (index == _chatRecords.length - 1)
- InkWell(
- onTap: () {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => OnlineConsultationDetail()),
- );
- },
- child: Text('【点击查看详情】',
- style: TextStyle(
- color: Colors.blue,
- )),
- )*/
- ]),
- ),
- ),
- ],
- ),
- const SizedBox(
- width: 10,
- ),
- ],
- ),
- ],
- );
- }
- Widget editMessageWidget() {
- return Container(
- padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
- color: Colors.grey[100],
- child: Row(
- children: <Widget>[
- Expanded(
- child: Container(
- color: Colors.white,
- child: TextField(
- enabled: sendEnable,
- focusNode: _focusNode,
- controller: _textEditingController,
- minLines: 1,
- maxLines: 9,
- cursorColor: Colors.black87,
- decoration: const InputDecoration(
- isDense: true,
- border: OutlineInputBorder(borderSide: BorderSide.none),
- contentPadding: EdgeInsets.all(8),
- ),
- ),
- ),
- ),
- const SizedBox(
- width: 10,
- ),
- Material(
- color: const Color(0xFF08C060),
- borderRadius: BorderRadius.circular(5),
- child: InkWell(
- onTap: () {
- onSendMessage(0, "");
- },
- child: Container(
- padding:
- const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
- alignment: Alignment.center,
- child: const Text(
- "发送",
- style: TextStyle(
- color: Colors.white,
- ),
- )),
- ),
- ),
- const SizedBox(
- width: 5,
- ),
- GestureDetector(
- onTap: () async {
- String uploadImg = await uploadFile();
- if (uploadImg != "") {
- onSendMessage(1, uploadImg.toString());
- }
- uploadImg = "";
- },
- child: const Icon(
- Icons.add_circle_outline,
- color: Colors.black87,
- size: 30,
- ),
- ),
- ],
- ),
- );
- }
- Future<String> uploadFile() async {
- FilePickerResult? result = await FilePicker.platform.pickFiles(
- allowedExtensions: ["jpg", "avi", "mov"], type: FileType.custom);
- if (result != null) {
- String fileName = result.files.single.name;
- String? filePath = result.files.single.path;
- FormData formData = FormData.fromMap({
- "file": await MultipartFile.fromFile(filePath!, filename: fileName),
- "path": "chat",
- });
- try {
- Map<String, String> headers = {
- 'token': Global.token,
- };
- Response response = await Dio().post(
- '${Global.BaseUrl}common/minioUploadImage',
- data: formData,
- options: Options(headers: headers));
- if (response.statusCode == 200) {
- var json = decodeBodyToJson(response.data);
- logd("聊天上传文件结果=$json");
- Normal2Response mNormal2Response = new Normal2Response.fromJson(json);
- if (mNormal2Response.code == Global.responseSuccessCode) {
- // Component.toast("上传成功!", 2);
- return mNormal2Response.msg.toString();
- } else {
- Component.toast(mNormal2Response.msg.toString(), 0);
- return "";
- }
- } else {
- Component.toast("出错了,请稍后再试!", 0);
- return "";
- }
- } catch (e) {
- logd(e);
- return "";
- }
- }
- return "";
- }
- void scrollListener() {
- if (_scrollController.position.pixels >=
- _scrollController.position.maxScrollExtent) {
- if (_isLoading) return;
- _isLoading = true;
- onLoadMore();
- }
- }
- void textFocusListener() {
- _scrollController.animateTo(0.0,
- duration: const Duration(milliseconds: 200), curve: Curves.easeInOut);
- }
- void onLoadMore() async {
- // 模拟请求接口
- await Future.delayed(const Duration(seconds: 1));
- // for (int i = 0; i < 10; i++) {
- // _chatRecords.addAll([
- // {
- // "host": i % 2 == 0 ? false : true,
- // "name": i % 2 == 0 ? "大夫" : "你",
- // "content": i % 2 == 0 ? "hello" : "hi"
- // },
- // ]);
- // }
- _isLoading = false;
- setState(() {});
- }
- void onSendMessage(int msgType, String info) async {
- String socketMsg = "";
- switch (msgType) {
- case 0:
- // 文字
- if (_textEditingController.text.trim().isEmpty) return;
- /*_chatRecords.insert(0, {
- "host": true,
- "name": "你",
- "content": _textEditingController.text.trim(),
- "type": 0,
- "info": info,
- });*/
- socketMsg =
- "[CHAT][${DateTime.now().millisecondsSinceEpoch}][$userId][][$doctorId][2] - ${_textEditingController.text.trim().replaceAll("\n", "<br/>")}";
- break;
- case 1:
- // 图片
- /*_chatRecords.insert(0, {
- "host": true,
- "name": "你",
- "content": _textEditingController.text.trim(),
- "type": 1,
- "info": info,
- });*/
- socketMsg =
- "[CHAT][${DateTime.now().millisecondsSinceEpoch}][$userId][][$doctorId][2] - [image]$info";
- break;
- case 2:
- // 病历卡
- /*_chatRecords.insert(0, {
- "host": true,
- "name": "你",
- "content": _textEditingController.text.trim(),
- "type": 2,
- "info": info,
- });*/
- socketMsg =
- "[CHAT][${DateTime.now().millisecondsSinceEpoch}][$userId][][$doctorId][2] - $info";
- break;
- }
- socket.sendMessage(socketMsg);
- _textEditingController.text = "";
- setState(() {});
- }
- Future<ChatDisease?> fetchData() async {
- logd(Global.token);
- final response = await http.get(
- Uri.parse(
- '${Global.BaseUrl}chat/getChatDisease?patientId=${Global.selectPatient}'),
- headers: jsonHeaders(withToken: true));
- if (response.statusCode == 200) {
- final json = decodeBodyToJson(response.bodyBytes);
- logd("获取病例结果=json");
- ChatDisease mChatDisease = ChatDisease.fromJson(json);
- if (mChatDisease.code == Global.responseSuccessCode) {
- logd(mChatDisease.data?.patientId);
- } else {
- Component.toast(mChatDisease.msg.toString(), 0);
- return null;
- }
- return mChatDisease;
- } else {
- Component.toast("出错了,请稍后再试!", 0);
- return null;
- }
- }
- }
|