Pārlūkot izejas kodu

Merge branch 'LCD组装' of http://49.235.130.76/XXF_1122/DaisyNPILine into LCD组装

zly 3 mēneši atpakaļ
vecāks
revīzija
aacd991e11

+ 3 - 2
TeamAAS-VM/Core/Management.cs

@@ -3620,7 +3620,7 @@ namespace TeamAAS_VP.Core
             {
                 return;
             }
-            concurrenty = true;
+            concurrenty = false;
             bool connectStaus = true;
             bool mesStatus = true;
             //bool paraStatus = true;
@@ -3664,7 +3664,8 @@ namespace TeamAAS_VP.Core
                 //paraStatus = false;
 
             }
-            if (connectStaus && mesStatus && _LastAutoRunning)
+            var user = _systemDatabaseService.GetCurrentUser();
+            if (connectStaus && mesStatus && _LastAutoRunning&& user.userPart == UserPart.Operator)
             {
                 RunModelBackground = System.Windows.Media.Brushes.Green;
                 ClearModeBackground = System.Windows.Media.Brushes.White;

+ 2 - 2
TeamAAS-VM/Data/DatabaseInitializer.cs

@@ -35,8 +35,8 @@ namespace TeamAAS_VP.Data
                 var userCount = _db.Queryable<User>().Count();
                 if (userCount == 0)
                 {
-                    var op = new User { UserName = "操作员", UserPassword = "123", userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now, IsRemember = false };
-                    var eng = new User { UserName = "工程师", UserPassword = "123", userPart = Enums.UserPart.Engineer, CreateTime = DateTime.Now, IsRemember = false };
+                    var op = new User { UserName = "操作员", UserPassword = "", userPart = Enums.UserPart.Operator, CreateTime = DateTime.Now, IsRemember = false };
+                    var eng = new User { UserName = "工程师", UserPassword = "10086", userPart = Enums.UserPart.Engineer, CreateTime = DateTime.Now, IsRemember = false };
                     var admin = new User { UserName = "管理员", UserPassword = "team123456", userPart = Enums.UserPart.Administrator, CreateTime = DateTime.Now, IsRemember = false };
                     _db.Insertable(new List<User> { op, eng, admin }).ExecuteCommand();
                 }

+ 170 - 2
TeamAAS-VM/ViewModels/MainWindowViewModel.cs

@@ -11,7 +11,12 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+using System.Windows.Interop;
 using System.Windows.Media;
+using System.Windows.Threading;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP.Controls;
 using TeamAAS_VP.Core;
@@ -49,6 +54,33 @@ namespace TeamAAS_VP.ViewModels
         private Guid currentUser;
         Timer timer;
 
+        /// <summary>
+        /// 工程师和管理员权限的无操作超时时长。
+        /// 超过该时长后自动切回操作员权限。
+        /// </summary>
+        private readonly TimeSpan PermissionIdleTimeout = TimeSpan.FromMinutes(3);
+
+        /// <summary>
+        /// 权限空闲检测计时器。
+        /// 周期性检查最近一次用户输入距离当前时间是否超过超时阈值。
+        /// </summary>
+        private DispatcherTimer _permissionIdleTimer;
+
+        /// <summary>
+        /// 最近一次键盘、鼠标、触摸或手写笔输入时间。
+        /// </summary>
+        private DateTime _lastPermissionActivityTime;
+
+        /// <summary>
+        /// 标识权限超时处理是否正在执行,避免计时器重入。
+        /// </summary>
+        private bool _isPermissionTimeoutRunning;
+
+        /// <summary>
+        /// 标识是否已订阅全局输入事件,避免重复订阅。
+        /// </summary>
+        private bool _isTrackingUserInput;
+
         #region 属性
 
         private Management _management;
@@ -363,11 +395,16 @@ namespace TeamAAS_VP.ViewModels
             {
                 _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.完成}", Value = -1 });
             }));
+
+            // 初始弹窗登录发生在主窗口加载前,加载后需按当前用户补启权限空闲计时。
+            ConfigurePermissionIdleMonitor(_systemDatabaseService.GetCurrentUser());
+
         }
 
         private void OnClose()
         {
-
+            StopPermissionIdleMonitor();
+            Stop();
         }
 
         private void SendTaskMessage(string msg, MessageLevel level)
@@ -387,14 +424,145 @@ namespace TeamAAS_VP.ViewModels
             d.Message = string.Format(Lang.已运行时长, (int)run.TotalDays, run.Hours, run.Minutes, run.Seconds);
         }
 
+        /// <summary>
+        /// 处理用户登录或权限切换事件。
+        /// 更新状态栏当前用户,登录后跳转主页,并根据用户权限启停空闲降权检测。
+        /// </summary>
+        /// <param name="user">触发事件的用户对象。</param>
         private void LoginChange(Models.User user)
         {
+            var current = _systemDatabaseService.GetCurrentUser();
             var us = Status.FirstOrDefault(u => u.ID == currentUser);
             if (us != null)
             {
-                us.Message = $"{Lang.当前用户}:{_systemDatabaseService.GetCurrentUser().UserName}[{_systemDatabaseService.GetCurrentUser().userPart.ToString()}]";
+                us.Message = $"{Lang.当前用户}:{current.UserName}[{current.userPart.ToString()}]";
+            }
+
+            _regionManager.RequestNavigate("MainRegionContext", "HomeView");
+            lastNavigationName = "Home";
+            NavigationIndex = 0;
+            _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
+            ConfigurePermissionIdleMonitor(current);
+        }
+
+        /// <summary>
+        /// 配置工程师和管理员权限的空闲检测。
+        /// 操作员权限不需要超时检测,会停止现有计时和输入监听。
+        /// </summary>
+        /// <param name="user">当前登录用户。</param>
+        private void ConfigurePermissionIdleMonitor(Models.User user)
+        {
+            if (user == null || user.userPart == UserPart.Operator)
+            {
+                StopPermissionIdleMonitor();
+                return;
+            }
+
+            _lastPermissionActivityTime = DateTime.Now;
+            if (!_isTrackingUserInput)
+            {
+                InputManager.Current.PostProcessInput += OnUserInput;
+                _isTrackingUserInput = true;
+            }
+
+            if (_permissionIdleTimer == null)
+            {
+                _permissionIdleTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
+                _permissionIdleTimer.Tick += PermissionIdleTimer_Tick;
+            }
+
+            _permissionIdleTimer.Start();
+        }
+
+        /// <summary>
+        /// 停止权限空闲检测并解绑全局输入事件。
+        /// </summary>
+        private void StopPermissionIdleMonitor()
+        {
+            if (_permissionIdleTimer != null)
+            {
+                _permissionIdleTimer.Stop();
+            }
+
+            if (_isTrackingUserInput)
+            {
+                InputManager.Current.PostProcessInput -= OnUserInput;
+                _isTrackingUserInput = false;
             }
         }
+
+        /// <summary>
+        /// 记录用户输入时间。
+        /// 键盘、鼠标、滚轮、触摸和手写笔输入都会刷新空闲计时。
+        /// </summary>
+        /// <param name="sender">输入事件发送方。</param>
+        /// <param name="e">输入事件参数。</param>
+        private void OnUserInput(object sender, ProcessInputEventArgs e)
+        {
+            //var input = e?.StagingItem?.Input;
+            //if (input is KeyEventArgs ||
+            //    input is MouseButtonEventArgs ||
+            //    input is MouseWheelEventArgs ||
+            //    input is MouseEventArgs ||
+            //    input is TouchEventArgs ||
+            //    input is StylusEventArgs)
+            //{
+            //    _lastPermissionActivityTime = DateTime.Now;
+            //}
+        }
+
+        /// <summary>
+        /// 周期性检查工程师或管理员权限是否超时。
+        /// 如果 3 分钟无操作,则将当前用户切回操作员并广播权限切换事件。
+        /// </summary>
+        /// <param name="sender">计时器事件发送方。</param>
+        /// <param name="e">计时器事件参数。</param>
+        private async void PermissionIdleTimer_Tick(object sender, EventArgs e)
+        {
+            if (_isPermissionTimeoutRunning)
+                return;
+
+            var current = _systemDatabaseService.GetCurrentUser();
+            if (current == null || current.userPart == UserPart.Operator)
+            {
+                StopPermissionIdleMonitor();
+                return;
+            }
+
+            if (DateTime.Now - _lastPermissionActivityTime < PermissionIdleTimeout)
+                return;
+
+            _isPermissionTimeoutRunning = true;
+            try
+            {
+                var users = await _systemDatabaseService.GetAllUsersAsync();
+                var operatorUser = users?.FirstOrDefault(u => u.userPart == UserPart.Operator);
+                if (operatorUser == null)
+                {
+                    App.Current.Dispatcher.Invoke(() =>
+                    {
+                        _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = "权限超时,但未找到操作员账号", level = MessageLevel.Error });
+                    });
+                    StopPermissionIdleMonitor();
+                    return;
+                }
+
+                await _systemDatabaseService.SetCurrentUserAsync(operatorUser.Id);
+                _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
+                _eventAggregator.GetEvent<CurrentStatusNotification>().Publish();
+                App.Current.Dispatcher.Invoke(() =>
+                {
+                    _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = "权限超时,已自动退出工程师/管理员权限", level = MessageLevel.Error });
+                });
+            }
+            finally
+            {
+                _isPermissionTimeoutRunning = false;
+            }
+        }
+
+
+
         #endregion
     }
 }

+ 118 - 25
TeamAAS-VM/ViewModels/User/CardLoginWindowViewModel.cs

@@ -51,26 +51,51 @@ namespace TeamAAS_VP.ViewModels.User
         }
 
         private string _SelectedUserName;
+
+        /// <summary>
+        /// 当前在账号登录页选择的用户名。
+        /// 窗口加载时会清空;切换用户名时会同步清空密码。
+        /// </summary>
         public string SelectedUserName
         {
             get { return _SelectedUserName; }
-            set { SetProperty(ref _SelectedUserName, value); }
+            set
+            {
+                if (SetProperty(ref _SelectedUserName, value))
+                {
+                    Password = string.Empty;
+                }
+            }
         }
 
         private string _Password;
+
+        /// <summary>
+        /// 当前输入的账号登录密码。
+        /// 登录流程结束后会清空,避免窗口保留密码记录。
+        /// </summary>
         public string Password
         {
             get { return _Password; }
             set { SetProperty(ref _Password, value); }
         }
 
-        public bool IsRemember { get; set; }
-
+        /// <summary>
+        /// 标识当前登录是否为本地登录模式。
+        /// </summary>
         public bool IsLocal { get; private set; }
+
+        /// <summary>
+        /// 当前弹窗实例,用于登录成功或取消时设置 DialogResult。
+        /// </summary>
         public CardLoginWindow View { get; set; }
 
         //正在登录中
         private bool _IsLogining;
+
+        /// <summary>
+        /// 标识账号登录流程是否正在执行,用于禁用重复点击登录。
+        /// </summary>
         public bool IsLogining
         {
             get { return _IsLogining; }
@@ -78,6 +103,11 @@ namespace TeamAAS_VP.ViewModels.User
         }
 
         private int _PageIndex;
+
+        /// <summary>
+        /// 当前登录页索引。
+        /// 0 表示刷卡登录页,1 表示账号登录页。
+        /// </summary>
         public int PageIndex
         {
             get { return _PageIndex; }
@@ -85,29 +115,54 @@ namespace TeamAAS_VP.ViewModels.User
         }
 
         private Visibility _CardBtnVisibility = Visibility.Visible;
+
+        /// <summary>
+        /// 刷卡登录按钮可见性。
+        /// 管理员模式下隐藏刷卡入口。
+        /// </summary>
         public Visibility CardBtnVisibility
         {
             get { return _CardBtnVisibility; }
             set { SetProperty(ref _CardBtnVisibility, value); }
         }
 
+        /// <summary>
+        /// 刷卡器串口通讯对象。
+        /// 窗口关闭时需要解绑事件并释放连接。
+        /// </summary>
         public SerialPortProtocol serialPortProtocol { get; set; }
         #endregion
 
         #region 命令
         private DelegateCommand _LoginCommand;
+
+        /// <summary>
+        /// 账号登录命令。
+        /// </summary>
         public DelegateCommand LoginCommand =>
             _LoginCommand ?? (_LoginCommand = new DelegateCommand(ExecuteLoginCommand, CanExecuteLoginCommand).ObservesProperty(() => SelectedUserName).ObservesProperty(() => Password).ObservesProperty(() => IsLogining));
 
         private DelegateCommand _CloseCommand;
+
+        /// <summary>
+        /// 关闭登录窗口命令。
+        /// </summary>
         public DelegateCommand CloseCommand =>
             _CloseCommand ?? (_CloseCommand = new DelegateCommand(ExecuteCloseCommand));
 
         private DelegateCommand<string> _TransitionerCommand;
+
+        /// <summary>
+        /// 切换登录页命令。
+        /// </summary>
         public DelegateCommand<string> TransitionerCommand =>
             _TransitionerCommand ?? (_TransitionerCommand = new DelegateCommand<string>(ExecuteTransitionerCommand));
 
         private DelegateCommand _LoadedCommand;
+
+        /// <summary>
+        /// 窗口加载命令。
+        /// </summary>
         public DelegateCommand LoadedCommand =>
             _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
         #endregion
@@ -126,7 +181,8 @@ namespace TeamAAS_VP.ViewModels.User
 
         #region 方法
         /// <summary>
-        /// 加载
+        /// 窗口加载时初始化用户列表和刷卡器通讯。
+        /// 同时清除记住用户标记,并清空用户名选择和密码输入。
         /// </summary>
         async void ExecuteLoadedCommand()
         {
@@ -140,28 +196,25 @@ namespace TeamAAS_VP.ViewModels.User
             {
                 Users = new List<TeamAAS_VP.Models.User>();
             }
-            TeamAAS_VP.Models.User rememberUser = null;
             if (Users != null)
             {
                 UserNameList.Clear();
                 foreach (var user in Users)
                 {
                     UserNameList.Add(user.UserName);
-                    if (user.IsRemember)
-                    {
-                        rememberUser = user;
-                    }
                 }
             }
-
-            //对记住的用户界面绑定
-            if (rememberUser != null)
+            try
+            {
+                await _systemDatabaseService.ClearRememberUserAsync();
+            }
+            catch
             {
-                SelectedUserName = UserNameList.FirstOrDefault(p => p == rememberUser.UserName);
-                Password = rememberUser.UserPassword;
-                IsRemember = true;
             }
 
+            SelectedUserName = null;
+            Password = string.Empty;
+
             //创建刷卡器通讯
             serialPortProtocol = new SerialPortProtocol(cardConfig);
             serialPortProtocol.DataReceived += CardSerial_DataReceived;
@@ -172,6 +225,12 @@ namespace TeamAAS_VP.ViewModels.User
             }
         }
 
+        /// <summary>
+        /// 处理刷卡器收到的卡号。
+        /// 在刷卡登录页中将卡号作为临时工程师账号登录,并发布用户切换事件。
+        /// </summary>
+        /// <param name="sender">串口事件发送方。</param>
+        /// <param name="code">刷卡器读取到的卡号。</param>
         private async void CardSerial_DataReceived(object sender, string code)
         {
             if (PageIndex == 0)
@@ -191,13 +250,19 @@ namespace TeamAAS_VP.ViewModels.User
 
                 App.Current.Dispatcher.Invoke(() =>
                 {
-                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
+                    user.UserPassword = string.Empty;
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
                     View.DialogResult = true;
                 });
 
             }
         }
 
+        /// <summary>
+        /// 窗口关闭时释放刷卡器通讯资源。
+        /// </summary>
+        /// <param name="sender">窗口关闭事件发送方。</param>
+        /// <param name="e">关闭事件参数。</param>
         public async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
         {
             if (serialPortProtocol!=null)
@@ -210,6 +275,11 @@ namespace TeamAAS_VP.ViewModels.User
             serialPortProtocol = null;
         }
 
+        /// <summary>
+        /// 执行账号登录。
+        /// 操作员账号允许免密码登录;工程师和管理员仍需要密码认证。
+        /// 登录完成后清除记住用户标记、清空密码,并关闭登录弹窗。
+        /// </summary>
         async void ExecuteLoginCommand()
         {
             if (Users == null || string.IsNullOrEmpty(SelectedUserName))
@@ -218,26 +288,30 @@ namespace TeamAAS_VP.ViewModels.User
             IsLogining = true;
             try
             {
+                var selectedUser = Users.FirstOrDefault(u => u.UserName == SelectedUserName);
+                if (selectedUser == null)
+                    return;
+
                 if (_systemDatabaseService.GetCurrentUser() != null)
                 {
                     if (_systemDatabaseService.GetCurrentUser().UserName == SelectedUserName)
                     {
+                        Password = string.Empty;
+                        _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
                         View.DialogResult = true;
                         return;
                     }
                 }
 
-                // authenticate via system database service
-                var user = await _systemDatabaseService.AuthenticateUserAsync(SelectedUserName, Password);
+                var user = selectedUser.userPart == UserPart.Operator
+                    ? selectedUser
+                    : await _systemDatabaseService.AuthenticateUserAsync(SelectedUserName, Password);
+
                 if (user != null)
                 {
-                    // set remember flag via system database service
                     try
                     {
-                        if (IsRemember)
-                            await _systemDatabaseService.SetRememberUserAsync(SelectedUserName);
-                        else
-                            await _systemDatabaseService.ClearRememberUserAsync();
+                        await _systemDatabaseService.ClearRememberUserAsync();
                     }
                     catch
                     {
@@ -246,8 +320,9 @@ namespace TeamAAS_VP.ViewModels.User
 
                     // mark current user in system DB and record login
                     await _systemDatabaseService.SetCurrentUserAsync(user.Id);
-                    await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.UtcNow, Message = "Login success" });
-                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
+                    await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.Now, Message = "Login success" });
+                    Password = string.Empty;
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
                     View.DialogResult = true;
                 }
 
@@ -258,25 +333,43 @@ namespace TeamAAS_VP.ViewModels.User
             }
             finally
             {
+                Password = string.Empty;
                 IsLogining = false;
             }
         }
 
+        /// <summary>
+        /// 判断账号登录命令是否可执行。
+        /// 选择用户名且当前未处于登录中时允许执行。
+        /// </summary>
+        /// <returns>可执行返回 true,否则返回 false。</returns>
         bool CanExecuteLoginCommand()
         {
             return !string.IsNullOrEmpty(SelectedUserName) && !IsLogining;
         }
 
+        /// <summary>
+        /// 关闭登录窗口并返回取消结果。
+        /// </summary>
         void ExecuteCloseCommand()
         {
             View.DialogResult = false;
         }
 
+        /// <summary>
+        /// 切换刷卡登录页和账号登录页。
+        /// </summary>
+        /// <param name="parameter">目标页索引字符串。</param>
         void ExecuteTransitionerCommand(string parameter)
         {
             PageIndex = int.Parse(parameter);
         }
 
+        /// <summary>
+        /// 设置登录窗口模式。
+        /// 管理员模式进入账号登录页并隐藏刷卡按钮,普通模式进入刷卡登录页。
+        /// </summary>
+        /// <param name="isAdmin">是否使用管理员登录模式。</param>
         public void SetModel(bool isAdmin)
         {
             PageIndex = isAdmin ? 1 : 0;

+ 96 - 42
TeamAAS-VM/ViewModels/User/LoginViewModel.cs

@@ -13,6 +13,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using TeamAAS_VP.Core;
 using TeamAAS_VP.Data;
+using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
@@ -41,11 +42,47 @@ namespace TeamAAS_VP.ViewModels.User
             set { _UserNameList = value; }
         }
 
-        public string SelectedUserName { get; set; }
+        private string _SelectedUserName;
 
-        public string UserPassword { get; set; }
+        /// <summary>
+        /// 当前在登录界面选择的用户名。
+        /// 加载登录页时会清空;切换用户名时会同步清空密码。
+        /// </summary>
+        public string SelectedUserName
+        {
+            get { return _SelectedUserName; }
+            set
+            {
+                if (SetProperty(ref _SelectedUserName, value))
+                {
+                    UserPassword = string.Empty;
+                }
+            }
+        }
+
+        private string _UserPassword;
+
+        /// <summary>
+        /// 当前输入的登录密码。
+        /// 登录成功或失败后都会清空,避免界面保留密码记录。
+        /// </summary>
+        public string UserPassword
+        {
+            get { return _UserPassword; }
+            set { SetProperty(ref _UserPassword, value); }
+        }
 
-        public bool IsRemember { get; set; }
+        private bool _IsRemember;
+
+        /// <summary>
+        /// 是否记住用户的界面状态。
+        /// 当前登录策略不允许记住用户,加载和登录时会强制置为 false。
+        /// </summary>
+        public bool IsRemember
+        {
+            get { return _IsRemember; }
+            set { SetProperty(ref _IsRemember, value); }
+        }
 
         #endregion
 
@@ -58,9 +95,18 @@ namespace TeamAAS_VP.ViewModels.User
         /// 登录命令
         /// </summary>
         public DelegateCommand LoginCommand { get; set; }
+
+        /// <summary>
+        /// 登录页加载命令。
+        /// 用于清除记住用户和密码输入。
+        /// </summary>
         public DelegateCommand LoadedCommand { get; set; }
 
         private DelegateCommand _CardLoginCommand;
+
+        /// <summary>
+        /// 打开刷卡登录窗口命令。
+        /// </summary>
         public DelegateCommand CardLoginCommand =>
             _CardLoginCommand ?? (_CardLoginCommand = new DelegateCommand(ExecuteCardLoginCommand));
 
@@ -78,7 +124,7 @@ namespace TeamAAS_VP.ViewModels.User
             _container = container;
             _systemDatabaseService = systemDatabaseService;
 
-			LoadedCommand = new DelegateCommand(OnLoad);
+			LoadedCommand = new DelegateCommand(async () => await OnLoadAsync());
             SignUpCommand = new DelegateCommand(new Action(SignUp));
             LoginCommand = new DelegateCommand(async () => await LoginAsync());
 
@@ -94,67 +140,68 @@ namespace TeamAAS_VP.ViewModels.User
                 Users = new List<TeamAAS_VP.Models.User>();
             }
 
-            TeamAAS_VP.Models.User rememberUser = null;
             if (Users != null)
             {
                 foreach (var user in Users)
                 {
                     UserNameList.Add(user.UserName);
-                    if (user.IsRemember)
-                    {
-                        rememberUser = user;
-                    }
                 }
             }
-
-            //对记住的用户界面绑定
-            if (rememberUser != null)
-            {
-                SelectedUserName = rememberUser.UserName;
-                UserPassword = rememberUser.UserPassword;
-                IsRemember = true;
-            }
         }
 
         #region 方法
+
+        /// <summary>
+        /// 执行账号登录。
+        /// 操作员账号允许免密码登录;工程师和管理员仍需要通过密码认证。
+        /// 登录完成后清除记住用户标记、清空密码,并发布用户切换事件交由主窗口跳转主页。
+        /// </summary>
         private async Task LoginAsync()
         {
             if (Users == null || string.IsNullOrEmpty(SelectedUserName) || UserPassword == null)
                 return;
 
-            if (_systemDatabaseService.GetCurrentUser() != null)
+            var selectedUser = Users.FirstOrDefault(u => u.UserName == SelectedUserName);
+            if (selectedUser == null)
+                return;
+
+            var currentUser = _systemDatabaseService.GetCurrentUser();
+            if (currentUser != null)
             {
-                if (_systemDatabaseService.GetCurrentUser().UserName == SelectedUserName)
+                if (currentUser.UserName == SelectedUserName)
                 {
+                    UserPassword = string.Empty;
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(currentUser);
                     return;
                 }
             }
 
             try
             {
-                // authenticate via system database service
-                var user = await _systemDatabaseService.AuthenticateUserAsync(SelectedUserName, UserPassword);
+                var user = selectedUser.userPart == UserPart.Operator
+                    ? selectedUser
+                    : await _systemDatabaseService.AuthenticateUserAsync(SelectedUserName, UserPassword);
+
                 if (user != null)
                 {
-                    // set remember flag via system database service
                     try
                     {
-                        if (IsRemember)
-                            await _systemDatabaseService.SetRememberUserAsync(SelectedUserName);
-                        else
-                            await _systemDatabaseService.ClearRememberUserAsync();
+                        await _systemDatabaseService.ClearRememberUserAsync();
                     }
                     catch
                     {
                         
                     }
+                    IsRemember = false;
 
                     // mark current user in system DB and record login
                     await _systemDatabaseService.SetCurrentUserAsync(user.Id);
                     //await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.UtcNow, Message = "Login success" });
+                    _eventAggregator.GetEvent<CurrentStatusNotification>().Publish();
 
                     _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.登录成功}!", Duration = 1 });
-                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
+                    UserPassword = string.Empty;
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
                 }
             }
             catch (Exception ex)
@@ -162,9 +209,14 @@ namespace TeamAAS_VP.ViewModels.User
                 // authentication failed or error
                 await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = 0, Success = false, Time = DateTime.UtcNow, Message = $"Login failed for {SelectedUserName}: {ex.Message}" });
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.密码错误, Duration = 1 });
+                UserPassword = string.Empty;
             }
         }
 
+        /// <summary>
+        /// 进入注册页。
+        /// 只有当前用户为管理员时允许注册新用户。
+        /// </summary>
         private void SignUp()
         {
             var management = _container.Resolve<Management>();
@@ -177,13 +229,27 @@ namespace TeamAAS_VP.ViewModels.User
             _regionManager.RequestNavigate("UserRegionContext", "SignUpView");
         }
 
-        private void OnLoad()
+        /// <summary>
+        /// 登录页加载时执行安全清理。
+        /// 清除数据库中的记住用户标记,并清空用户名选择和密码输入。
+        /// </summary>
+        private async Task OnLoadAsync()
         {
-
+            try
+            {
+                await _systemDatabaseService.ClearRememberUserAsync();
+            }
+            catch
+            {
+            }
+            IsRemember = false;
+            SelectedUserName = null;
+            UserPassword = string.Empty;
         }
 
         /// <summary>
-        /// 更新用户列表
+        /// 更新用户列表。
+        /// 只刷新可选用户名,不读取或回填任何记住用户和密码信息。
         /// </summary>
         public void UpdateUserList()
         {
@@ -198,25 +264,13 @@ namespace TeamAAS_VP.ViewModels.User
                 Users = new List<TeamAAS_VP.Models.User>();
             }
 
-            TeamAAS_VP.Models.User rememberUser = null;
             if (Users != null)
             {
                 foreach (var user in Users)
                 {
                     UserNameList.Add(user.UserName);
-                    if (user.IsRemember)
-                    {
-                        rememberUser = user;
-                    }
                 }
             }
-            //对记住的用户界面绑定
-            if (rememberUser != null)
-            {
-                SelectedUserName = rememberUser.UserName;
-                UserPassword = rememberUser.UserPassword;
-                IsRemember = true;
-            }
         }
 
         /// <summary>

+ 4 - 4
TeamAAS-VM/Views/User/LoginView.xaml

@@ -64,18 +64,18 @@
                     <Grid Grid.Row="3"
                           Height="40"
                           Margin="20,5,20,0">
-                        <CheckBox Content="{lex:Loc 记住用户}"
+                        <!--<CheckBox Content="{lex:Loc 记住用户}"
                                   Foreground="Black"
                                   Style="{StaticResource MaterialDesignCheckBox}"
                                   HorizontalAlignment="Left"
                                   VerticalAlignment="Center"
-                                  IsChecked="{Binding IsRemember}"/>
-                        <Button Content="{lex:Loc 注册}"
+                                  IsChecked="{Binding IsRemember}"/>-->
+                        <!--<Button Content="{lex:Loc 注册}"
                                 HorizontalAlignment="Right"
                                 Background="Transparent"
                                 BorderBrush="Transparent"
                                 Foreground="Black"
-                                Command="{Binding SignUpCommand}"/>
+                                Command="{Binding SignUpCommand}"/>-->
                     </Grid>
                     <Button Grid.Row="4"
                             Margin="20,10"