Files
TtxPageAdmin/lib/studio_app_pages_history.dart
T
2026-05-16 01:10:06 +08:00

193 lines
6.1 KiB
Dart

part of 'studio_app.dart';
class StudioHistoryPage extends StatefulWidget {
const StudioHistoryPage({
super.key,
required this.tab,
required this.runtime,
required this.onAction,
});
final StudioTab tab;
final StudioRuntime runtime;
final void Function(String title, String detail) onAction;
@override
State<StudioHistoryPage> createState() => _StudioHistoryPageState();
}
class _StudioHistoryPageState extends State<StudioHistoryPage> {
bool _loading = true;
String? _errorMessage;
List<Map<String, dynamic>> _rows = <Map<String, dynamic>>[];
@override
void initState() {
super.initState();
_reload();
}
Future<void> _reload() async {
setState(() {
_loading = true;
_errorMessage = null;
});
try {
final rows = await widget.runtime.client.fetchUpgradeLogs(
session: widget.runtime.session,
);
if (!mounted) {
return;
}
setState(() {
_rows = rows;
});
} catch (error, stackTrace) {
_printError('加载历史失败', error, stackTrace);
if (!mounted) {
return;
}
setState(() {
_errorMessage = _normalizeError(error);
});
} finally {
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
void _printError(String title, Object error, StackTrace stackTrace) {
debugPrint('========== $title ==========');
debugPrint('error: $error');
debugPrint('stackTrace:\n$stackTrace');
debugPrint('========== $title end ==========');
}
String _normalizeError(Object error) {
final text = error.toString();
const marker = 'Bad state: ';
if (text.startsWith(marker)) {
return text.substring(marker.length);
}
return text;
}
String _value(Map<String, dynamic> row, List<String> keys, [String fallback = '']) {
for (final key in keys) {
final value = row[key];
if (value != null && value.toString().trim().isNotEmpty) {
return value.toString();
}
}
return fallback;
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Card(
child: Padding(
padding: const EdgeInsets.all(18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.tab.title,
style: Theme.of(context).textTheme.headlineSmall
?.copyWith(fontWeight: FontWeight.w700),
),
const SizedBox(height: 6),
Text(
'从后端读取发布与修改历史。',
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: _mutedColor(context)),
),
],
),
),
FilledButton.tonalIcon(
onPressed: _loading ? null : _reload,
icon: _loading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.refresh_rounded),
label: const Text('刷新'),
),
],
),
const SizedBox(height: 12),
if (_errorMessage != null) ...[
SelectableText(
_errorMessage!,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.error,
),
),
const SizedBox(height: 12),
],
if (_loading)
const Padding(
padding: EdgeInsets.symmetric(vertical: 28),
child: Center(child: CircularProgressIndicator()),
)
else
SizedBox(
width: double.infinity,
child: DataTable(
columns: const [
DataColumn(label: Text('版本')),
DataColumn(label: Text('操作者')),
DataColumn(label: Text('时间')),
DataColumn(label: Text('状态')),
DataColumn(label: Text('说明')),
],
rows: _rows
.map(
(row) => DataRow(
cells: [
DataCell(Text(_value(row, const ['version', 'tag', 'name'], ''))),
DataCell(Text(_value(row, const ['user', 'operator', 'creator'], ''))),
DataCell(Text(_value(row, const ['time', 'createTime', 'date'], ''))),
DataCell(Text(_value(row, const ['status', 'state'], ''))),
DataCell(
SelectableText(
_value(
row,
const ['note', 'msg', 'message', 'summary', 'desc'],
row.toString(),
),
),
),
],
),
)
.toList(),
),
),
const SizedBox(height: 8),
Text(
'共 ${_rows.length} 条历史记录',
style: Theme.of(context).textTheme.bodySmall
?.copyWith(color: _mutedColor(context, 0.62)),
),
],
),
),
),
);
}
}