mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-20 08:29:50 +08:00
364 lines
12 KiB
C#
364 lines
12 KiB
C#
using BetterGenshinImpact.Core.Config;
|
||
using BetterGenshinImpact.Core.Script;
|
||
using BetterGenshinImpact.Core.Script.Group;
|
||
using BetterGenshinImpact.Core.Script.Project;
|
||
using BetterGenshinImpact.Service.Interface;
|
||
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using CommunityToolkit.Mvvm.Input;
|
||
using Microsoft.Extensions.Logging;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Media;
|
||
using BetterGenshinImpact.Helpers;
|
||
using BetterGenshinImpact.View.Controls.Drawer;
|
||
using BetterGenshinImpact.View.Controls.Webview;
|
||
using BetterGenshinImpact.ViewModel.Message;
|
||
using CommunityToolkit.Mvvm.Messaging;
|
||
using Microsoft.Web.WebView2.Wpf;
|
||
using Wpf.Ui;
|
||
using Wpf.Ui.Violeta.Controls;
|
||
using Button = Wpf.Ui.Controls.Button;
|
||
using StackPanel = Wpf.Ui.Controls.StackPanel;
|
||
using TextBlock = Wpf.Ui.Controls.TextBlock;
|
||
using TextBox = Wpf.Ui.Controls.TextBox;
|
||
|
||
namespace BetterGenshinImpact.ViewModel.Pages;
|
||
|
||
public partial class JsListViewModel : ViewModel
|
||
{
|
||
private readonly ILogger<JsListViewModel> _logger = App.GetLogger<JsListViewModel>();
|
||
private readonly string scriptPath = Global.ScriptPath();
|
||
|
||
[ObservableProperty] private ObservableCollection<ScriptProject> _scriptItems = [];
|
||
|
||
private readonly IScriptService _scriptService;
|
||
|
||
public AllConfig Config { get; set; }
|
||
|
||
public DrawerViewModel DrawerVm { get; } = new DrawerViewModel();
|
||
|
||
private WebView2? _webView2;
|
||
|
||
private WebpagePanel? _mdWebpagePanel;
|
||
|
||
private TaskCompletionSource<bool>? _navigationCompletionSource;
|
||
private const int NavigationTimeoutMs = 10000; // 10秒超时
|
||
|
||
[ObservableProperty]
|
||
private ScriptProject? _selectedScriptProject;
|
||
|
||
[ObservableProperty]
|
||
private bool _isRightClickSelection;
|
||
|
||
public JsListViewModel(IScriptService scriptService, IConfigService configService)
|
||
{
|
||
_scriptService = scriptService;
|
||
Config = configService.Get();
|
||
|
||
// 注册消息
|
||
WeakReferenceMessenger.Default.Register<RefreshDataMessage>(this, (r, m) => InitScriptListViewData());
|
||
}
|
||
|
||
private void InitScriptListViewData()
|
||
{
|
||
ScriptItems.Clear();
|
||
var directoryInfos = LoadScriptFolder(scriptPath);
|
||
foreach (var f in directoryInfos)
|
||
{
|
||
try
|
||
{
|
||
ScriptItems.Add(new ScriptProject(f.Name));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Toast.Warning($"脚本 {f.Name} 载入失败:{e.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
private IEnumerable<DirectoryInfo> LoadScriptFolder(string folder)
|
||
{
|
||
if (!Directory.Exists(folder))
|
||
{
|
||
Directory.CreateDirectory(folder);
|
||
}
|
||
|
||
var di = new DirectoryInfo(folder);
|
||
|
||
return di.GetDirectories();
|
||
}
|
||
|
||
public override void OnNavigatedTo()
|
||
{
|
||
InitScriptListViewData();
|
||
}
|
||
|
||
[RelayCommand]
|
||
public void OnOpenScriptsFolder()
|
||
{
|
||
Process.Start("explorer.exe", scriptPath);
|
||
}
|
||
|
||
[RelayCommand]
|
||
public void OnOpenScriptProjectFolder(ScriptProject? item)
|
||
{
|
||
Process.Start("explorer.exe", item == null ? scriptPath : item.ProjectPath);
|
||
}
|
||
|
||
[RelayCommand]
|
||
public async Task OnStartRun(ScriptProject? item)
|
||
{
|
||
if (item == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(item.Manifest.SettingsUi))
|
||
{
|
||
Toast.Information("此脚本存在配置,不配置可能无法正常运行,建议请添加至【调度器】,并右键修改配置后使用!");
|
||
_logger.LogWarning("此脚本存在配置,可能无法直接从脚本界面运行,建议请添加至【调度器】,并右键修改配置后使用!");
|
||
}
|
||
|
||
await _scriptService.RunMulti([new ScriptGroupProject(item)]);
|
||
}
|
||
|
||
[RelayCommand]
|
||
public void OnRefresh(ScriptProject? item)
|
||
{
|
||
InitScriptListViewData();
|
||
}
|
||
|
||
[RelayCommand]
|
||
public async Task OnDeleteScript(ScriptProject? item)
|
||
{
|
||
if (item == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 显示确认对话框
|
||
var messageBox = new Wpf.Ui.Controls.MessageBox
|
||
{
|
||
Title = "删除确认",
|
||
Content = $"确定要删除脚本 \"{item.Manifest.Name}\" 吗?\n\n此操作将永久删除脚本文件夹及其所有内容,无法恢复!",
|
||
PrimaryButtonText = "删除",
|
||
CloseButtonText = "取消",
|
||
Owner = Application.Current.MainWindow,
|
||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||
};
|
||
|
||
var result = await messageBox.ShowDialogAsync();
|
||
if (result == Wpf.Ui.Controls.MessageBoxResult.Primary)
|
||
{
|
||
try
|
||
{
|
||
// 删除脚本文件夹
|
||
if (Directory.Exists(item.ProjectPath))
|
||
{
|
||
Directory.Delete(item.ProjectPath, true);
|
||
Toast.Success($"已删除脚本: {item.Manifest.Name}");
|
||
_logger.LogInformation("已删除脚本: {Name} ({Path})", item.Manifest.Name, item.ProjectPath);
|
||
|
||
// 刷新列表
|
||
InitScriptListViewData();
|
||
}
|
||
else
|
||
{
|
||
Toast.Warning("脚本目录不存在");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Toast.Error($"删除脚本失败: {ex.Message}");
|
||
_logger.LogError(ex, "删除脚本失败");
|
||
}
|
||
}
|
||
}
|
||
|
||
[RelayCommand]
|
||
public void OnGoToJsScriptUrl()
|
||
{
|
||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/jsscript.html")
|
||
{ UseShellExecute = true });
|
||
}
|
||
|
||
[RelayCommand]
|
||
public void OnOpenLocalScriptRepo()
|
||
{
|
||
Config.ScriptConfig.ScriptRepoHintDotVisible = false;
|
||
ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void SetRightClickSelection(string isRightClick)
|
||
{
|
||
IsRightClickSelection = "True".Equals(isRightClick, StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
|
||
[RelayCommand]
|
||
private void OpenScriptDetailDrawer(object? scriptItem)
|
||
{
|
||
if (scriptItem == null || IsRightClickSelection)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (scriptItem is ScriptProject scriptProject)
|
||
{
|
||
// 检查是否存在README.md或其他md文件
|
||
var mdFilePath = FindMdFilePath(scriptProject);
|
||
|
||
// 设置抽屉位置和大小
|
||
DrawerVm.DrawerPosition = DrawerPosition.Right;
|
||
|
||
if (!string.IsNullOrEmpty(mdFilePath))
|
||
{
|
||
DrawerVm.DrawerWidth = 450;
|
||
// 注册抽屉关闭前事件 - 简化处理,不再需要WebView2相关逻辑
|
||
DrawerVm.SetDrawerClosingAction(_ => { });
|
||
DrawerVm.setDrawerOpenedAction(() =>
|
||
{
|
||
SelectedScriptProject = null;
|
||
});
|
||
}
|
||
else
|
||
{
|
||
DrawerVm.SetDrawerClosingAction(_ => { });
|
||
DrawerVm.setDrawerOpenedAction(() =>
|
||
{
|
||
SelectedScriptProject = null;
|
||
});
|
||
DrawerVm.DrawerWidth = 300;
|
||
}
|
||
|
||
// 创建要在抽屉中显示的内容
|
||
var content = CreateScriptDetailContent(scriptProject, mdFilePath);
|
||
|
||
// 打开抽屉
|
||
DrawerVm.OpenDrawer(content);
|
||
}
|
||
}
|
||
|
||
private object CreateScriptDetailContent(ScriptProject scriptProject, string? mdFilePath)
|
||
{
|
||
// 创建显示脚本详情的控件
|
||
var border = new Border
|
||
{
|
||
//Background = new SolidColorBrush(Color.FromRgb(0x2B, 0x2B, 0x2B)),
|
||
Padding = new Thickness(20),
|
||
CornerRadius = new CornerRadius(8)
|
||
};
|
||
border.SetResourceReference(Border.BackgroundProperty, "ApplicationBackgroundBrush");
|
||
|
||
|
||
// 使用Grid替代StackPanel以实现更好的布局控制
|
||
var mainGrid = new Grid();
|
||
mainGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); // 标题行
|
||
mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); // 内容行,占满剩余空间
|
||
|
||
border.Child = mainGrid;
|
||
|
||
// 添加标题
|
||
var titleTextBlock = new TextBlock
|
||
{
|
||
Text = scriptProject.Manifest.Name,
|
||
FontSize = 20,
|
||
FontWeight = FontWeights.Bold,
|
||
Margin = new Thickness(0, 0, 0, 10)
|
||
};
|
||
Grid.SetRow(titleTextBlock, 0);
|
||
mainGrid.Children.Add(titleTextBlock);
|
||
|
||
// 如果找到md文件,使用RichTextBox显示
|
||
if (!string.IsNullOrEmpty(mdFilePath))
|
||
{
|
||
string markdown = File.ReadAllText(mdFilePath);
|
||
var flowDoc = MarkdownToFlowDocumentConverter.ConvertToFlowDocument(markdown);
|
||
var richTextBox = new RichTextBox
|
||
{
|
||
IsReadOnly = true,
|
||
IsDocumentEnabled = true,
|
||
BorderThickness = new Thickness(0),
|
||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
|
||
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
|
||
Document = flowDoc,
|
||
Background = Brushes.Transparent,
|
||
VerticalAlignment = VerticalAlignment.Stretch,
|
||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||
Margin = new Thickness(0, 10, 0, 0)
|
||
};
|
||
|
||
Grid.SetRow(richTextBox, 1);
|
||
mainGrid.Children.Add(richTextBox);
|
||
}
|
||
else
|
||
{
|
||
// 显示脚本基本信息 - 使用StackPanel包装多个TextBlock
|
||
var contentPanel = new StackPanel
|
||
{
|
||
Margin = new Thickness(0, 10, 0, 0),
|
||
VerticalAlignment = VerticalAlignment.Top
|
||
};
|
||
|
||
contentPanel.Children.Add(new TextBlock
|
||
{
|
||
Text = $"版本: {scriptProject.Manifest.Version}",
|
||
Margin = new Thickness(0, 0, 0, 10)
|
||
});
|
||
|
||
contentPanel.Children.Add(new TextBlock
|
||
{
|
||
Text = scriptProject.Manifest.Description,
|
||
TextWrapping = TextWrapping.Wrap,
|
||
Margin = new Thickness(0, 0, 0, 15)
|
||
});
|
||
|
||
Grid.SetRow(contentPanel, 1);
|
||
mainGrid.Children.Add(contentPanel);
|
||
}
|
||
|
||
// 添加操作按钮
|
||
// var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal };
|
||
//
|
||
// var runButton = new Button
|
||
// {
|
||
// Content = "执行脚本",
|
||
// Margin = new Thickness(0, 0, 10, 0)
|
||
// };
|
||
// runButton.Click += async (s, e) => await OnStartRun(script);
|
||
// buttonPanel.Children.Add(runButton);
|
||
//
|
||
// var openFolderButton = new Button { Content = "打开目录" };
|
||
// openFolderButton.Click += (s, e) => OnOpenScriptProjectFolder(script);
|
||
// buttonPanel.Children.Add(openFolderButton);
|
||
|
||
// panel.Children.Add(buttonPanel);
|
||
|
||
return border;
|
||
}
|
||
|
||
private static string? FindMdFilePath(ScriptProject script)
|
||
{
|
||
string[] possibleMdFiles = { "README.md", "readme.md" };
|
||
string? mdFilePath = null;
|
||
|
||
foreach (var mdFile in possibleMdFiles)
|
||
{
|
||
string fullPath = Path.Combine(script.ProjectPath, mdFile);
|
||
if (File.Exists(fullPath))
|
||
{
|
||
mdFilePath = fullPath;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return mdFilePath;
|
||
}
|
||
} |