调整部分前端布局和后端增删改逻辑
This commit is contained in:
@@ -0,0 +1,898 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
from textual.app import App
|
||||
from textual.css.query import NoMatches
|
||||
|
||||
from cli.tui.adapters import (
|
||||
TUI_SNAPSHOT_COLLECTOR_REGISTRY,
|
||||
BrowserPageSnapshot,
|
||||
DashboardSnapshot,
|
||||
DetailPageSnapshot,
|
||||
)
|
||||
from cli.tui.copy import TUI_COPY
|
||||
from cli.tui.keymaps import TUI_KEYMAP_REGISTRY
|
||||
from cli.tui.screens import BrowserScreen, DashboardScreen, DetailScreen
|
||||
from cli.tui.widgets import NavigationItem, WorkspaceSidebar
|
||||
|
||||
SnapshotCollector = Callable[['ShierfengTuiApp'], BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiViewSpec:
|
||||
"""
|
||||
TUI 视图规格定义。
|
||||
|
||||
:param view_key: 视图标识
|
||||
:param include_query: 是否注入搜索词
|
||||
:param include_filter: 是否注入筛选键
|
||||
"""
|
||||
|
||||
view_key: str
|
||||
include_query: bool = False
|
||||
include_filter: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiViewDefinition:
|
||||
"""
|
||||
TUI 视图定义。
|
||||
|
||||
:param view_key: 视图标识
|
||||
:param collector: 页面快照采集器
|
||||
"""
|
||||
|
||||
view_key: str
|
||||
collector: SnapshotCollector
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiViewRegistry:
|
||||
"""
|
||||
TUI 视图注册表。
|
||||
|
||||
该对象集中维护视图打开、刷新与顺序导航所需的元数据,
|
||||
避免 `ShierfengTuiApp` 内继续手工维护多份视图映射。
|
||||
|
||||
:param definitions: 视图定义映射
|
||||
:param navigation_items: 导航项列表
|
||||
"""
|
||||
|
||||
definitions: dict[str, TuiViewDefinition]
|
||||
navigation_items: list[NavigationItem]
|
||||
|
||||
@staticmethod
|
||||
def normalize_view_key(view_key: str) -> str:
|
||||
"""
|
||||
规范化视图标识。
|
||||
|
||||
:param view_key: 原始视图标识
|
||||
:return: 规范化后的视图标识
|
||||
"""
|
||||
return str(view_key).strip().lower()
|
||||
|
||||
def resolve_view_key(self, view_key: str) -> str:
|
||||
"""
|
||||
解析可用视图标识,不存在时回退到 dashboard。
|
||||
|
||||
:param view_key: 原始视图标识
|
||||
:return: 可用视图标识
|
||||
"""
|
||||
normalized_view_key = self.normalize_view_key(view_key)
|
||||
if normalized_view_key in self.definitions:
|
||||
return normalized_view_key
|
||||
return 'dashboard'
|
||||
|
||||
def collect_snapshot(
|
||||
self, app: 'ShierfengTuiApp', view_key: str
|
||||
) -> BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot:
|
||||
"""
|
||||
采集指定视图的页面快照。
|
||||
|
||||
:param app: 当前 TUI 应用实例
|
||||
:param view_key: 目标视图标识
|
||||
:return: 页面快照
|
||||
"""
|
||||
resolved_view_key = self.resolve_view_key(view_key)
|
||||
return self.definitions[resolved_view_key].collector(app)
|
||||
|
||||
def get_navigation_index(self, view_key: str) -> int:
|
||||
"""
|
||||
获取视图在导航列表中的索引。
|
||||
|
||||
:param view_key: 目标视图标识
|
||||
:return: 导航索引
|
||||
"""
|
||||
resolved_view_key = self.resolve_view_key(view_key)
|
||||
return next(
|
||||
(index for index, item in enumerate(self.navigation_items) if item.view_key == resolved_view_key),
|
||||
0,
|
||||
)
|
||||
|
||||
def get_relative_view_key(self, view_key: str, offset: int) -> str:
|
||||
"""
|
||||
获取相对当前视图偏移后的目标视图标识。
|
||||
|
||||
:param view_key: 当前视图标识
|
||||
:param offset: 偏移量
|
||||
:return: 目标视图标识
|
||||
"""
|
||||
current_index = self.get_navigation_index(view_key)
|
||||
target_index = (current_index + offset) % len(self.navigation_items)
|
||||
return self.navigation_items[target_index].view_key
|
||||
|
||||
|
||||
_TUI_VIEW_SPECS: tuple[TuiViewSpec, ...] = (
|
||||
TuiViewSpec('dashboard'),
|
||||
TuiViewSpec('app', include_query=True),
|
||||
TuiViewSpec('ops', include_query=True),
|
||||
TuiViewSpec('database', include_query=True),
|
||||
TuiViewSpec('cache', include_query=True),
|
||||
TuiViewSpec('jobs', include_query=True, include_filter=True),
|
||||
TuiViewSpec('gen', include_query=True),
|
||||
TuiViewSpec('configs', include_query=True, include_filter=True),
|
||||
TuiViewSpec('crypto', include_query=True),
|
||||
)
|
||||
|
||||
_TUI_APP_CSS = """
|
||||
Screen {
|
||||
layout: vertical;
|
||||
background: #04080f;
|
||||
color: #d8f7ff;
|
||||
scrollbar-size-horizontal: 1;
|
||||
scrollbar-size-vertical: 1;
|
||||
scrollbar-background: #06111b;
|
||||
scrollbar-background-hover: #0a1723;
|
||||
scrollbar-background-active: #0d1f2d;
|
||||
scrollbar-color: #1f6f8d;
|
||||
scrollbar-color-hover: #38d8ff;
|
||||
scrollbar-color-active: #6df7ff;
|
||||
}
|
||||
|
||||
Widget {
|
||||
scrollbar-size-horizontal: 1;
|
||||
scrollbar-size-vertical: 1;
|
||||
scrollbar-background: #06111b;
|
||||
scrollbar-background-hover: #0a1723;
|
||||
scrollbar-background-active: #0d1f2d;
|
||||
scrollbar-color: #1f6f8d;
|
||||
scrollbar-color-hover: #38d8ff;
|
||||
scrollbar-color-active: #6df7ff;
|
||||
}
|
||||
|
||||
#workspace-shell {
|
||||
height: 1fr;
|
||||
background: #050c15;
|
||||
}
|
||||
|
||||
#workspace-main {
|
||||
padding: 1 2 1 1;
|
||||
background: #050c15;
|
||||
border: round #18425d;
|
||||
margin: 1 1 1 0;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
scrollbar-size-horizontal: 1;
|
||||
scrollbar-size-vertical: 1;
|
||||
scrollbar-background: #06111b;
|
||||
scrollbar-background-hover: #0a1723;
|
||||
scrollbar-background-active: #0d1f2d;
|
||||
scrollbar-color: #1f6f8d;
|
||||
scrollbar-color-hover: #38d8ff;
|
||||
scrollbar-color-active: #6df7ff;
|
||||
}
|
||||
|
||||
#workspace-main:focus {
|
||||
border: heavy #6df7ff;
|
||||
background: #071320;
|
||||
}
|
||||
|
||||
#detail-body {
|
||||
height: auto;
|
||||
margin-top: 1;
|
||||
border: double #18425d;
|
||||
background: #06111b;
|
||||
padding: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#browser-body {
|
||||
height: auto;
|
||||
margin-top: 1;
|
||||
border: double #18425d;
|
||||
background: #06111b;
|
||||
padding: 1;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#browser-filter-bar {
|
||||
border: round #163c57;
|
||||
padding: 1 2;
|
||||
margin-bottom: 1;
|
||||
background: #07131f;
|
||||
color: #d9fbff;
|
||||
}
|
||||
|
||||
#browser-detail-pane {
|
||||
width: 1fr;
|
||||
min-width: 48;
|
||||
border: round #163c57;
|
||||
padding: 1;
|
||||
background: #07131f;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#browser-detail-pane.is-ok {
|
||||
border: round #22c983;
|
||||
}
|
||||
|
||||
#browser-detail-pane.is-fail {
|
||||
border: round #ff6b7a;
|
||||
background: #190d12;
|
||||
}
|
||||
|
||||
#browser-detail-pane.is-warn {
|
||||
border: round #f2b84b;
|
||||
background: #171108;
|
||||
}
|
||||
|
||||
#browser-detail-pane.is-info {
|
||||
border: round #38d8ff;
|
||||
}
|
||||
|
||||
#browser-section-body {
|
||||
height: auto;
|
||||
border-top: solid #163c57;
|
||||
padding-top: 1;
|
||||
background: #06111b;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#dashboard-metrics {
|
||||
grid-size: 4;
|
||||
grid-gutter: 1 2;
|
||||
grid-columns: 1fr 1fr 1fr 1fr;
|
||||
height: auto;
|
||||
margin-top: 1;
|
||||
margin-bottom: 1;
|
||||
padding: 1 0;
|
||||
}
|
||||
|
||||
#dashboard-grid, #detail-grid {
|
||||
grid-size: 2;
|
||||
grid-gutter: 1 2;
|
||||
grid-columns: 1fr 1fr;
|
||||
height: auto;
|
||||
margin-top: 1;
|
||||
}
|
||||
|
||||
#detail-grid {
|
||||
margin-bottom: 1;
|
||||
}
|
||||
|
||||
Footer {
|
||||
background: #060d16;
|
||||
color: #b6efff;
|
||||
border-top: heavy #163c57;
|
||||
}
|
||||
|
||||
FooterKey {
|
||||
background: #0b2130;
|
||||
color: #ecfcff;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiBindingSpec:
|
||||
"""
|
||||
TUI 快捷键绑定定义。
|
||||
|
||||
:param key: 快捷键
|
||||
:param action: 动作名称
|
||||
:param label: 展示标签
|
||||
"""
|
||||
|
||||
key: str
|
||||
action: str
|
||||
label: str
|
||||
|
||||
|
||||
class TuiBindingBuilder:
|
||||
"""
|
||||
TUI 快捷键绑定构建器。
|
||||
|
||||
该对象负责统一装配应用级固定绑定和视图跳转绑定,避免 `ShierfengTuiApp`
|
||||
继续手工维护重复的绑定元组列表。
|
||||
"""
|
||||
|
||||
def build_base_bindings(self) -> list[TuiBindingSpec]:
|
||||
"""
|
||||
构建应用级固定绑定。
|
||||
|
||||
:return: 绑定定义列表
|
||||
"""
|
||||
return [
|
||||
TuiBindingSpec('q', 'quit', TUI_COPY.build_app_binding_label('quit')),
|
||||
TuiBindingSpec('s', 'focus_sidebar', TUI_COPY.build_app_binding_label('sidebar')),
|
||||
TuiBindingSpec('[', 'show_previous_view', TUI_COPY.build_app_binding_label('previous')),
|
||||
TuiBindingSpec(']', 'show_next_view', TUI_COPY.build_app_binding_label('next')),
|
||||
TuiBindingSpec('r', 'refresh_current_view', TUI_COPY.build_app_binding_label('refresh')),
|
||||
]
|
||||
|
||||
def build_navigation_bindings(self) -> list[TuiBindingSpec]:
|
||||
"""
|
||||
构建页面导航绑定。
|
||||
|
||||
:return: 绑定定义列表
|
||||
"""
|
||||
return [
|
||||
TuiBindingSpec(
|
||||
TUI_KEYMAP_REGISTRY.get_navigation_shortcut(spec.view_key),
|
||||
f'show_{spec.view_key}',
|
||||
TUI_COPY.render_view_label(spec.view_key),
|
||||
)
|
||||
for spec in _TUI_VIEW_SPECS
|
||||
]
|
||||
|
||||
def build(self) -> list[tuple[str, str, str]]:
|
||||
"""
|
||||
构建 Textual 所需的绑定元组列表。
|
||||
|
||||
:return: 绑定元组列表
|
||||
"""
|
||||
return [
|
||||
(binding.key, binding.action, binding.label)
|
||||
for binding in [*self.build_base_bindings(), *self.build_navigation_bindings()]
|
||||
]
|
||||
|
||||
|
||||
TUI_APP_BINDINGS = TuiBindingBuilder().build()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiViewRegistryBuilder:
|
||||
"""
|
||||
TUI 视图注册表构建器。
|
||||
|
||||
该对象负责根据统一规格构建导航项和快照采集定义,避免模块内
|
||||
继续维护重复的 `lambda collect(...)` 配置块。
|
||||
"""
|
||||
|
||||
def build_navigation_items(self) -> list[NavigationItem]:
|
||||
"""
|
||||
构建工作台导航项列表。
|
||||
|
||||
:return: 导航项列表
|
||||
"""
|
||||
return [
|
||||
NavigationItem(
|
||||
view_key,
|
||||
TUI_COPY.render_view_label(view_key),
|
||||
shortcut,
|
||||
TUI_COPY.render_navigation_description(view_key),
|
||||
)
|
||||
for view_key, shortcut in TUI_KEYMAP_REGISTRY.navigation_shortcuts.items()
|
||||
]
|
||||
|
||||
def build_collector(self, spec: TuiViewSpec) -> SnapshotCollector:
|
||||
"""
|
||||
根据视图规格构建页面快照采集器。
|
||||
|
||||
:param spec: 视图规格
|
||||
:return: 页面快照采集器
|
||||
"""
|
||||
|
||||
def collect(app: 'ShierfengTuiApp') -> BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot:
|
||||
collect_kwargs: dict[str, str] = {}
|
||||
if spec.include_filter:
|
||||
collect_kwargs['filter_key'] = app.get_browser_filter(spec.view_key)
|
||||
if spec.include_query:
|
||||
collect_kwargs['query'] = app.get_browser_query(spec.view_key)
|
||||
return TUI_SNAPSHOT_COLLECTOR_REGISTRY.collect(spec.view_key, app.env, **collect_kwargs)
|
||||
|
||||
return collect
|
||||
|
||||
def build_definitions(self) -> dict[str, TuiViewDefinition]:
|
||||
"""
|
||||
构建视图定义映射。
|
||||
|
||||
:return: 视图定义映射
|
||||
"""
|
||||
return {spec.view_key: TuiViewDefinition(spec.view_key, self.build_collector(spec)) for spec in _TUI_VIEW_SPECS}
|
||||
|
||||
def build(self) -> TuiViewRegistry:
|
||||
"""
|
||||
构建 TUI 视图注册表。
|
||||
|
||||
:return: 视图注册表
|
||||
"""
|
||||
return TuiViewRegistry(
|
||||
definitions=self.build_definitions(),
|
||||
navigation_items=self.build_navigation_items(),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TuiViewStateStore:
|
||||
"""
|
||||
TUI 视图状态存储。
|
||||
|
||||
该对象集中维护页面级动作反馈、浏览页筛选键和搜索词,避免
|
||||
`ShierfengTuiApp` 本体继续直接操作多份同类字典状态。
|
||||
|
||||
:param action_feedback_by_view: 动作反馈映射
|
||||
:param browser_filter_by_view: 浏览页筛选映射
|
||||
:param browser_query_by_view: 浏览页搜索词映射
|
||||
"""
|
||||
|
||||
action_feedback_by_view: dict[str, list[str]]
|
||||
browser_filter_by_view: dict[str, str]
|
||||
browser_query_by_view: dict[str, str]
|
||||
|
||||
@classmethod
|
||||
def create_default(cls) -> 'TuiViewStateStore':
|
||||
"""
|
||||
创建默认视图状态存储。
|
||||
|
||||
:return: 视图状态存储
|
||||
"""
|
||||
return cls(
|
||||
action_feedback_by_view={},
|
||||
browser_filter_by_view={'jobs': 'all', 'configs': 'all'},
|
||||
browser_query_by_view={},
|
||||
)
|
||||
|
||||
def remember_action_feedback(self, view_key: str, lines: list[str]) -> None:
|
||||
"""
|
||||
记录指定页面最近一次动作反馈。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param lines: 反馈文本行
|
||||
:return: None
|
||||
"""
|
||||
self.action_feedback_by_view[view_key] = list(lines)
|
||||
|
||||
def get_action_feedback_lines(self, view_key: str) -> list[str]:
|
||||
"""
|
||||
读取指定页面最近一次动作反馈。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:return: 反馈文本行
|
||||
"""
|
||||
return list(self.action_feedback_by_view.get(view_key, []))
|
||||
|
||||
def remember_browser_filter(self, view_key: str, filter_key: str) -> None:
|
||||
"""
|
||||
记录指定浏览页当前筛选键。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param filter_key: 当前筛选键
|
||||
:return: None
|
||||
"""
|
||||
self.browser_filter_by_view[view_key] = filter_key
|
||||
|
||||
def get_browser_filter(self, view_key: str, default: str = 'all') -> str:
|
||||
"""
|
||||
读取指定浏览页当前筛选键。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param default: 默认筛选键
|
||||
:return: 当前筛选键
|
||||
"""
|
||||
return str(self.browser_filter_by_view.get(view_key, default) or default)
|
||||
|
||||
def remember_browser_query(self, view_key: str, query: str) -> None:
|
||||
"""
|
||||
记录指定浏览页当前搜索词。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param query: 当前搜索词
|
||||
:return: None
|
||||
"""
|
||||
normalized_query = str(query or '').strip()
|
||||
if normalized_query:
|
||||
self.browser_query_by_view[view_key] = normalized_query
|
||||
return
|
||||
self.browser_query_by_view.pop(view_key, None)
|
||||
|
||||
def get_browser_query(self, view_key: str, default: str = '') -> str:
|
||||
"""
|
||||
读取指定浏览页当前搜索词。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param default: 默认搜索词
|
||||
:return: 当前搜索词
|
||||
"""
|
||||
return str(self.browser_query_by_view.get(view_key, default) or default)
|
||||
|
||||
|
||||
TUI_VIEW_REGISTRY = TuiViewRegistryBuilder().build()
|
||||
NAVIGATION_ITEMS = TUI_VIEW_REGISTRY.navigation_items
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiScreenFactory:
|
||||
"""
|
||||
TUI 工作台页面工厂。
|
||||
|
||||
该对象负责把页面快照转换为具体 screen 实例,避免 `ShierfengTuiApp`
|
||||
同时承担状态协调和 screen 构建职责。
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def build_refresh_timestamp() -> str:
|
||||
"""
|
||||
构建当前页面刷新时间文本。
|
||||
|
||||
:return: 格式化后的刷新时间
|
||||
"""
|
||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
def build(
|
||||
self,
|
||||
*,
|
||||
snapshot: BrowserPageSnapshot | DashboardSnapshot | DetailPageSnapshot,
|
||||
env: str,
|
||||
active_view: str,
|
||||
navigation_items: list[NavigationItem],
|
||||
action_feedback_lines: list[str],
|
||||
) -> BrowserScreen | DashboardScreen | DetailScreen:
|
||||
"""
|
||||
根据快照类型构建对应的工作台页面。
|
||||
|
||||
:param snapshot: 页面快照
|
||||
:param env: 当前运行环境
|
||||
:param active_view: 当前激活视图
|
||||
:param navigation_items: 导航项列表
|
||||
:param action_feedback_lines: 当前视图动作反馈
|
||||
:return: 对应的页面对象
|
||||
"""
|
||||
refreshed_at = self.build_refresh_timestamp()
|
||||
if isinstance(snapshot, DashboardSnapshot):
|
||||
return DashboardScreen(
|
||||
snapshot,
|
||||
env=env,
|
||||
active_view=active_view,
|
||||
navigation_items=navigation_items,
|
||||
refreshed_at=refreshed_at,
|
||||
)
|
||||
if isinstance(snapshot, BrowserPageSnapshot):
|
||||
return BrowserScreen(
|
||||
snapshot,
|
||||
env=env,
|
||||
active_view=active_view,
|
||||
navigation_items=navigation_items,
|
||||
refreshed_at=refreshed_at,
|
||||
action_feedback_lines=action_feedback_lines,
|
||||
)
|
||||
return DetailScreen(
|
||||
snapshot,
|
||||
env=env,
|
||||
active_view=active_view,
|
||||
navigation_items=navigation_items,
|
||||
refreshed_at=refreshed_at,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TuiScreenNavigator:
|
||||
"""
|
||||
TUI 工作台页面导航器。
|
||||
|
||||
该对象负责处理首屏展示与后续切屏差异,让应用壳更专注于状态切换。
|
||||
|
||||
:param app: 当前 TUI 应用实例
|
||||
:param initialized: 工作台屏幕是否已初始化
|
||||
"""
|
||||
|
||||
app: 'ShierfengTuiApp'
|
||||
initialized: bool = False
|
||||
|
||||
def show(self, screen: BrowserScreen | DashboardScreen | DetailScreen) -> None:
|
||||
"""
|
||||
显示工作台页面,并兼容首屏初始化与后续切屏。
|
||||
|
||||
Textual 在默认根屏阶段直接调用 `switch_screen()` 会触发内部回调栈异常,
|
||||
因此首次展示页面需要使用 `push_screen()` 建立活动屏,后续再改用
|
||||
`switch_screen()` 做同层页面替换。
|
||||
|
||||
:param screen: 待显示的页面对象
|
||||
:return: None
|
||||
"""
|
||||
if not self.initialized:
|
||||
self.app.push_screen(screen)
|
||||
self.initialized = True
|
||||
return
|
||||
self.app.switch_screen(screen)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiViewOpeningCoordinator:
|
||||
"""
|
||||
TUI 视图打开协调器。
|
||||
|
||||
该对象负责串联视图解析、快照采集、screen 构建与切屏展示,
|
||||
让 `ShierfengTuiApp` 更专注于状态暴露与 Textual action 桥接。
|
||||
|
||||
:param view_registry: 视图注册表
|
||||
:param screen_factory: 工作台页面工厂
|
||||
:param navigation_items: 导航项列表
|
||||
"""
|
||||
|
||||
view_registry: TuiViewRegistry
|
||||
screen_factory: TuiScreenFactory
|
||||
navigation_items: list[NavigationItem]
|
||||
|
||||
def show(self, app: 'ShierfengTuiApp', view_key: str) -> None:
|
||||
"""
|
||||
切换并展示指定视图。
|
||||
|
||||
:param app: 当前 TUI 应用实例
|
||||
:param view_key: 目标视图标识
|
||||
:return: None
|
||||
"""
|
||||
resolved_view_key = self.view_registry.resolve_view_key(view_key)
|
||||
app.current_view = resolved_view_key
|
||||
snapshot = self.view_registry.collect_snapshot(app, resolved_view_key)
|
||||
screen = self.screen_factory.build(
|
||||
snapshot=snapshot,
|
||||
env=app.env,
|
||||
active_view=app.current_view,
|
||||
navigation_items=self.navigation_items,
|
||||
action_feedback_lines=app.get_action_feedback_lines(app.current_view),
|
||||
)
|
||||
app.screen_navigator.show(screen)
|
||||
|
||||
|
||||
class ShierfengTuiApp(App[None]):
|
||||
"""
|
||||
ShiErFeng CLI Textual 应用。
|
||||
|
||||
:param env: 当前运行环境
|
||||
"""
|
||||
|
||||
CSS = _TUI_APP_CSS
|
||||
BINDINGS = TUI_APP_BINDINGS
|
||||
|
||||
def __init__(self, env: str) -> None:
|
||||
"""
|
||||
初始化 TUI 应用实例。
|
||||
|
||||
:param env: 当前运行环境
|
||||
:return: None
|
||||
"""
|
||||
self.env = env
|
||||
self.current_view = 'dashboard'
|
||||
self.view_state_store = TuiViewStateStore.create_default()
|
||||
self.screen_factory = TuiScreenFactory()
|
||||
self.screen_navigator = TuiScreenNavigator(self)
|
||||
self.view_opening_coordinator = TuiViewOpeningCoordinator(
|
||||
view_registry=TUI_VIEW_REGISTRY,
|
||||
screen_factory=self.screen_factory,
|
||||
navigation_items=NAVIGATION_ITEMS,
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
def show_view(self, view_key: str) -> None:
|
||||
"""
|
||||
切换并展示指定视图。
|
||||
|
||||
:param view_key: 目标视图标识
|
||||
:return: None
|
||||
"""
|
||||
self.view_opening_coordinator.show(self, view_key)
|
||||
|
||||
def remember_action_feedback(self, view_key: str, lines: list[str]) -> None:
|
||||
"""
|
||||
记录指定页面最近一次动作反馈。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param lines: 反馈文本行
|
||||
:return: None
|
||||
"""
|
||||
self.view_state_store.remember_action_feedback(view_key, lines)
|
||||
|
||||
def get_action_feedback_lines(self, view_key: str) -> list[str]:
|
||||
"""
|
||||
读取指定页面最近一次动作反馈。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:return: 反馈文本行
|
||||
"""
|
||||
return self.view_state_store.get_action_feedback_lines(view_key)
|
||||
|
||||
def remember_browser_filter(self, view_key: str, filter_key: str) -> None:
|
||||
"""
|
||||
记录指定浏览页当前筛选键。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param filter_key: 当前筛选键
|
||||
:return: None
|
||||
"""
|
||||
self.view_state_store.remember_browser_filter(view_key, filter_key)
|
||||
|
||||
def get_browser_filter(self, view_key: str, default: str = 'all') -> str:
|
||||
"""
|
||||
读取指定浏览页当前筛选键。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param default: 默认筛选键
|
||||
:return: 当前筛选键
|
||||
"""
|
||||
return self.view_state_store.get_browser_filter(view_key, default)
|
||||
|
||||
def remember_browser_query(self, view_key: str, query: str) -> None:
|
||||
"""
|
||||
记录指定浏览页当前搜索词。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param query: 当前搜索词
|
||||
:return: None
|
||||
"""
|
||||
self.view_state_store.remember_browser_query(view_key, query)
|
||||
|
||||
def get_browser_query(self, view_key: str, default: str = '') -> str:
|
||||
"""
|
||||
读取指定浏览页当前搜索词。
|
||||
|
||||
:param view_key: 页面视图标识
|
||||
:param default: 默认搜索词
|
||||
:return: 当前搜索词
|
||||
"""
|
||||
return self.view_state_store.get_browser_query(view_key, default)
|
||||
|
||||
def on_mount(self) -> None:
|
||||
"""
|
||||
在应用启动时载入首页。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.show_view('dashboard')
|
||||
|
||||
def open_view(self, view_key: str) -> None:
|
||||
"""
|
||||
按视图标识打开对应页面。
|
||||
|
||||
:param view_key: 视图标识
|
||||
:return: None
|
||||
"""
|
||||
self.show_view(view_key)
|
||||
|
||||
def action_focus_sidebar(self) -> None:
|
||||
"""
|
||||
将焦点切换到左侧导航栏。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
try:
|
||||
self.screen.query_one(WorkspaceSidebar).focus()
|
||||
except NoMatches:
|
||||
return
|
||||
|
||||
def action_show_previous_view(self) -> None:
|
||||
"""
|
||||
切换到前一个工作台视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view(TUI_VIEW_REGISTRY.get_relative_view_key(self.current_view, -1))
|
||||
|
||||
def action_show_next_view(self) -> None:
|
||||
"""
|
||||
切换到后一个工作台视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view(TUI_VIEW_REGISTRY.get_relative_view_key(self.current_view, 1))
|
||||
|
||||
def action_refresh_current_view(self) -> None:
|
||||
"""
|
||||
刷新当前视图数据。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.show_view(self.current_view)
|
||||
|
||||
def action_show_dashboard(self) -> None:
|
||||
"""
|
||||
切换到首页视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('dashboard')
|
||||
|
||||
def action_show_app(self) -> None:
|
||||
"""
|
||||
切换到应用详情视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('app')
|
||||
|
||||
def action_show_ops(self) -> None:
|
||||
"""
|
||||
切换到运维详情视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('ops')
|
||||
|
||||
def action_show_database(self) -> None:
|
||||
"""
|
||||
切换到数据库详情视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('database')
|
||||
|
||||
def action_show_cache(self) -> None:
|
||||
"""
|
||||
切换到缓存浏览视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('cache')
|
||||
|
||||
def action_show_jobs(self) -> None:
|
||||
"""
|
||||
切换到任务浏览视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('jobs')
|
||||
|
||||
def action_show_gen(self) -> None:
|
||||
"""
|
||||
切换到代码生成浏览视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('gen')
|
||||
|
||||
def action_show_configs(self) -> None:
|
||||
"""
|
||||
切换到配置浏览视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('configs')
|
||||
|
||||
def action_show_crypto(self) -> None:
|
||||
"""
|
||||
切换到传输加密详情视图。
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.open_view('crypto')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TuiAppRunner:
|
||||
"""
|
||||
TUI 应用启动器。
|
||||
|
||||
该对象负责按环境创建并运行 Textual 应用实例,使命令入口不再依赖
|
||||
模块级启动函数。
|
||||
|
||||
:param app_factory: TUI 应用工厂
|
||||
"""
|
||||
|
||||
app_factory: Callable[[str], ShierfengTuiApp]
|
||||
|
||||
def run(self, env: str) -> None:
|
||||
"""
|
||||
启动 TUI 应用。
|
||||
|
||||
:param env: 当前运行环境
|
||||
:return: None
|
||||
"""
|
||||
self.app_factory(env).run()
|
||||
|
||||
|
||||
TUI_APP_RUNNER = TuiAppRunner(ShierfengTuiApp)
|
||||
Reference in New Issue
Block a user