audex 1.0.7a3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audex/__init__.py +9 -0
- audex/__main__.py +7 -0
- audex/cli/__init__.py +189 -0
- audex/cli/apis/__init__.py +12 -0
- audex/cli/apis/init/__init__.py +34 -0
- audex/cli/apis/init/gencfg.py +130 -0
- audex/cli/apis/init/setup.py +330 -0
- audex/cli/apis/init/vprgroup.py +125 -0
- audex/cli/apis/serve.py +141 -0
- audex/cli/args.py +356 -0
- audex/cli/exceptions.py +44 -0
- audex/cli/helper/__init__.py +0 -0
- audex/cli/helper/ansi.py +193 -0
- audex/cli/helper/display.py +288 -0
- audex/config/__init__.py +64 -0
- audex/config/core/__init__.py +30 -0
- audex/config/core/app.py +29 -0
- audex/config/core/audio.py +45 -0
- audex/config/core/logging.py +163 -0
- audex/config/core/session.py +11 -0
- audex/config/helper/__init__.py +1 -0
- audex/config/helper/client/__init__.py +1 -0
- audex/config/helper/client/http.py +28 -0
- audex/config/helper/client/websocket.py +21 -0
- audex/config/helper/provider/__init__.py +1 -0
- audex/config/helper/provider/dashscope.py +13 -0
- audex/config/helper/provider/unisound.py +18 -0
- audex/config/helper/provider/xfyun.py +23 -0
- audex/config/infrastructure/__init__.py +31 -0
- audex/config/infrastructure/cache.py +51 -0
- audex/config/infrastructure/database.py +48 -0
- audex/config/infrastructure/recorder.py +32 -0
- audex/config/infrastructure/store.py +19 -0
- audex/config/provider/__init__.py +18 -0
- audex/config/provider/transcription.py +109 -0
- audex/config/provider/vpr.py +99 -0
- audex/container.py +40 -0
- audex/entity/__init__.py +468 -0
- audex/entity/doctor.py +109 -0
- audex/entity/doctor.pyi +51 -0
- audex/entity/fields.py +401 -0
- audex/entity/segment.py +115 -0
- audex/entity/segment.pyi +38 -0
- audex/entity/session.py +133 -0
- audex/entity/session.pyi +47 -0
- audex/entity/utterance.py +142 -0
- audex/entity/utterance.pyi +48 -0
- audex/entity/vp.py +68 -0
- audex/entity/vp.pyi +35 -0
- audex/exceptions.py +157 -0
- audex/filters/__init__.py +692 -0
- audex/filters/generated/__init__.py +21 -0
- audex/filters/generated/doctor.py +987 -0
- audex/filters/generated/segment.py +723 -0
- audex/filters/generated/session.py +978 -0
- audex/filters/generated/utterance.py +939 -0
- audex/filters/generated/vp.py +815 -0
- audex/helper/__init__.py +1 -0
- audex/helper/hash.py +33 -0
- audex/helper/mixin.py +65 -0
- audex/helper/net.py +19 -0
- audex/helper/settings/__init__.py +830 -0
- audex/helper/settings/fields.py +317 -0
- audex/helper/stream.py +153 -0
- audex/injectors/__init__.py +1 -0
- audex/injectors/config.py +12 -0
- audex/injectors/lifespan.py +7 -0
- audex/lib/__init__.py +1 -0
- audex/lib/cache/__init__.py +383 -0
- audex/lib/cache/inmemory.py +513 -0
- audex/lib/database/__init__.py +83 -0
- audex/lib/database/sqlite.py +406 -0
- audex/lib/exporter.py +189 -0
- audex/lib/injectors/__init__.py +1 -0
- audex/lib/injectors/cache.py +25 -0
- audex/lib/injectors/container.py +47 -0
- audex/lib/injectors/exporter.py +26 -0
- audex/lib/injectors/recorder.py +33 -0
- audex/lib/injectors/server.py +17 -0
- audex/lib/injectors/session.py +18 -0
- audex/lib/injectors/sqlite.py +24 -0
- audex/lib/injectors/store.py +13 -0
- audex/lib/injectors/transcription.py +42 -0
- audex/lib/injectors/usb.py +12 -0
- audex/lib/injectors/vpr.py +65 -0
- audex/lib/injectors/wifi.py +7 -0
- audex/lib/recorder.py +844 -0
- audex/lib/repos/__init__.py +149 -0
- audex/lib/repos/container.py +23 -0
- audex/lib/repos/database/__init__.py +1 -0
- audex/lib/repos/database/sqlite.py +672 -0
- audex/lib/repos/decorators.py +74 -0
- audex/lib/repos/doctor.py +286 -0
- audex/lib/repos/segment.py +302 -0
- audex/lib/repos/session.py +285 -0
- audex/lib/repos/tables/__init__.py +70 -0
- audex/lib/repos/tables/doctor.py +137 -0
- audex/lib/repos/tables/segment.py +113 -0
- audex/lib/repos/tables/session.py +140 -0
- audex/lib/repos/tables/utterance.py +131 -0
- audex/lib/repos/tables/vp.py +102 -0
- audex/lib/repos/utterance.py +288 -0
- audex/lib/repos/vp.py +286 -0
- audex/lib/restful.py +251 -0
- audex/lib/server/__init__.py +97 -0
- audex/lib/server/auth.py +98 -0
- audex/lib/server/handlers.py +248 -0
- audex/lib/server/templates/index.html.j2 +226 -0
- audex/lib/server/templates/login.html.j2 +111 -0
- audex/lib/server/templates/static/script.js +68 -0
- audex/lib/server/templates/static/style.css +579 -0
- audex/lib/server/types.py +123 -0
- audex/lib/session.py +503 -0
- audex/lib/store/__init__.py +238 -0
- audex/lib/store/localfile.py +411 -0
- audex/lib/transcription/__init__.py +33 -0
- audex/lib/transcription/dashscope.py +525 -0
- audex/lib/transcription/events.py +62 -0
- audex/lib/usb.py +554 -0
- audex/lib/vpr/__init__.py +38 -0
- audex/lib/vpr/unisound/__init__.py +185 -0
- audex/lib/vpr/unisound/types.py +469 -0
- audex/lib/vpr/xfyun/__init__.py +483 -0
- audex/lib/vpr/xfyun/types.py +679 -0
- audex/lib/websocket/__init__.py +8 -0
- audex/lib/websocket/connection.py +485 -0
- audex/lib/websocket/pool.py +991 -0
- audex/lib/wifi.py +1146 -0
- audex/lifespan.py +75 -0
- audex/service/__init__.py +27 -0
- audex/service/decorators.py +73 -0
- audex/service/doctor/__init__.py +652 -0
- audex/service/doctor/const.py +36 -0
- audex/service/doctor/exceptions.py +96 -0
- audex/service/doctor/types.py +54 -0
- audex/service/export/__init__.py +236 -0
- audex/service/export/const.py +17 -0
- audex/service/export/exceptions.py +34 -0
- audex/service/export/types.py +21 -0
- audex/service/injectors/__init__.py +1 -0
- audex/service/injectors/container.py +53 -0
- audex/service/injectors/doctor.py +34 -0
- audex/service/injectors/export.py +27 -0
- audex/service/injectors/session.py +49 -0
- audex/service/session/__init__.py +754 -0
- audex/service/session/const.py +34 -0
- audex/service/session/exceptions.py +67 -0
- audex/service/session/types.py +91 -0
- audex/types.py +39 -0
- audex/utils.py +287 -0
- audex/valueobj/__init__.py +81 -0
- audex/valueobj/common/__init__.py +1 -0
- audex/valueobj/common/auth.py +84 -0
- audex/valueobj/common/email.py +16 -0
- audex/valueobj/common/ops.py +22 -0
- audex/valueobj/common/phone.py +84 -0
- audex/valueobj/common/version.py +72 -0
- audex/valueobj/session.py +19 -0
- audex/valueobj/utterance.py +15 -0
- audex/view/__init__.py +51 -0
- audex/view/container.py +17 -0
- audex/view/decorators.py +303 -0
- audex/view/pages/__init__.py +1 -0
- audex/view/pages/dashboard/__init__.py +286 -0
- audex/view/pages/dashboard/wifi.py +407 -0
- audex/view/pages/login.py +110 -0
- audex/view/pages/recording.py +348 -0
- audex/view/pages/register.py +202 -0
- audex/view/pages/sessions/__init__.py +196 -0
- audex/view/pages/sessions/details.py +224 -0
- audex/view/pages/sessions/export.py +443 -0
- audex/view/pages/settings.py +374 -0
- audex/view/pages/voiceprint/__init__.py +1 -0
- audex/view/pages/voiceprint/enroll.py +195 -0
- audex/view/pages/voiceprint/update.py +195 -0
- audex/view/static/css/dashboard.css +452 -0
- audex/view/static/css/glass.css +22 -0
- audex/view/static/css/global.css +541 -0
- audex/view/static/css/login.css +386 -0
- audex/view/static/css/recording.css +439 -0
- audex/view/static/css/register.css +293 -0
- audex/view/static/css/sessions/styles.css +501 -0
- audex/view/static/css/settings.css +186 -0
- audex/view/static/css/voiceprint/enroll.css +43 -0
- audex/view/static/css/voiceprint/styles.css +209 -0
- audex/view/static/css/voiceprint/update.css +44 -0
- audex/view/static/images/logo.svg +95 -0
- audex/view/static/js/recording.js +42 -0
- audex-1.0.7a3.dist-info/METADATA +361 -0
- audex-1.0.7a3.dist-info/RECORD +192 -0
- audex-1.0.7a3.dist-info/WHEEL +4 -0
- audex-1.0.7a3.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
from dependency_injector.wiring import Provide
|
|
6
|
+
from dependency_injector.wiring import inject
|
|
7
|
+
from fastapi import Depends
|
|
8
|
+
from nicegui import ui
|
|
9
|
+
|
|
10
|
+
from audex.config import Config
|
|
11
|
+
from audex.container import Container
|
|
12
|
+
from audex.lib.wifi import WiFiManager
|
|
13
|
+
from audex.service.doctor import DoctorService
|
|
14
|
+
from audex.service.session import SessionService
|
|
15
|
+
from audex.view.decorators import handle_errors
|
|
16
|
+
from audex.view.pages.dashboard.wifi import WiFiIndicator
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@ui.page("/")
|
|
20
|
+
@handle_errors
|
|
21
|
+
@inject
|
|
22
|
+
async def render(
|
|
23
|
+
doctor_service: DoctorService = Depends(Provide[Container.service.doctor]),
|
|
24
|
+
session_service: SessionService = Depends(Provide[Container.service.session]),
|
|
25
|
+
wifi_manager: WiFiManager = Depends(Provide[Container.infrastructure.wifi]),
|
|
26
|
+
config: Config = Depends(Provide[Container.config]),
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Render dashboard page with clean Apple-inspired design."""
|
|
29
|
+
|
|
30
|
+
# Get current doctor
|
|
31
|
+
doctor = await doctor_service.current_doctor()
|
|
32
|
+
has_vp = await doctor_service.has_voiceprint()
|
|
33
|
+
|
|
34
|
+
# Add CSS
|
|
35
|
+
ui.add_head_html('<link rel="stylesheet" href="/static/css/dashboard.css">')
|
|
36
|
+
|
|
37
|
+
# Header
|
|
38
|
+
with ui.header().classes("header-glass items-center justify-between px-6 py-3"):
|
|
39
|
+
with ui.row().classes("items-center gap-3"):
|
|
40
|
+
ui.image("/static/images/logo.svg").classes("w-8 h-8")
|
|
41
|
+
ui.label(config.core.app.app_name).classes("text-h6 font-semibold text-grey-9")
|
|
42
|
+
|
|
43
|
+
with ui.row().classes("items-center gap-4"):
|
|
44
|
+
# WiFi indicator
|
|
45
|
+
wifi_indicator = WiFiIndicator(wifi_manager)
|
|
46
|
+
wifi_indicator.render()
|
|
47
|
+
|
|
48
|
+
# Doctor info
|
|
49
|
+
with (
|
|
50
|
+
ui.card()
|
|
51
|
+
.classes("px-4 py-2")
|
|
52
|
+
.style(
|
|
53
|
+
"border-radius: 16px; box-shadow: none; background: rgba(248, 249, 250, 0.6); backdrop-filter: blur(20px);"
|
|
54
|
+
)
|
|
55
|
+
):
|
|
56
|
+
ui.label(f"{doctor.name}").classes("text-sm font-medium text-grey-9")
|
|
57
|
+
|
|
58
|
+
# Screen mode toggle button
|
|
59
|
+
is_fullscreen_state = {"value": True}
|
|
60
|
+
|
|
61
|
+
if config.core.app.native:
|
|
62
|
+
from nicegui.native import WindowProxy
|
|
63
|
+
|
|
64
|
+
# Native mode - use pywebview API
|
|
65
|
+
window = WindowProxy()
|
|
66
|
+
fullscreen_btn = (
|
|
67
|
+
ui.button(icon="fullscreen", on_click=None)
|
|
68
|
+
.props("flat round size=md")
|
|
69
|
+
.classes("press-button")
|
|
70
|
+
.tooltip("切换全屏")
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def toggle_fullscreen() -> None:
|
|
74
|
+
window.toggle_fullscreen()
|
|
75
|
+
is_fullscreen_state["value"] = not is_fullscreen_state["value"]
|
|
76
|
+
fullscreen_btn.props(
|
|
77
|
+
f"icon={'fullscreen_exit' if not is_fullscreen_state['value'] else 'fullscreen'}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
fullscreen_btn.on("click", toggle_fullscreen)
|
|
81
|
+
|
|
82
|
+
# Logout button
|
|
83
|
+
@handle_errors
|
|
84
|
+
async def do_logout() -> None:
|
|
85
|
+
await doctor_service.logout()
|
|
86
|
+
ui.notify("已退出登录", type="info")
|
|
87
|
+
ui.navigate.to("/login")
|
|
88
|
+
|
|
89
|
+
ui.button(icon="logout", on_click=do_logout).props("flat round size=md").classes(
|
|
90
|
+
"press-button"
|
|
91
|
+
).tooltip("退出登录")
|
|
92
|
+
|
|
93
|
+
# Main content
|
|
94
|
+
with (
|
|
95
|
+
ui.element("div")
|
|
96
|
+
.classes("w-full bg-white")
|
|
97
|
+
.style(
|
|
98
|
+
"position: fixed; "
|
|
99
|
+
"top: 0; "
|
|
100
|
+
"left: 0; "
|
|
101
|
+
"right: 0; "
|
|
102
|
+
"bottom: 0; "
|
|
103
|
+
"display: flex; "
|
|
104
|
+
"align-items: center; "
|
|
105
|
+
"justify-content: center; "
|
|
106
|
+
"gap: 30px; "
|
|
107
|
+
"padding: 40px clamp(20px, 5vw, 80px);"
|
|
108
|
+
"box-sizing: border-box; "
|
|
109
|
+
"overflow: auto;"
|
|
110
|
+
),
|
|
111
|
+
):
|
|
112
|
+
# Left column
|
|
113
|
+
with ui.column().classes("gap-8").style("min-width: 100px; max-width: 360px; width: 100%;"):
|
|
114
|
+
# Welcome
|
|
115
|
+
with ui.column().classes("gap-2 mb-6"):
|
|
116
|
+
candidate_words = ["Hi,", "Hello,", "您好,", ":)", "欢迎回来,", "很高兴见到您,"]
|
|
117
|
+
ui.label(random.choice(candidate_words)).classes("text-h3 font-bold text-grey-9")
|
|
118
|
+
ui.label(doctor.name).classes("text-h2 gradient-text").style("line-height: 1.2;")
|
|
119
|
+
|
|
120
|
+
info_parts = []
|
|
121
|
+
if doctor.title:
|
|
122
|
+
info_parts.append(doctor.title)
|
|
123
|
+
if doctor.department:
|
|
124
|
+
info_parts.append(doctor.department)
|
|
125
|
+
if doctor.hospital:
|
|
126
|
+
info_parts.append(doctor.hospital)
|
|
127
|
+
|
|
128
|
+
if info_parts:
|
|
129
|
+
ui.label(" · ".join(info_parts)).classes("text-body2 text-grey-6 mt-3")
|
|
130
|
+
|
|
131
|
+
# Overview
|
|
132
|
+
overview = await session_service.stats()
|
|
133
|
+
with ui.card().classes("glass-card p-5 w-full").style("margin-top: 20px; width: 100%;"):
|
|
134
|
+
ui.label("概览").classes("text-subtitle2 font-semibold mb-4 text-grey-8")
|
|
135
|
+
|
|
136
|
+
with ui.column().classes("gap-3 w-full"):
|
|
137
|
+
with ui.row().classes("items-center justify-between w-full"):
|
|
138
|
+
ui.label("本月会话").classes("text-xs text-grey-7")
|
|
139
|
+
ui.label(str(overview.get("sessions_count_in_this_month", 0))).classes(
|
|
140
|
+
"text-body1 font-bold text-primary"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
with ui.row().classes("items-center justify-between w-full"):
|
|
144
|
+
ui.label("总会话数").classes("text-xs text-grey-7")
|
|
145
|
+
ui.label(str(overview.get("total_sessions_count", 0))).classes(
|
|
146
|
+
"text-body1 font-bold text-secondary"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
with ui.row().classes("items-center justify-between w-full"):
|
|
150
|
+
ui.label("录音时长").classes("text-xs text-grey-7")
|
|
151
|
+
ui.label(
|
|
152
|
+
f"{(overview.get('total_duration_in_minutes', 0) / 60.0):.2f}h"
|
|
153
|
+
).classes("text-body1 font-bold text-positive")
|
|
154
|
+
|
|
155
|
+
# Right column - 2x2 grid
|
|
156
|
+
with ui.element("div").style(
|
|
157
|
+
"flex: 1; "
|
|
158
|
+
"display: grid; "
|
|
159
|
+
"grid-template-columns: repeat(2, 1fr); "
|
|
160
|
+
"gap: 15px; "
|
|
161
|
+
"max-width: 850px; "
|
|
162
|
+
"margin-left: auto;"
|
|
163
|
+
):
|
|
164
|
+
# Card 1: Start new session
|
|
165
|
+
with (
|
|
166
|
+
ui.card()
|
|
167
|
+
.classes("super-card cursor-pointer")
|
|
168
|
+
.on("click", lambda: ui.navigate.to("/recording"))
|
|
169
|
+
.style(
|
|
170
|
+
"height: 220px; "
|
|
171
|
+
"display: flex; "
|
|
172
|
+
"flex-direction: column; "
|
|
173
|
+
"padding: 1.5rem; "
|
|
174
|
+
"box-sizing: border-box;"
|
|
175
|
+
)
|
|
176
|
+
):
|
|
177
|
+
ui.icon("mic", size="3em").classes("text-primary rotate-icon").style(
|
|
178
|
+
"flex-shrink: 0; margin-bottom: 0.75rem;"
|
|
179
|
+
)
|
|
180
|
+
with ui.column().classes("gap-2").style("flex: 1;"):
|
|
181
|
+
ui.label("开始新会话").classes("text-h6 font-bold text-grey-9")
|
|
182
|
+
ui.label("创建新的门诊录音会话").classes("text-sm text-grey-7")
|
|
183
|
+
ui.button("开始", icon="arrow_forward").props("color=primary flat dense").classes(
|
|
184
|
+
"press-button"
|
|
185
|
+
).style(
|
|
186
|
+
"align-self: flex-end; "
|
|
187
|
+
"flex-shrink: 0; "
|
|
188
|
+
"background: transparent !important; "
|
|
189
|
+
"box-shadow: none !important;"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Card 2: Session history
|
|
193
|
+
with (
|
|
194
|
+
ui.card()
|
|
195
|
+
.classes("super-card cursor-pointer")
|
|
196
|
+
.on("click", lambda: ui.navigate.to("/sessions"))
|
|
197
|
+
.style(
|
|
198
|
+
"height: 220px; "
|
|
199
|
+
"display: flex; "
|
|
200
|
+
"flex-direction: column; "
|
|
201
|
+
"padding: 1.5rem; "
|
|
202
|
+
"box-sizing: border-box;"
|
|
203
|
+
)
|
|
204
|
+
):
|
|
205
|
+
ui.icon("history", size="3em").classes("text-secondary rotate-icon").style(
|
|
206
|
+
"flex-shrink: 0; margin-bottom: 0.75rem;"
|
|
207
|
+
)
|
|
208
|
+
with ui.column().classes("gap-2").style("flex: 1;"):
|
|
209
|
+
ui.label("历史会话").classes("text-h6 font-bold text-grey-9")
|
|
210
|
+
ui.label("查看和管理历史录音").classes("text-sm text-grey-7")
|
|
211
|
+
ui.button("查看", icon="arrow_forward").props("color=secondary flat dense").classes(
|
|
212
|
+
"press-button"
|
|
213
|
+
).style(
|
|
214
|
+
"align-self: flex-end; "
|
|
215
|
+
"flex-shrink: 0; "
|
|
216
|
+
"background: transparent !important; "
|
|
217
|
+
"box-shadow: none !important;"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Card 3: Voiceprint
|
|
221
|
+
with (
|
|
222
|
+
ui.card()
|
|
223
|
+
.classes("super-card cursor-pointer")
|
|
224
|
+
.on(
|
|
225
|
+
"click",
|
|
226
|
+
lambda: ui.navigate.to(
|
|
227
|
+
"/voiceprint/update" if has_vp else "/voiceprint/enroll"
|
|
228
|
+
),
|
|
229
|
+
)
|
|
230
|
+
.style(
|
|
231
|
+
"height: 220px; "
|
|
232
|
+
"display: flex; "
|
|
233
|
+
"flex-direction: column; "
|
|
234
|
+
"padding: 1.5rem; "
|
|
235
|
+
"box-sizing: border-box;"
|
|
236
|
+
)
|
|
237
|
+
):
|
|
238
|
+
ui.icon("fingerprint", size="3em").classes("text-warning rotate-icon").style(
|
|
239
|
+
"flex-shrink: 0; margin-bottom: 0.75rem;"
|
|
240
|
+
)
|
|
241
|
+
with ui.column().classes("gap-2").style("flex: 1;"):
|
|
242
|
+
ui.label("声纹管理").classes("text-h6 font-bold text-grey-9")
|
|
243
|
+
if has_vp:
|
|
244
|
+
with ui.row().classes("items-center gap-2"):
|
|
245
|
+
ui.icon("check_circle", size="sm").classes("text-positive")
|
|
246
|
+
ui.label("已注册").classes("text-sm text-positive font-medium")
|
|
247
|
+
else:
|
|
248
|
+
with ui.row().classes("items-center gap-2"):
|
|
249
|
+
ui.icon("warning", size="sm").classes("text-warning")
|
|
250
|
+
ui.label("未注册").classes("text-sm text-warning font-medium")
|
|
251
|
+
ui.button("管理" if has_vp else "注册", icon="arrow_forward").props(
|
|
252
|
+
"color=warning flat dense"
|
|
253
|
+
).classes("press-button").style(
|
|
254
|
+
"align-self: flex-end; "
|
|
255
|
+
"flex-shrink: 0; "
|
|
256
|
+
"background: transparent !important; "
|
|
257
|
+
"box-shadow: none !important;"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Card 4: Settings
|
|
261
|
+
with (
|
|
262
|
+
ui.card()
|
|
263
|
+
.classes("super-card cursor-pointer")
|
|
264
|
+
.on("click", lambda: ui.navigate.to("/settings"))
|
|
265
|
+
.style(
|
|
266
|
+
"height: 220px; "
|
|
267
|
+
"display: flex; "
|
|
268
|
+
"flex-direction: column; "
|
|
269
|
+
"padding: 1.5rem; "
|
|
270
|
+
"box-sizing: border-box;"
|
|
271
|
+
)
|
|
272
|
+
):
|
|
273
|
+
ui.icon("settings", size="3em").classes("text-info rotate-icon").style(
|
|
274
|
+
"flex-shrink: 0; margin-bottom: 0.75rem;"
|
|
275
|
+
)
|
|
276
|
+
with ui.column().classes("gap-2").style("flex: 1;"):
|
|
277
|
+
ui.label("个人设置").classes("text-h6 font-bold text-grey-9")
|
|
278
|
+
ui.label("修改个人信息和密码").classes("text-sm text-grey-7")
|
|
279
|
+
ui.button("设置", icon="arrow_forward").props("color=info flat dense").classes(
|
|
280
|
+
"press-button"
|
|
281
|
+
).style(
|
|
282
|
+
"align-self: flex-end; "
|
|
283
|
+
"flex-shrink: 0; "
|
|
284
|
+
"background: transparent !important; "
|
|
285
|
+
"box-shadow: none !important;"
|
|
286
|
+
)
|