MainViewModel.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. using CommunityToolkit.Mvvm.ComponentModel;
  2. using CommunityToolkit.Mvvm.Input;
  3. using Serilog;
  4. using System.ComponentModel;
  5. using YZWater.Core.Models;
  6. using YZWater.Core.Services;
  7. namespace YZWater.Core.ViewModels;
  8. /// <summary>
  9. /// 主窗口 ViewModel
  10. /// </summary>
  11. public partial class MainViewModel : ObservableObject, IDisposable
  12. {
  13. private readonly ViewAViewModel _viewAViewModel;
  14. private readonly ViewBViewModel _viewBViewModel;
  15. private readonly ViewCViewModel _viewCViewModel;
  16. private readonly ViewDViewModel _viewDViewModel;
  17. private readonly ViewEViewModel _viewEViewModel;
  18. private readonly ViewFViewModel _viewFViewModel;
  19. private ProjectViewModel? _projectViewModel;
  20. private readonly ThemeService _themeService = ThemeService.Instance;
  21. private readonly LanguageService _langService = LanguageService.Instance;
  22. private readonly SynchronizationContext? _uiContext = SynchronizationContext.Current;
  23. private System.Timers.Timer? _clockTimer;
  24. private bool _disposed;
  25. [ObservableProperty]
  26. private ObservableObject _currentView;
  27. [ObservableProperty]
  28. private string _title;
  29. [ObservableProperty]
  30. private string _currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  31. [ObservableProperty]
  32. private bool _isPlcConnected;
  33. [ObservableProperty]
  34. private int _selectedTabIndex;
  35. // 各 Tab 激活状态
  36. [ObservableProperty] private bool _isTabAActive = true;
  37. [ObservableProperty] private bool _isTabBActive;
  38. [ObservableProperty] private bool _isTabCActive;
  39. [ObservableProperty] private bool _isTabDActive;
  40. [ObservableProperty] private bool _isTabEActive;
  41. [ObservableProperty] private bool _isTabFActive;
  42. // 当前 Tab 标题
  43. [ObservableProperty] private string _tabTitle;
  44. // 主题/语言状态
  45. [ObservableProperty] private bool _isDarkTheme = true;
  46. [ObservableProperty] private bool _isChinese = true;
  47. [ObservableProperty] private string _themeIcon = "🌙";
  48. [ObservableProperty] private string _langIcon = "中";
  49. // 导航标签
  50. [ObservableProperty] private string _navProcess;
  51. [ObservableProperty] private string _navParams;
  52. [ObservableProperty] private string _navFlow;
  53. [ObservableProperty] private string _navAlarm;
  54. [ObservableProperty] private string _navAbout;
  55. [ObservableProperty] private string _navAudit;
  56. // ═══ 用户管理翻译 ═══
  57. [ObservableProperty] private string _userManageText;
  58. [ObservableProperty] private string _createNewUserText;
  59. [ObservableProperty] private string _userNameLabel;
  60. [ObservableProperty] private string _passwordLabel;
  61. [ObservableProperty] private string _displayNameLabel;
  62. [ObservableProperty] private string _roleLabel;
  63. [ObservableProperty] private string _statusLabel;
  64. [ObservableProperty] private string _lastLoginLabel;
  65. [ObservableProperty] private string _actionLabel;
  66. [ObservableProperty] private string _createText;
  67. [ObservableProperty] private string _closeText;
  68. [ObservableProperty] private string _logoutText;
  69. // ═══ 运行模式与连接状态 ═══
  70. [ObservableProperty] private string _runMode = "Direct";
  71. [ObservableProperty] private string _hubStatusText = "";
  72. [ObservableProperty] private string _plcStatusText = "";
  73. [ObservableProperty] private string _plcStatusColor = "#E74C3C";
  74. // ═══ 子 ViewModel(供 View 绑定 DataContext) ═══
  75. public ViewAViewModel ViewA => _viewAViewModel;
  76. public ViewBViewModel ViewB => _viewBViewModel;
  77. public ViewCViewModel ViewC => _viewCViewModel;
  78. public ViewDViewModel ViewD => _viewDViewModel;
  79. public ViewEViewModel ViewE => _viewEViewModel;
  80. public ViewFViewModel ViewF => _viewFViewModel;
  81. /// <summary>项目化 ViewModel(当加载了项目档案时可用)</summary>
  82. public ProjectViewModel? ProjectView => _projectViewModel;
  83. /// <summary>是否加载了项目档案</summary>
  84. [ObservableProperty] private bool _hasProject;
  85. // ═══ 用户与权限 ═══
  86. [ObservableProperty] private string _currentUserName = string.Empty;
  87. [ObservableProperty] private string _currentUserRole = string.Empty;
  88. [ObservableProperty] private bool _canControl;
  89. [ObservableProperty] private bool _canModifyParams;
  90. [ObservableProperty] private bool _canManageUsers;
  91. [ObservableProperty] private bool _showUserDialog;
  92. public MainViewModel()
  93. {
  94. _viewAViewModel = new ViewAViewModel();
  95. _viewBViewModel = new ViewBViewModel();
  96. _viewCViewModel = new ViewCViewModel();
  97. _viewDViewModel = new ViewDViewModel();
  98. _viewEViewModel = new ViewEViewModel();
  99. _viewFViewModel = new ViewFViewModel();
  100. _currentView = _viewAViewModel;
  101. UpdateAllTexts();
  102. RefreshUserInfo();
  103. LoadRunMode();
  104. _langService.LanguageChanged += OnLanguageChanged;
  105. _themeService.PropertyChanged += OnThemeServicePropertyChanged;
  106. // PLC 连接状态监听
  107. PlcPollingService.Instance.ConnectionStateChanged += OnConnectionStateChanged;
  108. StartTimer();
  109. // 项目档案初始化
  110. InitializeProject();
  111. }
  112. /// <summary>
  113. /// 根据当前配置初始化项目 ViewModel
  114. /// </summary>
  115. private void InitializeProject()
  116. {
  117. var profile = ProjectService.Current;
  118. if (profile != null)
  119. {
  120. _projectViewModel = new ProjectViewModel(profile);
  121. CurrentView = _projectViewModel;
  122. HasProject = true;
  123. Log.Information("已初始化项目视图: {Name}", profile.Name);
  124. }
  125. }
  126. private void OnLanguageChanged()
  127. {
  128. RunOnUi(() =>
  129. {
  130. IsChinese = _langService.IsChinese;
  131. UpdateAllTexts();
  132. RefreshUserInfo();
  133. });
  134. }
  135. private void OnThemeServicePropertyChanged(object? sender, PropertyChangedEventArgs e)
  136. {
  137. if (e.PropertyName != nameof(ThemeService.IsDarkTheme)) return;
  138. RunOnUi(() =>
  139. {
  140. IsDarkTheme = _themeService.IsDarkTheme;
  141. ThemeIcon = IsDarkTheme ? "🌙" : "☀️";
  142. });
  143. }
  144. private void OnConnectionStateChanged(bool connected)
  145. {
  146. RunOnUi(() =>
  147. {
  148. PlcStatusText = connected ? _langService.Get("PlcConnectedStatus") : _langService.Get("PlcDisconnected");
  149. PlcStatusColor = connected ? "#27AE60" : "#E74C3C";
  150. IsPlcConnected = connected;
  151. });
  152. }
  153. private void RunOnUi(Action action)
  154. {
  155. if (_disposed) return;
  156. if (_uiContext != null && SynchronizationContext.Current != _uiContext)
  157. _uiContext.Post(_ =>
  158. {
  159. if (!_disposed) action();
  160. }, null);
  161. else
  162. action();
  163. }
  164. private void UpdateAllTexts()
  165. {
  166. Title = _langService.Get("AppName");
  167. TabTitle = _langService.Get("ProcessFlow");
  168. NavProcess = _langService.Get("NavProcess");
  169. NavParams = _langService.Get("NavParams");
  170. NavFlow = _langService.Get("NavFlow");
  171. NavAlarm = _langService.Get("NavAlarm");
  172. NavAbout = _langService.Get("NavAbout");
  173. NavAudit = _langService.Get("AuditLog");
  174. UserManageText = _langService.Get("UserManage");
  175. CreateNewUserText = _langService.Get("CreateNewUser");
  176. UserNameLabel = _langService.Get("UserName");
  177. PasswordLabel = _langService.Get("Password");
  178. DisplayNameLabel = _langService.Get("DisplayName");
  179. RoleLabel = _langService.Get("Role");
  180. StatusLabel = _langService.Get("Status");
  181. LastLoginLabel = _langService.Get("LastLogin");
  182. ActionLabel = _langService.Get("Action");
  183. CreateText = _langService.Get("Create");
  184. CloseText = _langService.Get("Close");
  185. LogoutText = _langService.Get("Logout");
  186. IsChinese = _langService.IsChinese;
  187. LangIcon = IsChinese ? "中" : "EN";
  188. IsDarkTheme = _themeService.IsDarkTheme;
  189. ThemeIcon = IsDarkTheme ? "🌙" : "☀️";
  190. }
  191. /// <summary>
  192. /// 加载运行模式
  193. /// </summary>
  194. private void LoadRunMode()
  195. {
  196. var config = ConfigService.GetConfig();
  197. RunMode = config?.RunMode ?? "Direct";
  198. HubStatusText = RunMode switch
  199. {
  200. "Server" => $"{_langService.Get("ServerMode")} :{config?.HubPort ?? 8765}",
  201. "Client" => $"{_langService.Get("ClientMode")} → {config?.HubServerUrl ?? "ws://localhost:8765"}",
  202. "Mock" => _langService.Get("MockMode"),
  203. _ => _langService.Get("DirectMode")
  204. };
  205. }
  206. [RelayCommand]
  207. private void ToggleTheme()
  208. {
  209. _themeService.ToggleTheme();
  210. }
  211. [RelayCommand]
  212. private void ToggleLanguage()
  213. {
  214. _langService.ToggleLanguage();
  215. }
  216. [RelayCommand]
  217. private async Task ConnectPlcAsync() => await _viewAViewModel.ConnectPlcCommand.ExecuteAsync(null);
  218. [RelayCommand]
  219. private void DisconnectPlc() => _viewAViewModel.DisconnectPlcCommand.Execute(null);
  220. [RelayCommand]
  221. private async Task RefreshDataAsync() => await _viewAViewModel.RefreshDataCommand.ExecuteAsync(null);
  222. // ─── Tab 切换 ───
  223. private readonly string[] _tabTitleKeys = { "ProcessFlow", "Parameters", "FlowRecords", "AlarmLog", "About", "AuditLog" };
  224. private void SetActiveTab(int index)
  225. {
  226. IsTabAActive = index == 0;
  227. IsTabBActive = index == 1;
  228. IsTabCActive = index == 2;
  229. IsTabDActive = index == 3;
  230. IsTabEActive = index == 4;
  231. IsTabFActive = index == 5;
  232. TabTitle = _langService.Get(_tabTitleKeys[index]);
  233. }
  234. [RelayCommand] private void ShowViewA()
  235. {
  236. CurrentView = HasProject && _projectViewModel != null
  237. ? _projectViewModel
  238. : _viewAViewModel;
  239. SetActiveTab(0);
  240. }
  241. [RelayCommand] private void ShowViewB() { CurrentView = _viewBViewModel; SetActiveTab(1); }
  242. [RelayCommand] private void ShowViewC() { CurrentView = _viewCViewModel; SetActiveTab(2); }
  243. [RelayCommand] private void ShowViewD() { CurrentView = _viewDViewModel; SetActiveTab(3); }
  244. [RelayCommand] private void ShowViewE() { CurrentView = _viewEViewModel; SetActiveTab(4); }
  245. [RelayCommand] private void ShowViewF() { CurrentView = _viewFViewModel; SetActiveTab(5); }
  246. private void StartTimer()
  247. {
  248. _clockTimer = new System.Timers.Timer(1000);
  249. _clockTimer.Elapsed += (s, e) =>
  250. {
  251. var time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  252. RunOnUi(() => CurrentTime = time);
  253. };
  254. _clockTimer.Start();
  255. }
  256. private void StopTimer()
  257. {
  258. _clockTimer?.Stop();
  259. _clockTimer?.Dispose();
  260. _clockTimer = null;
  261. }
  262. private void RefreshUserInfo()
  263. {
  264. var user = AuthService.CurrentUser;
  265. if (user != null)
  266. {
  267. // 管理员显示名跟随语言更新
  268. if (user.UserName == "admin")
  269. {
  270. var adminDisplayName = _langService.Get("AdminName");
  271. if (user.DisplayName != adminDisplayName)
  272. {
  273. user.DisplayName = adminDisplayName;
  274. DatabaseService.Db.Updateable(user).UpdateColumns(u => u.DisplayName).ExecuteCommand();
  275. }
  276. }
  277. CurrentUserName = user.DisplayName;
  278. CurrentUserRole = user.Role switch
  279. {
  280. UserRole.Viewer => _langService.Get("Viewer"),
  281. UserRole.Operator => _langService.Get("OperatorRole"),
  282. UserRole.Engineer => _langService.Get("Engineer"),
  283. UserRole.Admin => _langService.Get("AdminRole"),
  284. _ => "Unknown"
  285. };
  286. CanControl = AuthService.HasPermission(UserRole.Operator);
  287. CanModifyParams = AuthService.HasPermission(UserRole.Engineer);
  288. CanManageUsers = AuthService.HasPermission(UserRole.Admin);
  289. }
  290. }
  291. [RelayCommand]
  292. private void Logout()
  293. {
  294. AuthService.Logout();
  295. }
  296. [RelayCommand]
  297. private void ToggleUserDialog()
  298. {
  299. ShowUserDialog = !ShowUserDialog;
  300. if (ShowUserDialog)
  301. {
  302. LoadUsers();
  303. }
  304. }
  305. // ═══ 用户管理 ═══
  306. [ObservableProperty] private List<User> _userList = new();
  307. [ObservableProperty] private string _newUserName = string.Empty;
  308. [ObservableProperty] private string _newUserPassword = string.Empty;
  309. [ObservableProperty] private string _newUserDisplayName = string.Empty;
  310. [ObservableProperty] private UserRole _newUserRole = UserRole.Operator;
  311. [ObservableProperty] private string _userManageMessage = string.Empty;
  312. private void LoadUsers()
  313. {
  314. UserList = AuthService.GetAllUsers();
  315. }
  316. [RelayCommand]
  317. private void CreateUser()
  318. {
  319. if (string.IsNullOrWhiteSpace(NewUserName) || string.IsNullOrWhiteSpace(NewUserPassword))
  320. {
  321. UserManageMessage = _langService.Get("UserNamePasswordRequired");
  322. return;
  323. }
  324. var (success, message) = AuthService.CreateUser(NewUserName, NewUserPassword, NewUserDisplayName, NewUserRole);
  325. UserManageMessage = message;
  326. if (success)
  327. {
  328. NewUserName = string.Empty;
  329. NewUserPassword = string.Empty;
  330. NewUserDisplayName = string.Empty;
  331. NewUserRole = UserRole.Operator;
  332. LoadUsers();
  333. }
  334. }
  335. [RelayCommand]
  336. private void ToggleUserActive(User? user)
  337. {
  338. if (user == null) return;
  339. AuthService.SetUserActive(user.UserName, !user.IsActive);
  340. LoadUsers();
  341. }
  342. public void Dispose()
  343. {
  344. if (_disposed) return;
  345. _disposed = true;
  346. StopTimer();
  347. _langService.LanguageChanged -= OnLanguageChanged;
  348. _themeService.PropertyChanged -= OnThemeServicePropertyChanged;
  349. PlcPollingService.Instance.ConnectionStateChanged -= OnConnectionStateChanged;
  350. _viewAViewModel.Dispose();
  351. _viewBViewModel.Dispose();
  352. _viewCViewModel.Dispose();
  353. _viewDViewModel.Dispose();
  354. _viewEViewModel.Dispose();
  355. _viewFViewModel.Dispose();
  356. _projectViewModel?.Dispose();
  357. }
  358. }