360 lines
14 KiB
Dart
360 lines
14 KiB
Dart
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import '../../../constants/app_constants.dart';
|
|
|
|
class ServerInfoDialog {
|
|
static Future<void> show(BuildContext context, {Map<String, dynamic>? data}) {
|
|
final server = data?['server'] as Map<String, dynamic>?;
|
|
final network = data?['network'] as Map<String, dynamic>?;
|
|
final timestamp = data?['timestamp'] as Map<String, dynamic>?;
|
|
|
|
final load = server?['load'] as Map<String, dynamic>?;
|
|
final latency = network?['latency'] as List<dynamic>?;
|
|
final serverResponseTime = network?['server_response_time'];
|
|
|
|
return showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Container(
|
|
constraints: const BoxConstraints(maxWidth: 340),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: AppConstants.primaryColor,
|
|
borderRadius: const BorderRadius.vertical(
|
|
top: Radius.circular(16),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: const Icon(
|
|
Icons.cloud_outlined,
|
|
color: Colors.white,
|
|
size: 20,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
const Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'广州 server-ls',
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
SizedBox(height: 2),
|
|
Text(
|
|
'服务器信息',
|
|
style: TextStyle(
|
|
color: Colors.white70,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
children: [
|
|
_buildInfoCard(
|
|
icon: Icons.schedule,
|
|
iconColor: const Color(0xFF007AFF),
|
|
title: '服务器时间',
|
|
content: timestamp?['datetime'] ?? '--',
|
|
),
|
|
const SizedBox(height: 12),
|
|
_buildInfoCard(
|
|
icon: Icons.speed,
|
|
iconColor: const Color(0xFF34C759),
|
|
title: '服务器负载',
|
|
content:
|
|
'1分钟: ${_formatLoad(load?['1min'])}\n5分钟: ${_formatLoad(load?['5min'])}\n15分钟: ${_formatLoad(load?['15min'])}',
|
|
),
|
|
const SizedBox(height: 12),
|
|
_buildInfoCard(
|
|
icon: Icons.bolt,
|
|
iconColor: const Color(0xFFFF9500),
|
|
title: '服务器响应',
|
|
content: '${serverResponseTime ?? '--'} ms',
|
|
trailing: _buildResponseTimeIndicator(
|
|
serverResponseTime,
|
|
),
|
|
),
|
|
if (latency != null && latency.isNotEmpty) ...[
|
|
const SizedBox(height: 16),
|
|
Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF2F2F7),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Icon(
|
|
Icons.public,
|
|
size: 16,
|
|
color: Colors.grey[600],
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
'网络延迟',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 14,
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
...latency.map<Widget>((item) {
|
|
final host = item['host'] as String?;
|
|
final ip = item['ip'] as String?;
|
|
final lat = item['latency'];
|
|
final status = item['status'] as String?;
|
|
final isOnline = status == 'online';
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 8),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 8,
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
color: isOnline
|
|
? const Color(0xFF34C759)
|
|
: const Color(0xFFFF3B30),
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
host ?? '--',
|
|
style: const TextStyle(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
Text(
|
|
ip ?? '--',
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.grey[500],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 4,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: isOnline
|
|
? const Color(
|
|
0xFF34C759,
|
|
).withValues(alpha: 0.1)
|
|
: const Color(
|
|
0xFFFF3B30,
|
|
).withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(
|
|
8,
|
|
),
|
|
),
|
|
child: Text(
|
|
isOnline ? '$lat ms' : '离线',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: isOnline
|
|
? const Color(0xFF34C759)
|
|
: const Color(0xFFFF3B30),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
|
|
child: SizedBox(
|
|
width: double.infinity,
|
|
child: TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
style: TextButton.styleFrom(
|
|
backgroundColor: const Color(0xFFF2F2F7),
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
child: const Text(
|
|
'关闭',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: Color(0xFF007AFF),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
static Widget _buildInfoCard({
|
|
required IconData icon,
|
|
required Color iconColor,
|
|
required String title,
|
|
required String content,
|
|
Widget? trailing,
|
|
}) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF2F2F7),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: iconColor.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(icon, color: iconColor, size: 18),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
content,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (trailing != null) trailing,
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
static String _formatLoad(dynamic value) {
|
|
if (value == null) return '--';
|
|
|
|
double? loadValue;
|
|
if (value is num) {
|
|
loadValue = value.toDouble();
|
|
} else if (value is String) {
|
|
loadValue = double.tryParse(value);
|
|
}
|
|
|
|
if (loadValue == null) return '--';
|
|
|
|
final percentage = (loadValue * 100).round();
|
|
return '$percentage%';
|
|
}
|
|
|
|
static Widget _buildResponseTimeIndicator(dynamic responseTime) {
|
|
int? time;
|
|
if (responseTime is int) {
|
|
time = responseTime;
|
|
} else if (responseTime is String) {
|
|
time = int.tryParse(responseTime);
|
|
}
|
|
|
|
if (time == null) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
Color color;
|
|
String label;
|
|
if (time < 100) {
|
|
color = const Color(0xFF34C759);
|
|
label = '极快';
|
|
} else if (time < 300) {
|
|
color = const Color(0xFF34C759);
|
|
label = '快速';
|
|
} else if (time < 500) {
|
|
color = const Color(0xFFFF9500);
|
|
label = '正常';
|
|
} else {
|
|
color = const Color(0xFFFF3B30);
|
|
label = '较慢';
|
|
}
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: color.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
label,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: color,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|