feat:详情页面

This commit is contained in:
YuanXuan 2025-08-28 17:26:47 +08:00
parent 72ca9ca94e
commit bd50a97f8f
5 changed files with 123 additions and 71 deletions

View File

@ -33,3 +33,5 @@
}
]
}

View File

@ -65,6 +65,7 @@ class YxNetInspectorController extends ChangeNotifier {
queryParameters: queryParameters,
timestamp: DateTime.now(),
isSuccess: false, //
status: NetworkRequestStatus.pending, //
);
_logs.insert(0, entry);
@ -98,6 +99,9 @@ class YxNetInspectorController extends ChangeNotifier {
responseData: responseData,
duration: duration,
isSuccess: isSuccess,
status: isSuccess
? NetworkRequestStatus.success
: NetworkRequestStatus.failed,
);
_logs[index] = updatedLog;
@ -139,6 +143,7 @@ class YxNetInspectorController extends ChangeNotifier {
errorMessage: error,
duration: duration,
isSuccess: false,
status: NetworkRequestStatus.failed,
);
_logs[index] = updatedLog;
@ -216,6 +221,11 @@ class YxNetInspectorController extends ChangeNotifier {
return _logs.where((log) => log.isSuccess == isSuccess).toList();
}
///
List<NetworkLogEntry> getLogsByRequestStatus(NetworkRequestStatus status) {
return _logs.where((log) => log.status == status).toList();
}
///
List<NetworkLogEntry> searchLogs(String keyword) {
if (keyword.isEmpty) return _logs.toList();

View File

@ -1,5 +1,17 @@
import 'package:flutter/material.dart';
///
enum NetworkRequestStatus {
///
pending,
///
success,
///
failed,
}
///
class NetworkLogEntry {
final String id;
@ -14,6 +26,7 @@ class NetworkLogEntry {
final DateTime timestamp;
final Duration? duration;
final bool isSuccess;
final NetworkRequestStatus status;
NetworkLogEntry({
required this.id,
@ -28,22 +41,31 @@ class NetworkLogEntry {
required this.timestamp,
this.duration,
required this.isSuccess,
this.status = NetworkRequestStatus.pending,
});
///
///
Color get statusColor {
if (!isSuccess) return Colors.red;
if (statusCode == null) return Colors.orange;
if (statusCode! >= 200 && statusCode! < 300) return Colors.green;
if (statusCode! >= 400 && statusCode! < 500) return Colors.orange;
return Colors.red;
switch (status) {
case NetworkRequestStatus.pending:
return Colors.blue; //
case NetworkRequestStatus.success:
return Colors.green; // 绿
case NetworkRequestStatus.failed:
return Colors.red; //
}
}
///
String get statusText {
if (statusCode != null) return '$statusCode';
if (!isSuccess) return '错误';
return '未知';
switch (status) {
case NetworkRequestStatus.pending:
return '进行中';
case NetworkRequestStatus.success:
return statusCode != null ? '$statusCode' : '成功';
case NetworkRequestStatus.failed:
return statusCode != null ? '$statusCode' : '错误';
}
}
///
@ -108,6 +130,7 @@ class NetworkLogEntry {
DateTime? timestamp,
Duration? duration,
bool? isSuccess,
NetworkRequestStatus? status,
}) {
return NetworkLogEntry(
id: id ?? this.id,
@ -122,6 +145,7 @@ class NetworkLogEntry {
timestamp: timestamp ?? this.timestamp,
duration: duration ?? this.duration,
isSuccess: isSuccess ?? this.isSuccess,
status: status ?? this.status,
);
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../controller/yx_net_inspector_controller.dart';
import '../models/inspector_theme.dart';
import '../models/network_log_entry.dart';
@ -61,7 +62,9 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
//
if (_showOnlyErrors) {
logs = logs.where((log) => !log.isSuccess).toList();
logs = logs
.where((log) => log.status == NetworkRequestStatus.failed)
.toList();
}
return logs;
@ -167,6 +170,14 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
),
),
const Spacer(),
IconButton(
onPressed: () => _copyLogDetails(_selectedLog!),
icon: const Icon(
Icons.copy,
color: Colors.white,
),
tooltip: '复制详情',
),
IconButton(
onPressed: () {
_isFullScreen = !_isFullScreen;
@ -403,10 +414,10 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
color: widget.theme.cardColor,
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: log.isSuccess
? Colors.transparent
: widget.theme.errorColor.withValues(alpha: 0.3),
width: log.isSuccess ? 0 : 1,
color: log.status == NetworkRequestStatus.failed
? widget.theme.errorColor.withValues(alpha: 0.3)
: Colors.transparent,
width: log.status == NetworkRequestStatus.failed ? 1 : 0,
),
),
child: InkWell(
@ -489,7 +500,9 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
//
Text(
log.formattedTime.split(' ')[1], //
log.formattedTime.contains(' ')
? log.formattedTime.split(' ')[1]
: log.formattedTime, //
style: TextStyle(
fontSize: 10,
color: widget.theme.secondaryTextColor,
@ -524,4 +537,57 @@ class _YxInspectorPanelState extends State<YxInspectorPanel> {
return widget.theme.secondaryTextColor;
}
}
void _copyLogDetails(NetworkLogEntry log) {
final details = '''
Network Request Details
=======================
Basic Information:
- Method: ${log.method}
- Status Code: ${log.statusCode ?? 'N/A'}
- Status: ${log.statusText}
- Duration: ${log.formattedDuration}
- Request Size: ${log.requestSize} B
- Response Size: ${log.responseSize} B
Full URL:
${log.url}
${log.requestData != null ? 'Request Body:\n${_formatRequestBody(log.requestData)}\n\n' : ''}${log.responseData != null ? 'Response Data:\n${log.responseData}\n\n' : ''}${log.errorMessage != null ? 'Error Message:\n${log.errorMessage}\n\n' : ''}Time Information:
- Request Time: ${log.formattedTime}
- Start Time: ${_formatDateTime(log.timestamp)}
${log.duration != null ? '- End Time: ${_formatDateTime(log.timestamp.add(log.duration!))}' : ''}
''';
Clipboard.setData(ClipboardData(text: details));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('详情已复制到剪贴板'),
duration: Duration(seconds: 2),
),
);
}
String _formatRequestBody(dynamic requestData) {
if (requestData == null) return 'No request data';
if (requestData is String) {
return requestData;
} else if (requestData is Map) {
try {
return requestData.toString();
} catch (e) {
return requestData.toString();
}
} else {
return requestData.toString();
}
}
String _formatDateTime(DateTime dateTime) {
return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} '
'${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}';
}
}

View File

@ -1,6 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/network_log_entry.dart';
import '../models/inspector_theme.dart';
@ -13,23 +12,9 @@ class YxLogDetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: theme.backgroundColor,
appBar: AppBar(
title: const Text('请求详情'),
backgroundColor: theme.primaryColor,
foregroundColor: Colors.white,
actions: [
IconButton(
onPressed: () {
_copyToClipboard(context);
},
icon: const Icon(Icons.copy),
tooltip: '复制详情',
),
],
),
body: SingleChildScrollView(
return Container(
color: theme.backgroundColor,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -402,39 +387,4 @@ class YxLogDetailPage extends StatelessWidget {
return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} '
'${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}:${dateTime.second.toString().padLeft(2, '0')}';
}
void _copyToClipboard(BuildContext context) {
final details = '''
Network Request Details
=======================
Basic Information:
- Method: ${log.method}
- Status Code: ${log.statusCode ?? 'N/A'}
- Status: ${log.statusText}
- Duration: ${log.formattedDuration}
- Request Size: ${log.requestSize} B
- Response Size: ${log.responseSize} B
Full URL:
${log.url}
${log.requestData != null ? 'Request Body:\n${_formatRequestBody(log.requestData)}\n\n' : ''}
${log.responseData != null ? 'Response Data:\n${log.responseData}\n\n' : ''}
${log.errorMessage != null ? 'Error Message:\n${log.errorMessage}\n\n' : ''}
Time Information:
- Request Time: ${log.formattedTime}
- Start Time: ${_formatDateTime(log.timestamp)}
${log.duration != null ? '- End Time: ${_formatDateTime(log.timestamp.add(log.duration!))}' : ''}
''';
Clipboard.setData(ClipboardData(text: details));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('详情已复制到剪贴板'),
duration: Duration(seconds: 2),
),
);
}
}