import 'dart:ffi'; 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'; import '../services/kodbox_service.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:url_launcher/url_launcher.dart'; class KodBoxCard extends StatefulWidget { const KodBoxCard({super.key}); @override State createState() => _KodBoxCardState(); } class _KodBoxCardState extends State { List _logs = []; bool _isLoading = false; String? _error; final _kodboxService = KodBoxService(); Future _fetchLogs() async { setState(() { _isLoading = true; _error = null; }); try { final authService = Provider.of(context, listen: false); final token = await _kodboxService.getValidToken(authService); // 计算时间范围(最近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('${KodBoxService.baseUrl}/?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 _getFilePath(Map desc) { if (desc['sourceInfo'] == false) { // 处理删除文件的情况 final parentInfo = desc['parentInfo']; final fileName = desc['desc']?['content'] ?? '未知文件'; return '${parentInfo['pathDisplay']}$fileName'; } else if (desc['sourceInfo'] != null) { return desc['sourceInfo']['pathDisplay'] ?? '未知路径'; } return '未知路径'; } int? _getSourceID(Map desc) { if (desc['sourceInfo'] == false) { // 处理删除文件的情况 return desc['parentInfo']?['sourceID']; } else if (desc['sourceInfo'] != null) { return desc['sourceInfo']?['sourceID']; } return null; } Future _launchFileUrl(int sourceID) async { final url = Uri.parse('${KodBoxService.baseUrl}/#explorer&sidf=$sourceID'); if (await canLaunchUrl(url)) { await launchUrl(url); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('无法打开文件链接')), ); } } } Widget _buildLogItem(Map log) { final desc = log['desc']; final isFileOperation = desc != null && (desc['sourceInfo'] != null || desc['parentInfo'] != null); final sourceID = isFileOperation ? _getSourceID(desc) : null; return Card( margin: const EdgeInsets.symmetric(vertical: 4.0), child: InkWell( onTap: sourceID != null ? () => _launchFileUrl(sourceID) : null, 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) ...[ const SizedBox(height: 8), Text( '文件路径: ${_getFilePath(desc)}', style: TextStyle( color: sourceID != null ? Colors.blue : Colors.black, ), ), ], const SizedBox(height: 8), Text( '时间: ${timeago.format(DateTime.fromMillisecondsSinceEpoch(int.parse(log['createTime']) * 1000), locale: 'zh_CN')}', 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]); }, ), ), ], ), ), ); } }