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

269 lines
8.1 KiB
Dart

part of 'studio_app.dart';
class StudioShell extends StatefulWidget {
const StudioShell({
super.key,
required this.runtime,
required this.languageCode,
required this.themeMode,
required this.onToggleTheme,
required this.onLogout,
});
final StudioRuntime runtime;
final String languageCode;
final ThemeMode themeMode;
final VoidCallback onToggleTheme;
final VoidCallback onLogout;
@override
State<StudioShell> createState() => _StudioShellState();
}
class _StudioShellState extends State<StudioShell> {
final List<StudioActivity> _activities = <StudioActivity>[];
final List<StudioTab> _tabs = <StudioTab>[StudioTab.dashboard()];
String _searchQuery = '';
String _selectedTabId = StudioTab.dashboardId;
int _refreshRevision = 0;
StudioUser get user => widget.runtime.user;
@override
void initState() {
super.initState();
_activities.add(
StudioActivity(
title: '登录成功',
detail: '${user.displayName} / ${user.workspace}',
),
);
}
StudioTab get _selectedTab =>
_tabs.firstWhere((tab) => tab.id == _selectedTabId);
void _log(String title, String detail) {
setState(() {
_activities.insert(0, StudioActivity(title: title, detail: detail));
if (_activities.length > 24) {
_activities.removeLast();
}
});
}
void _openNode(StudioNavNode node) {
final existingIndex = _tabs.indexWhere((tab) => tab.id == node.id);
setState(() {
if (existingIndex == -1) {
_tabs.add(
StudioTab(
id: node.id,
title: node.title,
summary: node.summary,
pageType: node.pageType,
icon: node.icon,
),
);
}
_selectedTabId = node.id;
});
_log('打开页面', node.title);
}
void _selectTab(String tabId) {
setState(() {
_selectedTabId = tabId;
});
}
void _closeTab(String tabId) {
if (tabId == StudioTab.dashboardId) {
return;
}
setState(() {
_tabs.removeWhere((tab) => tab.id == tabId);
if (_selectedTabId == tabId) {
_selectedTabId = _tabs.isNotEmpty
? _tabs.last.id
: StudioTab.dashboardId;
}
});
_log('关闭页面', tabId);
}
void _refreshPage() {
setState(() {
_refreshRevision += 1;
});
_log('刷新页面', _selectedTab.title);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('已刷新 ${_selectedTab.title}')));
}
void _handlePageAction(String title, String detail) {
_log(title, detail);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(detail)));
}
List<StudioNavNode> _allLeaves(List<StudioNavNode> nodes) {
final result = <StudioNavNode>[];
for (final node in nodes) {
if (node.children.isEmpty) {
result.add(node);
} else {
result.addAll(_allLeaves(node.children));
}
}
return result;
}
@override
Widget build(BuildContext context) {
final wide = MediaQuery.of(context).size.width >= 1080;
final visibleRoots = _filterNavNodes(
studioNavTree,
widget.runtime.allowedMenuIds,
);
final leaves = _allLeaves(visibleRoots);
final filteredLeaves = _searchQuery.trim().isEmpty
? leaves
: leaves
.where(
(node) =>
node.title.contains(_searchQuery) ||
node.summary.contains(_searchQuery),
)
.toList();
return Scaffold(
appBar: wide
? null
: AppBar(
title: const Text('TTX Studio'),
actions: [
IconButton(
tooltip: widget.themeMode == ThemeMode.dark
? '切换亮色主题'
: '切换暗色主题',
onPressed: widget.onToggleTheme,
icon: Icon(
widget.themeMode == ThemeMode.dark
? Icons.light_mode_rounded
: Icons.dark_mode_rounded,
),
),
IconButton(
tooltip: '刷新',
onPressed: _refreshPage,
icon: const Icon(Icons.refresh_rounded),
),
IconButton(
tooltip: '退出登录',
onPressed: widget.onLogout,
icon: const Icon(Icons.logout_rounded),
),
],
),
drawer: wide
? null
: Drawer(
child: SafeArea(
child: StudioSidebar(
user: user,
themeMode: widget.themeMode,
onToggleTheme: widget.onToggleTheme,
searchQuery: _searchQuery,
filteredLeaves: filteredLeaves,
rootNodes: visibleRoots,
onSearchChanged: (value) {
setState(() {
_searchQuery = value;
});
},
onOpenNode: (node) {
Navigator.of(context).pop();
_openNode(node);
},
onLogout: widget.onLogout,
),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: _shellGradientColors(context),
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: SafeArea(
child: wide
? Row(
children: [
SizedBox(
width: 336,
child: StudioSidebar(
user: user,
themeMode: widget.themeMode,
onToggleTheme: widget.onToggleTheme,
searchQuery: _searchQuery,
filteredLeaves: filteredLeaves,
rootNodes: visibleRoots,
onSearchChanged: (value) {
setState(() {
_searchQuery = value;
});
},
onOpenNode: _openNode,
onLogout: widget.onLogout,
),
),
Expanded(
child: StudioWorkspaceView(
runtime: widget.runtime,
user: user,
selectedTab: _selectedTab,
tabs: List<StudioTab>.unmodifiable(_tabs),
activities: List<StudioActivity>.unmodifiable(
_activities,
),
rootNodes: visibleRoots,
refreshToken: _refreshRevision,
onSelectTab: _selectTab,
onCloseTab: _closeTab,
onRefresh: _refreshPage,
onLogout: widget.onLogout,
themeMode: widget.themeMode,
onToggleTheme: widget.onToggleTheme,
onPageAction: _handlePageAction,
onOpenNode: _openNode,
),
),
],
)
: StudioWorkspaceView(
runtime: widget.runtime,
user: user,
selectedTab: _selectedTab,
tabs: List<StudioTab>.unmodifiable(_tabs),
activities: List<StudioActivity>.unmodifiable(_activities),
rootNodes: visibleRoots,
refreshToken: _refreshRevision,
onSelectTab: _selectTab,
onCloseTab: _closeTab,
onRefresh: _refreshPage,
onLogout: widget.onLogout,
themeMode: widget.themeMode,
onToggleTheme: widget.onToggleTheme,
onPageAction: _handlePageAction,
onOpenNode: _openNode,
),
),
),
);
}
}