This commit is contained in:
liubiren 2026-10-19 20:12:52 +08:00
parent 06e6caab26
commit 0b4b02a43d
6 changed files with 334 additions and 228 deletions

View File

@ -6,6 +6,7 @@ import reflex as rx
from application.states import AuthState
def login_page() -> rx.Component:
"""
登录页面
@ -32,12 +33,12 @@ def login_page() -> rx.Component:
on_change=AuthState.set_email,
# 获取焦点事件:重置错误信息
on_focus=AuthState.reset_error,
line_height="16px",
width="100%",
height="45px",
padding="4px",
margin_bottom="24px",
border_radius="var(--prismui-border-radius-4)",
line_height="16px",
),
# 验证码输入框
rx.hstack(
@ -58,30 +59,40 @@ def login_page() -> rx.Component:
border="none !important",
),
rx.button(
# 若再次发送验证码倒计时大于0则显示倒计时,否则显示获取验证码
rx.text(
rx.cond(
AuthState.resend_captcha_code_countdown > 0,
f"{AuthState.resend_captcha_code_countdown} s",
"获取验证码",
),
# 点击事件:发送验证码
on_click=AuthState.send_captcha_code,
# 是否禁用绑定验证码发送禁用状态
disabled=AuthState.is_captcha_code_sending_button_disabled,
margin="0 8px",
background_color="var(--prismui-background-color-1)",
line_height="16px",
white_space="nowrap",
# 若验证码发送禁用状态为是则颜色设置为 color-4,否则设置为 color-5(主题色)
color=rx.cond(
AuthState.is_captcha_code_sending_button_disabled,
"var(--prismui-color-4)",
"var(--prismui-color-5)",
),
padding="0 12px",
),
# 若再次发送验证码倒计时大于0则显示倒计时,否则显示获取验证码
# 点击事件:发送验证码
on_click=AuthState.send_captcha_code,
# 是否禁用绑定验证码发送禁用状态
disabled=AuthState.is_captcha_code_sending_button_disabled,
margin="0 8px",
background_color="var(--prismui-background-color-1)",
border="none",
cursor=rx.cond(
AuthState.is_captcha_code_sending_button_disabled,
"not-allowed",
"pointer",
),
style={
"_hover": {
"background-color": "var(--prismui-background-color-1)"
}
},
),
width="100%",
height="45px",
@ -161,14 +172,19 @@ def login_page() -> rx.Component:
),
rx.button(
"登录",
width="100%",
height="38px",
border_radius="var(--prismui-border-radius-4)",
background_color="var(--prismui-color-5)",
color="var(--prismui-color-9)",
cursor="pointer",
on_click=AuthState.login,
loading=AuthState.is_logging_in,
color="var(--prismui-color-9)",
width="100%",
height="38px",
background_color="var(--prismui-background-color-4)",
border_radius="var(--prismui-border-radius-4)",
cursor="pointer",
style={
"_hover": {
"background-color": "var(--prismui-background-color-5)"
}
},
),
rx.text(
"登录遇到问题?加他微信去吐槽",

View File

@ -8,9 +8,9 @@ from application.states import AuthState, TaskState
from application.states.models import MessageHistoryItem, MessageType, WorkflowType
def workflow_button(workflow_type: WorkflowType):
def shortcut(workflow_type: WorkflowType):
"""
工作流按钮
快捷方式
:param workflow_type: 工作类型
:return: rx.Component
"""
@ -19,17 +19,16 @@ def workflow_button(workflow_type: WorkflowType):
# 点击事件:设置工作流
on_click=lambda: TaskState.set_workflow(workflow_type),
padding="10px 16px",
background_color="var(--prismui-background-color-6)",
background_color="var(--prismui-background-color-9)",
border_radius="var(--prismui-border-radius-9)",
color="var(--prismui-color-10)",
cursor="pointer",
)
def use_guidance() -> rx.Component:
def welcome() -> rx.Component:
"""
使用指引
:return: Component
欢迎
:return: rx.Component
"""
return rx.vstack(
# 品牌图标、名称和介绍
@ -77,13 +76,13 @@ def use_guidance() -> rx.Component:
),
rx.hstack(
# 工作流按钮:预定航班
workflow_button(WorkflowType.BOOK_FLIGHT),
shortcut(WorkflowType.BOOK_FLIGHT),
# 工作流按钮:银行客户服务
workflow_button(WorkflowType.BANK_CUSTOMER_SERVICE),
shortcut(WorkflowType.BANK_CUSTOMER_SERVICE),
# 工作流按钮:联网检索
workflow_button(WorkflowType.WEB_RETRIEVE),
shortcut(WorkflowType.WEB_RETRIEVE),
# 工作流按钮:知识库检索
workflow_button(WorkflowType.KNOWLEDGE_BASE_RETRIEVE),
shortcut(WorkflowType.KNOWLEDGE_BASE_RETRIEVE),
flex_wrap="wrap",
align_items="center",
gap="12px",
@ -194,7 +193,7 @@ def thinking(item: MessageHistoryItem) -> rx.Component:
width="100%",
line_height="1.6",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-3)",
color="var(--prismui-color-2)",
),
rx.fragment(),
),
@ -305,7 +304,7 @@ def tool_call(item: MessageHistoryItem) -> rx.Component:
True, item.id
),
padding="0 12px",
background_color="var(--prismui-background-color-7)",
background_color="var(--prismui-background-color-9)",
border_radius="var(--prismui-border-radius-3)",
line_height="18px",
font_size="var(--prismui-font-size-1)",
@ -368,7 +367,7 @@ def text(item: MessageHistoryItem) -> rx.Component:
"code": lambda text: rx.text(
text,
display="inline",
background_color="var(--prismui-background-color-6)",
background_color="var(--prismui-background-color-9)",
border_radius="var(--prismui-border-radius-1)",
color="var(--prismui-color-5)",
),
@ -415,7 +414,7 @@ def text(item: MessageHistoryItem) -> rx.Component:
width="4.5px",
height="4.5px",
margin_right="8px",
background_color="var(--prismui-background-color-7)",
background_color="var(--prismui-background-color-4)",
border_radius="100%",
),
rx.text(
@ -445,7 +444,7 @@ def text(item: MessageHistoryItem) -> rx.Component:
"th": lambda children: rx.el.th(
children,
padding="8px 12px",
background_color="var(--prismui-background-color-6)",
background_color="var(--prismui-background-color-9)",
border="1px solid var(--prismui-background-color-2)",
text_align="left",
),
@ -487,10 +486,10 @@ def message_history() -> rx.Component:
:return: Component
"""
return rx.vstack(
# 若消息历史为空则显示使用指引,否则渲染消息历史项
# 若消息历史为空则渲染欢迎,否则渲染消息历史项
rx.cond(
TaskState.message_history.length() == 0,
use_guidance(),
welcome(),
rx.vstack(
rx.hstack(
rx.hstack(
@ -518,8 +517,9 @@ def message_history() -> rx.Component:
TaskState.task_last_run_at,
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-3)",
color="var(--prismui-color-2)",
),
# 渲染产品面板按钮
rx.tooltip(
rx.box(
rx.icon(
@ -544,8 +544,7 @@ def message_history() -> rx.Component:
),
content="产物面板",
),
rx.hstack(
# 渲染更多下拉菜单
# 渲染更多按钮
rx.menu.root(
rx.menu.trigger(
rx.box(
@ -588,7 +587,10 @@ def message_history() -> rx.Component:
height="28px",
width="100%",
cursor="pointer",
on_click=TaskState.show_task_delete_confirm,
# 点击事件:显示当前任务删除确认
on_select=TaskState.toggle_task_delete_confirm(
True
).stop_propagation,
style={
"_hover": {
"background_color": "var(--prismui-background-color-9)"
@ -597,16 +599,24 @@ def message_history() -> rx.Component:
),
),
),
# 渲染删除确认弹窗
# 渲染任务删除确认
rx.popover.root(
rx.popover.trigger(rx.box(width="0", height="32px")),
rx.popover.content(
rx.vstack(
rx.text("删除任务?", line_height="20px"),
rx.vstack(
rx.text(
"删除任务?",
line_height="20px",
font_weight="var(--prismui-font-weight-3)",
),
rx.text(
"删除后,任务将不可恢复。",
line_height="20px",
font_size="var(--prismui-font-size-1)",
font_size="var(--prismui-font-size-1) !important",
),
gap="8px",
width="100%",
),
rx.hstack(
rx.spacer(),
@ -614,21 +624,37 @@ def message_history() -> rx.Component:
rx.popover.close(
rx.button(
"取消",
# 点击事件:隐藏当前任务删除确认
on_click=TaskState.hide_task_delete_confirm,
style={
"_hover": {
"background_color": "var(--prismui-background-color-9)"
}
},
)
),
rx.popover.close(rx.button("确认")),
rx.button(
"确认",
color="var(--prismui-color-9)",
background_color="var(--prismui-background-color-6)",
style={
"_hover": {
"background_color": "var(--prismui-background-color-7)"
}
},
on_click=TaskState.delete_task,
),
),
align_items="space-between",
width="100%",
),
gap="24px",
width="100%",
)
),
width="240px",
padding="16px",
),
# 显示绑定:显示当前任务删除确认
open=TaskState.is_task_delete_confirm_shown,
),
on_open_change=TaskState.toggle_task_delete_confirm,
),
align_items="center",
gap="4px",
@ -748,8 +774,8 @@ def user_prompt_sending_box() -> rx.Component:
padding="0 12px",
background_color=rx.cond(
TaskState.is_user_prompt_sending_button_disabled,
"var(--prismui-color-11)",
"var(--prismui-color-5)",
"var(--prismui-background-color-8)",
"var(--prismui-background-color-4)",
),
border="none",
border_radius="var(--prismui-border-radius-5)",
@ -761,6 +787,14 @@ def user_prompt_sending_box() -> rx.Component:
"not-allowed",
"pointer",
),
style={
"_hover": {
"background-color": "var(--prismui-background-color-5)",
},
"&:disabled:hover": {
"background-color": "var(--prismui-background-color-8)",
},
},
),
justify_content="flex-end",
align_items="center",

View File

@ -103,7 +103,7 @@ def task_history_item(
item.last_run_at,
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-3)",
color="var(--prismui-color-2)",
flex_shrink=0, # 禁止缩放
),
align_items="center",
@ -148,91 +148,6 @@ def task_history() -> rx.Component:
)
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",
),
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:
"""
侧边栏
@ -241,6 +156,17 @@ def sidebar() -> 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(
# 系统图标
@ -291,12 +217,128 @@ def sidebar() -> rx.Component:
width="100%",
),
rx.vstack(
rx.vstack(
# 设置按钮
settings_button(),
rx.divider(
width="100%", background_color="var(--prismui-background-color-3)"
),
padding_bottom="16px",
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",

View File

@ -4,7 +4,7 @@
"""
from datetime import date as Date, datetime
from typing import AsyncGenerator
from reflex_base.event import EventSpec
from pydantic_ai import DeferredToolRequests, DeferredToolResults, ToolApproved
from pydantic_ai.messages import (
FunctionToolResultEvent,
@ -51,8 +51,10 @@ class TaskState(rx.State):
# 临时用户提示词
temporary_user_prompt: str = ""
# 显示当前任务删除确认
# 当前任务删除确认显示
is_task_delete_confirm_shown: bool = False
# 上一次任务删除确认显示日期时间
last_task_delete_confirm_show_at: float = 0
# 当前数据库状态(私有变量)
# 私有变量:reflex 约定以 _ 开头的变量为私有变量,后端不序列化,前端不可使用
@ -155,26 +157,26 @@ class TaskState(rx.State):
return f"更新于 {task.last_run_at.strftime("%Y-%m-%d %H:%M:%S")}"
@rx.event
async def show_task_delete_confirm(self) -> None:
async def toggle_task_delete_confirm(self, is_shown: bool) -> None:
"""
显示当前任务删除确认
切换当前任务删除确认
:param is_shown: 显示当前任务删除确认
:return: None
"""
self.is_task_delete_confirm_shown = True
# 当前日期时间
now = datetime.now().timestamp()
# 根据当前时间与上一次任务删除确认显示日期时间间隔防抖
if now - self.last_task_delete_confirm_show_at < 0.2:
return
self.is_task_delete_confirm_shown = is_shown
self.last_task_delete_confirm_show_at = now
@rx.event
async def hide_task_delete_confirm(self) -> None:
async def delete_task(self) -> EventSpec | None:
"""
隐藏当前任务删除确认
:return: None
"""
self.is_task_delete_confirm_shown = False
@rx.event
async def confirm_task_delete(self) -> None:
"""
确认当前任务删除
:return: None
删除当前任务
:return: RX事件
"""
from application.states import SidebarState
@ -195,6 +197,9 @@ class TaskState(rx.State):
sidebar_state.nav_button = ""
# 将空设置为当前任务唯一标识
self.task_id = ""
# 将任务删除确认显示设置为否
self.is_task_delete_confirm_shown = False
return rx.toast.success("删除成功")
@rx.event
def set_task_id(self, task_id: str) -> None:

View File

@ -2,10 +2,10 @@
--prismui-background-color-1: #ffffff;
--prismui-background-color-2: #e6e6e6;
--prismui-background-color-3: rgb(230, 230, 230);
--prismui-background-color-4: #babbc0;
--prismui-background-color-5: #ffffff33;
--prismui-background-color-6: #f2f2f3;
--prismui-background-color-7: #5e7ce0;
--prismui-background-color-4: #5e7ce0;
--prismui-background-color-5: #758ee0;
--prismui-background-color-6: rgb(232, 92, 92);
--prismui-background-color-7: rgb(236, 116, 116);
--prismui-background-color-8: #beccfa;
--prismui-background-color-9: rgb(242, 242, 242);
/*
@ -26,7 +26,7 @@
--prismui-font-weight-4: 700;
--prismui-color-1: #252b3a;
--prismui-color-2: #575d6c;
--prismui-color-3: #babbc0;
--prismui-color-3: rgb(222, 222, 222);
--prismui-color-4: rgba(37, 43, 58, 0.5);
--prismui-color-5: #5e7ce0;
--prismui-color-6: #f66f6a;
@ -147,7 +147,16 @@ pre, pre code {
/* Button */
.rt-Button {
font-family: var(--prismui-font-family) !important;
font-family: var(--prismui-font-family);
font-size: var(--prismui-font-size-2);
color: var(--prismui-color-1);
height: 32px;
width: 68px;
background-color: var(--prismui-background-color-1);
border: 1px solid var(--prismui-color-3);
border-radius: var(--prismui-border-radius-3);
cursor: pointer;
}
.rt-Button:hover {
background-color: inherit;
}

Binary file not shown.