二次开发源码提交

This commit is contained in:
2026-05-15 08:52:48 +08:00
parent 4131f7321a
commit eb35a1d067
191 changed files with 34566 additions and 0 deletions
@@ -0,0 +1,336 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../data/models/upload_task_model.dart';
/// 上传任务列表项
class UploadProgressItem extends StatelessWidget {
final UploadTaskModel task;
final VoidCallback? onPause;
final VoidCallback? onResume;
final VoidCallback? onCancel;
final VoidCallback? onDelete;
final VoidCallback? onRetry;
final VoidCallback? onNavigate;
const UploadProgressItem({
super.key,
required this.task,
this.onPause,
this.onResume,
this.onCancel,
this.onDelete,
this.onRetry,
this.onNavigate,
});
@override
Widget build(BuildContext context) {
final isUploading = task.status == UploadStatus.uploading;
final isWaiting = task.status == UploadStatus.waiting;
final isPaused = task.status == UploadStatus.paused;
final isFailed = task.status == UploadStatus.failed;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(
color: _getCardColor(context, task.status),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: _getBorderColor(context, task.status),
),
),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
_buildStatusIcon(context, task.status),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
task.fileName,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 14,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
_buildStatusRow(context, task),
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: _buildActionButtons(context, task),
),
],
),
if (isUploading || isWaiting || isPaused) ...[
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: LinearProgressIndicator(
value: isPaused ? null : task.progress,
backgroundColor: Colors.grey.shade200,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
),
const SizedBox(width: 12),
Text(
isPaused ? '已暂停' : task.progressText,
style: const TextStyle(fontSize: 12),
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
'${task.uploadedBytes}/${task.fileSize} bytes',
style: const TextStyle(fontSize: 12),
),
const Spacer(),
if (isUploading && task.speedText.isNotEmpty) ...[
Text(
task.speedText,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 8),
],
Text(
task.readableFileSize,
style: const TextStyle(fontSize: 12),
),
],
),
] else if (isFailed && task.errorMessage != null) ...[
const SizedBox(height: 8),
Text(
task.errorMessage!,
style: TextStyle(
fontSize: 12,
color: Colors.red.shade700,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
),
),
);
}
Widget _buildStatusIcon(BuildContext context, UploadStatus status) {
final color = _getStatusColor(status);
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: color.withValues(alpha: isDark ? 0.2 : 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
_getStatusIcon(status),
size: 18,
color: color,
),
);
}
Widget _buildStatusRow(BuildContext context, UploadTaskModel task) {
final color = _getStatusColor(task.status);
final isCompleted = task.status == UploadStatus.completed;
return Row(
children: [
Text(
task.statusText,
style: TextStyle(fontSize: 12, color: color),
),
if (isCompleted) ...[
Text(
' · ',
style: TextStyle(fontSize: 12, color: Theme.of(context).hintColor),
),
Text(
task.readableFileSize,
style: TextStyle(fontSize: 12, color: Theme.of(context).hintColor),
),
Text(
' · ',
style: TextStyle(fontSize: 12, color: Theme.of(context).hintColor),
),
Text(
_formatDateTime(task.completedAt!),
style: TextStyle(fontSize: 12, color: Theme.of(context).hintColor),
),
],
],
);
}
List<Widget> _buildActionButtons(
BuildContext context,
UploadTaskModel task,
) {
final errorColor = Theme.of(context).colorScheme.error;
switch (task.status) {
case UploadStatus.waiting:
case UploadStatus.uploading:
return [
IconButton(
icon: const Icon(Icons.pause, size: 20),
onPressed: onPause,
tooltip: '暂停',
),
];
case UploadStatus.paused:
return [
IconButton(
icon: const Icon(Icons.play_arrow, size: 20),
onPressed: onResume,
tooltip: '继续',
),
IconButton(
icon: Icon(Icons.cancel, size: 20, color: errorColor),
onPressed: onCancel,
tooltip: '取消',
),
];
case UploadStatus.failed:
return [
IconButton(
icon: const Icon(Icons.refresh, size: 20),
onPressed: onRetry,
tooltip: '重试',
),
IconButton(
icon: Icon(Icons.delete, size: 20, color: errorColor),
onPressed: onDelete,
tooltip: '删除',
),
];
case UploadStatus.completed:
return [
if (onNavigate != null)
IconButton(
icon: const Icon(LucideIcons.folderOpen, size: 20),
onPressed: onNavigate,
tooltip: '打开文件夹',
),
IconButton(
icon: Icon(Icons.delete_outline, size: 20, color: errorColor),
onPressed: onDelete,
tooltip: '删除',
),
];
case UploadStatus.cancelled:
return [
IconButton(
icon: Icon(Icons.delete, size: 20, color: errorColor),
onPressed: onDelete,
tooltip: '删除',
),
];
}
}
IconData _getStatusIcon(UploadStatus status) {
switch (status) {
case UploadStatus.waiting:
return LucideIcons.clock;
case UploadStatus.uploading:
return LucideIcons.upload;
case UploadStatus.completed:
return LucideIcons.checkCircle2;
case UploadStatus.paused:
return LucideIcons.pause;
case UploadStatus.failed:
case UploadStatus.cancelled:
return LucideIcons.xCircle;
}
}
Color _getStatusColor(UploadStatus status) {
switch (status) {
case UploadStatus.waiting:
return Colors.orange;
case UploadStatus.uploading:
return Colors.blue;
case UploadStatus.completed:
return Colors.green;
case UploadStatus.paused:
return Colors.orange;
case UploadStatus.failed:
case UploadStatus.cancelled:
return Colors.red;
}
}
Color _getCardColor(BuildContext context, UploadStatus status) {
final isDark = Theme.of(context).brightness == Brightness.dark;
switch (status) {
case UploadStatus.completed:
return isDark ? Colors.green.withValues(alpha: 0.08) : Colors.green.withValues(alpha: 0.05);
case UploadStatus.failed:
case UploadStatus.cancelled:
return isDark ? Colors.red.withValues(alpha: 0.08) : Colors.red.withValues(alpha: 0.05);
default:
return isDark ? Colors.white.withValues(alpha: 0.05) : Colors.white.withValues(alpha: 0.6);
}
}
Color _getBorderColor(BuildContext context, UploadStatus status) {
final isDark = Theme.of(context).brightness == Brightness.dark;
switch (status) {
case UploadStatus.completed:
return Colors.green.withValues(alpha: isDark ? 0.2 : 0.15);
case UploadStatus.failed:
case UploadStatus.cancelled:
return Colors.red.withValues(alpha: isDark ? 0.2 : 0.15);
default:
return isDark ? Colors.white.withValues(alpha: 0.1) : Colors.white.withValues(alpha: 0.3);
}
}
String _formatDateTime(DateTime dateTime) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inSeconds < 60) {
return '刚刚';
} else if (difference.inMinutes < 60) {
return '${difference.inMinutes}分钟前';
} else if (difference.inHours < 24) {
return '${difference.inHours}小时前';
} else {
return '${dateTime.month}/${dateTime.day} ${dateTime.hour}:${dateTime.minute.toString().padLeft(2, '0')}';
}
}
}