You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
12 KiB
370 lines
12 KiB
import 'dart:async';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
import '../../../../data/services/db/sqflite_api.dart';
|
|
import '../../../../data/services/deapsound_ai_service.dart';
|
|
import '../../../../data/services/network/api.dart';
|
|
import '../../controllers/meeting_details_controller.dart';
|
|
|
|
class TemplateEditView extends StatefulWidget {
|
|
final Map? data;
|
|
final int? categoryid;
|
|
|
|
const TemplateEditView({super.key, this.data, this.categoryid});
|
|
|
|
@override
|
|
State<TemplateEditView> createState() => _TemplateEditViewState();
|
|
}
|
|
|
|
class _TemplateEditViewState extends State<TemplateEditView> {
|
|
final _controller = Get.find<MeetingDetailsController>();
|
|
|
|
final TextEditingController _nameController = TextEditingController();
|
|
final TextEditingController _descController = TextEditingController();
|
|
final TextEditingController _promptController = TextEditingController();
|
|
|
|
bool _isEnable = false;
|
|
|
|
final _deapsoundAI = DeapsoundAIService();
|
|
StreamSubscription? _aiResponseSubscription;
|
|
bool _isLoading = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
if (widget.data != null) {
|
|
_nameController.text = widget.data?['name'];
|
|
_descController.text = widget.data?['desc'];
|
|
_promptController.text = widget.data?['prompt'];
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameController.dispose();
|
|
_descController.dispose();
|
|
_promptController.dispose();
|
|
_aiResponseSubscription?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
return GestureDetector(
|
|
onTap: () => FocusScope.of(context).unfocus(),
|
|
child: Scaffold(
|
|
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
|
|
appBar: _buildAppBar(isDarkMode),
|
|
body: _buildBody(isDarkMode),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 构建应用栏
|
|
PreferredSizeWidget _buildAppBar(bool isDarkMode) {
|
|
return AppBar(
|
|
backgroundColor: isDarkMode ? Colors.black : Colors.white,
|
|
elevation: 0.5,
|
|
leading: IconButton(
|
|
icon: Icon(
|
|
Icons.arrow_back_ios,
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
size: 20.sp,
|
|
),
|
|
onPressed: () => Get.back(),
|
|
),
|
|
title: Text(
|
|
widget.data != null ? '编辑模版' : '创建模版',
|
|
style: TextStyle(
|
|
fontSize: 17.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
),
|
|
),
|
|
centerTitle: true,
|
|
);
|
|
}
|
|
|
|
// 构建主体内容
|
|
Widget _buildBody(bool isDarkMode) {
|
|
return Column(
|
|
children: [
|
|
Expanded(
|
|
child: ListView(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
|
children: [
|
|
_input(isDarkMode, '名称', _nameController),
|
|
_input(isDarkMode, '描述', _descController),
|
|
_input(
|
|
isDarkMode,
|
|
'模版',
|
|
_promptController,
|
|
minLines: 8,
|
|
maxLines: 100,
|
|
hintText: '请输入模版内容!温馨提示:点击上方AI,根据上面填写的名称和描述,可以帮助你快速生成模版内容',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 20.w),
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
if (_nameController.text.isNotEmpty &&
|
|
_descController.text.isNotEmpty &&
|
|
_promptController.text.isNotEmpty) {
|
|
_submit();
|
|
}
|
|
},
|
|
child: Container(
|
|
height: 48.w,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: _nameController.text.isNotEmpty &&
|
|
_descController.text.isNotEmpty &&
|
|
_promptController.text.isNotEmpty
|
|
? (isDarkMode ? Colors.white : Colors.black)
|
|
: Colors.grey,
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
),
|
|
child: Text(
|
|
'提交',
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
color: _nameController.text.isNotEmpty &&
|
|
_descController.text.isNotEmpty &&
|
|
_promptController.text.isNotEmpty
|
|
? (isDarkMode ? Colors.black : Colors.white)
|
|
: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _input(
|
|
bool isDarkMode,
|
|
String title,
|
|
TextEditingController controller, {
|
|
int minLines = 1,
|
|
int maxLines = 1,
|
|
String? hintText,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Padding(
|
|
padding: EdgeInsets.only(left: 10.w),
|
|
child: Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black87,
|
|
),
|
|
),
|
|
),
|
|
if (maxLines > 1)
|
|
GestureDetector(
|
|
onTap: () {
|
|
_generateTemplatePrompt();
|
|
},
|
|
child: Container(
|
|
width: 26.w,
|
|
height: 26.w,
|
|
margin: EdgeInsets.only(right: 10.w),
|
|
decoration: BoxDecoration(
|
|
color: _isLoading
|
|
? const Color(0xFF4C3F91)
|
|
: const Color(0xFF6A5AE0),
|
|
borderRadius: BorderRadius.circular(26.r),
|
|
),
|
|
child: Icon(
|
|
_isLoading ? Icons.pause : Icons.smart_toy,
|
|
size: 18.w,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
Container(
|
|
margin: EdgeInsets.only(top: 10.w, bottom: 20.w),
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? const Color(0xFF2D2D2D) : Colors.white,
|
|
borderRadius: BorderRadius.circular(12.w),
|
|
boxShadow: isDarkMode
|
|
? null
|
|
: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: 0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: TextField(
|
|
controller: controller,
|
|
minLines: minLines,
|
|
maxLines: maxLines,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.white : Colors.black87,
|
|
fontSize: 14.sp,
|
|
),
|
|
decoration: InputDecoration(
|
|
hintText: hintText ?? '请输入$title',
|
|
hintStyle: TextStyle(
|
|
color: isDarkMode ? Colors.grey[500] : Colors.black54,
|
|
fontSize: 14.sp,
|
|
),
|
|
isDense: true,
|
|
fillColor: Colors.transparent,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.all(10.w),
|
|
),
|
|
onChanged: (value) {
|
|
bool isEnable = false;
|
|
if (_nameController.text.isNotEmpty &&
|
|
_descController.text.isNotEmpty &&
|
|
_promptController.text.isNotEmpty) {
|
|
isEnable = true;
|
|
}
|
|
if (_isEnable != isEnable) {
|
|
setState(() {
|
|
_isEnable = isEnable;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _submit() async {
|
|
EasyLoading.show();
|
|
Map data = {
|
|
"template": <String, Object?>{
|
|
'ttype': '自定义',
|
|
'tags': '',
|
|
'icon': 'auto_fix_high',
|
|
'outline': '您将扮演一名会议事件归纳专家,能准确抓住会议中事件的关键点,并每个事件只需一句话精简总结。',
|
|
'title': _nameController.text,
|
|
'description': _descController.text,
|
|
'template': _promptController.text,
|
|
},
|
|
};
|
|
if (widget.data != null) {
|
|
data['template']['id'] = widget.data?['id'];
|
|
var response = await Api.updateEchomeetTemplates(data);
|
|
if (response != null) {
|
|
await SqfliteApi.editMeetingTemplate(widget.data?['id'], {
|
|
'name': _nameController.text,
|
|
'desc': _descController.text,
|
|
'prompt': _promptController.text,
|
|
});
|
|
_controller.editTemplate = {
|
|
'id': widget.data?['id'],
|
|
'name': _nameController.text,
|
|
'desc': _descController.text,
|
|
'prompt': _promptController.text,
|
|
};
|
|
final GetStorage storage = GetStorage();
|
|
Map lastTemplateData = storage.read("meeting_last_template_Data") ?? {};
|
|
List originalTemplate = storage.read("meeting_template_list") ?? [];
|
|
if (lastTemplateData.isNotEmpty &&
|
|
lastTemplateData['id'] == widget.data?['id']) {
|
|
lastTemplateData['name'] = _nameController.text;
|
|
lastTemplateData['desc'] = _descController.text;
|
|
lastTemplateData['prompt'] = _promptController.text;
|
|
storage.write("meeting_last_template_Data", lastTemplateData);
|
|
}
|
|
if (originalTemplate.isNotEmpty) {
|
|
Map? originalMap = originalTemplate.firstWhere(
|
|
(element) => element['id'] == widget.data?['id'],
|
|
orElse: () => null);
|
|
if (originalMap != null) {
|
|
originalMap['name'] = _nameController.text;
|
|
originalMap['desc'] = _descController.text;
|
|
originalMap['prompt'] = _promptController.text;
|
|
storage.write("meeting_template_list", originalTemplate);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var response = await Api.addEchomeetTemplates(data);
|
|
if (response != null) {
|
|
await SqfliteApi.insertMeetingTemplate({
|
|
"id": response['template']['id'],
|
|
'categoryid': widget.categoryid,
|
|
'name': _nameController.text,
|
|
'icon': 'auto_fix_high',
|
|
'tag': '',
|
|
'desc': _descController.text,
|
|
'outline': '您将扮演一名会议事件归纳专家,能准确抓住会议中事件的关键点,并每个事件只需一句话精简总结。',
|
|
'prompt': _promptController.text,
|
|
});
|
|
_controller.editTemplate = {
|
|
'id': response['template']['id'],
|
|
'name': _nameController.text,
|
|
'desc': _descController.text,
|
|
'prompt': _promptController.text,
|
|
};
|
|
}
|
|
}
|
|
_controller.categoryTemplateList =
|
|
await SqfliteApi.getMeetingCategoryTemplateList();
|
|
Get.back(result: {
|
|
'name': _nameController.text,
|
|
'desc': _descController.text,
|
|
'prompt': _promptController.text,
|
|
});
|
|
EasyLoading.dismiss();
|
|
}
|
|
|
|
void _generateTemplatePrompt() {
|
|
if (_isLoading ||
|
|
_nameController.text.isEmpty ||
|
|
_descController.text.isEmpty) {
|
|
return;
|
|
}
|
|
_promptController.clear();
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
final responseStream = _deapsoundAI.sendMessageStream(
|
|
messages: [
|
|
{'role': 'system', 'content': '名称:${_nameController.text}'},
|
|
{'role': 'system', 'content': '描述:${_descController.text}'},
|
|
{
|
|
'role': 'user',
|
|
'content':
|
|
'请基于以上名称与描述,生成一个高质量会议总结提示词(Prompt),用于指导模型对会议记录进行结构化总结。只返回提示词文本,不要提供示例或额外解释。',
|
|
},
|
|
],
|
|
);
|
|
_aiResponseSubscription = responseStream.listen(
|
|
(chunk) {
|
|
_promptController.text += chunk;
|
|
},
|
|
onDone: () {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|