# -*- coding: utf-8 -*- """ 工作台 """ import reflex as rx from application.pages import create_task_page, knowledge_base_page, login_page from application.states import AuthState, TaskState 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_actived = nav_button == AuthState.activated_nav_button return rx.hstack( # 导航按钮的图标 rx.image( NAV_BUTTONS[nav_button]["src"], width="16px", height="16px", object_fit="contain", ), # 导航按钮的文本 rx.text( NAV_BUTTONS[nav_button]["text"], line_height="20px", font_size="var(--prismui-font-size-2)", font_weight=rx.cond( is_actived, "--prismui-font-weight-3", None ), # 通过加粗字体区分是否激活 color="var(--prismui-color-1)", ), align_items="center", gap="8px", padding="5px 12px", background_color=rx.cond( is_actived, "var(--prismui-background-color-2)", "transparent" ), cursor="pointer", # 点击事件:设置激活导航按钮 on_click=AuthState.set_activated_nav_button(nav_button), ) def task_history_item( item: TaskHistoryItem, ) -> rx.Component: """ 任务历史项 :param item: 任务历史项 :return: Component """ # 高亮 is_highlight = (item.id == TaskState.popover_shown_task_history_item_id) | ( item.id == TaskState.task_id ) return rx.list.item( rx.hstack( # 渲染任务正在运行图标 rx.cond( item.is_running, rx.icon( "play", width="14px", height="14px", color="var(--prismui-color-2)" ), rx.fragment(), ), # 渲染任务描述 rx.text( item.description, flex=1, height="22px", line_height="22px", padding_right="18px", color="var(--prismui-color-2)", text_overflow="ellipsis", white_space="nowrap", overflow="hidden", ), display="flex", align_items="center", width="100%", margin_bottom="8px", # 点击事件:设置激活任务 on_click=lambda: TaskState.set_task(item.id), ), # 任务更多按钮 rx.box( rx.popover.root( rx.popover.trigger( rx.box( rx.icon( "ellipsis", width="14px", height="14px", color="var(--prismui-color-2)", ), cursor="pointer", ) ), rx.popover.content( rx.vstack( rx.box( position="absolute", top="-6px", left="50%", transform="translateX(-50%) rotate(45deg)", width="8px", height="8px", background_color="var(--prismui-background-color-1)", ), rx.popover.close( rx.box( "删除", # 点击事件:删除任务 on_click=lambda: TaskState.delete_task(item.id), display="flex", align_items="center", justify_content="center", width="100%", height="24px", border_radius="var(--prismui-border-radius-1)", line_height="16px", font_family="var(--prismui-font-family)", font_size="var(--prismui-font-size-1)", color="var(--prismui-color-1)", style={ "_hover": { "background_color": "var(--prismui-background-color-6)", }, # 鼠标悬停显示背景颜色 }, cursor="pointer", ), ), position="relative", width="100%", ), side="bottom", side_offset=8, position="relative", align="center", padding="4px 8px", border_radius="var(--prismui-border-radius-1)", box_shadow="var(--prismui-box-shadow-6)", overflow="visible", ), # 悬停气泡显示 / 隐藏变化事件:设置任务历史项悬停气泡显示 / 隐藏 on_open_change=lambda is_shown: TaskState.set_task_history_item_popover_shown( item.id, is_shown ), open_delay=0, ), position="absolute", top="27px", right="16px", transform="translateY(-50%)", z_index="99", min_width="14px", cursor="pointer", opacity=rx.cond(is_highlight, "1", "0"), transition="opacity 0.18s ease-in-out", pointer_events=rx.cond(is_highlight, "auto", "none"), ), display="flex", position="relative", align_items="flex-start", width="100%", padding="16px", margin_bottom="8px", background=rx.cond( is_highlight, "var(--prismui-background-3)", "var(--prismui-background-color-1)", ), border_radius="var(--prismui-border-radius-3)", box_shadow=rx.cond(is_highlight, "var(--prismui-box-shadow-7)", "none"), cursor="pointer", style={ "&:hover": { "background": "var(--prismui-background-3)", "box_shadow": "var(--prismui-box-shadow-7)", }, # 鼠标悬停背景颜色和阴影 "&:hover > div:last-child": { "opacity": "1", "pointer_events": "auto", }, # 鼠标悬停显示更多按钮 }, ) def task_history() -> rx.Component: """ 任务历史 :return: rx.Component """ return rx.auto_scroll( rx.foreach( TaskState.task_history, task_history_item, ), flex="1", align_items="stretch", width="100%", margin_top="8px", style={ "&::-webkit-scrollbar": { "display": "none", }, }, overflow="auto", ) def settings_button(): """ 设置按钮 :return: Component """ return rx.box( rx.hover_card.root( # 触发事件:点击自定义按钮 rx.hover_card.trigger( rx.box( rx.icon( "settings", stroke_width=1.5, width="20px", height="20px", color="var(--prismui-color-1)", ), display="flex", flex_flow="column", justify_content="center", align_items="center", width="36px", height="36px", border_radius="var(--prismui-border-radius-3)", style={ "&:hover": { "background": "var(--prismui-background-color-5)", }, # 鼠标悬停背景颜色 }, cursor="pointer", ) ), # 悬停卡片 rx.hover_card.content( rx.box( rx.box( transform="translateX(-50%) rotate(45deg)", position="absolute", top="calc(50% - 4px)", left="0", width="8px", height="8px", background_color="var(--prismui-background-color-1)", ), rx.box( "退出登录", display="flex", justify_content="center", align_items="center", width="100%", height="24px", line_height="16px", border_radius="var(--prismui-border-radius-1)", style={ "_hover": { "background_color": "var(--prismui-background-color-6)", }, }, # 点击事件:退出登录 on_click=lambda: AuthState.logout(), ), cursor="pointer", ), side="left", side_offset=8, position="relative", align="center", padding="4px 12px", border_radius="var(--prismui-border-radius-1)", box_shadow="var(--prismui-box-shadow-3)", font_family="var(--prismui-font-family)", overflow="visible", style={ "font-size": "var(--prismui-font-size-1) !important", "color": "var(--prismui-color-1) !important", }, ), open=AuthState.is_settings_hover_card_open, # 悬停卡片打开状态变化事件:设置设置悬停卡片打开状态 on_open_change=AuthState.set_settings_hover_card_open, ), min_width="14px", cursor="pointer", ) def sidebar() -> rx.Component: """ 侧边栏 :return: rx.Component """ return rx.vstack( rx.vstack( # 渲染系统图标和名称 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", height="56px", padding="0 0 12px 12px", ), # 渲染导航按钮 rx.vstack( # 渲染导航按钮:创建任务 nav_button("create_task"), gap="2px", padding="0 0 16px 0", ), # 渲染最近任务 rx.vstack( rx.text( "最近", line_height="20px", font_size="var(--prismui-font-size-2)", padding="0 0 0 10px", ), task_history(), padding="0 12px", ), align_items="flex-start", gap="2px", width="100%", ), rx.vstack( rx.vstack( # 设置按钮 settings_button(), ), padding_bottom="16px", gap="8px", ), flex_shrink="0", justify_content="space-between", align_items="flex-start", width="264px", height="100%", padding="0 12px", background="var(--prismui-background-1)", ) def workbench() -> rx.Component: """ 工作台 """ return rx.fragment( # 若用户未登录则渲染登录页面 rx.cond( AuthState.user_id, rx.fragment(), login_page(), ), rx.hstack( # 侧边栏 sidebar(), # 默认新建任务页面 rx.match( AuthState.activated_nav_button, # 知识库页面 ("knowledge_base", knowledge_base_page()), # 创建任务页面 ("create_task", create_task_page()), # 会话页面 create_task_page(), ), width="100%", height="100vh", overflow="auto", ), )