mirror of
https://github.com/2dust/v2rayN.git
synced 2026-05-30 01:34:08 +00:00
Show routing import errors in modal dialog
This commit is contained in:
parent
807f0aba06
commit
1520343c29
8 changed files with 123 additions and 3 deletions
|
|
@ -33,4 +33,5 @@ public enum EViewAction
|
|||
DispatcherRefreshServersBiz,
|
||||
DispatcherRefreshIcon,
|
||||
DispatcherShowMsg,
|
||||
ShowMessage,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,13 +305,16 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
}
|
||||
if (clipboardData.IsNullOrEmpty())
|
||||
{
|
||||
await ShowImportErrorAsync($"{ResUI.OperationFailed}: {ResUI.FailedReadConfiguration}");
|
||||
return -1;
|
||||
}
|
||||
var lstRules = JsonUtils.Deserialize<List<RulesItem>>(clipboardData);
|
||||
if (lstRules == null)
|
||||
|
||||
if (!TryDeserializeRoutingRules(clipboardData, out var lstRules, out var errorMsg))
|
||||
{
|
||||
await ShowImportErrorAsync($"{ResUI.OperationFailed}: {errorMsg}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
foreach (var rule in lstRules)
|
||||
{
|
||||
rule.Id = Utils.GetGuid(false);
|
||||
|
|
@ -328,5 +331,74 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||
return 0;
|
||||
}
|
||||
|
||||
private async Task ShowImportErrorAsync(string message)
|
||||
{
|
||||
if (_updateView != null)
|
||||
{
|
||||
await _updateView.Invoke(EViewAction.ShowMessage, message);
|
||||
return;
|
||||
}
|
||||
|
||||
NoticeManager.Instance.Enqueue(message);
|
||||
}
|
||||
|
||||
private static bool TryDeserializeRoutingRules(string clipboardData, out List<RulesItem> rules, out string errorMsg)
|
||||
{
|
||||
rules = [];
|
||||
errorMsg = string.Empty;
|
||||
|
||||
var trimmed = clipboardData.Trim();
|
||||
if (trimmed.IsNullOrEmpty())
|
||||
{
|
||||
errorMsg = ResUI.FailedReadConfiguration;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trimmed.StartsWith('{'))
|
||||
{
|
||||
if (trimmed.Contains("\"routing\"", StringComparison.OrdinalIgnoreCase)
|
||||
|| trimmed.Contains("\"route\"", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
errorMsg = "你粘贴的是完整配置文件,不是路由规则数组。请只粘贴 routing.rules 或 route.rules 的 JSON 数组。";
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMsg = "当前内容是 JSON 对象。路由规则导入需要顶层 JSON 数组,例如 [ { \"outboundTag\": \"proxy\" } ]。";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
};
|
||||
|
||||
rules = JsonSerializer.Deserialize<List<RulesItem>>(trimmed, options) ?? [];
|
||||
if (rules.Count == 0)
|
||||
{
|
||||
errorMsg = "未解析到任何路由规则,请确认内容是非空 JSON 数组。";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
var location = ex.LineNumber is not null && ex.BytePositionInLine is not null
|
||||
? $"第 {ex.LineNumber + 1} 行,第 {ex.BytePositionInLine + 1} 列"
|
||||
: "未知位置";
|
||||
errorMsg = $"JSON 解析错误,{location}。{ex.Message}";
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMsg = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Import rules
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@ internal class UI
|
|||
{
|
||||
private static readonly string caption = Global.AppName;
|
||||
|
||||
public static async Task Show(Window owner, string msg)
|
||||
{
|
||||
var box = new MessageBoxDialog(caption, msg, true);
|
||||
await box.ShowDialog<ButtonResult>(owner);
|
||||
}
|
||||
|
||||
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
|
||||
{
|
||||
var box = new MessageBoxDialog(caption, msg);
|
||||
|
|
|
|||
|
|
@ -247,6 +247,15 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
case EViewAction.AddServerViaClipboard:
|
||||
await AddServerViaClipboardAsync();
|
||||
break;
|
||||
|
||||
case EViewAction.ShowMessage:
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await UI.Show(this, obj.ToString() ?? string.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
|
|
|
|||
|
|
@ -9,13 +9,18 @@ public partial class MessageBoxDialog : Window
|
|||
{
|
||||
}
|
||||
|
||||
public MessageBoxDialog(string caption, string message)
|
||||
public MessageBoxDialog(string caption, string message, bool okOnly = false)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Title = caption;
|
||||
txtMessage.Text = message;
|
||||
|
||||
if (okOnly)
|
||||
{
|
||||
btnNo.IsVisible = false;
|
||||
}
|
||||
|
||||
btnYes.Click += BtnYes_Click;
|
||||
btnNo.Click += BtnNo_Click;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,15 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
case EViewAction.ShowMessage:
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await UI.Show(this, obj.ToString() ?? string.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
|
|
|
|||
|
|
@ -242,6 +242,15 @@ public partial class MainWindow
|
|||
case EViewAction.AddServerViaClipboard:
|
||||
await AddServerViaClipboardAsync();
|
||||
break;
|
||||
|
||||
case EViewAction.ShowMessage:
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UI.Show(obj.ToString() ?? string.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,15 @@ public partial class RoutingRuleSettingWindow
|
|||
ViewModel?.ImportRulesFromClipboardAsync(clipboardData);
|
||||
}
|
||||
break;
|
||||
|
||||
case EViewAction.ShowMessage:
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UI.Show(obj.ToString() ?? string.Empty);
|
||||
break;
|
||||
}
|
||||
|
||||
return await Task.FromResult(true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue