基本实现kodbox的动态展示

This commit is contained in:
高手 2025-06-09 17:05:39 +08:00
parent 89c0dfc1aa
commit ae49aa13df
2 changed files with 197 additions and 1 deletions

View File

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import '../services/auth_service.dart'; import '../services/auth_service.dart';
import '../widgets/leetcode_card.dart'; import '../widgets/leetcode_card.dart';
import '../widgets/gitea_card.dart'; import '../widgets/gitea_card.dart';
import '../widgets/kodbox_card.dart';
import 'settings_screen.dart'; import 'settings_screen.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
@ -90,7 +91,7 @@ class _HomeScreenState extends State<HomeScreen> {
case 'Gitea': case 'Gitea':
return const GiteaCard(); return const GiteaCard();
case 'KodBox': case 'KodBox':
return const Center(child: Text('KodBox 数据卡片开发中...')); return const KodBoxCard();
default: default:
return const Center(child: Text('未知平台')); return const Center(child: Text('未知平台'));
} }

View File

@ -0,0 +1,195 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../services/auth_service.dart';
class KodBoxCard extends StatefulWidget {
const KodBoxCard({super.key});
@override
State<KodBoxCard> createState() => _KodBoxCardState();
}
class _KodBoxCardState extends State<KodBoxCard> {
List<dynamic> _logs = [];
bool _isLoading = false;
String? _error;
Future<void> _fetchLogs() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final authService = Provider.of<AuthService>(context, listen: false);
final token = authService.credentials['kodbox'];
if (token == null || token.isEmpty) {
throw Exception('未配置 KodBox Token');
}
// 7
final now = DateTime.now();
final sevenDaysAgo = now.subtract(const Duration(days: 7));
final timeTo = (now.millisecondsSinceEpoch / 1000).round();
final timeFrom = (sevenDaysAgo.millisecondsSinceEpoch / 1000).round();
final request = http.MultipartRequest(
'POST',
Uri.parse('https://cloud.jdysya.top/?admin/log/get&accessToken=$token'),
);
request.fields.addAll({
'page': '1',
'pageNum': '50',
'sortField': 'createTime',
'sortType': 'down',
'timeFrom': timeFrom.toString(),
'timeTo': timeTo.toString(),
});
final response = await request.send();
final responseBody = await response.stream.bytesToString();
if (response.statusCode == 200) {
final data = jsonDecode(responseBody);
if (data['code'] == true) {
setState(() {
_logs = data['data'];
_isLoading = false;
});
} else {
throw Exception('请求失败: ${data['info'] ?? '未知错误'}');
}
} else {
throw Exception('请求失败: ${response.statusCode}');
}
} catch (e) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
String _formatTimestamp(String timestamp) {
final date = DateTime.fromMillisecondsSinceEpoch(
(double.parse(timestamp) * 1000).round(),
);
return date.toString().substring(0, 19);
}
Widget _buildLogItem(Map<String, dynamic> log) {
final desc = log['desc'];
final isFileOperation =
desc != null &&
(desc['sourceInfo'] != null || desc['parentInfo'] != null);
return Card(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 16,
child: Text(log['nickName']?[0].toUpperCase() ?? '?'),
),
const SizedBox(width: 8),
Text(
log['nickName'] ?? log['name'] ?? '未知用户',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
Text(log['title'] ?? '未知操作'),
],
),
if (isFileOperation && desc['sourceInfo'] != null) ...[
const SizedBox(height: 8),
Text(
'文件路径: ${desc['sourceInfo']['pathDisplay']}',
style: const TextStyle(color: Colors.blue),
),
],
const SizedBox(height: 8),
Text(
'时间: ${_formatTimestamp(log['createTime'])}',
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
);
}
@override
void initState() {
super.initState();
_fetchLogs();
}
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'KodBox 最近操作',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _isLoading ? null : _fetchLogs,
),
],
),
if (_isLoading)
const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(),
),
)
else if (_error != null)
Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
_error!,
style: const TextStyle(color: Colors.red),
),
),
)
else if (_logs.isEmpty)
const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('暂无操作记录'),
),
)
else
Expanded(
child: ListView.builder(
itemCount: _logs.length,
itemBuilder: (context, index) {
return _buildLogItem(_logs[index]);
},
),
),
],
),
),
);
}
}