投稿功能

This commit is contained in:
Developer
2026-03-30 18:15:59 +08:00
parent aeddc200a7
commit 820d35fe16
3 changed files with 340 additions and 4 deletions

View File

@@ -0,0 +1,271 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
class ManuscriptRecord {
final String name;
final String catename;
final String url;
final String keywords;
final String introduce;
final String platform;
final DateTime submitTime;
ManuscriptRecord({
required this.name,
required this.catename,
required this.url,
required this.keywords,
required this.introduce,
required this.platform,
required this.submitTime,
});
Map<String, dynamic> toJson() {
return {
'name': name,
'catename': catename,
'url': url,
'keywords': keywords,
'introduce': introduce,
'platform': platform,
'submitTime': submitTime.toIso8601String(),
};
}
factory ManuscriptRecord.fromJson(Map<String, dynamic> json) {
return ManuscriptRecord(
name: json['name'] ?? '',
catename: json['catename'] ?? '',
url: json['url'] ?? '',
keywords: json['keywords'] ?? '',
introduce: json['introduce'] ?? '',
platform: json['platform'] ?? '',
submitTime: DateTime.parse(
json['submitTime'] ?? DateTime.now().toIso8601String(),
),
);
}
}
class TougaoPage extends StatefulWidget {
const TougaoPage({super.key});
@override
State<TougaoPage> createState() => _TougaoPageState();
}
class _TougaoPageState extends State<TougaoPage> {
List<ManuscriptRecord> _records = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadRecords();
}
Future<void> _loadRecords() async {
try {
final prefs = await SharedPreferences.getInstance();
final recordsJson = prefs.getStringList('manuscript_records') ?? [];
setState(() {
_records =
recordsJson
.map((json) => ManuscriptRecord.fromJson(jsonDecode(json)))
.toList()
..sort((a, b) => b.submitTime.compareTo(a.submitTime));
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
}
}
Future<void> _clearAllRecords() async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: const Row(
children: [
Icon(Icons.warning_amber_rounded, color: AppConstants.errorColor),
SizedBox(width: 8),
Text('确认清空'),
],
),
content: const Text('确定要清空所有投稿记录吗?此操作不可恢复。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: AppConstants.errorColor,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('确认清空'),
),
],
),
);
if (confirm == true) {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('manuscript_records');
await _loadRecords();
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('已清空所有记录')));
}
}
}
String _formatDate(DateTime date) {
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')} '
'${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('投稿记录'),
actions: [
if (_records.isNotEmpty)
IconButton(
icon: const Icon(Icons.delete_sweep_outlined),
onPressed: _clearAllRecords,
tooltip: '清空记录',
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: _records.isEmpty
? _buildEmptyState()
: _buildRecordsList(),
);
}
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history_outlined, size: 80, color: Colors.grey[300]),
const SizedBox(height: 16),
Text(
'暂无投稿记录',
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
),
],
),
);
}
Widget _buildRecordsList() {
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _records.length,
itemBuilder: (context, index) {
final record = _records[index];
return _buildRecordCard(record);
},
);
}
Widget _buildRecordCard(ManuscriptRecord record) {
return Card(
elevation: 0,
color: Colors.grey[50],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: ExpansionTile(
title: Row(
children: [
Expanded(
child: Text(
record.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppConstants.primaryColor.withAlpha(20),
borderRadius: BorderRadius.circular(8),
),
child: Text(
record.catename,
style: TextStyle(
fontSize: 12,
color: AppConstants.primaryColor,
fontWeight: FontWeight.w500,
),
),
),
],
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 4),
child: Row(
children: [
Icon(Icons.access_time, size: 14, color: Colors.grey[500]),
const SizedBox(width: 4),
Text(
_formatDate(record.submitTime),
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
],
),
),
childrenPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
children: [
_buildDetailItem('诗人和标题', record.url),
_buildDetailItem('关键词', record.keywords),
_buildDetailItem('平台', record.platform),
_buildDetailItem('诗词介绍', record.introduce, maxLines: 3),
],
),
);
}
Widget _buildDetailItem(String label, String value, {int maxLines = 1}) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
value,
style: const TextStyle(fontSize: 14),
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
),
],
),
);
}
}