2026-05-12 18:29:03 +08:00
|
|
|
|
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;
|
2026-05-13 12:35:02 +08:00
|
|
|
|
private readonly IPrintDotService _printDotService;
|
2026-05-12 18:29:03 +08:00
|
|
|
|
private readonly IEventAggregator _eventAggregator;
|
|
|
|
|
|
private SubscriptionToken? _changeToken;
|
|
|
|
|
|
|
|
|
|
|
|
private List<PrintTemplate> _allTemplates = new();
|
|
|
|
|
|
|
|
|
|
|
|
public ObservableCollection<PrintTemplate> Templates { get; } = new();
|
|
|
|
|
|
|
2026-05-13 12:35:02 +08:00
|
|
|
|
// ── 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 18:29:03 +08:00
|
|
|
|
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; }
|
2026-05-13 12:35:02 +08:00
|
|
|
|
public DelegateCommand RefreshPrintersCommand { get; }
|
2026-05-12 18:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
public PrintTemplateListViewModel(
|
|
|
|
|
|
IPrintTemplateService printTemplateService,
|
2026-05-13 12:35:02 +08:00
|
|
|
|
IPrintDotService printDotService,
|
2026-05-12 18:29:03 +08:00
|
|
|
|
IEventAggregator eventAggregator,
|
|
|
|
|
|
IContainerExtension container,
|
|
|
|
|
|
IRegionManager regionManager) : base(container, regionManager)
|
|
|
|
|
|
{
|
|
|
|
|
|
_printTemplateService = printTemplateService;
|
2026-05-13 12:35:02 +08:00
|
|
|
|
_printDotService = printDotService;
|
2026-05-12 18:29:03 +08:00
|
|
|
|
_eventAggregator = eventAggregator;
|
|
|
|
|
|
|
|
|
|
|
|
SearchCommand = new DelegateCommand(ApplyFilter);
|
|
|
|
|
|
PreviewCommand = new DelegateCommand<PrintTemplate>(ShowPreview);
|
|
|
|
|
|
ResetCommand = new DelegateCommand(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
FilterCode = null;
|
|
|
|
|
|
FilterName = null;
|
|
|
|
|
|
FilterCategory = null;
|
|
|
|
|
|
ApplyFilter();
|
|
|
|
|
|
});
|
2026-05-13 12:35:02 +08:00
|
|
|
|
RefreshPrintersCommand = new DelegateCommand(async () => await RefreshPrintersAsync(verbose: true));
|
2026-05-12 18:29:03 +08:00
|
|
|
|
|
|
|
|
|
|
_changeToken = _eventAggregator
|
|
|
|
|
|
.GetEvent<PrintTemplateChangedEvent>()
|
|
|
|
|
|
.Subscribe(_ => { RefreshSilentlyAsync().ConfigureAwait(false); }, ThreadOption.UIThread);
|
|
|
|
|
|
|
|
|
|
|
|
// 先用缓存立即填充,再后台静默刷新
|
|
|
|
|
|
ShowCached();
|
|
|
|
|
|
_ = RefreshSilentlyAsync();
|
2026-05-13 12:35:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 后台静默连接 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 未连接";
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-05-12 18:29:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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,预览窗口显示"尚未设计" */ }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-13 12:35:02 +08:00
|
|
|
|
var win = new PrintPreviewWindow(template, json, _printDotService, SelectedPrinter?.Name)
|
2026-05-12 18:29:03 +08:00
|
|
|
|
{
|
|
|
|
|
|
Owner = Application.Current.MainWindow
|
|
|
|
|
|
};
|
|
|
|
|
|
win.Show();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|