# -*- coding: utf-8 -*- """ 工作台 """ import reflex as rx from application.pages import task_page, knowledge_base_page, login_page from application.states import AuthState, TaskState, SidebarState from application.states.models import TaskHistoryItem # 导航按钮字典 NAV_BUTTONS: dict = { "create_task": {"src": "/create_task.svg", "text": "创建任务"}, } """ 约定: 1 若组件仅用于当前页面则无需抽象为通用组件(application.components.*) """ def nav_button( nav_button: str, ) -> rx.Component: """ 导航按钮 :param nav_button: 导航按钮 :return : Component """ # 选中状态 is_selected = nav_button == SidebarState.nav_button return rx.hstack( # 导航按钮的图标 rx.image( NAV_BUTTONS[nav_button]["src"], width="16px", height="16px", object_fit="contain", # 保持图标宽高比 color="var(--prismui-color-1)", ), # 导航按钮的文本 rx.text( NAV_BUTTONS[nav_button]["text"], line_height="20px", font_size="var(--prismui-font-size-2)", font_weight=rx.cond( is_selected, "var(--prismui-font-weight-3)", None ), # 通过加粗字体区分是否选中 ), align_items="center", gap="8px", width="100%", padding="5px 12px", background_color=rx.cond( is_selected, "var(--prismui-background-color-2)", "transparent" ), # 通过背景颜色区分是否选中 border_radius="var(--prismui-border-radius-3)", cursor="pointer", # 点击事件:将指定导航按钮设置为选中的导航按钮 on_click=SidebarState.set_nav_button(nav_button), ) def task_history_item( item: TaskHistoryItem, ) -> rx.Component: """ 任务历史项 :param item: 任务历史项 :return: Component """ # 选中状态 is_selected = item.id == TaskState.task_id return rx.hstack( rx.cond( item.is_running, rx.image( "/spinner.svg", width="16px", height="16px", object_fit="contain", animation="spin 1.6s linear infinite", ), ), # 渲染任务描述 rx.text( item.description, font_weight=rx.cond(is_selected, "var(--prismui-font-weight-3)", None), flex=1, min_width="0", # 自适应宽度 line_height="20px", white_space="nowrap", # 禁止换行 overflow="hidden", mask_image="linear-gradient(to right, white calc(100% - 24px), transparent 100%)", # 添加蒙版 ), # 渲染上一次运行日期时间 rx.text( item.last_run_at, line_height="20px", font_size="var(--prismui-font-size-1)", color="var(--prismui-color-2)", flex_shrink=0, # 禁止缩放 ), align_items="center", gap="8px", width="100%", padding="5px 12px", background_color=rx.cond( is_selected, "var(--prismui-background-color-2)", "transparent" ), # 通过背景颜色区分是否选中 border_radius="var(--prismui-border-radius-3)", cursor="pointer", # 点击事件:将指定任务唯一标识设置为当前任务唯一标识 on_click=SidebarState.set_task_id(item.id), style={ "_hover": { "background": "var(--prismui-background-color-2)", }, # 鼠标悬停背景颜色 }, ) def task_history() -> rx.Component: """ 任务历史 :return: rx.Component """ return rx.auto_scroll( rx.foreach( TaskState.task_history, task_history_item, ), display="flex", flex_direction="column", gap="2px", width="100%", overflow="auto", style={ "&::-webkit-scrollbar": { "display": "none", }, }, ) def sidebar() -> rx.Component: """ 侧边栏 :return: rx.Component """ return rx.vstack( rx.vstack( rx.alert_dialog.root( rx.alert_dialog.trigger(rx.box(height="0", width="100%")), rx.alert_dialog.content( rx.alert_dialog.title("确认退出登录?"), rx.alert_dialog.description( "退出登录不会丢失任何数据,你仍可以登录此账号。" ), rx.alert_dialog.cancel("取消"), rx.alert_dialog.action("退出登录"), ), ), # 渲染系统图标和名称 rx.hstack( # 系统图标 rx.image( src="/logo.png", width="20px", height="20px", object_fit="contain", ), # 系统名称 rx.text( "棱镜球", line_height="20px", font_size="var(--prismui-font-size-2)", font_weight="var(--prismui-font-weight-4)", ), align_items="center", gap="4px", width="100%", height="56px", padding="0 12px 12px", ), # 渲染导航按钮 rx.vstack( # 渲染导航按钮:创建任务 nav_button("create_task"), gap="2px", width="100%", padding_bottom="16px", ), # 最近任务 rx.vstack( # 标题 rx.text( "最近", line_height="20px", font_size="var(--prismui-font-size-2)", padding="5px 10px 5px 22px", ), # 渲染历史任务 task_history(), gap="2px", width="100%", padding="0 12px", ), align_items="flex-start", gap="2px", width="100%", ), rx.vstack( rx.divider( width="100%", background_color="var(--prismui-background-color-3)" ), rx.hstack( rx.popover.root( rx.popover.trigger( rx.hstack( rx.avatar( fallback="M", height="28px", width="28px", color="var(--prismui-color-5)", border_radius="100%", ), rx.text( "刘弼仁", line_height="20px", font_size="var(--prismui-font-size-1)", font_weight="var(--prismui-font-weight-3)", ), flex="1", align_items="center", gap="10px", padding="6px 8px", margin="8px 8px 8px 0", border_radius="var(--prismui-border-radius-3)", cursor="pointer", style={ "_hover": { "background": "var(--prismui-background-color-2)", }, }, ), ), rx.popover.content( rx.vstack( rx.hstack( rx.avatar( fallback="M", height="40px", width="40px", color="var(--prismui-color-5)", border_radius="100%", ), rx.text( "刘弼仁", line_height="24px", font_size="var(--prismui-font-size-3) !important", font_weight="var(--prismui-font-weight-3)", ), align_items="center", width="100%", padding="16px", background_color="var(--prismui-background-color-1)", border_radius="var(--prismui-border-radius-3)", ), rx.vstack( rx.popover.close( rx.hstack( rx.icon( "log-out", height="16px", width="16px", object_fit="contain", color="var(--prismui-color-1)", ), rx.text("退出登录", line_height="20px"), align_items="center", gap="8px", width="100%", padding="5px 8px", border_radius="var(--prismui-border-radius-3)", cursor="pointer", style={ "_hover": { "background": "var(--prismui-background-color-2)", }, }, ), ), width="100%", padding="8px", background_color="var(--prismui-background-color-1)", border_radius="var(--prismui-border-radius-3)", ), ), width="320px", padding="8px", background_color="var(--prismui-background-color-9)", ), ), rx.tooltip( rx.box( rx.icon( "bell", height="16px", width="16px", object_fit="contain", color="var(--prismui-color-1)", ), display="flex", align_items="center", justify_content="center", height="32px", flex_shrink="0", width="32px", border_radius="var(--prismui-border-radius-3)", cursor="pointer", style={ "_hover": { "background": "var(--prismui-background-color-2)", }, }, ), content="消息中心", ), align_items="center", justify_content="space-between", gap="4px", width="100%", ), width="100%", ), flex_shrink="0", justify_content="space-between", align_items="flex-start", width="264px", height="100%", padding="0 12px", background_color="var(--prismui-background-color-9)", ) def workbench() -> rx.Component: """ 工作台 """ return rx.fragment( # 若用户未登录则渲染登录页面 rx.cond( AuthState.user_id, rx.fragment(), login_page(), ), rx.hstack( # 侧边栏 sidebar(), # 根据选中的导航按钮渲染对应的页面 rx.match( SidebarState.nav_button, # 知识库页面 ("knowledge_base", knowledge_base_page()), # 任务页面 task_page(), ), width="100%", height="100vh", ), )