Files
TtxPageAdmin/lib/studio_app_pages_portal.dart
T
2026-05-16 19:03:01 +08:00

159 lines
4.9 KiB
Dart

part of 'studio_app.dart';
class StudioPortalMaintainPage extends StatefulWidget {
const StudioPortalMaintainPage({
super.key,
required this.tab,
required this.runtime,
required this.onAction,
required this.onOpenMenu,
});
final StudioTab tab;
final StudioRuntime runtime;
final void Function(String title, String detail) onAction;
final VoidCallback onOpenMenu;
@override
State<StudioPortalMaintainPage> createState() => _StudioPortalMaintainPageState();
}
class _StudioPortalMaintainPageState extends State<StudioPortalMaintainPage> {
bool _loading = true;
String _status = '正在加载门户';
List<Map<String, dynamic>> _rows = <Map<String, dynamic>>[];
@override
void initState() {
super.initState();
_reload();
}
Future<void> _reload() async {
setState(() {
_loading = true;
_status = '正在加载门户';
});
try {
final rows = await widget.runtime.client.fetchPortalConfigs(
session: widget.runtime.session,
);
if (!mounted) {
return;
}
setState(() {
_rows = rows;
_status = '门户已加载';
});
widget.onAction('加载门户', widget.tab.title);
} catch (error, stackTrace) {
debugPrint('========== 加载门户失败 ==========');
debugPrint('error: $error');
debugPrint('stackTrace:\n$stackTrace');
debugPrint('========== 加载门户失败 end ==========');
if (!mounted) {
return;
}
setState(() {
_status = error.toString();
});
} finally {
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
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(
'读取后端 portal 配置列表。',
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(color: _mutedColor(context)),
),
],
),
),
FilledButton.tonalIcon(
onPressed: _loading ? null : _reload,
icon: const Icon(Icons.refresh_rounded),
label: const Text('刷新'),
),
const SizedBox(width: 10),
FilledButton.tonalIcon(
onPressed: widget.onOpenMenu,
icon: const Icon(Icons.menu_rounded),
label: const Text('设置菜单'),
),
],
),
const SizedBox(height: 12),
SelectableText(_status),
const SizedBox(height: 12),
if (_loading)
const Padding(
padding: EdgeInsets.symmetric(vertical: 24),
child: Center(child: CircularProgressIndicator()),
)
else
DataTable(
columns: const [
DataColumn(label: Text('编码')),
DataColumn(label: Text('名称')),
DataColumn(label: Text('描述')),
],
rows: _rows
.map(
(row) => DataRow(
cells: [
DataCell(Text(_value(row, const ['id', 'code']))),
DataCell(Text(_value(row, const ['label', 'name']))),
DataCell(
SelectableText(
_value(row, const ['remark', 'description', 'portal']),
),
),
],
),
)
.toList(),
),
],
),
),
),
);
}
}