This commit is contained in:
parent
1e4da027ea
commit
a53535b785
|
|
@ -7,12 +7,15 @@ from application.pages.index import render_index
|
||||||
|
|
||||||
app = rx.App(
|
app = rx.App(
|
||||||
theme=rx.theme(),
|
theme=rx.theme(),
|
||||||
|
stylesheets=["/styles.css"],
|
||||||
style={
|
style={
|
||||||
"--prismui-background-color-1": "#ffffff", # 背景颜色:白色
|
"--prismui-background-color-1": "#ffffff", # 背景颜色:白色
|
||||||
"--prismui-background-color-2": "#ebebeb",
|
"--prismui-background-color-2": "#ebebeb",
|
||||||
"--prismui-background-color-3": "#f6f6f8",
|
"--prismui-background-color-3": "#f6f6f8",
|
||||||
"--prismui-background-color-4": "#babbc0",
|
"--prismui-background-color-4": "#babbc0",
|
||||||
|
"--prismui-background-color-5": "#ffffff33",
|
||||||
"--prismui-background-1": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)", # 背景:用于主页面渐变背景
|
"--prismui-background-1": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)", # 背景:用于主页面渐变背景
|
||||||
|
"--prismui-background-2": "linear-gradient(180deg, #fffffff2, #f8fafff2 99%)", # 背景:用于登录窗和工作区渐变背景
|
||||||
"--prismui-font-family": "HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei", # 字体
|
"--prismui-font-family": "HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei", # 字体
|
||||||
"--prismui-font-size-1": "12px",
|
"--prismui-font-size-1": "12px",
|
||||||
"--prismui-font-size-2": "14px", # 字体大小:常用于正文
|
"--prismui-font-size-2": "14px", # 字体大小:常用于正文
|
||||||
|
|
@ -23,13 +26,15 @@ app = rx.App(
|
||||||
"--prismui-color-1": "#252b3a", # 颜色:深灰色,常用于标题和正文颜色
|
"--prismui-color-1": "#252b3a", # 颜色:深灰色,常用于标题和正文颜色
|
||||||
"--prismui-color-2": "#575d6c", # 颜色:中灰色,常用于副标题
|
"--prismui-color-2": "#575d6c", # 颜色:中灰色,常用于副标题
|
||||||
"--prismui-color-3": "#babbc0", # 颜色:浅灰色,常用于占位符
|
"--prismui-color-3": "#babbc0", # 颜色:浅灰色,常用于占位符
|
||||||
"--prismui-color-4": "rgba(6, 10, 38, 0.24)", # 颜色:浅灰色,常用于不可点击元素
|
"--prismui-color-4": "#060a263d", # 颜色:浅灰色,常用于不可点击元素
|
||||||
"--prismui-color-5": "#5e7ce0", # 颜色:蓝色,常用于可点击元素
|
"--prismui-color-5": "#5e7ce0", # 颜色:蓝色,常用于可点击元素
|
||||||
"--prismui-color-6": "#f66f6a", # 颜色:红色,常用于错误提示
|
"--prismui-color-6": "#f66f6a", # 颜色:红色,常用于错误提示
|
||||||
"--prismui-border-1": "1px solid rgba(6, 10, 38, 0.06)", # 边框样式
|
"--prismui-color-7": "#f2f2f3", # 颜色:浅灰色,常用于背景
|
||||||
|
"--prismui-color-9": "#ffffff", # 颜色:白色
|
||||||
|
"--prismui-border-1": "1px solid #060a260f", # 边框样式
|
||||||
|
"--prismui-border-2": "1px solid #2222220f",
|
||||||
"--mc-float-block-shadow": "#d5d5d5",
|
"--mc-float-block-shadow": "#d5d5d5",
|
||||||
"--devui-global-bg": "#f6f6f8",
|
"--devui-global-bg": "#f6f6f8",
|
||||||
"--mc-global-bg": "linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%)",
|
|
||||||
"--mc-box-shadow": "#191919",
|
"--mc-box-shadow": "#191919",
|
||||||
"--devui-shadow-length-connected-overlay": "0px 0px 12px 0px #000000",
|
"--devui-shadow-length-connected-overlay": "0px 0px 12px 0px #000000",
|
||||||
"--prismui-box-shadow-1": "0 4px 64px 0 #0000001a", # 登录卡片阴影
|
"--prismui-box-shadow-1": "0 4px 64px 0 #0000001a", # 登录卡片阴影
|
||||||
|
|
@ -38,11 +43,9 @@ app = rx.App(
|
||||||
"--prismui-background-color-weak": "#babbc0",
|
"--prismui-background-color-weak": "#babbc0",
|
||||||
"--devui-border-radius-feedback": "4px",
|
"--devui-border-radius-feedback": "4px",
|
||||||
"--prismui-border-1": "1px solid rgba(6, 10, 38, 0.06)",
|
"--prismui-border-1": "1px solid rgba(6, 10, 38, 0.06)",
|
||||||
"--prismui-border-radius": "8px",
|
|
||||||
"--prismui-border-radius-1": "8px",
|
"--prismui-border-radius-1": "8px",
|
||||||
"--prismui-border-radius-2": "12px",
|
"--prismui-border-radius-2": "12px",
|
||||||
"--prismui-border-radius-3": "20px",
|
"--prismui-border-radius-3": "20px",
|
||||||
"--devui-border-radius-full": "100px",
|
|
||||||
"box-shadow": "none",
|
"box-shadow": "none",
|
||||||
"background-color": "var(--prismui-background-color-1)", # 默认背景颜色
|
"background-color": "var(--prismui-background-color-1)", # 默认背景颜色
|
||||||
"font_family": "var(--prismui-font-family)",
|
"font_family": "var(--prismui-font-family)",
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ def render_nav_button(
|
||||||
rx.text(
|
rx.text(
|
||||||
nav_buttons[nav_button]["text"],
|
nav_buttons[nav_button]["text"],
|
||||||
line_height="20px",
|
line_height="20px",
|
||||||
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-1)",
|
color="var(--prismui-color-1)",
|
||||||
font_size="var(--prismui-font-size-2)",
|
|
||||||
),
|
),
|
||||||
align_items="center",
|
align_items="center",
|
||||||
gap="4px",
|
gap="4px",
|
||||||
|
|
@ -61,6 +61,90 @@ def render_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:
|
def render_sidebar() -> rx.Component:
|
||||||
"""
|
"""
|
||||||
渲染侧边栏
|
渲染侧边栏
|
||||||
|
|
@ -73,16 +157,16 @@ def render_sidebar() -> rx.Component:
|
||||||
# 品牌图标
|
# 品牌图标
|
||||||
rx.image(
|
rx.image(
|
||||||
src="/logo.png",
|
src="/logo.png",
|
||||||
width="34px",
|
width="32px",
|
||||||
height="34px",
|
height="32px",
|
||||||
object_fit="contain",
|
object_fit="contain",
|
||||||
),
|
),
|
||||||
# 品牌名称
|
# 品牌名称
|
||||||
rx.text(
|
rx.text(
|
||||||
"棱镜球",
|
"棱镜球",
|
||||||
line_height="20px",
|
line_height="20px",
|
||||||
font_size="11px",
|
font_size="var(--prismui-font-size-1)",
|
||||||
font_weight="var(--prismui-font-weight-1)",
|
font_weight="var(--prismui-font-weight-2)",
|
||||||
color="var(--prismui-color-1)",
|
color="var(--prismui-color-1)",
|
||||||
),
|
),
|
||||||
align_items="center",
|
align_items="center",
|
||||||
|
|
@ -106,6 +190,14 @@ def render_sidebar() -> rx.Component:
|
||||||
width="100%",
|
width="100%",
|
||||||
margin_top="12px",
|
margin_top="12px",
|
||||||
),
|
),
|
||||||
|
rx.vstack(
|
||||||
|
rx.vstack(
|
||||||
|
# 渲染设置按钮
|
||||||
|
render_settings_button(),
|
||||||
|
),
|
||||||
|
padding_bottom="16px",
|
||||||
|
gap="8px",
|
||||||
|
),
|
||||||
flex_shrink="0",
|
flex_shrink="0",
|
||||||
justify_content="space-between",
|
justify_content="space-between",
|
||||||
align_items="center",
|
align_items="center",
|
||||||
|
|
@ -129,7 +221,7 @@ def render_login_window() -> rx.Component:
|
||||||
"登录解锁更多功能",
|
"登录解锁更多功能",
|
||||||
line_height="32px",
|
line_height="32px",
|
||||||
font_size="var(--prismui-font-size-3)",
|
font_size="var(--prismui-font-size-3)",
|
||||||
font_weight="var(--prismui-font-weight-2)",
|
font_weight="var(--prismui-font-weight-3)",
|
||||||
margin_bottom="24px",
|
margin_bottom="24px",
|
||||||
),
|
),
|
||||||
rx.input(
|
rx.input(
|
||||||
|
|
@ -143,8 +235,8 @@ def render_login_window() -> rx.Component:
|
||||||
line_height="16px",
|
line_height="16px",
|
||||||
# 值变化事件:设置邮箱
|
# 值变化事件:设置邮箱
|
||||||
on_change=AuthState.set_email,
|
on_change=AuthState.set_email,
|
||||||
# 获取焦点事件:清除提示信息
|
# 获取焦点事件:重置登录错误信息
|
||||||
on_focus=AuthState.reset_notification,
|
on_focus=AuthState.reset_error_message,
|
||||||
),
|
),
|
||||||
# 自定义验证码输入框
|
# 自定义验证码输入框
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
|
|
@ -152,27 +244,30 @@ def render_login_window() -> rx.Component:
|
||||||
placeholder="请输入验证码",
|
placeholder="请输入验证码",
|
||||||
max_length=6,
|
max_length=6,
|
||||||
value=AuthState.captcha,
|
value=AuthState.captcha,
|
||||||
|
# 值变化事件:设置验证码
|
||||||
|
on_change=AuthState.set_captcha,
|
||||||
|
# 获取焦点事件:重置登录错误信息
|
||||||
|
on_focus=AuthState.reset_error_message,
|
||||||
flex=1,
|
flex=1,
|
||||||
height="100%",
|
height="100%",
|
||||||
padding="4px",
|
padding="4px",
|
||||||
line_height="16px",
|
line_height="16px",
|
||||||
border="none",
|
border="none",
|
||||||
# 值变化事件:设置验证码
|
|
||||||
on_change=AuthState.set_captcha,
|
|
||||||
style={
|
style={
|
||||||
"&": {
|
"&": {
|
||||||
"border": "none !important",
|
"border": "none !important",
|
||||||
}, # 清除父元素边框
|
}, # 清除父元素边框
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
rx.text(
|
rx.button(
|
||||||
# 若验证码发送倒计时大于0则显示倒计时,否则显示获取验证码
|
# 若验证码发送倒计时大于0则显示倒计时,否则显示获取验证码
|
||||||
rx.cond(
|
rx.cond(
|
||||||
AuthState.captcha_sending_countdown,
|
AuthState.resend_captcha_countdown,
|
||||||
f"{AuthState.captcha_sending_countdown} s",
|
f"{AuthState.resend_captcha_countdown} s",
|
||||||
"获取验证码",
|
"获取验证码",
|
||||||
),
|
),
|
||||||
margin="0 8px",
|
margin="0 8px",
|
||||||
|
background_color="var(--prismui-background-color-1)",
|
||||||
line_height="16px",
|
line_height="16px",
|
||||||
color=rx.cond(
|
color=rx.cond(
|
||||||
AuthState.is_captcha_sending_disabled,
|
AuthState.is_captcha_sending_disabled,
|
||||||
|
|
@ -180,7 +275,9 @@ def render_login_window() -> rx.Component:
|
||||||
"var(--prismui-color-5)",
|
"var(--prismui-color-5)",
|
||||||
),
|
),
|
||||||
cursor="pointer",
|
cursor="pointer",
|
||||||
|
# 点击事件:发送验证码
|
||||||
on_click=AuthState.send_captcha,
|
on_click=AuthState.send_captcha,
|
||||||
|
disabled=AuthState.is_captcha_sending_disabled,
|
||||||
),
|
),
|
||||||
width="100%",
|
width="100%",
|
||||||
height="45px",
|
height="45px",
|
||||||
|
|
@ -188,84 +285,124 @@ def render_login_window() -> rx.Component:
|
||||||
border="var(--prismui-border-1)",
|
border="var(--prismui-border-1)",
|
||||||
border_radius="var(--prismui-border-radius-2)",
|
border_radius="var(--prismui-border-radius-2)",
|
||||||
),
|
),
|
||||||
rx.cond(
|
rx.text(
|
||||||
AuthState.captcha_sending_countdown,
|
AuthState.login_error_message,
|
||||||
rx.hstack(
|
width="100%",
|
||||||
rx.icon(
|
min_height="20px",
|
||||||
"circle-check",
|
text_align="left",
|
||||||
width="14px",
|
line_height="20px",
|
||||||
height="14px",
|
font_size="var(--prismui-font-size-1)",
|
||||||
color="var(--prismui-color-2)",
|
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",
|
||||||
),
|
),
|
||||||
rx.text(
|
|
||||||
"验证码已发送,请耐心等待",
|
|
||||||
line_height="14px",
|
|
||||||
font_size="var(--prismui-font-size-1)",
|
|
||||||
color="var(--prismui-color-2)",
|
|
||||||
),
|
|
||||||
margin="10px 0",
|
|
||||||
gap="4px",
|
|
||||||
),
|
|
||||||
rx.text(
|
|
||||||
AuthState.notification,
|
|
||||||
width="100%",
|
|
||||||
text_align="left",
|
|
||||||
line_height="20px",
|
|
||||||
font_size="12px",
|
|
||||||
color="var(--prismui-color-6)",
|
|
||||||
),
|
),
|
||||||
|
display="flex",
|
||||||
|
align_items="center",
|
||||||
|
min_height="34px",
|
||||||
),
|
),
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
rx.checkbox(
|
rx.checkbox(
|
||||||
checked=AuthState.is_policies_agreed,
|
checked=AuthState.is_policies_agreed,
|
||||||
|
# 值变化事件:切换协议同意状态
|
||||||
on_change=AuthState.toggle_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("我已阅读并同意 ", font_size="13px"),
|
rx.text(
|
||||||
rx.link("用户协议、隐私政策", font_size="13px", href="#"),
|
"我已阅读并同意",
|
||||||
|
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",
|
align_items="center",
|
||||||
|
gap="4px",
|
||||||
|
font_size="var(--prismui-font-size-1)",
|
||||||
),
|
),
|
||||||
rx.button(
|
rx.button(
|
||||||
"登录",
|
"登录",
|
||||||
width="100%",
|
width="100%",
|
||||||
bg="#0a0e29",
|
height="38px",
|
||||||
color="white",
|
border_radius="var(--prismui-border-radius-2)",
|
||||||
margin_top="12px",
|
background_color="var(--prismui-color-5)",
|
||||||
|
color="var(--prismui-color-9)",
|
||||||
|
cursor="pointer",
|
||||||
on_click=AuthState.login,
|
on_click=AuthState.login,
|
||||||
loading=AuthState.is_logging_in,
|
loading=AuthState.is_logging_in,
|
||||||
disabled=AuthState.is_logging_in,
|
|
||||||
),
|
),
|
||||||
rx.text(
|
rx.text(
|
||||||
"登录有问题?去反馈",
|
"登录遇到问题?加微信去吐槽",
|
||||||
|
margin="20px 0 40px 0",
|
||||||
|
line_height="14px",
|
||||||
font_size="12px",
|
font_size="12px",
|
||||||
color="#666",
|
color="var(--prismui-color-5)",
|
||||||
margin_top="8px",
|
|
||||||
),
|
),
|
||||||
width="396px",
|
width="396px",
|
||||||
|
height="100%",
|
||||||
padding="40px 48px 0 48px",
|
padding="40px 48px 0 48px",
|
||||||
background_color="var(--prismui-background-color-1)",
|
|
||||||
align_items="center",
|
align_items="center",
|
||||||
|
background_color="var(--prismui-background-color-1)",
|
||||||
),
|
),
|
||||||
rx.vstack(
|
rx.vstack(
|
||||||
rx.image(
|
rx.image(
|
||||||
src="/qrcode.png",
|
src="/qrcode.png",
|
||||||
width="160px",
|
width="208px",
|
||||||
height="160px",
|
height="208px",
|
||||||
padding="8px",
|
padding="20px",
|
||||||
border="1px solid #eee",
|
background_color="var(--prismui-background-color-1)",
|
||||||
border_radius="8px",
|
border="var(--prismui-border-2)",
|
||||||
bg="white",
|
border_radius="var(--prismui-border-radius-3)",
|
||||||
),
|
),
|
||||||
rx.text(
|
rx.text(
|
||||||
"添加微信申请使用",
|
"加微信好友申请使用",
|
||||||
font_size="18px",
|
margin="10px 0 12px 0",
|
||||||
font_weight="600",
|
line_height="32px",
|
||||||
margin_top="16px",
|
font_size="var(--prismui-font-size-2)",
|
||||||
|
font_weight="var(--prismui-font-weight-2)",
|
||||||
),
|
),
|
||||||
align_items="center",
|
align_items="center",
|
||||||
width="320px",
|
justify_content="center",
|
||||||
padding="40px 32px",
|
width="396px",
|
||||||
style={"background": "#f8f9fc"},
|
height="100%",
|
||||||
|
padding="72px 32px",
|
||||||
),
|
),
|
||||||
|
background="var(--prismui-background-2)",
|
||||||
border="1px solid #2222220f",
|
border="1px solid #2222220f",
|
||||||
border_radius="var(--prismui-border-radius-3)",
|
border_radius="var(--prismui-border-radius-3)",
|
||||||
box_shadow="0 4px 64px 0 #0000001a",
|
box_shadow="0 4px 64px 0 #0000001a",
|
||||||
|
|
@ -303,13 +440,11 @@ def render_index() -> rx.Component:
|
||||||
overflow="auto",
|
overflow="auto",
|
||||||
padding="8px 8px 8px 0",
|
padding="8px 8px 8px 0",
|
||||||
background="var(--prismui-background-1)",
|
background="var(--prismui-background-1)",
|
||||||
# 挂载事件:
|
|
||||||
on_mount=AuthState.check,
|
|
||||||
),
|
),
|
||||||
# 若用户唯一标识为空则渲染登录窗
|
# 若用户唯一标识为空则渲染登录窗
|
||||||
rx.cond(
|
rx.cond(
|
||||||
AuthState.user_id == "",
|
AuthState.user_id,
|
||||||
render_login_window(),
|
|
||||||
rx.fragment(),
|
rx.fragment(),
|
||||||
|
render_login_window(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -32,15 +32,18 @@ class AuthState(rx.State):
|
||||||
# 验证码
|
# 验证码
|
||||||
captcha: str = ""
|
captcha: str = ""
|
||||||
# 协议同意状态
|
# 协议同意状态
|
||||||
is_policies_agreed: bool = False
|
is_policies_agreed: bool = True
|
||||||
# 提示信息
|
|
||||||
notification: str = ""
|
|
||||||
|
|
||||||
# 验证码发送倒计时
|
|
||||||
captcha_sending_countdown: int = 0
|
|
||||||
|
|
||||||
|
# 登录错误信息
|
||||||
|
login_error_message: str = ""
|
||||||
|
# 再次发送验证码倒计时
|
||||||
|
resend_captcha_countdown: int = 0
|
||||||
|
# 验证码已发送状态
|
||||||
|
is_captcha_sent: bool = False
|
||||||
# 登录中状态
|
# 登录中状态
|
||||||
is_logging_in: bool = False
|
is_logging_in: bool = False
|
||||||
|
# 设置悬停卡片打开状态
|
||||||
|
is_settings_hover_card_open: bool = False
|
||||||
|
|
||||||
# 激活的导航按钮
|
# 激活的导航按钮
|
||||||
activated_nav_button: str = "conversation"
|
activated_nav_button: str = "conversation"
|
||||||
|
|
@ -54,12 +57,12 @@ class AuthState(rx.State):
|
||||||
self.user_id = ""
|
self.user_id = ""
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def reset_notification(self) -> None:
|
def reset_error_message(self) -> None:
|
||||||
"""
|
"""
|
||||||
清除提示信息
|
重置登录错误信息
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.notification = ""
|
self.login_error_message = ""
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def set_email(self, email: str) -> None:
|
def set_email(self, email: str) -> None:
|
||||||
|
|
@ -70,28 +73,17 @@ class AuthState(rx.State):
|
||||||
"""
|
"""
|
||||||
self.email = email.strip()
|
self.email = email.strip()
|
||||||
|
|
||||||
@rx.var
|
|
||||||
def is_captcha_sending_disabled(self) -> bool:
|
|
||||||
"""
|
|
||||||
验证码发送禁用状态
|
|
||||||
:return: 禁用状态,True 表示禁用,False 表示未禁用
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
邮箱为空或验证码发送倒计时大于0时禁用
|
|
||||||
"""
|
|
||||||
return not self.email or self.captcha_sending_countdown > 0
|
|
||||||
|
|
||||||
def validate_email(self) -> bool:
|
def validate_email(self) -> bool:
|
||||||
"""
|
"""
|
||||||
校验邮箱
|
校验邮箱
|
||||||
:return: 校验结果,True 表示校验通过,False 表示校验不通过
|
:return: 校验是否通过,True 表示通过,False 表示不通过
|
||||||
"""
|
"""
|
||||||
# 非空校验
|
# 邮箱非空校验
|
||||||
if not self.email:
|
if not self.email:
|
||||||
self.notification = "请输入邮箱"
|
self.login_error_message = "请输入邮箱"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 格式校验
|
# 邮箱格式校验
|
||||||
"""
|
"""
|
||||||
邮箱由用户名@域名组成,其中:
|
邮箱由用户名@域名组成,其中:
|
||||||
1)用户名首字符为字母或数字,中间可穿插下划线、短横线、加号或点,符号后为字母或数字;
|
1)用户名首字符为字母或数字,中间可穿插下划线、短横线、加号或点,符号后为字母或数字;
|
||||||
|
|
@ -103,34 +95,42 @@ class AuthState(rx.State):
|
||||||
r"^[a-zA-Z0-9]+([_\-+.][a-zA-Z0-9_\-+]+)*@[a-zA-Z0-9]+(\-[a-zA-Z0-9\-]+)*(\.[a-zA-Z0-9]+(\-[a-zA-Z0-9\-]+)*)*\.[a-zA-Z0-9]{2,}$",
|
r"^[a-zA-Z0-9]+([_\-+.][a-zA-Z0-9_\-+]+)*@[a-zA-Z0-9]+(\-[a-zA-Z0-9\-]+)*(\.[a-zA-Z0-9]+(\-[a-zA-Z0-9\-]+)*)*\.[a-zA-Z0-9]{2,}$",
|
||||||
self.email,
|
self.email,
|
||||||
):
|
):
|
||||||
self.notification = "邮箱格式不正确,请重新输入"
|
self.login_error_message = "邮箱格式不正确,请重新输入"
|
||||||
return False
|
return False
|
||||||
self.notification = ""
|
self.login_error_message = ""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@rx.event(background=True)
|
@rx.var
|
||||||
async def resend_captcha_countdown_(self) -> None:
|
def is_captcha_sending_disabled(self) -> bool:
|
||||||
"""
|
"""
|
||||||
再次发送验证码倒计时(后台任务)
|
验证码发送禁用状态
|
||||||
|
邮箱为空或再次发送验证码倒计时大于0时禁用
|
||||||
|
:return: 禁用状态,True 表示禁用,False 表示未禁用
|
||||||
|
"""
|
||||||
|
return not self.email or self.resend_captcha_countdown > 0
|
||||||
|
|
||||||
|
@rx.event(background=True)
|
||||||
|
async def countdown(self) -> None:
|
||||||
|
"""
|
||||||
|
倒计时(后台任务)
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
async with self:
|
async with self:
|
||||||
self.captcha_sending_countdown = 59
|
self.resend_captcha_countdown = 59
|
||||||
|
|
||||||
while self.captcha_sending_countdown > 0:
|
while self.resend_captcha_countdown > 0:
|
||||||
await sleep(1)
|
await sleep(1)
|
||||||
async with self:
|
async with self:
|
||||||
self.captcha_sending_countdown -= 1
|
self.resend_captcha_countdown -= 1
|
||||||
|
|
||||||
@rx.event(background=True)
|
@rx.event(background=True)
|
||||||
async def send_captcha_(self) -> None:
|
async def send_captcha_background(self) -> None:
|
||||||
"""
|
"""
|
||||||
发送验证码(后台任务)
|
发送验证码(后台任务)
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
async with self:
|
async with self:
|
||||||
self.notification = "验证码已发送"
|
# 创建验证码记录并获取验证码
|
||||||
# 创建验证码记录并赋值
|
|
||||||
database_state = await self.get_state(DatabaseState)
|
database_state = await self.get_state(DatabaseState)
|
||||||
captcha = await database_state.create_captchas_record(email=self.email)
|
captcha = await database_state.create_captchas_record(email=self.email)
|
||||||
|
|
||||||
|
|
@ -141,13 +141,13 @@ class AuthState(rx.State):
|
||||||
"utf-8",
|
"utf-8",
|
||||||
)
|
)
|
||||||
message["Subject"] = Header("验证码", "utf-8").encode()
|
message["Subject"] = Header("验证码", "utf-8").encode()
|
||||||
message["From"] = "office@liubiren.cloud"
|
message["From"] = "prism@liubiren.cloud"
|
||||||
message["To"] = self.email
|
message["To"] = self.email
|
||||||
try:
|
try:
|
||||||
async with SMTP(
|
async with SMTP(
|
||||||
hostname="smtp.feishu.cn",
|
hostname="smtp.feishu.cn",
|
||||||
port=465,
|
port=465,
|
||||||
username="office@liubiren.cloud",
|
username="prism@liubiren.cloud",
|
||||||
password="cx1dM5tBcbyWftpS",
|
password="cx1dM5tBcbyWftpS",
|
||||||
use_tls=True,
|
use_tls=True,
|
||||||
local_hostname="localhost",
|
local_hostname="localhost",
|
||||||
|
|
@ -155,9 +155,7 @@ class AuthState(rx.State):
|
||||||
await smtp.send_message(message)
|
await smtp.send_message(message)
|
||||||
|
|
||||||
except SMTPException:
|
except SMTPException:
|
||||||
async with self:
|
pass
|
||||||
self.notification = f"发送失败,请稍后重试"
|
|
||||||
self.captcha_sending_countdown = 0
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def send_captcha(self) -> AsyncGenerator[EventCallback, None]:
|
async def send_captcha(self) -> AsyncGenerator[EventCallback, None]:
|
||||||
|
|
@ -169,9 +167,14 @@ class AuthState(rx.State):
|
||||||
if not self.validate_email():
|
if not self.validate_email():
|
||||||
return
|
return
|
||||||
|
|
||||||
# 将再次发送验证码倒计时和发送验证码事件添加至后台任务队列
|
if self.resend_captcha_countdown > 0:
|
||||||
yield type(self).resend_captcha_countdown_
|
return
|
||||||
yield type(self).send_captcha_
|
|
||||||
|
self.is_captcha_sent = True
|
||||||
|
|
||||||
|
# 将倒计时和发送验证码事件添加至后台任务队列
|
||||||
|
yield type(self).countdown()
|
||||||
|
yield type(self).send_captcha_background()
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def set_captcha(self, captcha: str) -> None:
|
def set_captcha(self, captcha: str) -> None:
|
||||||
|
|
@ -182,22 +185,6 @@ class AuthState(rx.State):
|
||||||
"""
|
"""
|
||||||
self.captcha = captcha.strip()
|
self.captcha = captcha.strip()
|
||||||
|
|
||||||
def validate_captcha(self) -> bool:
|
|
||||||
"""
|
|
||||||
校验验证码
|
|
||||||
:return: 验证是否通过,True 表示校验通过,False 表示校验不通过
|
|
||||||
"""
|
|
||||||
self.notification = ""
|
|
||||||
# 非空校验
|
|
||||||
if not self.captcha:
|
|
||||||
self.notification = "请输入验证码"
|
|
||||||
return False
|
|
||||||
# 格式校验
|
|
||||||
if not re.fullmatch(r"^\d{6}$", self.captcha):
|
|
||||||
self.notification = "验证码格式不正确,请重新输入"
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def toggle_policies_agreed(self) -> None:
|
def toggle_policies_agreed(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -212,31 +199,63 @@ class AuthState(rx.State):
|
||||||
登录
|
登录
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.is_logging_in = True
|
# 验证邮箱
|
||||||
|
|
||||||
# 验证邮箱格式
|
|
||||||
if not self.validate_email():
|
if not self.validate_email():
|
||||||
return
|
return
|
||||||
|
|
||||||
# 验证验证码
|
# 验证码非空校验
|
||||||
if not self.validate_captcha():
|
if not self.captcha:
|
||||||
|
self.login_error_message = "请输入验证码"
|
||||||
return
|
return
|
||||||
|
|
||||||
# 创建验证码记录并赋值验证码
|
# 验证码格式校验
|
||||||
database_state = await self.get_state(DatabaseState)
|
if not re.fullmatch(r"^\d{6}$", self.captcha):
|
||||||
|
self.login_error_message = "验证码格式不正确,请重新输入"
|
||||||
|
return
|
||||||
|
|
||||||
|
# 校验协议同意状态
|
||||||
|
if not self.is_policies_agreed:
|
||||||
|
self.login_error_message = "请先阅读并同意协议和政策"
|
||||||
|
return
|
||||||
|
|
||||||
|
self.is_logging_in = True
|
||||||
|
|
||||||
|
# 更新验证码记录,若更新成功则创建用户记录
|
||||||
|
database_state = await self.get_state(DatabaseState)
|
||||||
if not await database_state.update_captcha(
|
if not await database_state.update_captcha(
|
||||||
email=self.email, captcha=self.captcha
|
email=self.email, captcha=self.captcha
|
||||||
):
|
):
|
||||||
self.notification = "验证码错误"
|
self.is_logging_in = False
|
||||||
|
self.login_error_message = "验证码错误"
|
||||||
return
|
return
|
||||||
|
self.user_id = await database_state.create_users_record(email=self.email)
|
||||||
|
|
||||||
# 创建用户记录
|
self.email = ""
|
||||||
user_id = await database_state.create_users_record(email=self.email)
|
self.captcha = ""
|
||||||
self.user_id = user_id
|
self.is_policies_agreed = True
|
||||||
self.notification = "登录成功"
|
self.login_error_message = ""
|
||||||
|
self.resend_captcha_countdown = 0
|
||||||
|
self.is_captcha_sent = False
|
||||||
self.is_logging_in = False
|
self.is_logging_in = False
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
def set_settings_hover_card_open(self, is_settings_hover_card_open: bool) -> None:
|
||||||
|
"""
|
||||||
|
设置设置悬停卡片打开状态
|
||||||
|
:param is_settings_hover_card_open: 设置悬停卡片打开状态,True 表示打开,False 表示关闭
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.is_settings_hover_card_open = is_settings_hover_card_open
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
def logout(self) -> None:
|
||||||
|
"""
|
||||||
|
退出登录
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.is_settings_hover_card_open = False
|
||||||
|
self.user_id = ""
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def switch_activated_nav_button(self, button: str):
|
def switch_activated_nav_button(self, button: str):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,16 @@ class ConversationState(rx.State):
|
||||||
# 正在新建会话状态,True表示正在新建,False表示未正在新建
|
# 正在新建会话状态,True表示正在新建,False表示未正在新建
|
||||||
is_creating_conversation: bool = False
|
is_creating_conversation: bool = False
|
||||||
|
|
||||||
|
@rx.event
|
||||||
|
def load_conversation_id(self, conversation_id: str) -> None:
|
||||||
|
"""
|
||||||
|
设置当前会话唯一标识
|
||||||
|
:param conversation_id: 当前会话唯一标识
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.conversation_id = conversation_id
|
||||||
|
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
async def on_mount(self):
|
async def on_mount(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class DatabaseState(rx.State):
|
||||||
|
|
||||||
async def update_captcha(self, email: str, captcha: str) -> bool:
|
async def update_captcha(self, email: str, captcha: str) -> bool:
|
||||||
"""
|
"""
|
||||||
更新验证码
|
更新验证码记录
|
||||||
:param email: 邮箱
|
:param email: 邮箱
|
||||||
:param captcha: 验证码
|
:param captcha: 验证码
|
||||||
:return: 是否更新成功,True 表示更新成功,False 表示更新失败
|
:return: 是否更新成功,True 表示更新成功,False 表示更新失败
|
||||||
|
|
@ -99,6 +99,7 @@ class DatabaseState(rx.State):
|
||||||
async with rx.asession() as session:
|
async with rx.asession() as session:
|
||||||
result = await session.exec(select(Users).where(Users.email == email))
|
result = await session.exec(select(Users).where(Users.email == email))
|
||||||
record = result.first()
|
record = result.first()
|
||||||
|
# 若用户记录存在则返回用户唯一标识,否则创建新用户记录并返回所创建的用户唯一标识
|
||||||
if record:
|
if record:
|
||||||
return record.id
|
return record.id
|
||||||
new_user = Users(email=email)
|
new_user = Users(email=email)
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
|
|
@ -0,0 +1,38 @@
|
||||||
|
:root {
|
||||||
|
--prismui-background-color-1: #ffffff;
|
||||||
|
--prismui-background-color-2: #ebebeb;
|
||||||
|
--prismui-background-color-3: #f6f6f8;
|
||||||
|
--prismui-background-color-4: #babbc0;
|
||||||
|
--prismui-background-color-5: #ffffff33;
|
||||||
|
--prismui-background-1: linear-gradient(to bottom, #D0C9FF 0%, #E6D6F0 8%, #F1DBEA 12%, #C8DCFB 40%, #ABC6F6 60%, #87AEFE 90%);
|
||||||
|
--prismui-background-2: linear-gradient(180deg, #fffffff2, #f8fafff2 99%);
|
||||||
|
--prismui-font-family: HuaweiFont,Helvetica,Arial,PingFangSC-Regular,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Microsoft JhengHei;
|
||||||
|
--prismui-font-size-1: 12px;
|
||||||
|
--prismui-font-size-2: 14px;
|
||||||
|
--prismui-font-size-3: 20px;
|
||||||
|
--prismui-font-weight-1: normal;
|
||||||
|
--prismui-font-weight-2: 500;
|
||||||
|
--prismui-font-weight-3: 700;
|
||||||
|
--prismui-color-1: #252b3a;
|
||||||
|
--prismui-color-2: #575d6c;
|
||||||
|
--prismui-color-3: #babbc0;
|
||||||
|
--prismui-color-4: #060a263d;
|
||||||
|
--prismui-color-5: #5e7ce0;
|
||||||
|
--prismui-color-6: #f66f6a;
|
||||||
|
--prismui-color-7: #f2f2f3;
|
||||||
|
--prismui-color-9: #ffffff;
|
||||||
|
--prismui-border-1: 1px solid rgba(6, 10, 38, 0.06);
|
||||||
|
--prismui-border-radius-1: 8px;
|
||||||
|
--prismui-border-radius-2: 12px;
|
||||||
|
--prismui-border-radius-3: 20px;
|
||||||
|
--mc-float-block-shadow: #d5d5d5;
|
||||||
|
--devui-global-bg: #f6f6f8;
|
||||||
|
--mc-box-shadow: #191919;
|
||||||
|
--devui-shadow-length-connected-overlay: 0px 0px 12px 0px #000000;
|
||||||
|
--prismui-box-shadow-1: 0 4px 64px 0 #0000001a;
|
||||||
|
--prismui-box-shadow-2: 0 4px 12px #0000001a;
|
||||||
|
--prismui-box-shadow-3: 0 2px 12px 0 #252b3a3d;
|
||||||
|
--devui-icon-fill-weak: #babbc0;
|
||||||
|
--prismui-background-color-weak: #babbc0;
|
||||||
|
--devui-border-radius-feedback: 4px;
|
||||||
|
}
|
||||||
Binary file not shown.
Loading…
Reference in New Issue