原材料入库结存
This commit is contained in:
@@ -140,7 +140,23 @@ namespace YY.Admin.ViewModels.Control
|
||||
// 已实现页面:库区管理
|
||||
["WarehouseAreaListView"] = "WarehouseAreaListView",
|
||||
["/xslmes/mesXslWarehouseArea"] = "WarehouseAreaListView",
|
||||
["mesXslWarehouseArea"] = "WarehouseAreaListView"
|
||||
["mesXslWarehouseArea"] = "WarehouseAreaListView",
|
||||
|
||||
// 已实现页面:打印设置
|
||||
["PrintSettingsView"] = "PrintSettingsView",
|
||||
["/system/printSettings"] = "PrintSettingsView",
|
||||
["printSettings"] = "PrintSettingsView",
|
||||
|
||||
// 已实现页面:打印模板
|
||||
["PrintTemplateListView"] = "PrintTemplateListView",
|
||||
["/platform/print"] = "PrintTemplateListView",
|
||||
["print"] = "PrintTemplateListView",
|
||||
["printTemplate"] = "PrintTemplateListView",
|
||||
|
||||
// 已实现页面:业务打印绑定(桌面端)
|
||||
["PrintBizTemplateBindListView"] = "PrintBizTemplateBindListView",
|
||||
["/platform/printBizBind"] = "PrintBizTemplateBindListView",
|
||||
["printBizBind"] = "PrintBizTemplateBindListView"
|
||||
};
|
||||
|
||||
private MenuItem? _selectedMenuItem;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Session;
|
||||
using YY.Admin.FluentValidation;
|
||||
using YY.Admin.Helper;
|
||||
using YY.Admin.Services;
|
||||
using YY.Admin.Services.Service.Auth;
|
||||
using YY.Admin.Services.Service.Jeecg;
|
||||
@@ -220,6 +221,31 @@ namespace YY.Admin.ViewModels
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
bool connected = false;
|
||||
var settings = ServerSettingsStore.Load();
|
||||
if (settings.DisconnectConnection)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
IsBackendConnected = false;
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略窗口关闭后的调度异常
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(ConnectivityCheckIntervalSeconds), cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
// 每轮都重新读取配置,保存服务器设置后可即时生效
|
||||
@@ -269,6 +295,11 @@ namespace YY.Admin.ViewModels
|
||||
{
|
||||
if (r.Result == ButtonResult.OK)
|
||||
{
|
||||
var settings = ServerSettingsStore.Load();
|
||||
if (settings.DisconnectConnection)
|
||||
{
|
||||
IsBackendConnected = false;
|
||||
}
|
||||
LoginMessage = "服务器配置已保存";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -165,6 +165,18 @@ namespace YY.Admin.ViewModels
|
||||
set => SetProperty(ref _isAppSettingsOpen, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 true 时隐藏左侧功能菜单树(次级侧栏)
|
||||
/// </summary>
|
||||
private bool _isMenuTreePanelCollapsed;
|
||||
public bool IsMenuTreePanelCollapsed
|
||||
{
|
||||
get => _isMenuTreePanelCollapsed;
|
||||
set => SetProperty(ref _isMenuTreePanelCollapsed, value);
|
||||
}
|
||||
|
||||
private NavItem? _menuTreeToggleNavItem;
|
||||
|
||||
private AppSettingsViewModel? _appSettingsViewModel;
|
||||
public AppSettingsViewModel? AppSettingsViewModel {
|
||||
get => _appSettingsViewModel;
|
||||
@@ -175,6 +187,7 @@ namespace YY.Admin.ViewModels
|
||||
public ICommand ResetAppSettingsCommand { get; }
|
||||
public ICommand OpenAppSettingsCommand { get; }
|
||||
public ICommand OpenServerSettingsCommand { get; }
|
||||
public ICommand ToggleMenuTreePanelCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -212,6 +225,7 @@ namespace YY.Admin.ViewModels
|
||||
OpenAppSettingsCommand = new DelegateCommand(OpenAppSettings);
|
||||
ResetAppSettingsCommand = new DelegateCommand(ResetAppSettings);
|
||||
OpenServerSettingsCommand = new DelegateCommand(OpenServerSettings);
|
||||
ToggleMenuTreePanelCommand = new DelegateCommand(ToggleMenuTreePanel);
|
||||
|
||||
// 初始化Sidebar数据
|
||||
InitNavItems();
|
||||
@@ -300,6 +314,13 @@ namespace YY.Admin.ViewModels
|
||||
Name = "Tab区域",
|
||||
Command = new DelegateCommand<NavItem>(OnOpenOrActivateTab)
|
||||
},
|
||||
(_menuTreeToggleNavItem = new NavItem {
|
||||
Icon = "ChevronDoubleLeft",
|
||||
Name = "折叠菜单",
|
||||
AlignBottom = true,
|
||||
IsActive = false,
|
||||
Command = ToggleMenuTreePanelCommand
|
||||
}),
|
||||
new NavItem {
|
||||
Icon = "Server",
|
||||
Name = "服务器设置",
|
||||
@@ -678,6 +699,29 @@ namespace YY.Admin.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 折叠 / 展示左侧菜单树区域
|
||||
/// </summary>
|
||||
private void ToggleMenuTreePanel()
|
||||
{
|
||||
IsMenuTreePanelCollapsed = !IsMenuTreePanelCollapsed;
|
||||
if (_menuTreeToggleNavItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsMenuTreePanelCollapsed)
|
||||
{
|
||||
_menuTreeToggleNavItem.Name = "展示菜单";
|
||||
_menuTreeToggleNavItem.Icon = "ChevronDoubleRight";
|
||||
}
|
||||
else
|
||||
{
|
||||
_menuTreeToggleNavItem.Name = "折叠菜单";
|
||||
_menuTreeToggleNavItem.Icon = "ChevronDoubleLeft";
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenServerSettings()
|
||||
{
|
||||
_dialogService.ShowDialog("ServerSettingsDialog", r =>
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using Prism.Commands;
|
||||
using Prism.Events;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Events;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Views.Print;
|
||||
|
||||
namespace YY.Admin.ViewModels.Print;
|
||||
|
||||
/// <summary>业务打印绑定列表(只读,本地缓存 + 同步)</summary>
|
||||
public class PrintBizTemplateBindListViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IPrintBizTemplateBindService _bindService;
|
||||
private SubscriptionToken? _changeToken;
|
||||
|
||||
private List<PrintBizTemplateBind> _all = new();
|
||||
|
||||
public ObservableCollection<PrintBizTemplateBind> Items { get; } = new();
|
||||
|
||||
private string _statusMessage = string.Empty;
|
||||
public string StatusMessage
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => SetProperty(ref _statusMessage, value);
|
||||
}
|
||||
|
||||
private string? _filterBizCode;
|
||||
public string? FilterBizCode
|
||||
{
|
||||
get => _filterBizCode;
|
||||
set => SetProperty(ref _filterBizCode, value);
|
||||
}
|
||||
|
||||
private string? _filterBizName;
|
||||
public string? FilterBizName
|
||||
{
|
||||
get => _filterBizName;
|
||||
set => SetProperty(ref _filterBizName, value);
|
||||
}
|
||||
|
||||
private string? _filterTemplateCode;
|
||||
public string? FilterTemplateCode
|
||||
{
|
||||
get => _filterTemplateCode;
|
||||
set => SetProperty(ref _filterTemplateCode, value);
|
||||
}
|
||||
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ResetCommand { get; }
|
||||
public DelegateCommand<PrintBizTemplateBind> DetailCommand { get; }
|
||||
|
||||
public PrintBizTemplateBindListViewModel(
|
||||
IPrintBizTemplateBindService bindService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_bindService = bindService;
|
||||
|
||||
SearchCommand = new DelegateCommand(ApplyFilter);
|
||||
ResetCommand = new DelegateCommand(() =>
|
||||
{
|
||||
FilterBizCode = null;
|
||||
FilterBizName = null;
|
||||
FilterTemplateCode = null;
|
||||
ApplyFilter();
|
||||
});
|
||||
DetailCommand = new DelegateCommand<PrintBizTemplateBind>(OpenDetail);
|
||||
|
||||
_changeToken = _eventAggregator
|
||||
.GetEvent<PrintBizTemplateBindChangedEvent>()
|
||||
.Subscribe(_ => { RefreshSilentlyAsync().ConfigureAwait(false); }, ThreadOption.UIThread);
|
||||
|
||||
ShowCached();
|
||||
_ = RefreshSilentlyAsync();
|
||||
}
|
||||
|
||||
private void ShowCached()
|
||||
{
|
||||
var cached = _bindService.GetCached();
|
||||
if (cached.Count == 0) return;
|
||||
_all = cached.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private async Task RefreshSilentlyAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var list = await _bindService.RefreshCacheAsync().ConfigureAwait(false);
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_all = list.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
var cached = _bindService.GetCached();
|
||||
if (cached.Count > 0 && _all.Count == 0)
|
||||
{
|
||||
await Application.Current.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
_all = cached.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilter()
|
||||
{
|
||||
IEnumerable<PrintBizTemplateBind> result = _all;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterBizCode))
|
||||
result = result.Where(x => (x.BizCode ?? string.Empty)
|
||||
.Contains(FilterBizCode, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterBizName))
|
||||
result = result.Where(x => (x.BizName ?? string.Empty)
|
||||
.Contains(FilterBizName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterTemplateCode))
|
||||
result = result.Where(x => (x.TemplateCode ?? string.Empty)
|
||||
.Contains(FilterTemplateCode, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var filtered = result.ToList();
|
||||
|
||||
for (int i = Items.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!filtered.Any(t => t.Id == Items[i].Id))
|
||||
Items.RemoveAt(i);
|
||||
}
|
||||
for (int i = 0; i < filtered.Count; i++)
|
||||
{
|
||||
var item = filtered[i];
|
||||
var existingIdx = -1;
|
||||
for (int j = 0; j < Items.Count; j++)
|
||||
{
|
||||
if (Items[j].Id == item.Id) { existingIdx = j; break; }
|
||||
}
|
||||
if (existingIdx < 0)
|
||||
Items.Insert(i, item);
|
||||
else
|
||||
{
|
||||
if (existingIdx != i) Items.Move(existingIdx, i);
|
||||
Items[i] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
var hasFilter = !string.IsNullOrWhiteSpace(FilterBizCode)
|
||||
|| !string.IsNullOrWhiteSpace(FilterBizName)
|
||||
|| !string.IsNullOrWhiteSpace(FilterTemplateCode);
|
||||
StatusMessage = hasFilter
|
||||
? $"筛选结果 {Items.Count} / {_all.Count} 条"
|
||||
: _all.Count > 0
|
||||
? $"共 {_all.Count} 条绑定(缓存于本地,可离线查看)"
|
||||
: "暂无数据(联网后将自动同步)";
|
||||
}
|
||||
|
||||
private async void OpenDetail(PrintBizTemplateBind? row)
|
||||
{
|
||||
if (row == null) return;
|
||||
PrintBizTemplateBind model = row;
|
||||
try
|
||||
{
|
||||
var fresh = await _bindService.GetByIdAsync(row.Id ?? "").ConfigureAwait(false);
|
||||
if (fresh != null) model = fresh;
|
||||
}
|
||||
catch { /* 使用列表行数据 */ }
|
||||
|
||||
// 网络请求 continuation 可能在非 STA 线程,Window 必须在 UI 线程创建
|
||||
var app = Application.Current;
|
||||
if (app?.Dispatcher == null) return;
|
||||
await app.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
var win = new PrintBizTemplateBindDetailWindow(model)
|
||||
{
|
||||
Owner = app.MainWindow
|
||||
};
|
||||
win.ShowDialog();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using Prism.Commands;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Services.Service.Print;
|
||||
using YY.Admin.Core;
|
||||
|
||||
namespace YY.Admin.ViewModels.Print;
|
||||
|
||||
public class PrintSettingsViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IPrintDotService _printDotService;
|
||||
private readonly IPrintTemplateService _printTemplateService;
|
||||
|
||||
// ── 连接设置 ──────────────────────────────────────────────────────────
|
||||
|
||||
private string _wsUrl = "ws://127.0.0.1:1122/ws";
|
||||
public string WsUrl
|
||||
{
|
||||
get => _wsUrl;
|
||||
set => SetProperty(ref _wsUrl, value);
|
||||
}
|
||||
|
||||
// ── 打印机列表 ──────────────────────────────────────────────────────────
|
||||
|
||||
public ObservableCollection<PrintDotPrinter> Printers { get; } = new();
|
||||
|
||||
private PrintDotPrinter? _selectedPrinter;
|
||||
public PrintDotPrinter? SelectedPrinter
|
||||
{
|
||||
get => _selectedPrinter;
|
||||
set => SetProperty(ref _selectedPrinter, value);
|
||||
}
|
||||
|
||||
// ── 模板列表 ────────────────────────────────────────────────────────────
|
||||
|
||||
public ObservableCollection<PrintTemplate> Templates { get; } = new();
|
||||
|
||||
// ── 状态 ────────────────────────────────────────────────────────────────
|
||||
|
||||
private bool _isBusy;
|
||||
public bool IsBusy
|
||||
{
|
||||
get => _isBusy;
|
||||
set => SetProperty(ref _isBusy, value);
|
||||
}
|
||||
|
||||
private string _statusMessage = string.Empty;
|
||||
public string StatusMessage
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => SetProperty(ref _statusMessage, value);
|
||||
}
|
||||
|
||||
// ── 命令 ────────────────────────────────────────────────────────────────
|
||||
|
||||
public DelegateCommand TestConnectionCommand { get; }
|
||||
public DelegateCommand SaveCommand { get; }
|
||||
public DelegateCommand RefreshTemplatesCommand { get; }
|
||||
|
||||
public PrintSettingsViewModel(
|
||||
IPrintDotService printDotService,
|
||||
IPrintTemplateService printTemplateService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_printDotService = printDotService;
|
||||
_printTemplateService = printTemplateService;
|
||||
|
||||
TestConnectionCommand = new DelegateCommand(async () => await TestConnectionAsync());
|
||||
SaveCommand = new DelegateCommand(SaveSettings);
|
||||
RefreshTemplatesCommand = new DelegateCommand(async () => await RefreshTemplatesAsync());
|
||||
|
||||
// 加载已保存的设置
|
||||
var saved = PrintDotSettings.Load();
|
||||
WsUrl = saved.WsUrl;
|
||||
if (!string.IsNullOrWhiteSpace(saved.SelectedPrinter))
|
||||
_selectedPrinter = new PrintDotPrinter(saved.SelectedPrinter, false);
|
||||
}
|
||||
|
||||
private async Task TestConnectionAsync()
|
||||
{
|
||||
IsBusy = true;
|
||||
StatusMessage = "正在连接...";
|
||||
Printers.Clear();
|
||||
try
|
||||
{
|
||||
// 临时用当前输入的 URL 测试
|
||||
PrintDotSettings.Current!.WsUrl = WsUrl;
|
||||
var list = await _printDotService.GetPrintersAsync();
|
||||
foreach (var p in list) Printers.Add(p);
|
||||
|
||||
// 还原保存的已选打印机
|
||||
var saved = PrintDotSettings.Load();
|
||||
var match = list.FirstOrDefault(p => p.Name == saved.SelectedPrinter);
|
||||
SelectedPrinter = match ?? (list.Count > 0 ? list[0] : null);
|
||||
|
||||
StatusMessage = $"连接成功,共 {list.Count} 台打印机";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"连接失败:{ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
var settings = new PrintDotSettings
|
||||
{
|
||||
WsUrl = WsUrl.Trim(),
|
||||
SelectedPrinter = SelectedPrinter?.Name ?? string.Empty
|
||||
};
|
||||
settings.Save();
|
||||
StatusMessage = "设置已保存";
|
||||
MessageBox.Show("PrintDot 设置已保存", "保存成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private async Task RefreshTemplatesAsync()
|
||||
{
|
||||
IsBusy = true;
|
||||
Templates.Clear();
|
||||
try
|
||||
{
|
||||
var list = await _printTemplateService.ListAsync();
|
||||
foreach (var t in list) Templates.Add(t);
|
||||
StatusMessage = $"已加载 {list.Count} 个打印模板";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"加载模板失败:{ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using Prism.Commands;
|
||||
using Prism.Events;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Events;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Services.Service.Print;
|
||||
using YY.Admin.Views.Print;
|
||||
|
||||
namespace YY.Admin.ViewModels.Print;
|
||||
|
||||
public class PrintTemplateListViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IPrintTemplateService _printTemplateService;
|
||||
private readonly IPrintDotService _printDotService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private SubscriptionToken? _changeToken;
|
||||
|
||||
private List<PrintTemplate> _allTemplates = new();
|
||||
|
||||
public ObservableCollection<PrintTemplate> Templates { get; } = new();
|
||||
|
||||
// ── PrintDot 打印机选择 ────────────────────────────────────────────────
|
||||
public ObservableCollection<PrintDotPrinter> Printers { get; } = new();
|
||||
|
||||
private bool _suppressPrinterSave;
|
||||
private PrintDotPrinter? _selectedPrinter;
|
||||
public PrintDotPrinter? SelectedPrinter
|
||||
{
|
||||
get => _selectedPrinter;
|
||||
set
|
||||
{
|
||||
if (!SetProperty(ref _selectedPrinter, value)) return;
|
||||
if (_suppressPrinterSave) return;
|
||||
// 持久化用户选择,预览窗口和后续会话使用
|
||||
var s = PrintDotSettings.Load();
|
||||
s.SelectedPrinter = value?.Name ?? string.Empty;
|
||||
s.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private string _printerStatus = string.Empty;
|
||||
public string PrinterStatus
|
||||
{
|
||||
get => _printerStatus;
|
||||
set => SetProperty(ref _printerStatus, value);
|
||||
}
|
||||
|
||||
private string _statusMessage = string.Empty;
|
||||
public string StatusMessage
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => SetProperty(ref _statusMessage, value);
|
||||
}
|
||||
|
||||
private string? _filterCode;
|
||||
public string? FilterCode
|
||||
{
|
||||
get => _filterCode;
|
||||
set => SetProperty(ref _filterCode, value);
|
||||
}
|
||||
|
||||
private string? _filterName;
|
||||
public string? FilterName
|
||||
{
|
||||
get => _filterName;
|
||||
set => SetProperty(ref _filterName, value);
|
||||
}
|
||||
|
||||
private string? _filterCategory;
|
||||
public string? FilterCategory
|
||||
{
|
||||
get => _filterCategory;
|
||||
set => SetProperty(ref _filterCategory, value);
|
||||
}
|
||||
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ResetCommand { get; }
|
||||
public DelegateCommand<PrintTemplate> PreviewCommand { get; }
|
||||
public DelegateCommand RefreshPrintersCommand { get; }
|
||||
|
||||
public PrintTemplateListViewModel(
|
||||
IPrintTemplateService printTemplateService,
|
||||
IPrintDotService printDotService,
|
||||
IEventAggregator eventAggregator,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_printTemplateService = printTemplateService;
|
||||
_printDotService = printDotService;
|
||||
_eventAggregator = eventAggregator;
|
||||
|
||||
SearchCommand = new DelegateCommand(ApplyFilter);
|
||||
PreviewCommand = new DelegateCommand<PrintTemplate>(ShowPreview);
|
||||
ResetCommand = new DelegateCommand(() =>
|
||||
{
|
||||
FilterCode = null;
|
||||
FilterName = null;
|
||||
FilterCategory = null;
|
||||
ApplyFilter();
|
||||
});
|
||||
RefreshPrintersCommand = new DelegateCommand(async () => await RefreshPrintersAsync(verbose: true));
|
||||
|
||||
_changeToken = _eventAggregator
|
||||
.GetEvent<PrintTemplateChangedEvent>()
|
||||
.Subscribe(_ => { RefreshSilentlyAsync().ConfigureAwait(false); }, ThreadOption.UIThread);
|
||||
|
||||
// 先用缓存立即填充,再后台静默刷新
|
||||
ShowCached();
|
||||
_ = RefreshSilentlyAsync();
|
||||
|
||||
// 后台静默连接 PrintDot 桥接器,初次加载打印机
|
||||
_ = RefreshPrintersAsync(verbose: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过 PrintDot 桥接器拉取打印机列表,与后端 web 列表页的 fetchPrintDotPrinters 行为对齐。
|
||||
/// </summary>
|
||||
private async Task RefreshPrintersAsync(bool verbose)
|
||||
{
|
||||
if (verbose) PrinterStatus = "刷新打印机中...";
|
||||
var savedName = PrintDotSettings.Load().SelectedPrinter;
|
||||
try
|
||||
{
|
||||
var list = await _printDotService.GetPrintersAsync();
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Printers.Clear();
|
||||
foreach (var p in list) Printers.Add(p);
|
||||
|
||||
// 选中规则:上次保存 > 系统默认 > 首台
|
||||
var match = list.FirstOrDefault(p => p.Name == savedName)
|
||||
?? list.FirstOrDefault(p => p.IsDefault)
|
||||
?? (list.Count > 0 ? list[0] : null);
|
||||
|
||||
_suppressPrinterSave = true;
|
||||
SelectedPrinter = match;
|
||||
_suppressPrinterSave = false;
|
||||
|
||||
PrinterStatus = list.Count > 0 ? $"共 {list.Count} 台打印机" : "未检测到打印机";
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Printers.Clear();
|
||||
_suppressPrinterSave = true;
|
||||
SelectedPrinter = null;
|
||||
_suppressPrinterSave = false;
|
||||
PrinterStatus = verbose
|
||||
? $"PrintDot 未连接:{ex.Message}"
|
||||
: "PrintDot 未连接";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowCached()
|
||||
{
|
||||
var cached = _printTemplateService.GetCached();
|
||||
if (cached.Count == 0) return;
|
||||
_allTemplates = cached.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private async Task RefreshSilentlyAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var list = await _printTemplateService.RefreshCacheAsync().ConfigureAwait(false);
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
_allTemplates = list.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 静默失败:保留当前缓存内容不动,不显示错误
|
||||
var cached = _printTemplateService.GetCached();
|
||||
if (cached.Count > 0 && _allTemplates.Count == 0)
|
||||
{
|
||||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
_allTemplates = cached.ToList();
|
||||
ApplyFilter();
|
||||
UpdateStatus();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyFilter()
|
||||
{
|
||||
IEnumerable<PrintTemplate> result = _allTemplates;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterCode))
|
||||
result = result.Where(t => (t.TemplateCode ?? string.Empty)
|
||||
.Contains(FilterCode, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterName))
|
||||
result = result.Where(t => (t.TemplateName ?? string.Empty)
|
||||
.Contains(FilterName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FilterCategory))
|
||||
result = result.Where(t => (t.Category ?? string.Empty)
|
||||
.Contains(FilterCategory, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var filtered = result.ToList();
|
||||
|
||||
// 原地差量更新,避免滚动位置重置和闪烁
|
||||
for (int i = Templates.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!filtered.Any(t => t.Id == Templates[i].Id))
|
||||
Templates.RemoveAt(i);
|
||||
}
|
||||
for (int i = 0; i < filtered.Count; i++)
|
||||
{
|
||||
var item = filtered[i];
|
||||
var existingIdx = -1;
|
||||
for (int j = 0; j < Templates.Count; j++)
|
||||
{
|
||||
if (Templates[j].Id == item.Id) { existingIdx = j; break; }
|
||||
}
|
||||
if (existingIdx < 0)
|
||||
Templates.Insert(i, item);
|
||||
else
|
||||
{
|
||||
if (existingIdx != i) Templates.Move(existingIdx, i);
|
||||
Templates[i] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
var hasFilter = !string.IsNullOrWhiteSpace(FilterCode)
|
||||
|| !string.IsNullOrWhiteSpace(FilterName)
|
||||
|| !string.IsNullOrWhiteSpace(FilterCategory);
|
||||
StatusMessage = hasFilter
|
||||
? $"筛选结果 {Templates.Count} / {_allTemplates.Count} 个"
|
||||
: _allTemplates.Count > 0
|
||||
? $"共 {_allTemplates.Count} 个模板"
|
||||
: "暂无模板";
|
||||
}
|
||||
|
||||
private void ShowPreview(PrintTemplate template)
|
||||
{
|
||||
if (template == null) return;
|
||||
ShowPreviewAsync(template);
|
||||
}
|
||||
|
||||
private async Task ShowPreviewAsync(PrintTemplate template)
|
||||
{
|
||||
// 列表缓存可能不含 templateJson(大字段),按需通过 queryByCode 单独拉取
|
||||
var json = template.TemplateJson;
|
||||
if (string.IsNullOrWhiteSpace(json) || json == "{}")
|
||||
{
|
||||
try
|
||||
{
|
||||
var full = await _printTemplateService.GetByCodeAsync(template.TemplateCode ?? "");
|
||||
json = full?.TemplateJson;
|
||||
}
|
||||
catch { /* 保持 json 为 null,预览窗口显示"尚未设计" */ }
|
||||
}
|
||||
|
||||
var win = new PrintPreviewWindow(template, json, _printDotService, SelectedPrinter?.Name)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
win.Show();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,12 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Events;
|
||||
using YY.Admin.Core.Helper;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Services;
|
||||
using YY.Admin.Services.Service;
|
||||
using YY.Admin.Views.Print;
|
||||
using YY.Admin.Views.RawMaterialCard;
|
||||
|
||||
namespace YY.Admin.ViewModels.RawMaterialCard;
|
||||
@@ -18,6 +19,7 @@ public class RawMaterialCardListViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IRawMaterialCardService _cardService;
|
||||
private readonly IJeecgDictSyncService _dictSyncService;
|
||||
private readonly IPrintDotService _printDotService;
|
||||
private SubscriptionToken? _changedToken;
|
||||
private SubscriptionToken? _syncConflictToken;
|
||||
|
||||
@@ -60,6 +62,7 @@ public class RawMaterialCardListViewModel : BaseViewModel
|
||||
public DelegateCommand AddCommand { get; }
|
||||
public DelegateCommand<MesXslRawMaterialCard> EditCommand { get; }
|
||||
public DelegateCommand<MesXslRawMaterialCard> DeleteCommand { get; }
|
||||
public DelegateCommand<MesXslRawMaterialCard> PrintCommand { get; }
|
||||
public DelegateCommand<MesXslRawMaterialCard> TogglePriorityCommand { get; }
|
||||
public DelegateCommand PrevPageCommand { get; }
|
||||
public DelegateCommand NextPageCommand { get; }
|
||||
@@ -67,11 +70,13 @@ public class RawMaterialCardListViewModel : BaseViewModel
|
||||
public RawMaterialCardListViewModel(
|
||||
IRawMaterialCardService cardService,
|
||||
IJeecgDictSyncService dictSyncService,
|
||||
IPrintDotService printDotService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_cardService = cardService;
|
||||
_dictSyncService = dictSyncService;
|
||||
_printDotService = printDotService;
|
||||
|
||||
SearchCommand = new DelegateCommand(async () => { PageNo = 1; await LoadAsync(); });
|
||||
ResetCommand = new DelegateCommand(async () =>
|
||||
@@ -83,6 +88,7 @@ public class RawMaterialCardListViewModel : BaseViewModel
|
||||
AddCommand = new DelegateCommand(async () => await ShowAddDialogAsync());
|
||||
EditCommand = new DelegateCommand<MesXslRawMaterialCard>(async c => await ShowEditDialogAsync(c));
|
||||
DeleteCommand = new DelegateCommand<MesXslRawMaterialCard>(async c => await DeleteAsync(c));
|
||||
PrintCommand = new DelegateCommand<MesXslRawMaterialCard>(async c => await ShowPrintPreviewAsync(c));
|
||||
TogglePriorityCommand = new DelegateCommand<MesXslRawMaterialCard>(async c => await TogglePriorityAsync(c));
|
||||
PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } });
|
||||
NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } });
|
||||
@@ -255,6 +261,65 @@ public class RawMaterialCardListViewModel : BaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowPrintPreviewAsync(MesXslRawMaterialCard card)
|
||||
{
|
||||
if (card?.Id == null) return;
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
var (templateJson, printDataJson, errorMessage) = await _cardService.PrepareNativePrintAsync(card.Id);
|
||||
if (errorMessage != null)
|
||||
{
|
||||
Growl.Error(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造一个最简的 PrintTemplate 对象用于传入 PrintPreviewWindow(供显示标题 / 纸张信息)
|
||||
var tpl = BuildPrintTemplateFromJson(templateJson);
|
||||
|
||||
var win = new PrintPreviewWindow(tpl, templateJson, _printDotService, null, printDataJson)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
win.Show();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Growl.Error($"打开打印预览失败:{ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static PrintTemplate BuildPrintTemplateFromJson(string templateJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
var root = System.Text.Json.JsonDocument.Parse(templateJson).RootElement;
|
||||
double w = 210, h = 297;
|
||||
if (root.TryGetProperty("page", out var page))
|
||||
{
|
||||
if (page.TryGetProperty("width", out var wEl)) w = wEl.GetDouble();
|
||||
if (page.TryGetProperty("height", out var hEl)) h = hEl.GetDouble();
|
||||
}
|
||||
return new PrintTemplate
|
||||
{
|
||||
TemplateName = "原材料卡片",
|
||||
TemplateCode = "MES_RAW_MATERIAL_CARD",
|
||||
PaperWidthMm = w,
|
||||
PaperHeightMm = h,
|
||||
PaperOrientation = w > h ? "横向" : "纵向",
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new PrintTemplate { TemplateName = "原材料卡片", TemplateCode = "MES_RAW_MATERIAL_CARD" };
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CleanUp()
|
||||
{
|
||||
base.CleanUp();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools.Extension;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
@@ -366,44 +367,62 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
AddSplitDetailCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
/// <summary>校验并提交新增/编辑;不弹关闭、不执行独立页的 InitializeForAdd。成功时 Result=true。</summary>
|
||||
protected async Task<bool> PersistEntryCoreAsync()
|
||||
{
|
||||
if (Entry == null) return false;
|
||||
ApplySplitDetailsToEntry();
|
||||
ApplyDefaultEntryStatusForAdd();
|
||||
ApplyHiddenFieldDefaultsForAdd();
|
||||
|
||||
var missing = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(Entry.MaterialId)) missing.Add("密炼物料");
|
||||
if (string.IsNullOrWhiteSpace(Entry.BillNo)) missing.Add("榜单号");
|
||||
if (string.IsNullOrWhiteSpace(Entry.UnloadOperator)) missing.Add("卸货人");
|
||||
if (string.IsNullOrWhiteSpace(Entry.SupplierName)) missing.Add("供应商名称");
|
||||
if (missing.Count > 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Warning($"以下必填项不能为空:{string.Join("、", missing)}");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if (IsAddMode)
|
||||
{
|
||||
ok = await _entryService.AddAsync(Entry);
|
||||
if (!ok) { HandyControl.Controls.MessageBox.Error("新增失败!"); return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = await _entryService.EditAsync(Entry);
|
||||
if (!ok) { HandyControl.Controls.MessageBox.Error("编辑失败!"); return false; }
|
||||
}
|
||||
Result = ok;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual async Task SaveAsync()
|
||||
{
|
||||
if (Entry == null) return;
|
||||
try
|
||||
{
|
||||
ApplySplitDetailsToEntry();
|
||||
ApplyDefaultEntryStatusForAdd();
|
||||
ApplyHiddenFieldDefaultsForAdd();
|
||||
if (!await PersistEntryCoreAsync()) return;
|
||||
|
||||
var missing = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(Entry.MaterialId)) missing.Add("密炼物料");
|
||||
if (string.IsNullOrWhiteSpace(Entry.BillNo)) missing.Add("榜单号");
|
||||
if (string.IsNullOrWhiteSpace(Entry.UnloadOperator)) missing.Add("卸货人");
|
||||
if (string.IsNullOrWhiteSpace(Entry.SupplierName)) missing.Add("供应商名称");
|
||||
if (missing.Count > 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Warning($"以下必填项不能为空:{string.Join("、", missing)}");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if (IsAddMode)
|
||||
{
|
||||
ok = await _entryService.AddAsync(Entry);
|
||||
if (ok) HandyControl.Controls.MessageBox.Success("新增成功!");
|
||||
else { HandyControl.Controls.MessageBox.Error("新增失败!"); return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = await _entryService.EditAsync(Entry);
|
||||
if (!ok) { HandyControl.Controls.MessageBox.Error("编辑失败!"); return; }
|
||||
}
|
||||
Result = ok;
|
||||
if (IsAddMode && CloseAction == null)
|
||||
{
|
||||
// 独立新增页面:保存成功后自动清空表单,便于连续录入
|
||||
InitializeForAdd();
|
||||
return;
|
||||
// 非模态轻提示,约 1 秒后自动消失(与独立页「保存并打印」时的体验一致)
|
||||
Growl.Success(new GrowlInfo
|
||||
{
|
||||
Message = "保存成功",
|
||||
ShowDateTime = false,
|
||||
WaitTime = 1
|
||||
});
|
||||
if (CloseAction == null)
|
||||
{
|
||||
// 独立新增页面:保存成功后自动清空表单,便于连续录入
|
||||
InitializeForAdd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CloseAction?.Invoke();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user