using System.Collections.ObjectModel; using CloudPrint.Client.Models; using CloudPrint.Client.Services; using CloudPrint.Client.Services.Abstractions; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Win32; namespace CloudPrint.Client.ViewModels; public sealed partial class LibraryViewModel : ObservableObject { private readonly IFileService _fileService; private readonly IPrintBackendClient _backendClient; private readonly ClientSettings _settings; [ObservableProperty] private LibraryCategory? selectedCategory; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(RemoveDocumentCommand))] [NotifyCanExecuteChangedFor(nameof(SyncSelectedCommand))] private CloudPrintDocument? selectedDocument; [ObservableProperty] private string statusMessage = "文库就绪"; public LibraryViewModel() : this(new FileService(), new GrpcService(), new ClientSettings()) { } public LibraryViewModel(IFileService fileService, IPrintBackendClient backendClient, ClientSettings settings) { _fileService = fileService; _backendClient = backendClient; _settings = settings; Categories.Add(new LibraryCategory { Id = 1, Name = "教材资料", Icon = "📘", SortOrder = 1 }); Categories.Add(new LibraryCategory { Id = 2, Name = "考试真题", Icon = "📝", SortOrder = 2 }); Categories.Add(new LibraryCategory { Id = 3, Name = "证件照片", Icon = "🖼️", SortOrder = 3 }); Categories.Add(new LibraryCategory { Id = 4, Name = "其他文件", Icon = "📁", SortOrder = 99 }); SelectedCategory = Categories.FirstOrDefault(); } public ObservableCollection Categories { get; } = new(); public ObservableCollection Documents { get; } = new(); public void RefreshCommandState() { SyncSelectedCommand.NotifyCanExecuteChanged(); SyncAllCommand.NotifyCanExecuteChanged(); } [RelayCommand] private void AddDocument() { var dialog = new Microsoft.Win32.OpenFileDialog { Title = "选择要加入文库的文件", Multiselect = true, Filter = "支持的文件|*.pdf;*.doc;*.docx;*.txt;*.xls;*.xlsx;*.ppt;*.pptx;*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.zip;*.rar|所有文件|*.*" }; if (dialog.ShowDialog() != true) { return; } var addedCount = 0; var categoryName = SelectedCategory?.Name ?? "未分类"; var limit = Math.Max(1, _settings.MaxUploadSizeMb) * 1024L * 1024L; foreach (var fileName in dialog.FileNames) { var validation = _fileService.ValidateFile(fileName, limit); if (!validation.IsValid) { StatusMessage = $"跳过 {System.IO.Path.GetFileName(fileName)}:{validation.Message}"; continue; } var document = _fileService.CopyToLibrary(fileName, _settings.LibraryRoot, categoryName, SelectedCategory?.Id ?? 0); Documents.Insert(0, document); addedCount++; } StatusMessage = addedCount > 0 ? $"已加入 {addedCount} 个文件到文库" : "没有文件被加入文库"; RefreshCommandState(); } private bool CanRemoveDocument() => SelectedDocument is not null; [RelayCommand(CanExecute = nameof(CanRemoveDocument))] private void RemoveDocument() { if (SelectedDocument is null) { return; } var document = SelectedDocument; Documents.Remove(document); SelectedDocument = null; StatusMessage = $"已从列表移除:{document.Title}"; RefreshCommandState(); } private bool CanSyncSelected() => SelectedDocument is not null && _backendClient.IsConnected; [RelayCommand(CanExecute = nameof(CanSyncSelected))] private async Task SyncSelectedAsync() { if (SelectedDocument is null) { return; } await _backendClient.UploadLibraryDocumentAsync(SelectedDocument).ConfigureAwait(true); StatusMessage = $"已同步:{SelectedDocument.Title}"; RefreshCommandState(); } private bool CanSyncAll() => Documents.Any(document => !document.IsUploaded) && _backendClient.IsConnected; [RelayCommand(CanExecute = nameof(CanSyncAll))] private async Task SyncAllAsync() { var pendingDocuments = Documents.Where(document => !document.IsUploaded).ToList(); foreach (var document in pendingDocuments) { await _backendClient.UploadLibraryDocumentAsync(document).ConfigureAwait(true); } StatusMessage = $"已同步 {pendingDocuments.Count} 个文库文件"; RefreshCommandState(); } }