diff --git a/BetterGenshinImpact/App.xaml.cs b/BetterGenshinImpact/App.xaml.cs index 97f22c6d..1a23313a 100644 --- a/BetterGenshinImpact/App.xaml.cs +++ b/BetterGenshinImpact/App.xaml.cs @@ -106,6 +106,7 @@ public partial class App : Application services.AddView(); services.AddView(); services.AddSingleton(); + services.AddView(); // services.AddView(); services.AddView(); diff --git a/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj b/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj deleted file mode 100644 index 1bb2b60c..00000000 --- a/BetterGenshinImpact/BetterGenshinImpact - Backup.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - WinExe - net8.0-windows10.0.22621.0 - enable - true - true - 12.0 - true - Assets\Images\logo.ico - BetterGI - 0.36.2 - x64 - embedded - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - - - - - - - - \ No newline at end of file diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 38f7eab7..1a27bac9 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -47,6 +47,7 @@ + diff --git a/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.Designer.cs b/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.Designer.cs new file mode 100644 index 00000000..0dc2c490 --- /dev/null +++ b/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.Designer.cs @@ -0,0 +1,87 @@ +// +using System; +using BetterGenshinImpact.Model.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BetterGenshinImpact.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250413162606_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); + + modelBuilder.Entity("BetterGenshinImpact.Model.Database.TaskGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("category"); + + b.Property("Hotkey") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("hotkey"); + + b.Property("Hotkey2") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("hotkey_"); + + b.Property("LastRunTime") + .HasColumnType("TEXT") + .HasColumnName("last_run_time"); + + b.Property("NextRunTime") + .HasColumnType("TEXT") + .HasColumnName("next_run_time"); + + b.Property("OrderIndex") + .HasColumnType("INTEGER") + .HasColumnName("order_index"); + + b.Property("ScheduleExpression") + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("schedule_expression"); + + b.Property("ScheduleType") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("schedule_type"); + + b.Property("TaskName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("task_name"); + + b.Property("TaskParams") + .HasColumnType("TEXT") + .HasColumnName("task_params"); + + b.HasKey("Id"); + + b.HasIndex("OrderIndex") + .IsUnique(); + + b.ToTable("task_group"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.cs b/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.cs new file mode 100644 index 00000000..6fb04a86 --- /dev/null +++ b/BetterGenshinImpact/Migrations/20250413162606_InitialCreate.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BetterGenshinImpact.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "task_group", + columns: table => new + { + id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + order_index = table.Column(type: "INTEGER", nullable: false), + task_name = table.Column(type: "TEXT", maxLength: 255, nullable: false), + task_params = table.Column(type: "TEXT", nullable: true), + schedule_expression = table.Column(type: "TEXT", maxLength: 255, nullable: true), + schedule_type = table.Column(type: "TEXT", maxLength: 50, nullable: true), + next_run_time = table.Column(type: "TEXT", nullable: true), + last_run_time = table.Column(type: "TEXT", nullable: true), + hotkey = table.Column(type: "TEXT", maxLength: 50, nullable: true), + hotkey_ = table.Column(type: "TEXT", maxLength: 50, nullable: true), + category = table.Column(type: "TEXT", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_task_group", x => x.id); + }); + + migrationBuilder.CreateIndex( + name: "IX_task_group_order_index", + table: "task_group", + column: "order_index", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "task_group"); + } + } +} diff --git a/BetterGenshinImpact/Migrations/ApplicationDbContextModelSnapshot.cs b/BetterGenshinImpact/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 00000000..fe800712 --- /dev/null +++ b/BetterGenshinImpact/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,84 @@ +// +using System; +using BetterGenshinImpact.Model.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BetterGenshinImpact.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); + + modelBuilder.Entity("BetterGenshinImpact.Model.Database.TaskGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("id"); + + b.Property("Category") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("category"); + + b.Property("Hotkey") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("hotkey"); + + b.Property("Hotkey2") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("hotkey_"); + + b.Property("LastRunTime") + .HasColumnType("TEXT") + .HasColumnName("last_run_time"); + + b.Property("NextRunTime") + .HasColumnType("TEXT") + .HasColumnName("next_run_time"); + + b.Property("OrderIndex") + .HasColumnType("INTEGER") + .HasColumnName("order_index"); + + b.Property("ScheduleExpression") + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("schedule_expression"); + + b.Property("ScheduleType") + .HasMaxLength(50) + .HasColumnType("TEXT") + .HasColumnName("schedule_type"); + + b.Property("TaskName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT") + .HasColumnName("task_name"); + + b.Property("TaskParams") + .HasColumnType("TEXT") + .HasColumnName("task_params"); + + b.HasKey("Id"); + + b.HasIndex("OrderIndex") + .IsUnique(); + + b.ToTable("task_group"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BetterGenshinImpact/Model/Database/ApplicationDbContext.cs b/BetterGenshinImpact/Model/Database/ApplicationDbContext.cs index e3df9358..2d705b4f 100644 --- a/BetterGenshinImpact/Model/Database/ApplicationDbContext.cs +++ b/BetterGenshinImpact/Model/Database/ApplicationDbContext.cs @@ -2,24 +2,20 @@ using System.IO; using BetterGenshinImpact.Core.Config; using Microsoft.EntityFrameworkCore; -namespace BetterGenshinImpact.Model.Database +namespace BetterGenshinImpact.Model.Database; + +public class ApplicationDbContext : DbContext { - public class ApplicationDbContext : DbContext + public DbSet TaskGroups { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - public DbSet TaskLists { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - var dbPath = Path.Combine(Global.Absolute("User\\db"), "Data", "bgi_user.db"); - Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); - optionsBuilder.UseSqlite($"Data Source={dbPath}"); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity() - .HasIndex(t => t.OrderIndex) - .IsUnique(); - } + var dbPath = Path.Combine(Global.Absolute("User\\Db"), "bgi_user.db"); + Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!); + optionsBuilder.UseSqlite($"Data Source={dbPath}"); } -} \ No newline at end of file + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Model/Database/TaskGroup.cs b/BetterGenshinImpact/Model/Database/TaskGroup.cs new file mode 100644 index 00000000..9e9cf8af --- /dev/null +++ b/BetterGenshinImpact/Model/Database/TaskGroup.cs @@ -0,0 +1,50 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace BetterGenshinImpact.Model.Database; + +[Table("task_group")] +public class TaskGroup +{ + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("order_index")] + public int OrderIndex { get; set; } + + [Column("task_name")] + [Required] + [MaxLength(255)] + public string TaskName { get; set; } = string.Empty; + + [Column("task_params")] + public string? TaskParams { get; set; } + + [Column("schedule_expression")] + [MaxLength(255)] + public string? ScheduleExpression { get; set; } + + [Column("schedule_type")] + [MaxLength(50)] + public string? ScheduleType { get; set; } + + [Column("next_run_time")] + public DateTime? NextRunTime { get; set; } + + [Column("last_run_time")] + public DateTime? LastRunTime { get; set; } + + [Column("hotkey")] + [MaxLength(50)] + public string? Hotkey { get; set; } + + [Column("hotkey_")] + [MaxLength(50)] + public string? Hotkey2 { get; set; } + + [Column("category")] + [MaxLength(50)] + public string? Category { get; set; } +} \ No newline at end of file diff --git a/BetterGenshinImpact/Model/Database/TaskList.cs b/BetterGenshinImpact/Model/Database/TaskList.cs deleted file mode 100644 index 10037cbc..00000000 --- a/BetterGenshinImpact/Model/Database/TaskList.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace BetterGenshinImpact.Model.Database -{ - [Table("task_list")] - public class TaskList - { - [Key] - [Column("id")] - public int Id { get; set; } - - [Column("order_index")] - public int OrderIndex { get; set; } - - [Column("task_name")] - [Required] - [MaxLength(255)] - public string TaskName { get; set; } = string.Empty; - - [Column("task_params")] - public string? TaskParams { get; set; } - - [Column("schedule_expression")] - [MaxLength(255)] - public string? ScheduleExpression { get; set; } - - [Column("schedule_type")] - [MaxLength(50)] - public string? ScheduleType { get; set; } - - [Column("next_run_time")] - public DateTime? NextRunTime { get; set; } - - [Column("last_run_time")] - public DateTime? LastRunTime { get; set; } - - [Column("hotkey")] - [MaxLength(50)] - public string? Hotkey { get; set; } - - [Column("hotkey_")] - [MaxLength(50)] - public string? Hotkey2 { get; set; } - - [Column("category")] - [MaxLength(50)] - public string? Category { get; set; } - } -} \ No newline at end of file diff --git a/BetterGenshinImpact/Service/DatabaseInitializer.cs b/BetterGenshinImpact/Service/DatabaseInitializer.cs index 74e1faaf..b443bf8b 100644 --- a/BetterGenshinImpact/Service/DatabaseInitializer.cs +++ b/BetterGenshinImpact/Service/DatabaseInitializer.cs @@ -4,32 +4,37 @@ using BetterGenshinImpact.Model.Database; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -namespace BetterGenshinImpact.Service +namespace BetterGenshinImpact.Service; + +public class DatabaseInitializer { - public class DatabaseInitializer + private readonly ApplicationDbContext _context; + private readonly ILogger _logger; + + public DatabaseInitializer(ApplicationDbContext context, ILogger logger) { - private readonly ApplicationDbContext _context; - private readonly ILogger _logger; + _context = context; + _logger = logger; + } - public DatabaseInitializer(ApplicationDbContext context, ILogger logger) + public void Initialize() + { + try { - _context = context; - _logger = logger; + _logger.LogInformation("正在初始化数据库..."); + + // 确保数据库已创建 + _context.Database.EnsureCreated(); + + // 应用所有迁移 + _context.Database.Migrate(); + + _logger.LogInformation("数据库初始化完成"); } - - public void Initialize() + catch (Exception ex) { - try - { - _logger.LogInformation("正在初始化数据库..."); - _context.Database.Migrate(); - _logger.LogInformation("数据库初始化完成"); - } - catch (Exception ex) - { - _logger.LogError(ex, "数据库初始化失败"); - throw; - } + _logger.LogError(ex, "数据库初始化失败"); + throw; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/BetterGenshinImpact/View/MainWindow.xaml b/BetterGenshinImpact/View/MainWindow.xaml index ee303e8c..b83074e2 100644 --- a/BetterGenshinImpact/View/MainWindow.xaml +++ b/BetterGenshinImpact/View/MainWindow.xaml @@ -98,6 +98,13 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BetterGenshinImpact/View/Pages/TaskGroupPage.xaml.cs b/BetterGenshinImpact/View/Pages/TaskGroupPage.xaml.cs new file mode 100644 index 00000000..c2d23655 --- /dev/null +++ b/BetterGenshinImpact/View/Pages/TaskGroupPage.xaml.cs @@ -0,0 +1,12 @@ +using BetterGenshinImpact.ViewModel.Pages; + +namespace BetterGenshinImpact.View.Pages; + +public partial class TaskGroupPage +{ + public TaskGroupPage(TaskGroupPageViewModel viewModel) + { + InitializeComponent(); + DataContext = viewModel; + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/ViewModel/Pages/TaskGroupPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/TaskGroupPageViewModel.cs new file mode 100644 index 00000000..13f9cdbd --- /dev/null +++ b/BetterGenshinImpact/ViewModel/Pages/TaskGroupPageViewModel.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using BetterGenshinImpact.Model.Database; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Microsoft.Extensions.Logging; + +namespace BetterGenshinImpact.ViewModel.Pages; + +public partial class TaskGroupPageViewModel : ViewModel +{ + private readonly ApplicationDbContext _dbContext; + private readonly ILogger _logger; + + [ObservableProperty] private ObservableCollection _taskGroups = new(); + + [ObservableProperty] private TaskGroup? _selectedTaskGroup; + + [ObservableProperty] private bool _isEditing; + + public TaskGroupPageViewModel(ApplicationDbContext dbContext, ILogger logger) + { + _dbContext = dbContext; + _logger = logger; + LoadTaskGroups(); + } + + private void LoadTaskGroups() + { + var groups = _dbContext.TaskGroups + .OrderBy(t => t.OrderIndex) + .ToList(); + TaskGroups = new ObservableCollection(groups); + } + + [RelayCommand] + private void AddTaskGroup() + { + var newGroup = new TaskGroup + { + OrderIndex = TaskGroups.Count + 1, + TaskName = "新任务组", + Category = "默认" + }; + TaskGroups.Add(newGroup); + SelectedTaskGroup = newGroup; + IsEditing = true; + } + + [RelayCommand] + private void EditTaskGroup() + { + if (SelectedTaskGroup != null) + { + IsEditing = true; + } + } + + [RelayCommand] + private async Task SaveTaskGroup() + { + if (SelectedTaskGroup == null) return; + + + if (SelectedTaskGroup.Id == 0) + { + _dbContext.TaskGroups.Add(SelectedTaskGroup); + } + else + { + _dbContext.TaskGroups.Update(SelectedTaskGroup); + } + + await _dbContext.SaveChangesAsync(); + IsEditing = false; + LoadTaskGroups(); + } + + [RelayCommand] + private async Task DeleteTaskGroup() + { + if (SelectedTaskGroup == null) return; + + + _dbContext.TaskGroups.Remove(SelectedTaskGroup); + await _dbContext.SaveChangesAsync(); + TaskGroups.Remove(SelectedTaskGroup); + SelectedTaskGroup = null; + } + + [RelayCommand] + private void CancelEdit() + { + IsEditing = false; + LoadTaskGroups(); + } + + [RelayCommand] + private void MoveUp() + { + if (SelectedTaskGroup == null) return; + + var currentIndex = TaskGroups.IndexOf(SelectedTaskGroup); + if (currentIndex > 0) + { + var previousGroup = TaskGroups[currentIndex - 1]; + (SelectedTaskGroup.OrderIndex, previousGroup.OrderIndex) = + (previousGroup.OrderIndex, SelectedTaskGroup.OrderIndex); + TaskGroups.Move(currentIndex, currentIndex - 1); + _dbContext.SaveChanges(); + } + } + + [RelayCommand] + private void MoveDown() + { + if (SelectedTaskGroup == null) return; + + var currentIndex = TaskGroups.IndexOf(SelectedTaskGroup); + if (currentIndex < TaskGroups.Count - 1) + { + var nextGroup = TaskGroups[currentIndex + 1]; + (SelectedTaskGroup.OrderIndex, nextGroup.OrderIndex) = (nextGroup.OrderIndex, SelectedTaskGroup.OrderIndex); + TaskGroups.Move(currentIndex, currentIndex + 1); + _dbContext.SaveChanges(); + } + } +} \ No newline at end of file