新增横竖模式切换并支持侧栏悬浮展开

This commit is contained in:
JSR
2026-03-18 21:05:07 +08:00
parent d4cbfc556a
commit 224c89f4dd
3 changed files with 146 additions and 33 deletions
+36 -1
View File
@@ -51,6 +51,8 @@ function MainContent(): React.JSX.Element {
const [authVisible, setAuthVisible] = useState(false)
const [authPassword, setAuthPassword] = useState("")
const [authLoading, setAuthLoading] = useState(false)
const [isPortraitMode, setIsPortraitMode] = useState(false)
const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
const activeMenu = useMemo(() => {
const p = location.pathname
@@ -127,6 +129,30 @@ function MainContent(): React.JSX.Element {
if (key === "settings") navigate("/settings")
}
const toggleOrientationMode = async () => {
const api = (window as any).api
if (!api) return
const nextPortraitMode = !isPortraitMode
try {
const maximized = await api.windowIsMaximized()
if (maximized) {
await api.windowMaximize()
}
if (nextPortraitMode) {
await api.windowResize(800, 1000)
setSidebarCollapsed(true)
} else {
await api.windowResize(1180, 680)
setSidebarCollapsed(false)
}
setIsPortraitMode(nextPortraitMode)
} catch (error) {
messageApi.error(t("common.error"))
console.error("Failed to toggle orientation mode:", error)
}
}
const isDark = currentTheme?.mode === "dark"
const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9"
@@ -141,12 +167,21 @@ function MainContent(): React.JSX.Element {
>
{contextHolder}
<Layout style={{ height: "100vh", flexDirection: "row", overflow: "hidden" }}>
<Sidebar activeMenu={activeMenu} permission={permission} onMenuChange={onMenuChange} />
<Sidebar
activeMenu={activeMenu}
permission={permission}
onMenuChange={onMenuChange}
collapsed={sidebarCollapsed}
floatingExpand={isPortraitMode}
onCollapsedChange={setSidebarCollapsed}
/>
<ContentArea
permission={permission}
hasAnyPassword={hasAnyPassword}
onAuthClick={() => setAuthVisible(true)}
onLogout={logout}
isPortraitMode={isPortraitMode}
onToggleOrientation={toggleOrientationMode}
/>
<OOBE visible={wizardVisible} onComplete={() => setWizardVisible(false)} />
+11
View File
@@ -45,6 +45,8 @@ interface ContentAreaProps {
hasAnyPassword: boolean
onAuthClick: () => void
onLogout: () => void
isPortraitMode: boolean
onToggleOrientation: () => void
}
export function ContentArea({
@@ -52,6 +54,8 @@ export function ContentArea({
hasAnyPassword,
onAuthClick,
onLogout,
isPortraitMode,
onToggleOrientation,
}: ContentAreaProps): React.JSX.Element {
const { t } = useTranslation()
@@ -132,6 +136,13 @@ export function ContentArea({
}
>
<Space size="small">
<Button
size="small"
onClick={onToggleOrientation}
title={isPortraitMode ? "当前:竖屏模式" : "当前:横屏模式"}
>
</Button>
{permissionTag}
{hasAnyPassword && (
<>
+99 -32
View File
@@ -22,6 +22,9 @@ interface SidebarProps {
activeMenu: string
permission: "admin" | "points" | "view"
onMenuChange: (value: string) => void
collapsed: boolean
floatingExpand: boolean
onCollapsedChange: (collapsed: boolean) => void
}
interface DbStatus {
@@ -30,9 +33,16 @@ interface DbStatus {
error?: string
}
export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): React.JSX.Element {
export function Sidebar({
activeMenu,
permission,
onMenuChange,
collapsed,
floatingExpand,
onCollapsedChange,
}: SidebarProps): React.JSX.Element {
const { t } = useTranslation()
const [collapsed, setCollapsed] = useState(false)
const [floatingExpanded, setFloatingExpanded] = useState(false)
const [dbStatus, setDbStatus] = useState<DbStatus>({ type: "sqlite", connected: true })
const [syncLoading, setSyncLoading] = useState(false)
const [messageApi, contextHolder] = message.useMessage()
@@ -95,6 +105,12 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
const [forceSyncLoading, setForceSyncLoading] = useState(false)
useEffect(() => {
if (!floatingExpand || !collapsed) {
setFloatingExpanded(false)
}
}, [floatingExpand, collapsed])
const handleForceSync = async () => {
if (!(window as any).api) return
@@ -175,25 +191,15 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
},
]
return (
<Sider
className="ss-sidebar"
width={200}
collapsed={collapsed}
collapsedWidth={64}
style={{
background: "var(--ss-sidebar-bg)",
borderRight: "1px solid var(--ss-border-color)",
display: "flex",
flexDirection: "column",
}}
theme="light"
>
const showFloatingPanel = floatingExpand && collapsed && floatingExpanded
const renderSidebarBody = (isCollapsedView: boolean, isFloatingPanel = false) => (
<>
<div
data-tauri-drag-region
style={
{
padding: collapsed ? "24px 8px 12px" : "32px 24px 16px",
padding: isCollapsedView ? "24px 8px 12px" : "32px 24px 16px",
textAlign: "center",
WebkitAppRegion: "drag",
userSelect: "none",
@@ -205,25 +211,41 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
<Button
type="text"
size="small"
onClick={() => setCollapsed((prev) => !prev)}
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
style={{
position: "absolute",
top: "8px",
right: "8px",
WebkitAppRegion: "no-drag",
onClick={() => {
if (floatingExpand && collapsed) {
setFloatingExpanded((prev) => !prev)
return
}
onCollapsedChange(!collapsed)
}}
icon={
floatingExpand && collapsed
? isFloatingPanel
? <MenuFoldOutlined />
: <MenuUnfoldOutlined />
: isCollapsedView
? <MenuUnfoldOutlined />
: <MenuFoldOutlined />
}
style={
{
position: "absolute",
top: "8px",
right: "8px",
WebkitAppRegion: "no-drag",
} as React.CSSProperties
}
/>
<img
src={appLogo}
style={{
width: collapsed ? "40px" : "48px",
height: collapsed ? "40px" : "48px",
marginBottom: collapsed ? "0" : "12px",
width: isCollapsedView ? "40px" : "48px",
height: isCollapsedView ? "40px" : "48px",
marginBottom: isCollapsedView ? "0" : "12px",
}}
alt="logo"
/>
{!collapsed && (
{!isCollapsedView && (
<>
<h2
style={{
@@ -251,9 +273,14 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
<div style={{ flex: 1, overflowY: "auto", display: "flex", flexDirection: "column" }}>
<Menu
mode="inline"
inlineCollapsed={collapsed}
inlineCollapsed={isCollapsedView}
selectedKeys={[activeMenu]}
onClick={({ key }) => onMenuChange(key)}
onClick={({ key }) => {
onMenuChange(key)
if (floatingExpand && collapsed) {
setFloatingExpanded(false)
}
}}
style={{
width: "100%",
border: "none",
@@ -263,7 +290,7 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
/>
</div>
{!collapsed && dbStatus.type === "postgresql" && (
{!isCollapsedView && dbStatus.type === "postgresql" && (
<Card
size="small"
style={{
@@ -273,7 +300,6 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
}}
styles={{ body: { padding: "12px" } }}
>
{contextHolder}
<Space orientation="vertical" size={4} style={{ width: "100%" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<Space size={4}>
@@ -324,6 +350,47 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
</Space>
</Card>
)}
</>
)
return (
<Sider
className="ss-sidebar"
width={200}
collapsed={collapsed}
collapsedWidth={64}
style={{
background: "var(--ss-sidebar-bg)",
borderRight: "1px solid var(--ss-border-color)",
display: "flex",
flexDirection: "column",
overflow: "visible",
position: "relative",
}}
theme="light"
>
{contextHolder}
{renderSidebarBody(collapsed)}
{showFloatingPanel && (
<div
style={{
position: "absolute",
left: "64px",
top: 0,
bottom: 0,
width: "200px",
background: "var(--ss-sidebar-bg)",
borderRight: "1px solid var(--ss-border-color)",
boxShadow: "6px 0 18px rgba(0, 0, 0, 0.12)",
zIndex: 1200,
display: "flex",
flexDirection: "column",
}}
>
{renderSidebarBody(false, true)}
</div>
)}
</Sider>
)
}