mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
新增横竖模式切换并支持侧栏悬浮展开
This commit is contained in:
+36
-1
@@ -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)} />
|
||||
|
||||
@@ -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
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user