diff --git a/src/renderer/src/components/UserSelectionDialog.tsx b/src/renderer/src/components/UserSelectionDialog.tsx index 6b39da3..61d6237 100644 --- a/src/renderer/src/components/UserSelectionDialog.tsx +++ b/src/renderer/src/components/UserSelectionDialog.tsx @@ -7,8 +7,9 @@ * - Return selected user info */ -import React, { useState, useRef } from 'react' +import React, { useState, useRef, useEffect } from 'react' import { Modal } from './ui/Modal' +import { Users, Shield, UserCircle, Check } from 'lucide-react' export interface UserInfo { id: number @@ -37,6 +38,12 @@ export const UserSelectionDialog: React.FC = ({ const [selectedUserId, setSelectedUserId] = useState(null) const dialogRef = useRef(null) + useEffect(() => { + if (isOpen) { + setSelectedUserId(null) + } + }, [isOpen]) + const handleConfirm = () => { if (selectedUserId === null) { return @@ -59,11 +66,6 @@ export const UserSelectionDialog: React.FC = ({ onCancel() } - const userTypeStyles: Record = { - Admin: 'bg-amber-50 text-amber-600', - User: 'bg-blue-50 text-blue-600' - } - if (!isOpen) return null return ( @@ -73,67 +75,158 @@ export const UserSelectionDialog: React.FC = ({ title="选择用户" size="md" triggerRef={triggerRef} - initialFocusSelector={users.length > 0 ? '.user-item:first-child' : undefined} + initialFocusSelector={users.length > 0 ? '.user-card:first-child' : undefined} ariaDescribedBy="user-selection-description" > + +
-
当前登录:{currentUsername}
+ {/* Context header */} +
+
+ +
+
+
+ 当前登录 +
+
+ {currentUsername} +
+
+
+

从列表中选择一个用户,双击可直接确认选择

-
-
- {users.map((user) => ( + {/* User list */} +
+ {users.map((user, index) => { + const isSelected = selectedUserId === user.id + const isAdmin = user.userType === 'Admin' + + return (
setSelectedUserId(user.id)} onDoubleClick={() => handleDoubleClick(user)} tabIndex={0} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() setSelectedUserId(user.id) } }} > -
- {user.username} - - {user.userType} - + {/* Left accent bar for selected state */} +
+ + {/* Avatar */} +
+ {user.username.charAt(0).toUpperCase()}
- {user.createTime && ( -
- 创建于:{new Date(user.createTime).toLocaleString('zh-CN')} + + {/* Info */} +
+
+ + {user.username} + + + {isAdmin ? ( + + ) : ( + + )} + {user.userType} +
- )} + {user.createTime && ( +
+ {new Date(user.createTime).toLocaleDateString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + })} +
+ )} +
+ + {/* Check indicator */} +
+ +
- ))} -
+ ) + })}
-
-
+ {/* Footer */} +
+
-
双击用户可直接选择
+
+ 双击用户可直接选择 +
diff --git a/src/renderer/src/components/app/AuthenticatedAppShell.tsx b/src/renderer/src/components/app/AuthenticatedAppShell.tsx index 2a9de29..257356a 100644 --- a/src/renderer/src/components/app/AuthenticatedAppShell.tsx +++ b/src/renderer/src/components/app/AuthenticatedAppShell.tsx @@ -1,14 +1,14 @@ -import React, { Suspense } from 'react' +import React, { Suspense, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import { ArrowUpCircle, - Database, Download, LayoutDashboard, LoaderCircle, LogOut, Settings, Trash2, - User + User, + Zap } from 'lucide-react' import type { DownloadReleaseRequest, @@ -40,6 +40,12 @@ interface AuthenticatedAppShellProps { logoutButtonRef: React.RefObject } +const navItems = [ + { id: 'extractor' as const, label: '数据提取', icon: Download }, + { id: 'cleaner' as const, label: '物料清理', icon: Trash2 }, + { id: 'settings' as const, label: '系统设置', icon: Settings } +] + export function AuthenticatedAppShell({ currentUser, currentPage, @@ -56,11 +62,31 @@ export function AuthenticatedAppShell({ onLogout, logoutButtonRef }: AuthenticatedAppShellProps): React.JSX.Element { - const navItems = [ - { id: 'extractor' as const, label: '数据提取 (Extractor)', icon: }, - { id: 'cleaner' as const, label: '物料验证与清理 (Cleaner)', icon: }, - { id: 'settings' as const, label: '系统设置 (Settings)', icon: } - ] + const navRef = useRef(null) + const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0 }) + const [isMeasured, setIsMeasured] = useState(false) + + const isNavPage = navItems.some((item) => item.id === currentPage) + + const updateIndicator = useCallback(() => { + const el = navRef.current?.querySelector( + `[data-nav="${currentPage}"]` + ) as HTMLElement | null + if (!el) return + setIndicatorStyle({ left: el.offsetLeft, width: el.offsetWidth }) + setIsMeasured(true) + }, [currentPage]) + + useLayoutEffect(() => { + updateIndicator() + }, [updateIndicator]) + + useEffect(() => { + if (!navRef.current) return + const observer = new ResizeObserver(updateIndicator) + observer.observe(navRef.current) + return () => observer.disconnect() + }, [updateIndicator]) const showUpdateEntry = !!updateStatus && @@ -83,77 +109,116 @@ export function AuthenticatedAppShell({ return (
-
-
-
-
-
+ {/* Top accent line */} +
+ + {/* Left: Window dots + Logo */} +
+
+
+
+
-
onNavigate('home')} style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties} > -
- - ERP Auto +
+
- - {__APP_VERSION__}({__GIT_HASH__}) - -
+
+ + ERP Auto + + + {__APP_VERSION__}({__GIT_HASH__}) + +
+
+ {/* Center: Navigation with sliding pill indicator */} + {/* Right: Status + User */}
{showUpdateEntry && ( )} -
- - 数据库已连接 + +
+
+
+
+
+ 已连接
-
- + +
+
+ +
{currentUser?.username} @@ -162,10 +227,10 @@ export function AuthenticatedAppShell({ )}