import 'dart:convert'; import 'package:electronic_assistant/base/base_controller.dart'; import 'package:electronic_assistant/data/bean/chat_item.dart'; import 'package:electronic_assistant/data/bean/progressing_chat_item.dart'; import 'package:electronic_assistant/data/bean/stream_chat_origin_data.dart'; import 'package:electronic_assistant/data/repositories/chat_repository.dart'; import 'package:electronic_assistant/module/chat/start/view.dart'; import 'package:electronic_assistant/resource/colors.gen.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import '../../utils/http_handler.dart'; class ChatController extends BaseController { final RefreshController refreshController = RefreshController(); final ScrollController listScrollController = ScrollController(); final TextEditingController inputController = TextEditingController(); final RxList chatItems = [].obs; var isOpenStart = false; @override void onReady() { super.onReady(); refreshController.requestLoading(); } void loadMoreHistory() { bool isEmpty = chatItems.isEmpty; chatRepository .chatHistory(chatItems.isEmpty ? null : chatItems.last.id) .then((value) => chatItems.addAll(value)) .whenComplete(() => refreshController.loadComplete()) .whenComplete(() { if (isEmpty) { scrollToBottom(); } }); } void showStartSheet(BuildContext context) { if (!isOpenStart) { WidgetsBinding.instance.addPostFrameCallback((_) { showModalBottomSheet( context: context, isScrollControlled: true, barrierColor: ColorName.black55, backgroundColor: ColorName.transparent, builder: (BuildContext context) { return const ChatStartPage(); }, ); }); isOpenStart = true; } } sendMessage() { if (inputController.text.isEmpty) { return; } String chatContent = inputController.text; inputController.clear(); chatItems.insert( 0, ChatItem( id: chatItems.isEmpty ? 0 : chatItems.last.id + 1, conversationId: chatItems.isEmpty ? "" : chatItems.last.conversationId, role: "user", content: chatContent, createTime: DateTime.now().toString())); ProgressingChatItem progressingChatItem = ProgressingChatItem( id: chatItems.last.id + 1, conversationId: chatItems.last.conversationId, role: "assistant", content: "", createTime: DateTime.now().toString(), ); chatItems.insert(0, progressingChatItem); scrollToBottom(); chatRepository.streamChat(chatContent).then((stream) { stream.listen((event) { try { Map json = jsonDecode(event.data); if (json.isEmpty) { return; } StreamChatOriginData data = StreamChatOriginData.fromJson(json); if (data.choices == null || data.choices!.isEmpty) { return; } Delta? delta = data.choices![0].delta; if (delta == null) { return; } progressingChatItem.append(delta.content ?? ""); } catch (ignore) {} }, onDone: () { progressingChatItem.content = progressingChatItem.streamContent.value; progressingChatItem.isFinished.value = true; }, onError: (error) { progressingChatItem.isFailed.value = true; progressingChatItem.error.value = "网络错误,请检查网络连接"; debugPrint("error: $error"); debugPrintStack(); }); }).catchError((error) { progressingChatItem.isFailed.value = true; if (error is ServerErrorException) { progressingChatItem.error.value = error.message ?? "服务出错,请稍后再试"; } else { progressingChatItem.error.value = "网络错误,请检查网络连接"; debugPrint("error: $error"); debugPrintStack(); } }); } void scrollToBottom() { Future.delayed(const Duration(milliseconds: 300), () { listScrollController.animateTo( 0, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }); } }