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

234 lines
7.3 KiB
Dart

part of 'studio_app.dart';
class StudioWorkspaceView extends StatelessWidget {
const StudioWorkspaceView({
super.key,
required this.runtime,
required this.user,
required this.selectedTab,
required this.tabs,
required this.activities,
required this.refreshToken,
required this.onSelectTab,
required this.onCloseTab,
required this.onRefresh,
required this.onLogout,
required this.themeMode,
required this.onToggleTheme,
required this.onPageAction,
required this.onOpenNode,
});
final StudioRuntime runtime;
final StudioUser user;
final StudioTab selectedTab;
final List<StudioTab> tabs;
final List<StudioActivity> activities;
final int refreshToken;
final ValueChanged<String> onSelectTab;
final ValueChanged<String> onCloseTab;
final VoidCallback onRefresh;
final VoidCallback onLogout;
final ThemeMode themeMode;
final VoidCallback onToggleTheme;
final void Function(String title, String detail) onPageAction;
final ValueChanged<StudioNavNode> onOpenNode;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_TabStrip(
tabs: tabs,
selectedTabId: selectedTab.id,
onSelectTab: onSelectTab,
onCloseTab: onCloseTab,
),
const SizedBox(height: 8),
Expanded(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 260),
layoutBuilder: (currentChild, previousChildren) {
return Stack(
alignment: Alignment.topCenter,
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
);
},
child: _buildPage(
key: ValueKey('${selectedTab.id}:$refreshToken'),
tab: selectedTab,
activities: activities,
runtime: runtime,
onPageAction: onPageAction,
onOpenNode: onOpenNode,
),
),
),
],
),
);
}
Widget _buildPage({
required Key key,
required StudioTab tab,
required List<StudioActivity> activities,
required StudioRuntime runtime,
required void Function(String title, String detail) onPageAction,
required ValueChanged<StudioNavNode> onOpenNode,
}) {
switch (tab.pageType) {
case StudioPageType.dashboard:
return StudioDashboardPage(
key: key,
tab: tab,
user: user,
activities: activities,
);
case StudioPageType.history:
return StudioHistoryPage(
key: key,
tab: tab,
runtime: runtime,
onAction: onPageAction,
);
case StudioPageType.sync:
case StudioPageType.repair:
return StudioOperationsPage(
key: key,
tab: tab,
runtime: runtime,
onAction: onPageAction,
);
case StudioPageType.sqlTemplate:
case StudioPageType.sqlRunner:
return StudioSqlPage(
key: key,
tab: tab,
runtime: runtime,
onAction: onPageAction,
);
case StudioPageType.portalMaintain:
return StudioPortalMaintainPage(
key: key,
tab: tab,
runtime: runtime,
onAction: onPageAction,
onOpenMenu: () => onOpenNode(
studioNavTree
.expand((node) => [node, ...node.children])
.firstWhere((node) => node.id == 'NavigationEditor'),
),
);
default:
return StudioFormEditorPage(
key: key,
tab: tab,
runtime: runtime,
onAction: onPageAction,
);
}
}
}
class _TabStrip extends StatelessWidget {
const _TabStrip({
required this.tabs,
required this.selectedTabId,
required this.onSelectTab,
required this.onCloseTab,
});
final List<StudioTab> tabs;
final String selectedTabId;
final ValueChanged<String> onSelectTab;
final ValueChanged<String> onCloseTab;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 42,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: tabs.length,
separatorBuilder: (_, __) => const SizedBox(width: 4),
itemBuilder: (context, index) {
final tab = tabs[index];
final selected = tab.id == selectedTabId;
final scheme = Theme.of(context).colorScheme;
return InkWell(
borderRadius: BorderRadius.circular(18),
onTap: () => onSelectTab(tab.id),
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
width: 172,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: selected
? scheme.primary.withOpacity(0.18)
: _softPanelColor(context),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: selected
? scheme.primary.withOpacity(0.55)
: scheme.outlineVariant.withOpacity(0.8),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(tab.icon, size: 14),
const SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
tab.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
tab.pageType.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodySmall
?.copyWith(color: _mutedColor(context, 0.62)),
),
],
),
),
if (tab.id != StudioTab.dashboardId)
SizedBox(
width: 20,
height: 20,
child: IconButton(
tooltip: '关闭',
padding: EdgeInsets.zero,
constraints: const BoxConstraints.tightFor(
width: 20,
height: 20,
),
onPressed: () => onCloseTab(tab.id),
icon: const Icon(Icons.close_rounded, size: 12),
),
),
],
),
),
);
},
),
);
}
}