import 'dart:developer'; import 'dart:io'; import 'package:elysia/page/FollowListPage.dart'; import 'package:elysia/plugin/HTTP.dart'; import 'package:elysia/plugin/Remind.dart'; import 'package:elysia/plugin/Toast.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:oktoast/oktoast.dart'; import '../../plugin/C.dart'; import '../../plugin/LoadingOverlay.dart'; import '../../plugin/MarkdownSelectableText.dart'; import 'IconSwitch.dart'; import '../../plugin/AvatarPicker.dart'; class CreateRobotPage extends StatefulWidget { final List> templateList; const CreateRobotPage({super.key, required this.templateList}); @override State createState() => _CreateRobotPageState(); } class _CreateRobotPageState extends State { final _formKey = GlobalKey(); String name = ''; String describe = ''; String systemPrompt = ''; bool isPrivate = false; File? avatarFile; String _templateId="NULL"; final ImagePicker _picker = ImagePicker(); Future pickAvatar() async { final XFile? image = await _picker.pickImage(source: ImageSource.gallery); if (image != null) { setState(() { avatarFile = File(image.path); }); } } Future saveRobot() async { _formKey.currentState!.save(); if (avatarFile == null) { Toast.show("请选择头像", type: ToastType.Error, position: ToastPosition.center); return; } if (name.trim().isEmpty) { Toast.show("请输入机器人名称", type: ToastType.Error, position: ToastPosition.center); return; } if (_templateId == "NULL") { Toast.show("请选择回复模板", type: ToastType.Error, position: ToastPosition.center); return; } if (systemPrompt.trim().isEmpty) { Toast.show("请输入系统提示词", type: ToastType.Error, position: ToastPosition.center); return; } LoadingOverlay.show( context: context, barrierColor: Colors.black54, ); try { dynamic result = await HTTP .create("${C.BASE_URL}/robot/create") .setHeader(C.TOKEN) .setParam({ "name": name, "describe": describe, "systemPrompt": systemPrompt, "avatar": avatarFile, "isPrivate": isPrivate, "templateId": _templateId, }) .setRequestType(RequestType.POST) .setContentType(ContentType("application", "multipart/form-data")) .execute(); LoadingOverlay.hide(); if (result["code"] != 200) { showToast(result["message"]); } else { Navigator.pop(context); FollowListPage.flushData!(false); } } catch (e) { LoadingOverlay.hide(); } } @override void initState() { if (widget.templateList.isNotEmpty) { _templateId = widget.templateList[0]['value']!; } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: const Text("创建机器人", style: TextStyle(color: Colors.black)), centerTitle: true, backgroundColor: Colors.grey[100], leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black), onPressed: () => Navigator.pop(context), ), elevation: 0, // 去掉 AppBar 阴影 ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( children: [ SizedBox( width: 110, height: 110, child: AvatarPicker( onImageSelected: (file) { avatarFile = file; }, ), ), const SizedBox(height: 20), // 名称输入 Card( elevation: 0, color: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), child: TextFormField( decoration: const InputDecoration( hintText: '请输入机器人名称', border: InputBorder.none, ), onSaved: (value) => name = value!.trim(), ), ), ), const SizedBox(height: 12), // 描述输入 Card( elevation: 0, color: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), child: TextFormField( decoration: const InputDecoration( hintText: '简单描述', border: InputBorder.none, ), maxLines: 1, onSaved: (value) => describe = value!.trim(), ), ), ), const SizedBox(height: 12), Row( children: [ Expanded( child: Card( elevation: 0, color: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _templateId, isExpanded: true, icon: Icon( Icons.keyboard_arrow_down_rounded, color: theme.colorScheme.onSurface.withOpacity(0.6), ), borderRadius: BorderRadius.circular(16), dropdownColor: isDark ? theme.colorScheme.surfaceVariant : Colors.white, style: TextStyle( color: theme.colorScheme.onSurface, fontSize: 16, ), items: widget.templateList.map((option) { return DropdownMenuItem( value: option['value'], child: Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Text(option['key'] ?? ''), ), ); }).toList(), onChanged: (val) { setState(() { _templateId = val!; }); }, selectedItemBuilder: (BuildContext context) { return widget.templateList.map((item) { return Align( alignment: Alignment.centerLeft, child: Text( item['key'] ?? '', style: TextStyle( color: theme.colorScheme.onSurface, fontWeight: FontWeight.w500, ), ), ); }).toList(); }, ), ), ), ), ), const SizedBox(width: 12), // 添加间距 IconButton( onPressed: () { String example = ""; if(_templateId!="NULL"){ for(Map item in widget.templateList){ log(item.toString()); if(item["value"]==_templateId){ example = item["example"]!; break; } } } if(example==""){ return; } Remind.showWidget(context, "预览模板", Container( constraints: BoxConstraints( maxHeight: 400, // 设置最大高度 ), decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), child: Scrollbar( child: SingleChildScrollView( padding: const EdgeInsets.all(12), child: MarkdownSelectableText( example, style: TextStyle(fontSize: 16), ), ), ), ),showCancel: false,close: false,onTap: (s){ Navigator.pop(context); }); }, icon: Icon( Icons.preview, color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), ], ), const SizedBox(height: 12), // 系统提示词 Card( elevation: 0, color: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 4, ), child: TextFormField( maxLines: 15, scrollPhysics: BouncingScrollPhysics(), decoration: const InputDecoration( hintText: '系统提示词', border: InputBorder.none, ), onSaved: (value) => systemPrompt = value!.trim(), ), ), ), const SizedBox(height: 16), // 是否私有 Container( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '是否私有', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), ), IconSwitch( value: isPrivate, onChanged: (val) => setState(() => isPrivate = val), ), ], ), ), const SizedBox(height: 24), // 保存按钮 SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: saveRobot, style: ElevatedButton.styleFrom( elevation: 0, // 去掉按钮阴影 shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Text( '保存', style: TextStyle(fontSize: 18, color: Colors.white), ), ), ), ], ), ), ), ); } }