Python/agent/application/pages/index.py

451 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
首页
"""
import reflex as rx
from application.pages import render_conversation, render_library
from application.pages.conversation import render_conversation
from application.states import AuthState
# 导航按钮字典
nav_buttons: dict = {
"conversation": {"src": "/conversation.svg", "text": "会话"},
"library": {"src": "/library.svg", "text": "知识库"},
}
"""
约定:
1 若组件仅用于当前页面则无需抽象为通用组件(application.components.*)
"""
def render_nav_button(
nav_button: str,
) -> rx.Component:
"""
渲染导航按钮
:param nav_button: 指定的导航按钮
:return : Component
"""
# 导航按钮激活状态
is_actived = nav_button == AuthState.activated_nav_button
return rx.vstack(
# 渲染图标
rx.image(
nav_buttons[nav_button]["src"],
width="36px",
height="36px",
object_fit="contain",
padding="6px",
background_color=rx.cond(
is_actived, "var(--prismui-background-color-1)", "transparent"
),
border_radius="var(--prismui-border-radius-1)",
box_shadow=rx.cond(is_actived, "var(--prismui-box-shadow-2)", "none"),
),
# 渲染标签
rx.text(
nav_buttons[nav_button]["text"],
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-1)",
),
align_items="center",
gap="4px",
cursor="pointer",
# 点击事件:将指定导航按钮设置为激活的导航按钮
on_click=AuthState.switch_activated_nav_button(nav_button),
)
def render_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-1)",
cursor="pointer",
style={
"&:hover": {
"background": "var(--prismui-background-color-5)",
}, # 鼠标悬停渲染背景颜色
},
)
),
# 渲染悬停卡片
rx.hover_card.content(
rx.vstack(
rx.box(
transform="translateX(-50%) rotate(45deg)",
position="absolute",
top="16px",
left="0",
width="8px",
height="8px",
background_color="#ffffff",
box_shadow="var(--prismui-box-shadow-3)",
),
rx.box(
"退出登录",
width="100%",
height="24px",
padding="4px",
border_radius="4px",
line_height="16px",
style={
"_hover": {
"background_color": "var(--prismui-color-7, #f2f2f3)",
},
},
# 点击事件:退出登录
on_click=lambda: AuthState.logout(),
),
),
side="left",
side_offset=8,
position="relative",
align="center",
padding="8px",
border_radius="4px",
background_color="#ffffff",
box_shadow="0 2px 12px rgba(0,0,0,0.1)",
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 render_sidebar() -> rx.Component:
"""
渲染侧边栏
:return: Component
"""
return rx.vstack(
rx.vstack(
rx.vstack(
# 品牌图标
rx.image(
src="/logo.png",
width="32px",
height="32px",
object_fit="contain",
),
# 品牌名称
rx.text(
"棱镜球",
line_height="20px",
font_size="var(--prismui-font-size-1)",
font_weight="var(--prismui-font-weight-2)",
color="var(--prismui-color-1)",
),
align_items="center",
gap="4px",
),
# 渲染分隔线
rx.divider(
width="32px",
height="1px",
margin="16px 0",
background_color="var(--prismui-background-color-4)",
),
rx.vstack(
# 渲染会话导航按钮
render_nav_button("conversation"),
# 渲染知识库导航按钮
render_nav_button("library"),
gap="8px",
),
align_items="center",
width="100%",
margin_top="12px",
),
rx.vstack(
rx.vstack(
# 渲染设置按钮
render_settings_button(),
),
padding_bottom="16px",
gap="8px",
),
flex_shrink="0",
justify_content="space-between",
align_items="center",
width="60px",
min_width="60px",
max_width="60px",
height="100%",
)
def render_login_window() -> rx.Component:
"""
渲染登录窗
:return: Component
"""
return rx.center(
rx.hstack(
rx.vstack(
rx.text(
"登录解锁更多功能",
line_height="32px",
font_size="var(--prismui-font-size-3)",
font_weight="var(--prismui-font-weight-3)",
margin_bottom="24px",
),
rx.input(
placeholder="请输入邮箱地址",
value=AuthState.email,
width="100%",
height="45px",
margin_bottom="24px",
padding="4px",
border_radius="var(--prismui-border-radius-2)",
line_height="16px",
# 值变化事件:设置邮箱
on_change=AuthState.set_email,
# 获取焦点事件:重置登录错误信息
on_focus=AuthState.reset_error_message,
),
# 自定义验证码输入框
rx.hstack(
rx.input(
placeholder="请输入验证码",
max_length=6,
value=AuthState.captcha,
# 值变化事件:设置验证码
on_change=AuthState.set_captcha,
# 获取焦点事件:重置登录错误信息
on_focus=AuthState.reset_error_message,
flex=1,
height="100%",
padding="4px",
line_height="16px",
border="none",
style={
"&": {
"border": "none !important",
}, # 清除父元素边框
},
),
rx.button(
# 若验证码发送倒计时大于0则显示倒计时否则显示获取验证码
rx.cond(
AuthState.resend_captcha_countdown,
f"{AuthState.resend_captcha_countdown} s",
"获取验证码",
),
margin="0 8px",
background_color="var(--prismui-background-color-1)",
line_height="16px",
color=rx.cond(
AuthState.is_captcha_sending_disabled,
"var(--prismui-color-4)",
"var(--prismui-color-5)",
),
cursor="pointer",
# 点击事件:发送验证码
on_click=AuthState.send_captcha,
disabled=AuthState.is_captcha_sending_disabled,
),
width="100%",
height="45px",
align_items="center",
border="var(--prismui-border-1)",
border_radius="var(--prismui-border-radius-2)",
),
rx.text(
AuthState.login_error_message,
width="100%",
min_height="20px",
text_align="left",
line_height="20px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-6)",
),
rx.box(
rx.cond(
AuthState.is_captcha_sent,
rx.hstack(
rx.icon(
"circle-check",
width="12px",
height="12px",
color="var(--prismui-color-2)",
),
rx.text(
"验证码已发送,请耐心等待",
line_height="14px",
font_size="var(--prismui-font-size-1)",
color="var(--prismui-color-2)",
),
margin="0 0 10px 0",
gap="4px",
),
),
display="flex",
align_items="center",
min_height="34px",
),
rx.hstack(
rx.checkbox(
checked=AuthState.is_policies_agreed,
# 值变化事件:切换协议同意状态
on_change=AuthState.toggle_policies_agreed,
cursor="pointer",
style={
"&::before": {
"width": "12px !important",
"height": "12px !important",
},
"& svg": {
"width": "8px !important",
"height": "8px !important",
},
"&[data-state='checked']::before": {
"background-color": "var(--prismui-color-5) !important",
},
},
),
rx.text(
"我已阅读并同意",
color="var(--prismui-color-2)",
line_height="14px",
),
rx.link(
"用户协议、隐私政策",
href="#",
line_height="14px",
color="var(--prismui-color-5)",
cursor="pointer",
),
margin_bottom="10px",
align_items="center",
gap="4px",
font_size="var(--prismui-font-size-1)",
),
rx.button(
"登录",
width="100%",
height="38px",
border_radius="var(--prismui-border-radius-2)",
background_color="var(--prismui-color-5)",
color="var(--prismui-color-9)",
cursor="pointer",
on_click=AuthState.login,
loading=AuthState.is_logging_in,
),
rx.text(
"登录遇到问题?加微信去吐槽",
margin="20px 0 40px 0",
line_height="14px",
font_size="12px",
color="var(--prismui-color-5)",
),
width="396px",
height="100%",
padding="40px 48px 0 48px",
align_items="center",
background_color="var(--prismui-background-color-1)",
),
rx.vstack(
rx.image(
src="/qrcode.png",
width="208px",
height="208px",
padding="20px",
background_color="var(--prismui-background-color-1)",
border="var(--prismui-border-2)",
border_radius="var(--prismui-border-radius-3)",
),
rx.text(
"加微信好友申请使用",
margin="10px 0 12px 0",
line_height="32px",
font_size="var(--prismui-font-size-2)",
font_weight="var(--prismui-font-weight-2)",
),
align_items="center",
justify_content="center",
width="396px",
height="100%",
padding="72px 32px",
),
background="var(--prismui-background-2)",
border="1px solid #2222220f",
border_radius="var(--prismui-border-radius-3)",
box_shadow="0 4px 64px 0 #0000001a",
overflow="hidden",
),
position="absolute",
top="0",
left="0",
z_index="9999",
width="100vw",
height="100vh",
backdrop_filter="blur(4px)",
)
def render_index() -> rx.Component:
"""
渲染首页
"""
return rx.fragment(
rx.hstack(
# 渲染侧边栏
render_sidebar(),
# 根据侧边栏状态中激活的导航按钮渲染相应页面(本项目采用卡片布局)
rx.match(
AuthState.activated_nav_button,
# 渲染知识库页面
("library", render_library()),
# 渲染会话页面
render_conversation(),
),
width="100%",
height="100vh",
overflow="auto",
padding="8px 8px 8px 0",
background="var(--prismui-background-1)",
),
# 若用户唯一标识为空则渲染登录窗
rx.cond(
AuthState.user_id,
rx.fragment(),
render_login_window(),
),
)