Files
ai_english/client/lib/features/ai/pages/ai_writing_page.dart
2025-11-17 13:39:05 +08:00

351 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import '../../../core/network/ai_api_service.dart';
/// AI写作批改页面
class AIWritingPage extends StatefulWidget {
const AIWritingPage({Key? key}) : super(key: key);
@override
State<AIWritingPage> createState() => _AIWritingPageState();
}
class _AIWritingPageState extends State<AIWritingPage> {
final TextEditingController _contentController = TextEditingController();
final AIApiService _aiApiService = AIApiService();
String _selectedType = 'essay';
bool _isLoading = false;
Map<String, dynamic>? _correctionResult;
String? _error;
final List<Map<String, String>> _writingTypes = [
{'value': 'essay', 'label': '议论文'},
{'value': 'email', 'label': '邮件'},
{'value': 'report', 'label': '报告'},
{'value': 'letter', 'label': '信件'},
{'value': 'story', 'label': '故事'},
];
@override
void dispose() {
_contentController.dispose();
super.dispose();
}
Future<void> _submitForCorrection() async {
if (_contentController.text.trim().isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请输入要批改的内容')),
);
return;
}
setState(() {
_isLoading = true;
_error = null;
_correctionResult = null;
});
try {
final result = await _aiApiService.correctWriting(
content: _contentController.text.trim(),
taskType: _selectedType,
);
setState(() {
_correctionResult = result;
_isLoading = false;
});
} catch (e) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
void _clearContent() {
setState(() {
_contentController.clear();
_correctionResult = null;
_error = null;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AI写作批改'),
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.clear),
onPressed: _clearContent,
tooltip: '清空内容',
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 写作类型选择
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'写作类型',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
children: _writingTypes.map((type) {
final isSelected = _selectedType == type['value'];
return ChoiceChip(
label: Text(type['label']!),
selected: isSelected,
onSelected: (selected) {
if (selected) {
setState(() {
_selectedType = type['value']!;
});
}
},
);
}).toList(),
),
],
),
),
),
const SizedBox(height: 16),
// 内容输入区域
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'写作内容',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
TextField(
controller: _contentController,
maxLines: 10,
decoration: const InputDecoration(
hintText: '请输入您的英文写作内容...',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.all(12),
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isLoading ? null : _submitForCorrection,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: _isLoading
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 8),
Text('AI批改中...'),
],
)
: const Text(
'开始AI批改',
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
const SizedBox(height: 16),
// 错误显示
if (_error != null)
Card(
color: Colors.red.shade50,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Icon(Icons.error, color: Colors.red.shade700),
const SizedBox(width: 8),
Expanded(
child: Text(
_error!,
style: TextStyle(color: Colors.red.shade700),
),
),
],
),
),
),
// 批改结果显示
if (_correctionResult != null)
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.check_circle,
color: Colors.green.shade600,
),
const SizedBox(width: 8),
const Text(
'AI批改结果',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 16),
// 总体评分
if (_correctionResult!['score'] != null)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
const Icon(Icons.star, color: Colors.amber),
const SizedBox(width: 8),
Text(
'总体评分: ${_correctionResult!['score']}/100',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
const SizedBox(height: 12),
// 批改建议
if (_correctionResult!['suggestions'] != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'批改建议:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
_correctionResult!['suggestions'].toString(),
style: const TextStyle(fontSize: 14),
),
],
),
// 错误列表
if (_correctionResult!['errors'] != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
const Text(
'发现的错误:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
...(_correctionResult!['errors'] as List).map(
(error) => Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: Colors.orange.shade200,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'错误类型: ${error['type'] ?? '未知'}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.orange.shade700,
),
),
if (error['original'] != null)
Text('原文: ${error['original']}'),
if (error['corrected'] != null)
Text(
'建议: ${error['corrected']}',
style: TextStyle(
color: Colors.green.shade700,
fontWeight: FontWeight.bold,
),
),
if (error['explanation'] != null)
Text(
'说明: ${error['explanation']}',
style: const TextStyle(fontSize: 12),
),
],
),
),
),
],
),
],
),
),
),
],
),
),
);
}
}