feat:详情页面
This commit is contained in:
parent
72ca9ca94e
commit
bd50a97f8f
|
|
@ -33,3 +33,5 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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')}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue