settings_re8

This commit is contained in:
lincube
2026-03-14 22:45:09 +08:00
parent 91f9f3d6fb
commit 689be7b585
54 changed files with 5356 additions and 30 deletions

View File

@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models;
namespace LanMountainDesktop.Views.ComponentEditors;
public partial class ClassScheduleComponentEditor : ComponentEditorViewBase
{
private readonly List<ImportedClassScheduleSnapshot> _importedSchedules = [];
private string _activeScheduleId = string.Empty;
public ClassScheduleComponentEditor()
: this(null)
{
}
public ClassScheduleComponentEditor(DesktopComponentEditorContext? context)
: base(context)
{
InitializeComponent();
LoadState();
ApplyState();
RenderImportedSchedules();
}
private void LoadState()
{
var snapshot = LoadSnapshot();
_importedSchedules.Clear();
foreach (var item in snapshot.ImportedClassSchedules)
{
if (string.IsNullOrWhiteSpace(item.Id) || string.IsNullOrWhiteSpace(item.FilePath))
{
continue;
}
_importedSchedules.Add(new ImportedClassScheduleSnapshot
{
Id = item.Id.Trim(),
DisplayName = item.DisplayName?.Trim() ?? string.Empty,
FilePath = item.FilePath.Trim()
});
}
_activeScheduleId = snapshot.ActiveImportedClassScheduleId?.Trim() ?? string.Empty;
if (_importedSchedules.Count > 0 &&
!_importedSchedules.Any(item => string.Equals(item.Id, _activeScheduleId, StringComparison.OrdinalIgnoreCase)))
{
_activeScheduleId = _importedSchedules[0].Id;
}
}
private void ApplyState()
{
HeadlineTextBlock.Text = Context?.Definition.DisplayName ?? "Class Schedule";
DescriptionTextBlock.Text = L("schedule.settings.desc", "导入 ClassIsland 的 CSES 课表文件并选择启用项。");
AddScheduleButton.Content = L("schedule.settings.add", "添加课表");
EmptyStateTextBlock.Text = L("schedule.settings.empty", "暂无导入课表");
}
private async void OnAddScheduleClick(object? sender, RoutedEventArgs e)
{
_ = sender;
_ = e;
var topLevel = TopLevel.GetTopLevel(this);
if (topLevel?.StorageProvider is not { } storageProvider)
{
return;
}
var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = L("schedule.settings.picker_title", "选择 ClassIsland 课表文件"),
AllowMultiple = false,
FileTypeFilter =
[
new FilePickerFileType(L("schedule.settings.picker_file_type", "ClassIsland CSES 课表"))
{
Patterns = ["*.cses", "*.yaml", "*.yml"]
}
]
});
if (files.Count == 0)
{
return;
}
var importedPath = await ImportScheduleFileAsync(files[0]);
if (string.IsNullOrWhiteSpace(importedPath))
{
return;
}
var existing = _importedSchedules.FirstOrDefault(item =>
string.Equals(item.FilePath, importedPath, StringComparison.OrdinalIgnoreCase));
if (existing is not null)
{
_activeScheduleId = existing.Id;
}
else
{
_importedSchedules.Add(new ImportedClassScheduleSnapshot
{
Id = Guid.NewGuid().ToString("N"),
DisplayName = Path.GetFileNameWithoutExtension(importedPath)?.Trim()
?? L("schedule.settings.unnamed", "未命名课表"),
FilePath = importedPath
});
_activeScheduleId = _importedSchedules[^1].Id;
}
PersistState();
RenderImportedSchedules();
}
private async Task<string?> ImportScheduleFileAsync(IStorageFile file)
{
try
{
var extension = Path.GetExtension(file.Name);
if (string.IsNullOrWhiteSpace(extension))
{
extension = ".cses";
}
var importedDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"LanMountainDesktop",
"Schedules");
Directory.CreateDirectory(importedDirectory);
var destinationPath = Path.Combine(
importedDirectory,
$"{DateTime.Now:yyyyMMdd_HHmmss}_{Guid.NewGuid():N}{extension}");
await using var sourceStream = await file.OpenReadAsync();
await using var destinationStream = File.Create(destinationPath);
await sourceStream.CopyToAsync(destinationStream);
return destinationPath;
}
catch
{
return null;
}
}
private void RenderImportedSchedules()
{
ScheduleItemsPanel.Children.Clear();
EmptyStateTextBlock.IsVisible = _importedSchedules.Count == 0;
if (_importedSchedules.Count == 0)
{
return;
}
foreach (var item in _importedSchedules)
{
var selector = new RadioButton
{
GroupName = "class_schedule_imports",
IsChecked = string.Equals(item.Id, _activeScheduleId, StringComparison.OrdinalIgnoreCase),
VerticalAlignment = VerticalAlignment.Center,
Tag = item.Id
};
selector.IsCheckedChanged += OnScheduleSelectionChanged;
var title = new TextBlock
{
Text = string.IsNullOrWhiteSpace(item.DisplayName)
? L("schedule.settings.unnamed", "未命名课表")
: item.DisplayName,
FontWeight = FontWeight.SemiBold
};
var path = new TextBlock
{
Text = item.FilePath,
FontSize = 11,
Opacity = 0.7,
TextTrimming = TextTrimming.CharacterEllipsis
};
var deleteButton = new Button
{
Content = L("schedule.settings.delete", "删除"),
Tag = item.Id,
Padding = new Thickness(12, 8),
HorizontalAlignment = HorizontalAlignment.Right
};
deleteButton.Click += OnDeleteScheduleClick;
var row = new Grid
{
ColumnDefinitions = new ColumnDefinitions("Auto,*,Auto"),
ColumnSpacing = 12
};
var details = new StackPanel
{
Spacing = 4,
Children = { title, path }
};
row.Children.Add(selector);
row.Children.Add(details);
row.Children.Add(deleteButton);
Grid.SetColumn(details, 1);
Grid.SetColumn(deleteButton, 2);
ScheduleItemsPanel.Children.Add(new Border
{
Padding = new Thickness(12, 10),
CornerRadius = new CornerRadius(16),
Background = Brushes.Transparent,
BorderBrush = Brushes.Gray,
BorderThickness = new Thickness(1),
Child = row
});
}
}
private void OnScheduleSelectionChanged(object? sender, RoutedEventArgs e)
{
_ = e;
if (sender is not RadioButton radioButton || radioButton.IsChecked != true || radioButton.Tag is not string scheduleId)
{
return;
}
_activeScheduleId = scheduleId;
PersistState();
}
private void OnDeleteScheduleClick(object? sender, RoutedEventArgs e)
{
_ = e;
if (sender is not Button button || button.Tag is not string scheduleId)
{
return;
}
var removed = _importedSchedules.RemoveAll(item =>
string.Equals(item.Id, scheduleId, StringComparison.OrdinalIgnoreCase));
if (removed == 0)
{
return;
}
if (string.Equals(_activeScheduleId, scheduleId, StringComparison.OrdinalIgnoreCase))
{
_activeScheduleId = _importedSchedules.FirstOrDefault()?.Id ?? string.Empty;
}
PersistState();
RenderImportedSchedules();
}
private void PersistState()
{
var snapshot = LoadSnapshot();
snapshot.ImportedClassSchedules = _importedSchedules
.Select(item => new ImportedClassScheduleSnapshot
{
Id = item.Id,
DisplayName = item.DisplayName,
FilePath = item.FilePath
})
.ToList();
snapshot.ActiveImportedClassScheduleId = _activeScheduleId;
SaveSnapshot(
snapshot,
nameof(ComponentSettingsSnapshot.ImportedClassSchedules),
nameof(ComponentSettingsSnapshot.ActiveImportedClassScheduleId));
}
}