This commit is contained in:
liubiren 2026-07-10 00:59:32 +08:00
parent 33fb9d3d34
commit a4dc4ce40e
7 changed files with 135 additions and 106 deletions

View File

@ -23,9 +23,11 @@ app = rx.App(
"--devui-base-bg": "#ffffff", "--devui-base-bg": "#ffffff",
"--mc-global-bg": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)", "--mc-global-bg": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)",
"--mc-box-shadow": "rgba(25, 25, 25, .06)", "--mc-box-shadow": "rgba(25, 25, 25, .06)",
"--devui-shadow-length-connected-overlay": "0px 0px 12px 0px rgba(0, 0, 0, 0.1)",
"--devui-icon-fill-weak": "#babbc0", "--devui-icon-fill-weak": "#babbc0",
"--devui-border-radius-feedback": "4px", "--devui-border-radius-feedback": "4px",
"--devui-border-radius-card": "8px", "--devui-border-radius-card": "8px",
"--devui-border-radius-full": "100px",
"--font-family": "HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei", "--font-family": "HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei",
"--devui-font-size-sm": "12px", "--devui-font-size-sm": "12px",
"--devui-font-size": "14px", "--devui-font-size": "14px",
@ -36,6 +38,8 @@ app = rx.App(
"--placeholder": "#babbc0", "--placeholder": "#babbc0",
"--devui-aide-text": "#71757f", "--devui-aide-text": "#71757f",
"--devui-primary": "#5e7ce0", "--devui-primary": "#5e7ce0",
"--devui-brand": "#5e7ce0",
"--devui-brand-hover": "#7693f5",
} # 自定义主题颜色 } # 自定义主题颜色
) # 此处变量名需使用 app ,具体原因目前尚不清楚 ) # 此处变量名需使用 app ,具体原因目前尚不清楚
# 注册首页路由 # 注册首页路由

View File

@ -4,7 +4,6 @@
""" """
import reflex as rx import reflex as rx
from typing import Tuple
from application.models import Run, Reasoning, Conversation from application.models import Run, Reasoning, Conversation
from application.state.conversation import ConversationState from application.state.conversation import ConversationState
from application.state.create_conversation_modal import CreateConversationModalState from application.state.create_conversation_modal import CreateConversationModalState
@ -31,7 +30,7 @@ def render_conversation_history_item(
return rx.popover.root( return rx.popover.root(
# 触发事件:点击更多按钮 # 触发事件:点击更多按钮
rx.popover.trigger( rx.popover.trigger(
rx.button( rx.box(
rx.icon("ellipsis", size=14, color="var(--devui-text-weak)"), rx.icon("ellipsis", size=14, color="var(--devui-text-weak)"),
cursor="pointer", # 鼠标光标显示为手指 cursor="pointer", # 鼠标光标显示为手指
) )
@ -82,7 +81,7 @@ def render_conversation_history_item(
box_shadow="0 2px 12px rgba(0,0,0,0.1)", # 阴影 box_shadow="0 2px 12px rgba(0,0,0,0.1)", # 阴影
overflow="visible", # 溢出可见 overflow="visible", # 溢出可见
side="bottom", # 气泡卡片位于底部 side="bottom", # 气泡卡片位于底部
side_offset=-2, # 偏移量 side_offset=9, # 偏移量
), ),
open_delay=0, open_delay=0,
) )
@ -274,9 +273,9 @@ def render_run_component(run_id: str, run: Run) -> rx.Component:
) )
def render_conversation_component() -> rx.Component: def render_conversation_box() -> rx.Component:
""" """
渲染对话组件包括若干次运行 渲染对话
:return: Component :return: Component
""" """
# 运行字典 # 运行字典
@ -323,18 +322,45 @@ def render_conversation_component() -> rx.Component:
) )
def render_create_conversation_component() -> rx.Component: def render_create_conversation_button() -> rx.Component:
""" """
渲染创建对话组件 渲染新建对话按钮
:return: Component
""" """
return rx.hstack( return rx.hstack(
rx.spacer(), # 占位符 rx.spacer(), # 占位符
rx.dialog.root( rx.dialog.root(
# 触发事件:点击创建对话图标,支持鼠标悬停提示 # 触发事件:点击图标
rx.dialog.trigger( rx.dialog.trigger(
rx.box(rx.tooltip(rx.icon("circle-plus"), content="创建对话")) rx.box(
rx.tooltip(
rx.icon(
"plus",
size=14,
color="var(--devui-text)",
style={
"_hover": {
"color": "var(--devui-brand)",
}
},
),
content="新建对话",
background_color="var(--devui-base-bg)",
box_shadow="0 2px 12px 0 rgba(37, 43, 58, .24)",
side="top",
side_offset=9,
),
display="flex",
width="24px",
height="24px",
background_color="var(--devui-base-bg)",
box_shadow="0 1px 8px #1919190f",
border_radius="var(--devui-border-radius-full)",
justify_content="center",
align_items="center",
cursor="pointer",
)
), ),
# 内容层
rx.dialog.content( rx.dialog.content(
rx.form( rx.form(
rx.hstack( rx.hstack(
@ -349,6 +375,7 @@ def render_create_conversation_component() -> rx.Component:
wrap="wrap", wrap="wrap",
width="100%", width="100%",
), ),
# 提交事件:新建对话
on_submit=ConversationState.create_conversation, on_submit=ConversationState.create_conversation,
), ),
background_color=rx.color("mauve", 1), background_color=rx.color("mauve", 1),
@ -356,13 +383,20 @@ def render_create_conversation_component() -> rx.Component:
open=CreateConversationModalState.is_open, open=CreateConversationModalState.is_open,
on_open_change=CreateConversationModalState.toggle, on_open_change=CreateConversationModalState.toggle,
), ),
display="flex",
width="100%", width="100%",
max_width="1200px",
height="39px",
padding="0 12px",
justify_content="flex-end",
align_items="center",
gap="4px",
) )
def render_input_bar() -> rx.Component: def render_input_box() -> rx.Component:
""" """
渲染输入栏 渲染输入
""" """
return rx.vstack( return rx.vstack(
# 渲染自定义输入组件 # 渲染自定义输入组件

View File

@ -7,87 +7,78 @@ import reflex as rx
from application.state.frame import FrameState from application.state.frame import FrameState
# 初始化侧边栏图标导航按钮字典
sidebar_icon_nav_buttons = {
"conversation": {"src": "/conversation.svg", "text": "对话"},
"knowledge_base": {"src": "/knowledge_base.svg", "text": "知识库"},
}
def render_brand_logo(width: str = "34px", height: str = "34px") -> rx.Component:
"""
渲染品牌 Logo
:param width: 宽度默认为 34px
:param height: 高度默认为 34px
"""
return rx.image(
src="/logo.png",
width=width,
height=height,
object_fit="contain", # 等比缩放
)
def render_sidebar_icon_nav_button(
sidebar_icon_nav_button: str,
) -> rx.Component:
"""
渲染侧边栏图标导航按钮
:param sidebar_icon_nav_button: 侧边栏图标导航按钮
"""
# 指定侧边栏图标导航按钮的激活状态
is_actived = sidebar_icon_nav_button == FrameState.sidebar_icon_nav_button
return rx.box(
# 渲染图标盒子
rx.box(
rx.image(
src=sidebar_icon_nav_buttons[sidebar_icon_nav_button]["src"],
width="24px",
height="24px",
object_fit="contain", # 等比缩放
),
display="flex", # 弹性布局
justify_content="center", # 水平居中对齐
align_items="center", # 垂直居中对齐
width="36px", # 宽度
height="36px", # 高度
border_radius="var(--devui-border-radius-card)", # 圆角
# 若已激活则渲染图标盒子背景颜色和阴影,否则不渲染
style={
"background-color": rx.cond(
is_actived, "var(--devui-base-bg)", "transparent"
),
"box-shadow": rx.cond(is_actived, "0 4px 12px #0000001a", "none"),
},
),
# 渲染标签
rx.text(
sidebar_icon_nav_buttons[sidebar_icon_nav_button]["text"],
color="var(--devui-text)",
font_size="var(--devui-font-size-sm)",
line_height="20px",
),
display="flex", # 弹性布局
flex_direction="column", # 垂直方向布局
align_items="center", # 垂直居中对齐
gap="4px",
font_size="var(--devui-font-size-sm)", # 字体大小
cursor="pointer", # 鼠标指针
)
def render_sidebar() -> rx.Component: def render_sidebar() -> rx.Component:
""" """
渲染侧边栏 渲染侧边栏
""" """
# 初始化按钮字典
BUTTONS = {
"conversation": {"src": "/conversation.svg", "text": "对话"},
"knowledge_base": {"src": "/knowledge_base.svg", "text": "知识库"},
}
def render_button(
button: str,
) -> rx.Component:
"""
渲染按钮
:param button: 按钮名称
"""
# 指定按钮的激活状态
is_actived = button == FrameState.sidebar_icon_nav_button
return rx.box(
# 渲染图标
rx.box(
rx.image(
src=BUTTONS[button]["src"],
width="24px",
height="24px",
object_fit="contain", # 等比缩放
),
display="flex", # 弹性布局
justify_content="center", # 水平居中对齐
align_items="center", # 垂直居中对齐
width="36px", # 宽度
height="36px", # 高度
border_radius="var(--devui-border-radius-card)", # 圆角
# 若已激活则渲染图标盒子背景颜色和阴影,否则不渲染
style={
"background-color": rx.cond(
is_actived, "var(--devui-base-bg)", "transparent"
),
"box-shadow": rx.cond(is_actived, "0 4px 12px #0000001a", "none"),
},
),
# 渲染标签
rx.text(
BUTTONS[button]["text"],
color="var(--devui-text)",
font_size="var(--devui-font-size-sm)",
line_height="20px",
),
display="flex", # 弹性布局
flex_direction="column", # 垂直方向布局
align_items="center", # 垂直居中对齐
gap="4px",
font_size="var(--devui-font-size-sm)", # 字体大小
cursor="pointer", # 鼠标指针
)
return rx.vstack( return rx.vstack(
# 渲染顶部 # 渲染顶部
rx.vstack( rx.vstack(
# 渲染品牌 LOGO 和名称 # 渲染品牌 LOGO 和名称
rx.vstack( rx.vstack(
# 渲染品牌 Logo # 渲染品牌 Logo
render_brand_logo(), rx.image(
src="/logo.png",
width="34px",
height="34px",
object_fit="contain", # 等比缩放
),
# 渲染品牌名称 # 渲染品牌名称
rx.text( rx.text(
"Gluballu", "Gluballu",
@ -106,10 +97,10 @@ def render_sidebar() -> rx.Component:
rx.divider( rx.divider(
width="32px", height="1px", margin="16px 0", background_color="#babbc0" width="32px", height="1px", margin="16px 0", background_color="#babbc0"
), ),
# 渲染侧边栏图标导航按钮:对话 # 渲染按钮:对话
render_sidebar_icon_nav_button(sidebar_icon_nav_button="conversation"), render_button(button="conversation"),
# 渲染侧边栏图标导航按钮:知识库 # 渲染按钮:知识库
render_sidebar_icon_nav_button(sidebar_icon_nav_button="knowledge_base"), render_button(button="knowledge_base"),
margin_top="12px", # 上外边距 margin_top="12px", # 上外边距
display="flex", # 弹性布局 display="flex", # 弹性布局
flex_direction="column", # 垂直方向布局 flex_direction="column", # 垂直方向布局

View File

@ -5,10 +5,10 @@
import reflex as rx import reflex as rx
from application.components.conversation import ( from application.components.conversation import (
render_conversation_component, render_conversation_box,
render_conversation_history_item, render_conversation_history_item,
render_input_bar, render_input_box,
render_create_conversation_component, render_create_conversation_button,
) )
from application.state.conversation import ConversationState from application.state.conversation import ConversationState
from application.state.conversation_history_collapse import ( from application.state.conversation_history_collapse import (
@ -18,17 +18,17 @@ from application.state.conversation_history_collapse import (
def render_conversation_page() -> rx.Component: def render_conversation_page() -> rx.Component:
""" """
渲染对话页面左侧为对话历史折叠面板右侧为对话历史运行历史建和输入等组件 渲染对话页面左侧为对话历史折叠面板右侧为对话历史运行历史建和输入等组件
""" """
# 对话历史折叠面板打开状态 # 对话历史折叠面板打开状态
is_open = ConversationHistoryCollapseState.is_open is_open = ConversationHistoryCollapseState.is_open
return rx.box( return rx.box(
rx.hstack( rx.hstack(
# 渲染对话历史 # 渲染对话历史折叠面板
rx.box( rx.box(
rx.vstack( rx.vstack(
# 标题 # 渲染标题
rx.hstack( rx.hstack(
rx.text( rx.text(
"对话历史", "对话历史",
@ -42,7 +42,7 @@ def render_conversation_page() -> rx.Component:
justify_content="space-between", justify_content="space-between",
), ),
# 渲染列表 # 渲染列表
rx.vstack( rx.auto_scroll(
rx.foreach( rx.foreach(
ConversationState.get_conversation_history, ConversationState.get_conversation_history,
lambda item, _: render_conversation_history_item( lambda item, _: render_conversation_history_item(
@ -74,12 +74,12 @@ def render_conversation_page() -> rx.Component:
# 渲染对话区域 # 渲染对话区域
rx.box( rx.box(
rx.vstack( rx.vstack(
# 渲染运行历史 # 渲染对话框
render_conversation_component(), render_conversation_box(),
# 渲染建对话按钮 # 渲染建对话按钮
render_create_conversation_component(), render_create_conversation_button(),
# 渲染输入 # 渲染输入
render_input_bar(), render_input_box(),
display="flex", display="flex",
flex_flow="column", flex_flow="column",
width="100%", width="100%",

View File

@ -4,6 +4,7 @@
""" """
import reflex as rx import reflex as rx
def render_knowledge_base_page() -> rx.Component: def render_knowledge_base_page() -> rx.Component:
""" """
渲染知识库页面暂时为空 渲染知识库页面暂时为空

View File

@ -60,6 +60,7 @@ agent: Agent = Agent(
retries=1, retries=1,
) )
class ConversationState(rx.State): class ConversationState(rx.State):
""" """
对话状态 对话状态
@ -81,7 +82,7 @@ class ConversationState(rx.State):
@rx.event @rx.event
async def create_conversation(self, form_data: Dict[str, Any]) -> None: async def create_conversation(self, form_data: Dict[str, Any]) -> None:
""" """
建对话 建对话
:param form_data: 表单数据 :param form_data: 表单数据
:return: None :return: None
""" """
@ -90,16 +91,16 @@ class ConversationState(rx.State):
if not description: if not description:
description = "新对话" description = "新对话"
# 建对话 # 建对话
self.conversations[str(uuid7())] = Conversation(description=description) self.conversations[str(uuid7())] = Conversation(description=description)
# 将末位对话唯一标识设置为当前对话唯一标识 # 将末位对话唯一标识设置为当前对话唯一标识
self.conversation_id = next(reversed(self.conversations.keys())) self.conversation_id = next(reversed(self.conversations.keys()))
# 获取建对话模态窗状态 # 获取建对话模态窗状态
create_conversation_modal_state = await self.get_state( create_conversation_modal_state = await self.get_state(
CreateConversationModalState CreateConversationModalState
) )
# 关闭建对话模态窗 # 关闭建对话模态窗
create_conversation_modal_state.is_open = False create_conversation_modal_state.is_open = False
@rx.event @rx.event
@ -376,4 +377,3 @@ class ConversationState(rx.State):
# 指定运行 # 指定运行
run = self.conversations[self.conversation_id].runs[run_id] run = self.conversations[self.conversation_id].runs[run_id]
run.is_reasoning_panel_open = not run.is_reasoning_panel_open run.is_reasoning_panel_open = not run.is_reasoning_panel_open

View File

@ -1 +0,0 @@