|
|
|
@ -1,8 +1,11 @@ |
|
|
|
import 'dart:async'; |
|
|
|
|
|
|
|
import 'package:flutter/material.dart'; |
|
|
|
import 'package:flutter_markdown/flutter_markdown.dart'; |
|
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart'; |
|
|
|
import 'package:get/get.dart'; |
|
|
|
|
|
|
|
import '../../../data/services/deapsound_ai_service.dart'; |
|
|
|
import '../controllers/meeting_details_controller.dart'; |
|
|
|
|
|
|
|
class AIBottomSheet extends StatefulWidget { |
|
|
|
@ -14,6 +17,10 @@ class AIBottomSheet extends StatefulWidget { |
|
|
|
|
|
|
|
class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
final _controller = Get.find<MeetingDetailsController>(); |
|
|
|
final ScrollController _scrollController = ScrollController(); |
|
|
|
|
|
|
|
final _deapsoundAI = DeapsoundAIService(); |
|
|
|
StreamSubscription? _aiResponseSubscription; |
|
|
|
|
|
|
|
final TextEditingController _texController = TextEditingController(); |
|
|
|
|
|
|
|
@ -30,6 +37,8 @@ class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
@override |
|
|
|
void dispose() { |
|
|
|
_texController.dispose(); |
|
|
|
_scrollController.dispose(); |
|
|
|
_aiResponseSubscription?.cancel(); |
|
|
|
super.dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -80,12 +89,13 @@ class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
? Align( |
|
|
|
alignment: Alignment.topCenter, |
|
|
|
child: ListView.builder( |
|
|
|
controller: _scrollController, |
|
|
|
itemCount: _dataList.length, |
|
|
|
shrinkWrap: true, |
|
|
|
reverse: true, |
|
|
|
itemBuilder: (BuildContext context, int index) { |
|
|
|
Map data = _dataList[index]; |
|
|
|
return _item(data, index == 0); |
|
|
|
return _item(data); |
|
|
|
}, |
|
|
|
), |
|
|
|
) |
|
|
|
@ -97,47 +107,35 @@ class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
Widget _item(Map data, bool isBorder) { |
|
|
|
return Container( |
|
|
|
padding: EdgeInsets.symmetric(vertical: 20.w), |
|
|
|
decoration: BoxDecoration( |
|
|
|
border: isBorder |
|
|
|
? null |
|
|
|
: Border( |
|
|
|
bottom: BorderSide( |
|
|
|
width: 1, |
|
|
|
color: Colors.grey[400]!, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
child: Column( |
|
|
|
children: [ |
|
|
|
Align( |
|
|
|
alignment: Alignment.centerRight, |
|
|
|
child: Container( |
|
|
|
padding: EdgeInsets.all(10.w), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(12.r), |
|
|
|
), |
|
|
|
child: Text( |
|
|
|
data['user'], |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 14.sp, |
|
|
|
), |
|
|
|
Widget _item(Map data) { |
|
|
|
return Column( |
|
|
|
children: [ |
|
|
|
Container( |
|
|
|
padding: EdgeInsets.symmetric(vertical: 10.w), |
|
|
|
alignment: Alignment.centerRight, |
|
|
|
child: Container( |
|
|
|
padding: EdgeInsets.all(10.w), |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.grey[200], |
|
|
|
borderRadius: BorderRadius.circular(12.r), |
|
|
|
), |
|
|
|
child: Text( |
|
|
|
data['user'], |
|
|
|
style: TextStyle( |
|
|
|
fontSize: 14.sp, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
if (data['assistant'].isNotEmpty) |
|
|
|
Container( |
|
|
|
padding: EdgeInsets.symmetric(vertical: 20.w), |
|
|
|
alignment: Alignment.centerLeft, |
|
|
|
child: MarkdownBody( |
|
|
|
data: data['assistant'], |
|
|
|
), |
|
|
|
), |
|
|
|
if (data['assistant'].isNotEmpty) |
|
|
|
Container( |
|
|
|
padding: EdgeInsets.symmetric(vertical: 10.w), |
|
|
|
alignment: Alignment.centerLeft, |
|
|
|
child: MarkdownBody( |
|
|
|
data: data['assistant'], |
|
|
|
), |
|
|
|
], |
|
|
|
), |
|
|
|
), |
|
|
|
], |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
@ -256,11 +254,11 @@ class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
width: 26.w, |
|
|
|
height: 26.w, |
|
|
|
decoration: BoxDecoration( |
|
|
|
color: Colors.grey, |
|
|
|
color: _isLoading ? Colors.black87 : Colors.grey, |
|
|
|
borderRadius: BorderRadius.circular(26.r), |
|
|
|
), |
|
|
|
child: Icon( |
|
|
|
Icons.arrow_upward, |
|
|
|
_isLoading ? Icons.pause : Icons.arrow_upward, |
|
|
|
size: 18.w, |
|
|
|
color: Colors.white, |
|
|
|
), |
|
|
|
@ -273,47 +271,31 @@ class _AIBottomSheetState extends State<AIBottomSheet> { |
|
|
|
|
|
|
|
void _sendMessage(String value) { |
|
|
|
if (_isLoading || value.isEmpty) return; |
|
|
|
setState(() { |
|
|
|
_isLoading = true; |
|
|
|
}); |
|
|
|
_dataList.insert(0, { |
|
|
|
'user': value, |
|
|
|
'assistant': ''' |
|
|
|
# 角色 |
|
|
|
你是一名卓越的会议记录专家,拥有超强的信息归纳与细分能力。不仅能高效处理各类通用场景会议,精准提炼会议核心内容,为后续思维导图生成奠定坚实基础,还能将整理好的会议记录巧妙转化为清晰直观的思维导图。 |
|
|
|
|
|
|
|
## 技能 |
|
|
|
### 技能 1: 通用会议内容深度细分 |
|
|
|
1. 深入全面剖析给定的通用场景会议内容,精准区分不同主题板块,如会议目标、讨论事项、决策结果、行动安排、问题与挑战等。 |
|
|
|
2. 对每个主题板块进行细致分解,形成具体要点与细节。若涉及人员,人员名称保留[],清晰明确其角色与贡献;涉及流程,梳理出关键步骤与节点。 |
|
|
|
3. 以层次分明、逻辑紧密的方式整理细分内容,使其契合思维导图的结构特点。 |
|
|
|
===回复示例=== |
|
|
|
- **会议目标**: |
|
|
|
- 目标 1:[详细且全面阐述目标 1] |
|
|
|
- 目标 2:[如有多个目标,依次详细罗列] |
|
|
|
- **讨论事项**: |
|
|
|
- 事项 1: |
|
|
|
- 观点 1:[详细说明提出观点 1 的人员及具体内容] |
|
|
|
- 观点 2:[详细阐述不同观点及相关人员] |
|
|
|
- 事项 2:[按此格式详细罗列其他讨论事项] |
|
|
|
- **决策结果**: |
|
|
|
- 决策 1:[具体且完整表述决策内容] |
|
|
|
- 决策 2:[详细列出其他决策] |
|
|
|
- **行动安排**: |
|
|
|
- 任务 1: |
|
|
|
- 负责人:[具体姓名] |
|
|
|
- 时间节点:[精确到具体时间] |
|
|
|
- 任务内容:[详细描述任务具体内容] |
|
|
|
- 任务 2:[依此格式详细罗列其他任务] |
|
|
|
- **问题与挑战**: |
|
|
|
- 问题 1:[具体清晰描述问题] |
|
|
|
- 挑战 1:[详细说明面临的挑战及相关说明] |
|
|
|
===示例结束=== |
|
|
|
''', |
|
|
|
}); |
|
|
|
_texController.clear(); |
|
|
|
setState(() { |
|
|
|
_isLoading = false; |
|
|
|
_isLoading = true; |
|
|
|
_dataList.insert(0, { |
|
|
|
'user': value, |
|
|
|
'assistant': '', |
|
|
|
}); |
|
|
|
}); |
|
|
|
_scrollController.jumpTo(0); |
|
|
|
final responseStream = _deapsoundAI.sendMessageStream( |
|
|
|
messages: [ |
|
|
|
{'role': 'user', 'content': value}, |
|
|
|
], |
|
|
|
); |
|
|
|
_aiResponseSubscription = responseStream.listen( |
|
|
|
(chunk) { |
|
|
|
setState(() { |
|
|
|
_dataList.first['assistant'] += chunk; |
|
|
|
}); |
|
|
|
}, |
|
|
|
onDone: () { |
|
|
|
setState(() { |
|
|
|
_isLoading = false; |
|
|
|
}); |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|