初期稳定版本260119
This commit is contained in:
@@ -13,6 +13,7 @@ using System.Windows;
|
||||
using System.Text;
|
||||
using Prism.Events;
|
||||
using FATrace.WPLApp.Events;
|
||||
using System.Threading;
|
||||
|
||||
namespace FATrace.WPLApp.ViewModels
|
||||
{
|
||||
@@ -22,6 +23,9 @@ namespace FATrace.WPLApp.ViewModels
|
||||
private readonly ILogService _log;
|
||||
private readonly DataServices _data;
|
||||
|
||||
private int _dashboardRefreshRequested;
|
||||
private int _dashboardRefreshLoopRunning;
|
||||
|
||||
private DispatcherTimer _logTimer;
|
||||
private bool _initialized;
|
||||
private TextWriter _originalConsoleOut;
|
||||
@@ -37,6 +41,7 @@ namespace FATrace.WPLApp.ViewModels
|
||||
_ea = ea;
|
||||
|
||||
LiveMessages = new ObservableCollection<string>();
|
||||
LineTempCodes = new ObservableCollection<LineTempCode>();
|
||||
|
||||
RefreshCommand = new DelegateCommand(async () => await RefreshStatsAsync());
|
||||
ClearLogsCommand = new DelegateCommand(() => LiveMessages.Clear());
|
||||
@@ -66,7 +71,13 @@ namespace FATrace.WPLApp.ViewModels
|
||||
};
|
||||
_data.LineSglModel.BoxSprayCodeReqHandle += (s, e) =>
|
||||
{
|
||||
AppendLiveMessage("外箱喷码请求: 已向PLC下发喷码数据");
|
||||
var code = _data.BoxSprayCode;
|
||||
if (Application.Current?.Dispatcher?.CheckAccess() == true)
|
||||
LatestBoxSprayCode = code;
|
||||
else
|
||||
Application.Current?.Dispatcher?.BeginInvoke(new Action(() => LatestBoxSprayCode = code));
|
||||
//AppendLiveMessage("外箱喷码请求: 已向PLC下发喷码数据");
|
||||
AppendLiveMessage($"外箱喷码请求: 已向PLC下发喷码数据;{code}");
|
||||
};
|
||||
_data.LineSglModel.BoxScanCodeReqHandle += (s, e) =>
|
||||
{
|
||||
@@ -81,12 +92,89 @@ namespace FATrace.WPLApp.ViewModels
|
||||
// 订阅外部刷新事件(来自 DataServices 的 BoxScanCode 完成后)
|
||||
try
|
||||
{
|
||||
_dashEventToken = _ea.GetEvent<DashboardRefreshEvent>().Subscribe(_ =>
|
||||
{
|
||||
Application.Current?.Dispatcher?.BeginInvoke(new Action(async () => await RefreshStatsAsync()));
|
||||
});
|
||||
EnsureDashboardRefreshSubscription();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log?.Warn($"DashboardRefreshEvent 订阅失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureDashboardRefreshSubscription()
|
||||
{
|
||||
if (_dashEventToken != null) return;
|
||||
|
||||
// 注意:Prism 默认 keepSubscriberReferenceAlive=false(弱引用)。
|
||||
// 若使用 lambda,目标可能是 closure 对象,存在被 GC 导致订阅失效的风险。
|
||||
// 这里使用具名方法 + keepSubscriberReferenceAlive=true,确保订阅稳定。
|
||||
_dashEventToken = _ea.GetEvent<DashboardRefreshEvent>()
|
||||
.Subscribe(OnDashboardRefreshEvent, ThreadOption.UIThread, true);
|
||||
}
|
||||
|
||||
private void OnDashboardRefreshEvent(bool _)
|
||||
{
|
||||
RequestDashboardRefresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发一次 Dashboard 刷新(合并 + 互斥):短时间多次触发只会串行执行,并在执行结束后最多再补跑一次。
|
||||
/// </summary>
|
||||
private void RequestDashboardRefresh()
|
||||
{
|
||||
// 标记有刷新请求
|
||||
Interlocked.Exchange(ref _dashboardRefreshRequested, 1);
|
||||
|
||||
// 若刷新循环已在跑,则仅标记请求即可
|
||||
if (Interlocked.Exchange(ref _dashboardRefreshLoopRunning, 1) == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var dispatcher = Application.Current?.Dispatcher;
|
||||
if (dispatcher == null)
|
||||
{
|
||||
// 极少数场景(无 UI Dispatcher),直接后台跑
|
||||
_ = DashboardRefreshLoopAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatcher.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await DashboardRefreshLoopAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _dashboardRefreshLoopRunning, 0);
|
||||
|
||||
// 如果在执行过程中又来了刷新请求,则再次启动一次循环(确保不漏刷新)
|
||||
if (Interlocked.Exchange(ref _dashboardRefreshRequested, 0) == 1)
|
||||
{
|
||||
RequestDashboardRefresh();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在 UI 线程串行执行刷新,且合并多次触发。
|
||||
/// </summary>
|
||||
private async Task DashboardRefreshLoopAsync()
|
||||
{
|
||||
// 只要存在刷新请求,就执行一次刷新;过程中再来请求,会在下一轮继续跑
|
||||
while (Interlocked.Exchange(ref _dashboardRefreshRequested, 0) == 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RefreshStatsAsync();
|
||||
await RefreshLineTempCodesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"DashboardRefreshEvent 刷新失败: {ex}");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
#region Properties
|
||||
@@ -108,17 +196,28 @@ namespace FATrace.WPLApp.ViewModels
|
||||
private string _latestBoxScanCode;
|
||||
public string LatestBoxScanCode { get => _latestBoxScanCode; set { _latestBoxScanCode = value; RaisePropertyChanged(); } }
|
||||
|
||||
private string _latestBoxSprayCode;
|
||||
/// <summary>
|
||||
/// 最近一次外箱喷码数据(即下发给 PLC 的源字符串)
|
||||
/// </summary>
|
||||
public string LatestBoxSprayCode { get => _latestBoxSprayCode; set { _latestBoxSprayCode = value; RaisePropertyChanged(); } }
|
||||
|
||||
private bool _plcConnected;
|
||||
public bool PlcConnected { get => _plcConnected; set { _plcConnected = value; RaisePropertyChanged(); } }
|
||||
|
||||
public ObservableCollection<string> LiveMessages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 产线临时条码队列(内包扫码入队,外箱扫码确认后出队)
|
||||
/// </summary>
|
||||
public ObservableCollection<LineTempCode> LineTempCodes { get; }
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
public DelegateCommand RefreshCommand { get; }
|
||||
public DelegateCommand ClearLogsCommand { get; }
|
||||
#endregion
|
||||
|
||||
|
||||
private void AppendLiveMessage(string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message)) return;
|
||||
@@ -186,8 +285,63 @@ namespace FATrace.WPLApp.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从数据库刷新产线临时队列(LineTempCode 表)并同步到 UI。
|
||||
/// </summary>
|
||||
private async Task RefreshLineTempCodesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var list = await Task.Run(() =>
|
||||
{
|
||||
return _fsql.Select<LineTempCode>()
|
||||
.OrderBy(a => a.Id)
|
||||
.Limit(200)
|
||||
.ToList();
|
||||
});
|
||||
|
||||
void apply()
|
||||
{
|
||||
LineTempCodes.Clear();
|
||||
foreach (var item in list)
|
||||
{
|
||||
LineTempCodes.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
var dispatcher = Application.Current?.Dispatcher;
|
||||
if (dispatcher == null)
|
||||
{
|
||||
apply();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatcher.CheckAccess())
|
||||
{
|
||||
apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
await dispatcher.InvokeAsync(apply).Task.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"刷新 LineTempCode 队列失败: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(Prism.Regions.NavigationContext navigationContext)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureDashboardRefreshSubscription();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log?.Warn($"DashboardRefreshEvent OnNavigatedTo 订阅失败: {ex.Message}");
|
||||
}
|
||||
|
||||
if (!_initialized)
|
||||
{
|
||||
_initialized = true;
|
||||
@@ -196,6 +350,8 @@ namespace FATrace.WPLApp.ViewModels
|
||||
// 初始化展示最近一次扫描值
|
||||
LatestWeightScanCode = _data.WeightScanCode;
|
||||
LatestBoxScanCode = _data.BoxScanCode;
|
||||
LatestBoxSprayCode = _data.BoxSprayCode;
|
||||
await RefreshLineTempCodesAsync();
|
||||
TryHookConsole();
|
||||
}
|
||||
}
|
||||
@@ -208,7 +364,18 @@ namespace FATrace.WPLApp.ViewModels
|
||||
_logTimer = null;
|
||||
}
|
||||
UnhookConsole();
|
||||
try { if (_dashEventToken != null) _ea.GetEvent<DashboardRefreshEvent>().Unsubscribe(_dashEventToken); } catch { }
|
||||
try
|
||||
{
|
||||
if (_dashEventToken != null)
|
||||
{
|
||||
_ea.GetEvent<DashboardRefreshEvent>().Unsubscribe(_dashEventToken);
|
||||
_dashEventToken = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log?.Warn($"DashboardRefreshEvent 退订失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void TryHookConsole()
|
||||
|
||||
207
FATrace.WPLApp/ViewModels/DialogWeightUserEditViewModel.cs
Normal file
207
FATrace.WPLApp/ViewModels/DialogWeightUserEditViewModel.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using FATrace.Model;
|
||||
using FATrace.WPLApp.Core;
|
||||
using FATrace.WPLApp.Services;
|
||||
using FreeSql;
|
||||
using Prism.Commands;
|
||||
using Prism.Services.Dialogs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace FATrace.WPLApp.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 称重用户新增/编辑 弹窗 VM
|
||||
/// </summary>
|
||||
public class DialogWeightUserEditViewModel : DialogViewModel
|
||||
{
|
||||
private readonly IFreeSql _fsql;
|
||||
private readonly ILogService _log;
|
||||
|
||||
private string _mode = WeightUserManageViewModel.DialogModes.Add;
|
||||
private long _editUserId;
|
||||
|
||||
public DialogWeightUserEditViewModel(IFreeSql fsql, ILogService log)
|
||||
{
|
||||
_fsql = fsql;
|
||||
_log = log;
|
||||
|
||||
SaveCommand = new DelegateCommand(async () => await SaveAsync(), () => !IsBusy)
|
||||
.ObservesProperty(() => IsBusy);
|
||||
CancelCommand = new DelegateCommand(() => OnDialogClosed(ButtonResult.Cancel));
|
||||
}
|
||||
|
||||
private string? _checkName;
|
||||
/// <summary>
|
||||
/// 确认者
|
||||
/// </summary>
|
||||
public string? CheckName
|
||||
{
|
||||
get => _checkName;
|
||||
set { _checkName = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private string? _opName;
|
||||
/// <summary>
|
||||
/// 操作者
|
||||
/// </summary>
|
||||
public string? OpName
|
||||
{
|
||||
get => _opName;
|
||||
set { _opName = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private string? _password;
|
||||
/// <summary>
|
||||
/// 密码(当前按现有模型使用明文;如后续需哈希,可在保存处替换)
|
||||
/// </summary>
|
||||
public string? Password
|
||||
{
|
||||
get => _password;
|
||||
set { _password = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存
|
||||
/// </summary>
|
||||
public DelegateCommand SaveCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 取消
|
||||
/// </summary>
|
||||
public DelegateCommand CancelCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 打开弹窗时初始化
|
||||
/// </summary>
|
||||
/// <param name="parameters">参数</param>
|
||||
public override void OnDialogOpened(IDialogParameters parameters)
|
||||
{
|
||||
_mode = parameters.GetValue<string>(WeightUserManageViewModel.DialogKeys.Mode) ?? WeightUserManageViewModel.DialogModes.Add;
|
||||
Title = _mode == WeightUserManageViewModel.DialogModes.Edit ? "编辑称重用户" : "新增称重用户";
|
||||
|
||||
if (_mode == WeightUserManageViewModel.DialogModes.Edit)
|
||||
{
|
||||
_editUserId = parameters.GetValue<long>(WeightUserManageViewModel.DialogKeys.UserId);
|
||||
LoadUser(_editUserId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_editUserId = 0;
|
||||
CheckName = string.Empty;
|
||||
OpName = string.Empty;
|
||||
Password = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadUser(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = _fsql.Select<TbWeightUser>().Where(a => a.Id == id).First();
|
||||
if (user == null)
|
||||
{
|
||||
MessageBox.Show("称重用户不存在或已被删除", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
CheckName = user.CheckName;
|
||||
OpName = user.OpName;
|
||||
Password = user.Password;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"加载称重用户失败: {ex}");
|
||||
MessageBox.Show($"加载称重用户失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
if (IsBusy) return;
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(CheckName))
|
||||
{
|
||||
MessageBox.Show("请输入确认者", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(OpName))
|
||||
{
|
||||
MessageBox.Show("请输入操作者", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Password))
|
||||
{
|
||||
MessageBox.Show("请输入密码", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
IsBusy = true;
|
||||
|
||||
if (_mode == WeightUserManageViewModel.DialogModes.Add)
|
||||
{
|
||||
await AddAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await UpdateAsync();
|
||||
}
|
||||
|
||||
OnDialogClosed(ButtonResult.OK);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"保存称重用户失败: {ex}");
|
||||
MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var entity = new TbWeightUser
|
||||
{
|
||||
CheckName = CheckName!,
|
||||
OpName = OpName!,
|
||||
Password = Password!,
|
||||
CreateTime = DateTime.Now
|
||||
};
|
||||
|
||||
_fsql.Insert(entity).ExecuteAffrows();
|
||||
});
|
||||
|
||||
_log.Info($"新增称重用户成功: {CheckName}/{OpName}");
|
||||
}
|
||||
|
||||
private async Task UpdateAsync()
|
||||
{
|
||||
var id = _editUserId;
|
||||
if (id <= 0) throw new InvalidOperationException("编辑称重用户Id无效");
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var aff = _fsql.Update<TbWeightUser>()
|
||||
.Set(a => a.CheckName, CheckName!)
|
||||
.Set(a => a.OpName, OpName!)
|
||||
.Set(a => a.Password, Password!)
|
||||
.Where(a => a.Id == id)
|
||||
.ExecuteAffrows();
|
||||
|
||||
if (aff <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("更新失败:称重用户不存在或未发生变化");
|
||||
}
|
||||
});
|
||||
|
||||
_log.Info($"更新称重用户成功: Id={id}, CheckName={CheckName}, OpName={OpName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,9 @@ namespace FATrace.WPLApp.ViewModels
|
||||
case "用户管理":
|
||||
this.regionManager.Regions["ContentRegion"].RequestNavigate("UserManageView");
|
||||
break;
|
||||
case "称重用户":
|
||||
this.regionManager.Regions["ContentRegion"].RequestNavigate("WeightUserManageView");
|
||||
break;
|
||||
case "使用手册":
|
||||
this.regionManager.Regions["ContentRegion"].RequestNavigate("HelpManualView");
|
||||
break;
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace FATrace.WPLApp.ViewModels
|
||||
private string? _origin;
|
||||
public string? Origin { get => _origin; set { _origin = value; RaisePropertyChanged(); } }
|
||||
|
||||
private string? _batch;
|
||||
public string? Batch { get => _batch; set { _batch = value; RaisePropertyChanged(); } }
|
||||
|
||||
private string? _rawCode;
|
||||
public string? RawCode { get => _rawCode; set { _rawCode = value; RaisePropertyChanged(); } }
|
||||
|
||||
@@ -107,7 +110,7 @@ namespace FATrace.WPLApp.ViewModels
|
||||
|
||||
private void ClearFilters()
|
||||
{
|
||||
Origin = RawCode = RawName = string.Empty;
|
||||
Origin = Batch = RawCode = RawName = string.Empty;
|
||||
StartDate = null;
|
||||
EndDate = null;
|
||||
}
|
||||
@@ -126,6 +129,8 @@ namespace FATrace.WPLApp.ViewModels
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Origin))
|
||||
q = q.Where(a => a.Origin != null && a.Origin.Contains(Origin));
|
||||
if (!string.IsNullOrWhiteSpace(Batch))
|
||||
q = q.Where(a => a.Batch != null && a.Batch.Contains(Batch));
|
||||
if (!string.IsNullOrWhiteSpace(RawCode))
|
||||
q = q.Where(a => a.RawCode != null && a.RawCode.Contains(RawCode));
|
||||
if (!string.IsNullOrWhiteSpace(RawName))
|
||||
|
||||
367
FATrace.WPLApp/ViewModels/WeightUserManageViewModel.cs
Normal file
367
FATrace.WPLApp/ViewModels/WeightUserManageViewModel.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
using FATrace.Model;
|
||||
using FATrace.WPLApp.Core;
|
||||
using FATrace.WPLApp.Services;
|
||||
using FreeSql;
|
||||
using Prism.Commands;
|
||||
using Prism.Services.Dialogs;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace FATrace.WPLApp.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 称重用户管理页面 VM(TbWeightUser 增删改查)
|
||||
/// </summary>
|
||||
public class WeightUserManageViewModel : NavigationViewModel
|
||||
{
|
||||
private readonly IFreeSql _fsql;
|
||||
private readonly ILogService _log;
|
||||
private readonly IDialogService _dialog;
|
||||
private readonly SysRunService _sys;
|
||||
|
||||
public WeightUserManageViewModel(IFreeSql fsql, ILogService log, IDialogService dialogService, SysRunService sys)
|
||||
{
|
||||
_fsql = fsql;
|
||||
_log = log;
|
||||
_dialog = dialogService;
|
||||
_sys = sys;
|
||||
|
||||
Items = new ObservableCollection<TbWeightUser>();
|
||||
|
||||
SearchCommand = new DelegateCommand(async () => await SearchAsync(), () => !IsBusy)
|
||||
.ObservesProperty(() => IsBusy);
|
||||
ClearCommand = new DelegateCommand(ClearFilters, () => !IsBusy)
|
||||
.ObservesProperty(() => IsBusy);
|
||||
|
||||
AddCommand = new DelegateCommand(AddUser, () => !IsBusy && CanEditUsers)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => CanEditUsers);
|
||||
EditCommand = new DelegateCommand(EditUser, () => !IsBusy && CanEditUsers && SelectedItem != null)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => CanEditUsers)
|
||||
.ObservesProperty(() => SelectedItem);
|
||||
DeleteCommand = new DelegateCommand(async () => await DeleteUserAsync(), () => !IsBusy && CanEditUsers && SelectedItem != null)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => CanEditUsers)
|
||||
.ObservesProperty(() => SelectedItem);
|
||||
|
||||
FirstPageCommand = new DelegateCommand(async () => { if (PageIndex == 1) return; PageIndex = 1; await SearchAsync(); }, () => !IsBusy && PageIndex > 1)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => PageIndex);
|
||||
PrevPageCommand = new DelegateCommand(async () => { if (PageIndex <= 1) return; PageIndex -= 1; await SearchAsync(); }, () => !IsBusy && PageIndex > 1)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => PageIndex);
|
||||
NextPageCommand = new DelegateCommand(async () => { if (PageIndex >= TotalPages) return; PageIndex += 1; await SearchAsync(); }, () => !IsBusy && PageIndex < TotalPages)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => PageIndex)
|
||||
.ObservesProperty(() => TotalPages);
|
||||
LastPageCommand = new DelegateCommand(async () => { if (TotalPages <= 0 || PageIndex == TotalPages) return; PageIndex = TotalPages; await SearchAsync(); }, () => !IsBusy && PageIndex < TotalPages)
|
||||
.ObservesProperty(() => IsBusy)
|
||||
.ObservesProperty(() => PageIndex)
|
||||
.ObservesProperty(() => TotalPages);
|
||||
|
||||
_sys.PropertyChanged += SysOnPropertyChanged;
|
||||
RaisePropertyChanged(nameof(CanEditUsers));
|
||||
}
|
||||
|
||||
private void SysOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(SysRunService.CurAccessLevel) || e.PropertyName == nameof(SysRunService.CurUser))
|
||||
{
|
||||
RaisePropertyChanged(nameof(CanEditUsers));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许编辑用户(仅管理员)
|
||||
/// </summary>
|
||||
public bool CanEditUsers => string.Equals(_sys.CurAccessLevel, UserManageViewModel.AccessLevels.Admin, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
#region 查询条件
|
||||
private string? _checkName;
|
||||
/// <summary>
|
||||
/// 确认者(模糊匹配)
|
||||
/// </summary>
|
||||
public string? CheckName
|
||||
{
|
||||
get => _checkName;
|
||||
set { _checkName = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private string? _opName;
|
||||
/// <summary>
|
||||
/// 操作者(模糊匹配)
|
||||
/// </summary>
|
||||
public string? OpName
|
||||
{
|
||||
get => _opName;
|
||||
set { _opName = value; RaisePropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 列表与分页
|
||||
public ObservableCollection<TbWeightUser> Items { get; }
|
||||
|
||||
private TbWeightUser? _selectedItem;
|
||||
/// <summary>
|
||||
/// 当前选中行
|
||||
/// </summary>
|
||||
public TbWeightUser? SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set { _selectedItem = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private bool _isBusy;
|
||||
/// <summary>
|
||||
/// 是否忙碌
|
||||
/// </summary>
|
||||
public bool IsBusy
|
||||
{
|
||||
get => _isBusy;
|
||||
set { _isBusy = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private int _totalCount;
|
||||
/// <summary>
|
||||
/// 总数
|
||||
/// </summary>
|
||||
public int TotalCount
|
||||
{
|
||||
get => _totalCount;
|
||||
set { _totalCount = value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private int _pageIndex = 1;
|
||||
/// <summary>
|
||||
/// 页码(从1开始)
|
||||
/// </summary>
|
||||
public int PageIndex
|
||||
{
|
||||
get => _pageIndex;
|
||||
set { _pageIndex = value < 1 ? 1 : value; RaisePropertyChanged(); }
|
||||
}
|
||||
|
||||
private int _pageSize = 20;
|
||||
/// <summary>
|
||||
/// 页大小
|
||||
/// </summary>
|
||||
public int PageSize
|
||||
{
|
||||
get => _pageSize;
|
||||
set
|
||||
{
|
||||
var v = value <= 0 ? 20 : value;
|
||||
if (_pageSize != v)
|
||||
{
|
||||
_pageSize = v;
|
||||
RaisePropertyChanged();
|
||||
PageIndex = 1;
|
||||
if (!IsBusy) _ = SearchAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _totalPages;
|
||||
/// <summary>
|
||||
/// 总页数
|
||||
/// </summary>
|
||||
public int TotalPages
|
||||
{
|
||||
get => _totalPages;
|
||||
set { _totalPages = value; RaisePropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ClearCommand { get; }
|
||||
public DelegateCommand AddCommand { get; }
|
||||
public DelegateCommand EditCommand { get; }
|
||||
public DelegateCommand DeleteCommand { get; }
|
||||
|
||||
public DelegateCommand FirstPageCommand { get; }
|
||||
public DelegateCommand PrevPageCommand { get; }
|
||||
public DelegateCommand NextPageCommand { get; }
|
||||
public DelegateCommand LastPageCommand { get; }
|
||||
#endregion
|
||||
|
||||
private void ClearFilters()
|
||||
{
|
||||
CheckName = string.Empty;
|
||||
OpName = string.Empty;
|
||||
}
|
||||
|
||||
private async Task SearchAsync()
|
||||
{
|
||||
if (IsBusy) return;
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await SearchCoreAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"TbWeightUser 查询失败: {ex}");
|
||||
MessageBox.Show($"查询失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SearchCoreAsync()
|
||||
{
|
||||
_log.Info("TbWeightUser 查询开始");
|
||||
|
||||
var data = await Task.Run(() =>
|
||||
{
|
||||
var q = _fsql.Select<TbWeightUser>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(CheckName))
|
||||
q = q.Where(a => a.CheckName != null && a.CheckName.Contains(CheckName));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(OpName))
|
||||
q = q.Where(a => a.OpName != null && a.OpName.Contains(OpName));
|
||||
|
||||
q = q.OrderByDescending(a => a.Id);
|
||||
|
||||
var page = PageIndex < 1 ? 1 : PageIndex;
|
||||
var size = PageSize <= 0 ? 20 : PageSize;
|
||||
|
||||
var list = q.Count(out var total)
|
||||
.Page(page, size)
|
||||
.ToList();
|
||||
|
||||
var pages = total <= 0 || size <= 0 ? 0 : (int)Math.Ceiling(total * 1.0 / size);
|
||||
if (pages > 0 && page > pages)
|
||||
{
|
||||
page = pages;
|
||||
list = q.Page(page, size).ToList();
|
||||
}
|
||||
|
||||
return (items: list, total: (int)total, normalizedPage: page, totalPages: pages);
|
||||
});
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Items.Clear();
|
||||
foreach (var it in data.items) Items.Add(it);
|
||||
TotalCount = data.total;
|
||||
TotalPages = data.totalPages;
|
||||
PageIndex = data.normalizedPage == 0 ? 1 : data.normalizedPage;
|
||||
});
|
||||
|
||||
_log.Info($"TbWeightUser 查询完成,记录数: {TotalCount}");
|
||||
}
|
||||
|
||||
private void AddUser()
|
||||
{
|
||||
var p = new DialogParameters
|
||||
{
|
||||
{ DialogKeys.Mode, DialogModes.Add }
|
||||
};
|
||||
|
||||
_dialog.ShowDialog("DialogWeightUserEditView", p, async r =>
|
||||
{
|
||||
if (r.Result == ButtonResult.OK)
|
||||
{
|
||||
PageIndex = 1;
|
||||
await SearchAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void EditUser()
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show("请选择要编辑的用户", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var p = new DialogParameters
|
||||
{
|
||||
{ DialogKeys.Mode, DialogModes.Edit },
|
||||
{ DialogKeys.UserId, SelectedItem.Id }
|
||||
};
|
||||
|
||||
_dialog.ShowDialog("DialogWeightUserEditView", p, async r =>
|
||||
{
|
||||
if (r.Result == ButtonResult.OK)
|
||||
{
|
||||
await SearchAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task DeleteUserAsync()
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
{
|
||||
MessageBox.Show("请选择要删除的用户", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var ok = MessageBox.Show($"确认删除称重用户:{SelectedItem.CheckName}/{SelectedItem.OpName} ?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (ok != MessageBoxResult.Yes) return;
|
||||
|
||||
if (IsBusy) return;
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
|
||||
var id = SelectedItem.Id;
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_fsql.Delete<TbWeightUser>(id).ExecuteAffrows();
|
||||
});
|
||||
|
||||
_log.Info($"删除称重用户成功,Id={id}");
|
||||
SelectedItem = null;
|
||||
await SearchCoreAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error($"删除称重用户失败: {ex}");
|
||||
MessageBox.Show($"删除失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入页面自动加载
|
||||
/// </summary>
|
||||
/// <param name="navigationContext">导航上下文</param>
|
||||
public override async void OnNavigatedTo(Prism.Regions.NavigationContext navigationContext)
|
||||
{
|
||||
await SearchAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹窗参数 Key
|
||||
/// </summary>
|
||||
public static class DialogKeys
|
||||
{
|
||||
public const string Mode = "Mode";
|
||||
public const string UserId = "UserId";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹窗模式
|
||||
/// </summary>
|
||||
public static class DialogModes
|
||||
{
|
||||
public const string Add = "Add";
|
||||
public const string Edit = "Edit";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user