跳到主要内容

github/tai

公开

Tai · Windows 软件与网站使用时长统计。上游 Planshit/Tai 的源码镜像,非灵栈原创;MIT 许可。来源:https://github.com/Planshit/Tai 。本次为手动同步,暂不提供安装包。

浏览文件

DetailPageVM.cs

721 行 · 22.8 KB历史下载
1using Core.Librarys;
2using Core.Models;
3using Core.Models.Config;
4using Core.Servicers.Instances;
5using Core.Servicers.Interfaces;
6using System;
7using System.Collections.Generic;
8using System.IO;
9using System.Linq;
10using System.Text;
11using System.Text.RegularExpressions;
12using System.Threading.Tasks;
13using System.Windows;
14using System.Windows.Controls;
15using UI.Controls;
16using UI.Controls.Charts.Model;
17using UI.Controls.Select;
18using UI.Models;
19using UI.Servicers;
20
21namespace UI.ViewModels
22{
23 public class DetailPageVM : DetailPageModel
24 {
25 private readonly IData data;
26 private readonly MainViewModel main;
27 private readonly IAppConfig appConfig;
28 private readonly ICategorys categories;
29 private readonly IAppData appData;
30 private readonly IInputServicer inputServicer;
31 private readonly IUIServicer _uIServicer;
32
33 private ConfigModel config;
34 public Command BlockActionCommand { get; set; }
35 public Command ClearSelectMonthDataCommand { get; set; }
36 public Command RefreshCommand { get; set; }
37 private MenuItem _setCategoryMenuItem;
38 private MenuItem _whiteListMenuItem;
39 public DetailPageVM(
40 IData data,
41 MainViewModel main,
42 IAppConfig appConfig,
43 ICategorys categories,
44 IAppData appData,
45 IInputServicer inputServicer,
46 IUIServicer uIServicer_)
47 {
48 this.data = data;
49 this.main = main;
50 this.appConfig = appConfig;
51 this.categories = categories;
52 this.appData = appData;
53 this.inputServicer = inputServicer;
54 _uIServicer = uIServicer_;
55
56 BlockActionCommand = new Command(new Action<object>(OnBlockActionCommand));
57 ClearSelectMonthDataCommand = new Command(new Action<object>(OnClearSelectMonthDataCommand));
58 RefreshCommand = new Command(new Action<object>(OnRefreshCommand));
59
60 Init();
61 }
62
63
64
65 private async void Init()
66 {
67 App = main.Data as AppModel;
68
69 Date = DateTime.Now;
70
71 TabbarData = new System.Collections.ObjectModel.ObservableCollection<string>()
72 {
73 "按天","按周","按月","按年"
74 };
75
76 var weekOptions = new List<SelectItemModel>();
77 weekOptions.Add(new SelectItemModel()
78 {
79 Name = "本周"
80 });
81 weekOptions.Add(new SelectItemModel()
82 {
83 Name = "上周"
84 });
85
86 WeekOptions = weekOptions;
87
88 SelectedWeek = weekOptions[0];
89
90 MonthDate = DateTime.Now;
91
92 TabbarSelectedIndex = 0;
93
94 Date = DateTime.Now;
95
96 YearDate = DateTime.Now;
97
98 ChartDate = DateTime.Now;
99
100 PropertyChanged += DetailPageVM_PropertyChanged;
101
102 config = appConfig.GetConfig();
103
104 await LoadData();
105
106 await LoadInfo();
107
108 LoadDayData();
109
110 inputServicer.OnKeyUpInput += InputServicer_OnKeyUpInput;
111
112 // 判断正则忽略
113 var regexList = config.Behavior.IgnoreProcessList.Where(m => Regex.IsMatch(m, @"[\.|\*|\?|\{|\\|\[|\^|\|]"));
114 foreach (string reg in regexList)
115 {
116 if (RegexHelper.IsMatch(App.Name, reg) || RegexHelper.IsMatch(App.File, reg))
117 {
118 IsRegexIgnore = true;
119 break;
120 }
121 }
122
123 CreateContextMenu();
124 }
125
126 private void CreateContextMenu()
127 {
128 AppContextMenu = new System.Windows.Controls.ContextMenu();
129 AppContextMenu.Opened += AppContextMenu_Opened;
130 MenuItem open = new MenuItem();
131 open.Header = "启动应用";
132 open.Click += (e, c) =>
133 {
134 OnInfoMenuActionCommand("open exe");
135 };
136
137 MenuItem copyProcessName = new MenuItem();
138 copyProcessName.Header = "复制应用进程名称";
139 copyProcessName.Click += (e, c) =>
140 {
141 OnInfoMenuActionCommand("copy processname");
142 };
143
144 MenuItem copyProcessFile = new MenuItem();
145 copyProcessFile.Header = "复制应用文件路径";
146 copyProcessFile.Click += (e, c) =>
147 {
148 OnInfoMenuActionCommand("copy process file");
149 };
150
151 MenuItem openDir = new MenuItem();
152 openDir.Header = "打开应用所在目录";
153 openDir.Click += (e, c) =>
154 {
155 OnInfoMenuActionCommand("open dir");
156 };
157
158 MenuItem reLoadData = new MenuItem();
159 reLoadData.Header = "刷新";
160 reLoadData.Click += async (e, c) =>
161 {
162 LoadChartData();
163 await LoadData();
164 };
165
166 MenuItem clear = new MenuItem();
167 clear.Header = "清空统计";
168 clear.Click += ClearAppData_Click;
169
170 _setCategoryMenuItem = new MenuItem();
171 _setCategoryMenuItem.Header = "设置分类";
172 MenuItem editAlias = new MenuItem();
173 editAlias.Header = "编辑别名";
174 editAlias.Click += EditAlias_ClickAsync;
175
176 _whiteListMenuItem = new MenuItem();
177 _whiteListMenuItem.Click += (e, c) =>
178 {
179 if (config.Behavior.ProcessWhiteList.Contains(App.Name))
180 {
181 config.Behavior.ProcessWhiteList.Remove(App.Name);
182 main.Toast($"已从白名单移除此应用 {App.Description}", Controls.Window.ToastType.Success);
183 }
184 else
185 {
186 config.Behavior.ProcessWhiteList.Add(App.Name);
187 main.Toast($"已添加至白名单 {App.Description}", Controls.Window.ToastType.Success);
188 }
189 };
190
191 AppContextMenu.Items.Add(open);
192 AppContextMenu.Items.Add(new Separator());
193 AppContextMenu.Items.Add(reLoadData);
194 AppContextMenu.Items.Add(new Separator());
195 AppContextMenu.Items.Add(_setCategoryMenuItem);
196 AppContextMenu.Items.Add(editAlias);
197 AppContextMenu.Items.Add(new Separator());
198 AppContextMenu.Items.Add(copyProcessName);
199 AppContextMenu.Items.Add(copyProcessFile);
200 AppContextMenu.Items.Add(openDir);
201 AppContextMenu.Items.Add(new Separator());
202 AppContextMenu.Items.Add(clear);
203 AppContextMenu.Items.Add(_whiteListMenuItem);
204 }
205
206 private async void EditAlias_ClickAsync(object sender, RoutedEventArgs e)
207 {
208 var app = appData.GetApp(App.ID);
209 try
210 {
211 string input = await _uIServicer.ShowInputModalAsync("修改别名", "请输入别名", app.Alias, (val) =>
212 {
213 if (val.Length > 15)
214 {
215 main.Error("别名最大长度为15位字符");
216 return false;
217 }
218 return true;
219 });
220
221 // 开始更新别名
222 app.Alias = input;
223 appData.UpdateApp(app);
224 App = app;
225
226 main.Success("别名已更新");
227 }
228 catch
229 {
230 // 输入取消,无需处理异常
231 }
232 }
233
234
235 private async void ClearAppData_Click(object sender, RoutedEventArgs e)
236 {
237 bool isConfirm = await _uIServicer.ShowConfirmDialogAsync("清空确认", "是否确定清空此应用的所有统计数据?");
238 if (isConfirm)
239 {
240 data.Clear(App.ID);
241 LoadChartData();
242 await LoadData();
243 main.Toast("操作已执行", Controls.Window.ToastType.Success, Controls.Base.IconTypes.Accept);
244 }
245 }
246
247 private void AppContextMenu_Opened(object sender, RoutedEventArgs e)
248 {
249 _setCategoryMenuItem.Items.Clear();
250
251 foreach (var category in categories.GetCategories())
252 {
253 var categoryMenu = new MenuItem();
254 categoryMenu.Header = category.Name;
255 categoryMenu.IsChecked = App.CategoryID == category.ID;
256 categoryMenu.Click += (s, ea) =>
257 {
258 UpdateCategory(category);
259 };
260 _setCategoryMenuItem.Items.Add(categoryMenu);
261 }
262
263 _setCategoryMenuItem.IsEnabled = _setCategoryMenuItem.Items.Count > 0;
264
265 if (config.Behavior.ProcessWhiteList.Contains(App.Name))
266 {
267 _whiteListMenuItem.Header = "从白名单移除";
268 }
269 else
270 {
271 _whiteListMenuItem.Header = "添加到白名单";
272 }
273 }
274
275 /// <summary>
276 /// 更新分类
277 /// </summary>
278 private void UpdateCategory(CategoryModel category_)
279 {
280 Task.Run(() =>
281 {
282 var app = appData.GetApp(App.ID);
283 app.CategoryID = category_.ID;
284 app.Category = category_;
285 appData.UpdateApp(app);
286
287 if (Category == null || category_.ID != Category.Id)
288 {
289 Category = Categorys.Where(m => m.Id == category_.ID).FirstOrDefault();
290 }
291 });
292 }
293
294 private async void InputServicer_OnKeyUpInput(object sender, System.Windows.Forms.Keys key)
295 {
296 if (key == System.Windows.Forms.Keys.F5)
297 {
298 // 刷新
299 LoadChartData();
300 await LoadData();
301 }
302 }
303
304 private async void OnRefreshCommand(object obj)
305 {
306 // 刷新
307 LoadChartData();
308 await LoadData();
309 }
310
311 private void OnInfoMenuActionCommand(string action_)
312 {
313 switch (action_)
314 {
315 case "copy processname":
316 Clipboard.SetText(App.Name);
317
318 break;
319 case "copy process file":
320 Clipboard.SetText(App.File);
321 break;
322 case "open dir":
323 if (File.Exists(App.File))
324 {
325 System.Diagnostics.Process.Start("explorer.exe", "/select, " + App.File);
326 }
327 else
328 {
329 main.Toast("应用文件似乎不存在", Controls.Window.ToastType.Error, Controls.Base.IconTypes.IncidentTriangle);
330 }
331 break;
332 case "open exe":
333 if (File.Exists(App.File))
334 {
335 System.Diagnostics.Process.Start(App.File);
336 }
337 else
338 {
339 main.Toast("应用似乎不存在", Controls.Window.ToastType.Error, Controls.Base.IconTypes.IncidentTriangle);
340 }
341 break;
342 //case "geticon":
343 // string iconFile = Iconer.ExtractFromFile(App.File, App.Name, App.Description, false);
344 // if (string.IsNullOrEmpty(iconFile))
345 // {
346 // main.Toast("图标获取失败", Controls.Window.ToastType.Error, Controls.Base.IconTypes.Blocked);
347 // return;
348 // }
349 // var app = appData.GetApp(App.ID);
350 // if (app != null)
351 // {
352 // app.IconFile = iconFile;
353 // appData.UpdateApp(app);
354 // main.Toast("图标已更新", Controls.Window.ToastType.Success, Controls.Base.IconTypes.Accept);
355 // }
356
357 // break;
358 }
359 }
360
361 private async void OnClearSelectMonthDataCommand(object obj)
362 {
363 bool isConfirm = await _uIServicer.ShowConfirmDialogAsync("清空确认", $"是否确定清空此应用 {Date.Year}年{Date.Month}月 的数据?");
364 if (isConfirm)
365 {
366 await Clear();
367 }
368 }
369
370 private void OnBlockActionCommand(object obj)
371 {
372 if (obj.ToString() == "block")
373 {
374 config.Behavior.IgnoreProcessList.Add(App.Name);
375 IsIgnore = true;
376 main.Toast("应用已忽略", Controls.Window.ToastType.Success);
377 }
378 else
379 {
380 config.Behavior.IgnoreProcessList.Remove(App.Name);
381 IsIgnore = false;
382 main.Toast("已取消忽略", Controls.Window.ToastType.Success);
383 }
384
385 appConfig.Save();
386 }
387
388 private async void DetailPageVM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
389 {
390 if (e.PropertyName == nameof(Date))
391 {
392 await LoadData();
393 }
394
395 if (e.PropertyName == nameof(Category))
396 {
397 if (App.Category == null && Category != null || Category != null && App.Category.Name != Category.Name)
398 {
399 App.Category = Category.Data as CategoryModel;
400
401 var app = appData.GetApp(App.ID);
402 if (app != null)
403 {
404 app.CategoryID = App.Category.ID;
405 appData.UpdateApp(app);
406 }
407 }
408
409 }
410
411 // 处理图表数据加载
412 if (e.PropertyName == nameof(ChartDate))
413 {
414 LoadDayData();
415 }
416 if (e.PropertyName == nameof(TabbarSelectedIndex))
417 {
418 LoadChartData();
419
420 }
421 if (e.PropertyName == nameof(SelectedWeek))
422 {
423 LoadWeekData();
424 }
425 if (e.PropertyName == nameof(MonthDate))
426 {
427 LoadMonthlyData();
428 }
429 if (e.PropertyName == nameof(YearDate))
430 {
431 LoadYearData();
432 }
433 }
434
435 /// <summary>
436 /// 加载柱状图图表数据
437 /// </summary>
438 private void LoadChartData()
439 {
440 if (TabbarSelectedIndex == 0)
441 {
442 NameIndexStart = 0;
443
444 LoadDayData();
445 }
446 else if (TabbarSelectedIndex == 1)
447 {
448 LoadWeekData();
449 }
450 else if (TabbarSelectedIndex == 2)
451 {
452 NameIndexStart = 1;
453
454 LoadMonthlyData();
455 }
456 else if (TabbarSelectedIndex == 3)
457 {
458 LoadYearData();
459 }
460 }
461
462 private void LoadCategorys(string categoryName)
463 {
464 var list = new List<SelectItemModel>();
465 foreach (var item in categories.GetCategories())
466 {
467 var option = new SelectItemModel()
468 {
469 Id = item.ID,
470 Data = item,
471 Img = item.IconFile,
472 Name = item.Name,
473 };
474 list.Add(option);
475 if (categoryName == option.Name)
476 {
477 Category = option;
478 }
479 }
480
481 Categorys = list;
482
483 }
484
485 private async Task LoadInfo()
486 {
487 await Task.Run(() =>
488 {
489 if (App != null)
490 {
491
492 // 判断是否是忽略的进程
493 IsIgnore = config.Behavior.IgnoreProcessList.Contains(App.Name);
494
495 LoadCategorys(App.Category?.Name);
496
497 // 读取关联应用数据
498 var link = config.Links.Where(m => m.ProcessList.Contains(App.Name)).FirstOrDefault();
499 if (link != null)
500 {
501 LinkApps = appData.GetAllApps().Where(m => link.ProcessList.Contains(m.Name) && m.Name != App.Name).ToList();
502 }
503 }
504 }
505 );
506 }
507
508 private async Task LoadData()
509 {
510 await Task.Run(() =>
511 {
512 if (App != null)
513 {
514
515 var monthData = data.GetProcessMonthLogList(App.ID, Date);
516 int monthTotal = monthData.Sum(m => m.Time);
517 Total = Time.ToString(monthTotal);
518
519 DateTime start = new DateTime(Date.Year, Date.Month, 1);
520 DateTime end = new DateTime(Date.Year, Date.Month, DateTime.DaysInMonth(Date.Year, Date.Month));
521 var monthAllData = data.GetDateRangelogList(start, end);
522
523 LongDay = "暂无数据";
524 if (monthData.Count > 0)
525 {
526 var longDayData = monthData.OrderByDescending(m => m.Time).FirstOrDefault();
527 LongDay = longDayData.Date.ToString("最长一天是在 dd 号,使用了 " + Time.ToString(longDayData.Time));
528
529 }
530
531
532 int monthAllTotal = monthAllData.Sum(m => m.Time);
533 if (monthAllTotal > 0)
534 {
535 Ratio = (((double)monthTotal / (double)monthAllTotal)).ToString("P");
536 }
537 else
538 {
539 Ratio = "暂无数据";
540 }
541 Data = MapToChartsData(monthData);
542 if (Data.Count == 0)
543 {
544 Data.Add(new ChartsDataModel()
545 {
546 DateTime = Date,
547 });
548 }
549 }
550 });
551 }
552
553 private async Task Clear()
554 {
555 await Task.Run(async () =>
556 {
557 if (App != null)
558 {
559 main.Toast("正在处理");
560 data.Clear(App.ID, Date);
561
562 await LoadData();
563 await LoadInfo();
564 main.Toast("已清空");
565 }
566 }
567 );
568 }
569
570
571 private List<ChartsDataModel> MapToChartsData(List<Core.Models.DailyLogModel> list)
572 {
573 var resData = new List<ChartsDataModel>();
574
575 foreach (var item in list)
576 {
577 var bindModel = new ChartsDataModel();
578 bindModel.Data = item;
579 bindModel.Name = !string.IsNullOrEmpty(item.AppModel?.Alias) ? item.AppModel.Alias : string.IsNullOrEmpty(item.AppModel?.Description) ? item.AppModel.Name : item.AppModel.Description;
580 bindModel.Value = item.Time;
581 bindModel.Tag = Time.ToString(item.Time);
582 bindModel.PopupText = item.AppModel.File;
583 bindModel.Icon = item.AppModel.IconFile;
584 bindModel.DateTime = item.Date;
585 resData.Add(bindModel);
586 }
587
588 return resData;
589 }
590
591 #region 柱状图图表数据加载
592 /// <summary>
593 /// 加载天数据
594 /// </summary>
595 private void LoadDayData()
596 {
597 DataMaximum = 3600;
598 Task.Run(() =>
599 {
600 var list = data.GetAppDayData(App.ID, ChartDate);
601
602 var chartData = new List<ChartsDataModel>();
603
604 foreach (var item in list)
605 {
606
607 chartData.Add(new ChartsDataModel()
608 {
609 Name = App.Description,
610 Icon = App.IconFile,
611 Values = item.Values,
612 });
613 }
614
615 ChartData = chartData;
616 });
617 }
618
619 /// <summary>
620 /// 加载周数据
621 /// </summary>
622 private void LoadWeekData()
623 {
624 DataMaximum = 0;
625 Task.Run(() =>
626 {
627 var weekDateArr = SelectedWeek.Name == "本周" ? Time.GetThisWeekDate() : Time.GetLastWeekDate();
628
629 WeekDateStr = weekDateArr[0].ToString("yyyy年MM月dd日") + " 到 " + weekDateArr[1].ToString("yyyy年MM月dd日");
630
631 var list = data.GetAppRangeData(App.ID, weekDateArr[0], weekDateArr[1]);
632
633 var chartData = new List<ChartsDataModel>();
634
635 string[] weekNames = { "周一", "周二", "周三", "周四", "周五", "周六", "周日", };
636 foreach (var item in list)
637 {
638
639 chartData.Add(new ChartsDataModel()
640 {
641
642 Name = App.Description,
643 Icon = App.IconFile,
644 Values = item.Values,
645 ColumnNames = weekNames
646 });
647 }
648
649 ChartData = chartData;
650 });
651 }
652
653 /// <summary>
654 /// 加载月数据
655 /// </summary>
656 private void LoadMonthlyData()
657 {
658 DataMaximum = 0;
659 Task.Run(() =>
660 {
661 var dateArr = Time.GetMonthDate(MonthDate);
662
663 var list = data.GetAppRangeData(App.ID, dateArr[0], dateArr[1]);
664
665 var chartData = new List<ChartsDataModel>();
666
667 foreach (var item in list)
668 {
669
670 chartData.Add(new ChartsDataModel()
671 {
672
673 Name = App.Description,
674 Icon = App.IconFile,
675 Values = item.Values,
676 });
677 }
678
679 ChartData = chartData;
680
681 });
682
683 }
684
685 /// <summary>
686 /// 加载年份数据
687 /// </summary>
688 private void LoadYearData()
689 {
690 DataMaximum = 0;
691 Task.Run(() =>
692 {
693 var list = data.GetAppYearData(App.ID, YearDate);
694
695 var chartData = new List<ChartsDataModel>();
696
697 string[] names = new string[12];
698 for (int i = 0; i < 12; i++)
699 {
700 names[i] = (i + 1) + "月";
701 }
702
703 foreach (var item in list)
704 {
705
706 chartData.Add(new ChartsDataModel()
707 {
708 Name = App.Description,
709 Icon = App.IconFile,
710 Values = item.Values,
711 ColumnNames = names
712 });
713 }
714
715 ChartData = chartData;
716
717 });
718 }
719 #endregion
720 }
721}