mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 04:12:45 +08:00
automation实现inspect元素
This commit is contained in:
parent
021cce5947
commit
a3dc6479f2
9
automation.cs
Normal file
9
automation.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// 如果指定了过滤条件,创建一个组合条件
|
||||||
|
if (!string.IsNullOrEmpty(filter))
|
||||||
|
{
|
||||||
|
searchCondition = new OrCondition(
|
||||||
|
new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase),
|
||||||
|
new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase),
|
||||||
|
new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase)
|
||||||
|
);
|
||||||
|
}
|
@ -9,9 +9,16 @@ using System.Web.Script.Serialization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
public class AutomationManager
|
public class AutomationManager
|
||||||
{
|
{
|
||||||
|
// UIA Control Type IDs
|
||||||
|
private static class UIA_ControlTypeIds
|
||||||
|
{
|
||||||
|
public const int Window = 50032;
|
||||||
|
}
|
||||||
|
|
||||||
// 用于缓存找到的元素
|
// 用于缓存找到的元素
|
||||||
private static Dictionary<string, AutomationElement> elementCache = new Dictionary<string, AutomationElement>();
|
private static Dictionary<string, AutomationElement> elementCache = new Dictionary<string, AutomationElement>();
|
||||||
|
|
||||||
@ -59,6 +66,15 @@ public class AutomationManager
|
|||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern short GetAsyncKeyState(int vKey);
|
private static extern short GetAsyncKeyState(int vKey);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool BlockInput(bool fBlockIt);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "--help")
|
if (args.Length == 0 || args[0] == "-h" || args[0] == "--help")
|
||||||
@ -79,17 +95,10 @@ public class AutomationManager
|
|||||||
switch (type.ToLower())
|
switch (type.ToLower())
|
||||||
{
|
{
|
||||||
case "list":
|
case "list":
|
||||||
// 列出所有元素
|
|
||||||
string filter = GetArgumentValue(args, "-filter"); // 可选的过滤条件
|
|
||||||
ListElements(args, filter);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "find":
|
case "find":
|
||||||
// 查找元素
|
// 列出所有元素或查找元素
|
||||||
string by = GetArgumentValue(args, "-by") ?? "name";
|
string filter = GetArgumentValue(args, "-value") ?? GetArgumentValue(args, "-filter"); // 兼容两种参数名
|
||||||
string value = GetArgumentValue(args, "-value");
|
ListElements(args, filter);
|
||||||
string scope = GetArgumentValue(args, "-scope") ?? "children";
|
|
||||||
FindElement(args, by, value, scope);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "getinfo":
|
case "getinfo":
|
||||||
@ -135,8 +144,8 @@ public class AutomationManager
|
|||||||
|
|
||||||
case "wait":
|
case "wait":
|
||||||
// 等待元素
|
// 等待元素
|
||||||
by = GetArgumentValue(args, "-by") ?? "name";
|
string by = GetArgumentValue(args, "-by") ?? "name";
|
||||||
value = GetArgumentValue(args, "-value");
|
string value = GetArgumentValue(args, "-value");
|
||||||
int timeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
|
int timeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
|
||||||
WaitForElement(args, by, value, timeout);
|
WaitForElement(args, by, value, timeout);
|
||||||
break;
|
break;
|
||||||
@ -171,8 +180,7 @@ public class AutomationManager
|
|||||||
|
|
||||||
case "inspect":
|
case "inspect":
|
||||||
// 生成元素识别器
|
// 生成元素识别器
|
||||||
int inspectTimeout = int.Parse(GetArgumentValue(args, "-timeout") ?? "30");
|
InspectElement(args);
|
||||||
InspectElement(args, inspectTimeout);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -203,6 +211,7 @@ public class AutomationManager
|
|||||||
- process 进程名
|
- process 进程名
|
||||||
- active 当前活动窗口(默认)
|
- active 当前活动窗口(默认)
|
||||||
不指定时默认在整个桌面范围内查找
|
不指定时默认在整个桌面范围内查找
|
||||||
|
-background 后台操作模式,不激活窗口(可选)
|
||||||
|
|
||||||
元素识别参数:
|
元素识别参数:
|
||||||
-value <标识值> 要查找的元素的值
|
-value <标识值> 要查找的元素的值
|
||||||
@ -215,10 +224,8 @@ public class AutomationManager
|
|||||||
|
|
||||||
操作类型及示例:
|
操作类型及示例:
|
||||||
1. inspect - 生成元素识别器
|
1. inspect - 生成元素识别器
|
||||||
参数:
|
|
||||||
-timeout <超时> 等待超时时间(秒,默认30)
|
|
||||||
示例:
|
示例:
|
||||||
automation.exe -type inspect -timeout 60
|
automation.exe -type inspect
|
||||||
说明:
|
说明:
|
||||||
运行后点击要识别的元素,将返回元素的详细信息和示例命令
|
运行后点击要识别的元素,将返回元素的详细信息和示例命令
|
||||||
|
|
||||||
@ -465,7 +472,9 @@ public class AutomationManager
|
|||||||
var info = new Dictionary<string, object>();
|
var info = new Dictionary<string, object>();
|
||||||
info.Add("name", element.Current.Name);
|
info.Add("name", element.Current.Name);
|
||||||
info.Add("class", element.Current.ClassName);
|
info.Add("class", element.Current.ClassName);
|
||||||
info.Add("type", element.Current.ControlType.ProgrammaticName);
|
info.Add("controlType", element.Current.ControlType.ProgrammaticName);
|
||||||
|
info.Add("controlClass", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
info.Add("localizedType", element.Current.LocalizedControlType);
|
||||||
info.Add("automationId", element.Current.AutomationId);
|
info.Add("automationId", element.Current.AutomationId);
|
||||||
info.Add("processId", element.Current.ProcessId);
|
info.Add("processId", element.Current.ProcessId);
|
||||||
info.Add("boundingRectangle", new
|
info.Add("boundingRectangle", new
|
||||||
@ -486,7 +495,18 @@ public class AutomationManager
|
|||||||
string identifier = GetArgumentValue(args, "-value");
|
string identifier = GetArgumentValue(args, "-value");
|
||||||
string by = GetArgumentValue(args, "-by");
|
string by = GetArgumentValue(args, "-by");
|
||||||
string pattern = GetArgumentValue(args, "-pattern") ?? "invoke";
|
string pattern = GetArgumentValue(args, "-pattern") ?? "invoke";
|
||||||
|
bool background = HasArgument(args, "-background");
|
||||||
|
|
||||||
var element = GetElementByIdentifier(args, identifier, by);
|
var element = GetElementByIdentifier(args, identifier, by);
|
||||||
|
|
||||||
|
// 只在非后台操作时激活窗口
|
||||||
|
if (!background)
|
||||||
|
{
|
||||||
|
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
Thread.Sleep(50); // 等待窗口激活
|
||||||
|
}
|
||||||
|
|
||||||
switch (pattern.ToLower())
|
switch (pattern.ToLower())
|
||||||
{
|
{
|
||||||
case "invoke":
|
case "invoke":
|
||||||
@ -512,6 +532,11 @@ public class AutomationManager
|
|||||||
throw new Exception("元素不支持指定的操作模式");
|
throw new Exception("元素不支持指定的操作模式");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasArgument(string[] args, string key)
|
||||||
|
{
|
||||||
|
return args.Contains(key, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private static string[] GetSupportedPatterns(AutomationElement element)
|
private static string[] GetSupportedPatterns(AutomationElement element)
|
||||||
{
|
{
|
||||||
var patterns = new List<string>();
|
var patterns = new List<string>();
|
||||||
@ -527,8 +552,18 @@ public class AutomationManager
|
|||||||
{
|
{
|
||||||
string identifier = GetArgumentValue(args, "-value");
|
string identifier = GetArgumentValue(args, "-value");
|
||||||
string by = GetArgumentValue(args, "-by");
|
string by = GetArgumentValue(args, "-by");
|
||||||
|
bool background = HasArgument(args, "-background");
|
||||||
|
|
||||||
var element = GetElementByIdentifier(args, identifier, by);
|
var element = GetElementByIdentifier(args, identifier, by);
|
||||||
|
|
||||||
|
// 只在非后台操作时激活窗口
|
||||||
|
if (!background)
|
||||||
|
{
|
||||||
|
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
Thread.Sleep(50); // 等待窗口激活
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试ValuePattern
|
// 尝试ValuePattern
|
||||||
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
||||||
if (valuePattern != null)
|
if (valuePattern != null)
|
||||||
@ -698,7 +733,7 @@ public class AutomationManager
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FindElement(args, by, value, "subtree");
|
ListElements(args, value);
|
||||||
Console.WriteLine("成功找到元素");
|
Console.WriteLine("成功找到元素");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -734,15 +769,25 @@ public class AutomationManager
|
|||||||
|
|
||||||
private static void SendKeys(string[] args, string keys)
|
private static void SendKeys(string[] args, string keys)
|
||||||
{
|
{
|
||||||
|
string identifier = GetArgumentValue(args, "-value");
|
||||||
|
string by = GetArgumentValue(args, "-by");
|
||||||
|
bool background = HasArgument(args, "-background");
|
||||||
|
|
||||||
|
var element = GetElementByIdentifier(args, identifier, by);
|
||||||
|
|
||||||
|
// 只在非后台操作时激活窗口
|
||||||
|
if (!background)
|
||||||
|
{
|
||||||
|
var hwnd = new IntPtr(element.Current.NativeWindowHandle);
|
||||||
|
SetForegroundWindow(hwnd);
|
||||||
|
Thread.Sleep(50); // 等待窗口激活
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(keys))
|
if (string.IsNullOrEmpty(keys))
|
||||||
{
|
{
|
||||||
throw new Exception("必须指定要发送的按键");
|
throw new Exception("必须指定要发送的按键");
|
||||||
}
|
}
|
||||||
|
|
||||||
string identifier = GetArgumentValue(args, "-value");
|
|
||||||
string by = GetArgumentValue(args, "-by");
|
|
||||||
var element = GetElementByIdentifier(args, identifier, by);
|
|
||||||
|
|
||||||
// 确保元素可以接收输入
|
// 确保元素可以接收输入
|
||||||
if (!element.Current.IsKeyboardFocusable)
|
if (!element.Current.IsKeyboardFocusable)
|
||||||
{
|
{
|
||||||
@ -824,7 +869,9 @@ public class AutomationManager
|
|||||||
childInfo.Add("id", CacheElement(child));
|
childInfo.Add("id", CacheElement(child));
|
||||||
childInfo.Add("name", child.Current.Name);
|
childInfo.Add("name", child.Current.Name);
|
||||||
childInfo.Add("class", child.Current.ClassName);
|
childInfo.Add("class", child.Current.ClassName);
|
||||||
childInfo.Add("type", child.Current.ControlType.ProgrammaticName);
|
childInfo.Add("controlType", child.Current.ControlType.ProgrammaticName);
|
||||||
|
childInfo.Add("type", child.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
childInfo.Add("localizedType", child.Current.LocalizedControlType);
|
||||||
childInfo.Add("automationId", child.Current.AutomationId);
|
childInfo.Add("automationId", child.Current.AutomationId);
|
||||||
result.Add(childInfo);
|
result.Add(childInfo);
|
||||||
}
|
}
|
||||||
@ -846,7 +893,9 @@ public class AutomationManager
|
|||||||
parentInfo.Add("id", CacheElement(parent));
|
parentInfo.Add("id", CacheElement(parent));
|
||||||
parentInfo.Add("name", parent.Current.Name);
|
parentInfo.Add("name", parent.Current.Name);
|
||||||
parentInfo.Add("class", parent.Current.ClassName);
|
parentInfo.Add("class", parent.Current.ClassName);
|
||||||
parentInfo.Add("type", parent.Current.ControlType.ProgrammaticName);
|
parentInfo.Add("controlType", parent.Current.ControlType.ProgrammaticName);
|
||||||
|
parentInfo.Add("type", parent.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
parentInfo.Add("localizedType", parent.Current.LocalizedControlType);
|
||||||
parentInfo.Add("automationId", parent.Current.AutomationId);
|
parentInfo.Add("automationId", parent.Current.AutomationId);
|
||||||
|
|
||||||
var serializer = new JavaScriptSerializer();
|
var serializer = new JavaScriptSerializer();
|
||||||
@ -891,32 +940,131 @@ public class AutomationManager
|
|||||||
{
|
{
|
||||||
var root = GetRootElement(args);
|
var root = GetRootElement(args);
|
||||||
var result = new List<Dictionary<string, string>>();
|
var result = new List<Dictionary<string, string>>();
|
||||||
|
string window = GetArgumentValue(args, "-window");
|
||||||
|
string method = GetArgumentValue(args, "-method") ?? "active";
|
||||||
|
string by = GetArgumentValue(args, "-by") ?? "name";
|
||||||
|
string scope = GetArgumentValue(args, "-scope") ?? "children";
|
||||||
|
|
||||||
// 获取所有元素
|
// 确定搜索范围
|
||||||
var elements = root.FindAll(
|
TreeScope searchScope = TreeScope.Children;
|
||||||
TreeScope.Subtree,
|
switch (scope.ToLower())
|
||||||
Condition.TrueCondition
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach (AutomationElement element in elements)
|
|
||||||
{
|
{
|
||||||
// 如果指定了过滤条件,检查元素名称是否包含过滤文本
|
case "descendants":
|
||||||
if (!string.IsNullOrEmpty(filter) &&
|
searchScope = TreeScope.Descendants;
|
||||||
!element.Current.Name.Contains(filter) &&
|
break;
|
||||||
!element.Current.ClassName.Contains(filter) &&
|
case "subtree":
|
||||||
!element.Current.AutomationId.Contains(filter))
|
searchScope = TreeScope.Subtree;
|
||||||
{
|
break;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var elementInfo = new Dictionary<string, string>();
|
Condition searchCondition = Condition.TrueCondition;
|
||||||
elementInfo.Add("id", CacheElement(element));
|
|
||||||
elementInfo.Add("name", element.Current.Name);
|
// 如果指定了过滤条件,创建一个组合条件
|
||||||
elementInfo.Add("class", element.Current.ClassName);
|
if (!string.IsNullOrEmpty(filter))
|
||||||
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName);
|
{
|
||||||
elementInfo.Add("automationId", element.Current.AutomationId);
|
// 尝试直接使用ControlType
|
||||||
elementInfo.Add("path", GetElementPath(element));
|
if (filter.StartsWith("ControlType."))
|
||||||
result.Add(elementInfo);
|
{
|
||||||
|
string controlTypeName = filter.Substring("ControlType.".Length);
|
||||||
|
ControlType controlType = (ControlType)typeof(ControlType).GetField(controlTypeName).GetValue(null);
|
||||||
|
searchCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (by.ToLower())
|
||||||
|
{
|
||||||
|
case "name":
|
||||||
|
searchCondition = new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase);
|
||||||
|
break;
|
||||||
|
case "class":
|
||||||
|
searchCondition = new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase);
|
||||||
|
break;
|
||||||
|
case "automation":
|
||||||
|
searchCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase);
|
||||||
|
break;
|
||||||
|
case "xpath":
|
||||||
|
// XPath暂不支持
|
||||||
|
throw new Exception("XPath查找暂未实现");
|
||||||
|
default:
|
||||||
|
// 如果没有指定查找方式,则使用组合条件
|
||||||
|
searchCondition = new OrCondition(
|
||||||
|
new PropertyCondition(AutomationElement.NameProperty, filter, PropertyConditionFlags.IgnoreCase),
|
||||||
|
new PropertyCondition(AutomationElement.ClassNameProperty, filter, PropertyConditionFlags.IgnoreCase),
|
||||||
|
new PropertyCondition(AutomationElement.AutomationIdProperty, filter, PropertyConditionFlags.IgnoreCase)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是按标题搜索,先找到所有匹配的窗口
|
||||||
|
if (method.ToLower() == "title" && !string.IsNullOrEmpty(window))
|
||||||
|
{
|
||||||
|
searchScope = TreeScope.Children;
|
||||||
|
var windows = root.FindAll(searchScope, Condition.TrueCondition);
|
||||||
|
foreach (AutomationElement win in windows)
|
||||||
|
{
|
||||||
|
if (win.Current.Name.Contains(window))
|
||||||
|
{
|
||||||
|
// 对每个匹配的窗口进行处理
|
||||||
|
var windowElements = win.FindAll(TreeScope.Subtree, searchCondition);
|
||||||
|
foreach (AutomationElement element in windowElements)
|
||||||
|
{
|
||||||
|
var elementInfo = new Dictionary<string, string>();
|
||||||
|
elementInfo.Add("id", CacheElement(element));
|
||||||
|
elementInfo.Add("name", element.Current.Name);
|
||||||
|
elementInfo.Add("class", element.Current.ClassName);
|
||||||
|
elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
|
||||||
|
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
|
||||||
|
elementInfo.Add("automationId", element.Current.AutomationId);
|
||||||
|
elementInfo.Add("path", GetElementPath(element));
|
||||||
|
|
||||||
|
// 添加控件文本值
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
||||||
|
if (valuePattern != null)
|
||||||
|
{
|
||||||
|
elementInfo.Add("value", valuePattern.Current.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
result.Add(elementInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 其他查找方式保持原有逻辑
|
||||||
|
var elements = root.FindAll(searchScope, searchCondition);
|
||||||
|
foreach (AutomationElement element in elements)
|
||||||
|
{
|
||||||
|
var elementInfo = new Dictionary<string, string>();
|
||||||
|
elementInfo.Add("id", CacheElement(element));
|
||||||
|
elementInfo.Add("name", element.Current.Name);
|
||||||
|
elementInfo.Add("class", element.Current.ClassName);
|
||||||
|
elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
|
||||||
|
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
|
||||||
|
elementInfo.Add("automationId", element.Current.AutomationId);
|
||||||
|
elementInfo.Add("path", GetElementPath(element));
|
||||||
|
|
||||||
|
// 添加控件文本值
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
||||||
|
if (valuePattern != null)
|
||||||
|
{
|
||||||
|
elementInfo.Add("value", valuePattern.Current.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
result.Add(elementInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var serializer = new JavaScriptSerializer();
|
var serializer = new JavaScriptSerializer();
|
||||||
@ -963,7 +1111,15 @@ public class AutomationManager
|
|||||||
string window = GetArgumentValue(args, "-window");
|
string window = GetArgumentValue(args, "-window");
|
||||||
string method = GetArgumentValue(args, "-method") ?? "active";
|
string method = GetArgumentValue(args, "-method") ?? "active";
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(window) && method == "active")
|
// 如果是active方法,直接返回当前活动窗口
|
||||||
|
if (method.ToLower() == "active")
|
||||||
|
{
|
||||||
|
IntPtr activeHandle = GetForegroundWindow();
|
||||||
|
return AutomationElement.FromHandle(activeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他方法需要检查window参数
|
||||||
|
if (string.IsNullOrEmpty(window))
|
||||||
{
|
{
|
||||||
return AutomationElement.RootElement;
|
return AutomationElement.RootElement;
|
||||||
}
|
}
|
||||||
@ -977,10 +1133,15 @@ public class AutomationManager
|
|||||||
|
|
||||||
case "class":
|
case "class":
|
||||||
// 通过窗口类名查找
|
// 通过窗口类名查找
|
||||||
return AutomationElement.RootElement.FindFirst(
|
var classElements = AutomationElement.RootElement.FindAll(
|
||||||
TreeScope.Children,
|
TreeScope.Children,
|
||||||
new PropertyCondition(AutomationElement.ClassNameProperty, window)
|
new PropertyCondition(AutomationElement.ClassNameProperty, window)
|
||||||
);
|
);
|
||||||
|
if (classElements.Count > 0)
|
||||||
|
{
|
||||||
|
return classElements[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "process":
|
case "process":
|
||||||
// 通过进程名查找
|
// 通过进程名查找
|
||||||
@ -991,151 +1152,199 @@ public class AutomationManager
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "active":
|
|
||||||
// 获取当前活动窗口
|
|
||||||
IntPtr activeHandle = GetForegroundWindow();
|
|
||||||
return AutomationElement.FromHandle(activeHandle);
|
|
||||||
|
|
||||||
case "title":
|
case "title":
|
||||||
default:
|
default:
|
||||||
// 通过窗口标题查找(支持模糊匹配)
|
// 返回根元素,让调用方自己处理查找逻辑
|
||||||
var windows = AutomationElement.RootElement.FindAll(
|
return AutomationElement.RootElement;
|
||||||
TreeScope.Children,
|
|
||||||
Condition.TrueCondition
|
|
||||||
);
|
|
||||||
foreach (AutomationElement win in windows)
|
|
||||||
{
|
|
||||||
if (win.Current.Name.Contains(window))
|
|
||||||
{
|
|
||||||
return win;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("找不到指定的窗口");
|
throw new Exception("找不到指定的窗口");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FindElement(string[] args, string by, string value, string scope)
|
private static void InspectElement(string[] args)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value))
|
// 创建一个半透明的全屏窗口来捕获鼠标事件
|
||||||
{
|
Form overlayForm = new Form();
|
||||||
throw new Exception("必须指定查找值 (-value)");
|
overlayForm.FormBorderStyle = FormBorderStyle.None;
|
||||||
}
|
overlayForm.WindowState = FormWindowState.Maximized;
|
||||||
|
overlayForm.TopMost = true;
|
||||||
|
overlayForm.BackColor = Color.Black;
|
||||||
|
overlayForm.Opacity = 0.1; // 设置为淡淡的蒙版效果
|
||||||
|
overlayForm.Cursor = Cursors.Cross;
|
||||||
|
overlayForm.ShowInTaskbar = false;
|
||||||
|
|
||||||
AutomationElement root = GetRootElement(args);
|
bool completed = false;
|
||||||
TreeScope treeScope = TreeScope.Children;
|
|
||||||
switch (scope.ToLower())
|
|
||||||
{
|
|
||||||
case "descendants":
|
|
||||||
treeScope = TreeScope.Descendants;
|
|
||||||
break;
|
|
||||||
case "subtree":
|
|
||||||
treeScope = TreeScope.Subtree;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Condition condition;
|
overlayForm.MouseClick += (sender, e) => {
|
||||||
switch (by.ToLower())
|
if (e.Button == MouseButtons.Left) {
|
||||||
{
|
try {
|
||||||
case "name":
|
Point clickPosition;
|
||||||
condition = new PropertyCondition(AutomationElement.NameProperty, value);
|
GetCursorPos(out clickPosition);
|
||||||
break;
|
|
||||||
case "class":
|
|
||||||
condition = new PropertyCondition(AutomationElement.ClassNameProperty, value);
|
|
||||||
break;
|
|
||||||
case "automation":
|
|
||||||
condition = new PropertyCondition(AutomationElement.AutomationIdProperty, value);
|
|
||||||
break;
|
|
||||||
case "xpath":
|
|
||||||
FindElementByXPath(value);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
throw new Exception("不支持的查找方式: " + by);
|
|
||||||
}
|
|
||||||
|
|
||||||
var elements = root.FindAll(treeScope, condition);
|
// 先关闭覆盖窗口
|
||||||
var result = new List<Dictionary<string, string>>();
|
overlayForm.Hide();
|
||||||
|
|
||||||
foreach (AutomationElement element in elements)
|
// 等待一小段时间确保窗口完全消失
|
||||||
{
|
Thread.Sleep(100);
|
||||||
var elementInfo = new Dictionary<string, string>();
|
|
||||||
elementInfo.Add("id", CacheElement(element));
|
|
||||||
elementInfo.Add("name", element.Current.Name);
|
|
||||||
elementInfo.Add("class", element.Current.ClassName);
|
|
||||||
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName);
|
|
||||||
elementInfo.Add("automationId", element.Current.AutomationId);
|
|
||||||
result.Add(elementInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
var serializer = new JavaScriptSerializer();
|
// 获取点击位置的元素
|
||||||
Console.Write(serializer.Serialize(result));
|
var element = AutomationElement.FromPoint(clickPosition.ToWindowsPoint());
|
||||||
}
|
|
||||||
|
|
||||||
private static void InspectElement(string[] args, int timeout)
|
if (element != null && element != AutomationElement.RootElement) {
|
||||||
{
|
InspectElementInfo(element);
|
||||||
Console.WriteLine("请在" + timeout + "秒内点击要识别的元素...");
|
completed = true;
|
||||||
|
|
||||||
// 记录当前鼠标位置
|
|
||||||
DateTime endTime = DateTime.Now.AddSeconds(timeout);
|
|
||||||
AutomationElement clickedElement = null;
|
|
||||||
bool wasPressed = false;
|
|
||||||
|
|
||||||
while (DateTime.Now < endTime)
|
|
||||||
{
|
|
||||||
// 检测鼠标左键点击
|
|
||||||
bool isPressed = (GetAsyncKeyState(0x01) & 0x8000) != 0;
|
|
||||||
if (isPressed && !wasPressed) // 鼠标按下瞬间
|
|
||||||
{
|
|
||||||
// 获取当前鼠标位置
|
|
||||||
Point currentPosition;
|
|
||||||
GetCursorPos(out currentPosition);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 从鼠标位置获取元素
|
|
||||||
clickedElement = AutomationElement.FromPoint(currentPosition.ToWindowsPoint());
|
|
||||||
if (clickedElement != null && clickedElement != AutomationElement.RootElement)
|
|
||||||
{
|
|
||||||
// 构建元素信息
|
|
||||||
var elementInfo = new Dictionary<string, object>();
|
|
||||||
elementInfo.Add("id", CacheElement(clickedElement));
|
|
||||||
elementInfo.Add("name", clickedElement.Current.Name);
|
|
||||||
elementInfo.Add("class", clickedElement.Current.ClassName);
|
|
||||||
elementInfo.Add("type", clickedElement.Current.ControlType.ProgrammaticName);
|
|
||||||
elementInfo.Add("automationId", clickedElement.Current.AutomationId);
|
|
||||||
elementInfo.Add("path", GetElementPath(clickedElement));
|
|
||||||
elementInfo.Add("location", new {
|
|
||||||
x = clickedElement.Current.BoundingRectangle.X,
|
|
||||||
y = clickedElement.Current.BoundingRectangle.Y,
|
|
||||||
width = clickedElement.Current.BoundingRectangle.Width,
|
|
||||||
height = clickedElement.Current.BoundingRectangle.Height
|
|
||||||
});
|
|
||||||
elementInfo.Add("patterns", GetSupportedPatterns(clickedElement));
|
|
||||||
|
|
||||||
// 生成示例命令
|
|
||||||
var commands = new List<string>();
|
|
||||||
if (!string.IsNullOrEmpty(clickedElement.Current.Name))
|
|
||||||
commands.Add(string.Format("automation.exe -type find -by name -value \"{0}\"", clickedElement.Current.Name));
|
|
||||||
if (!string.IsNullOrEmpty(clickedElement.Current.AutomationId))
|
|
||||||
commands.Add(string.Format("automation.exe -type find -by automationid -value \"{0}\"", clickedElement.Current.AutomationId));
|
|
||||||
elementInfo.Add("commands", commands);
|
|
||||||
|
|
||||||
var serializer = new JavaScriptSerializer();
|
|
||||||
Console.Write(serializer.Serialize(elementInfo));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex) {
|
||||||
{
|
Console.Error.WriteLine(string.Format("Error: {0}", ex.Message));
|
||||||
// 忽略获取元素时的错误
|
}
|
||||||
|
finally {
|
||||||
|
overlayForm.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
wasPressed = isPressed;
|
overlayForm.KeyPress += (sender, e) => {
|
||||||
Thread.Sleep(100);
|
if (e.KeyChar == (char)27) { // ESC键
|
||||||
|
overlayForm.Close();
|
||||||
|
Environment.Exit(0); // 直接退出程序
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 在新线程中显示窗口
|
||||||
|
Thread thread = new Thread(() => {
|
||||||
|
overlayForm.Show();
|
||||||
|
while (!completed) {
|
||||||
|
Application.DoEvents();
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.SetApartmentState(ApartmentState.STA);
|
||||||
|
thread.Start();
|
||||||
|
thread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InspectElementInfo(AutomationElement element)
|
||||||
|
{
|
||||||
|
Dictionary<string, object> elementInfo = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 基本信息
|
||||||
|
elementInfo.Add("id", element.Current.AutomationId);
|
||||||
|
elementInfo.Add("name", element.Current.Name);
|
||||||
|
elementInfo.Add("class", element.Current.ClassName);
|
||||||
|
elementInfo.Add("controlType", element.Current.ControlType.ProgrammaticName);
|
||||||
|
elementInfo.Add("type", element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""));
|
||||||
|
elementInfo.Add("localizedType", element.Current.LocalizedControlType);
|
||||||
|
elementInfo.Add("automationId", element.Current.AutomationId);
|
||||||
|
elementInfo.Add("path", GetElementPath(element));
|
||||||
|
elementInfo.Add("handle", element.Current.NativeWindowHandle);
|
||||||
|
|
||||||
|
// 添加控件文本值
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var valuePattern = element.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
|
||||||
|
if (valuePattern != null)
|
||||||
|
{
|
||||||
|
elementInfo.Add("value", valuePattern.Current.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
// 获取窗口信息
|
||||||
|
var walker = TreeWalker.ControlViewWalker;
|
||||||
|
var parent = element;
|
||||||
|
while (parent != null && parent != AutomationElement.RootElement && parent.Current.ControlType.Id != UIA_ControlTypeIds.Window)
|
||||||
|
{
|
||||||
|
parent = walker.GetParent(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception("操作超时");
|
// 添加窗口详细信息
|
||||||
|
if (parent != null && parent != AutomationElement.RootElement)
|
||||||
|
{
|
||||||
|
var processId = parent.Current.ProcessId;
|
||||||
|
var process = System.Diagnostics.Process.GetProcessById(processId);
|
||||||
|
elementInfo.Add("window", new
|
||||||
|
{
|
||||||
|
title = parent.Current.Name,
|
||||||
|
@class = parent.Current.ClassName,
|
||||||
|
handle = parent.Current.NativeWindowHandle,
|
||||||
|
processName = process.ProcessName,
|
||||||
|
processId = processId,
|
||||||
|
processPath = process.MainModule.FileName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加父元素信息
|
||||||
|
var immediateParent = walker.GetParent(element);
|
||||||
|
if (immediateParent != null && immediateParent != AutomationElement.RootElement)
|
||||||
|
{
|
||||||
|
elementInfo.Add("parent", new
|
||||||
|
{
|
||||||
|
handle = immediateParent.Current.NativeWindowHandle,
|
||||||
|
path = GetElementPath(immediateParent),
|
||||||
|
@class = immediateParent.Current.ClassName,
|
||||||
|
name = immediateParent.Current.Name,
|
||||||
|
controlType = immediateParent.Current.ControlType.ProgrammaticName,
|
||||||
|
type = immediateParent.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""),
|
||||||
|
localizedType = immediateParent.Current.LocalizedControlType,
|
||||||
|
automationId = immediateParent.Current.AutomationId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 位置信息
|
||||||
|
elementInfo.Add("location", new {
|
||||||
|
x = element.Current.BoundingRectangle.X,
|
||||||
|
y = element.Current.BoundingRectangle.Y,
|
||||||
|
width = element.Current.BoundingRectangle.Width,
|
||||||
|
height = element.Current.BoundingRectangle.Height
|
||||||
|
});
|
||||||
|
|
||||||
|
// 支持的模式
|
||||||
|
List<string> patterns = new List<string>();
|
||||||
|
foreach (AutomationPattern pattern in element.GetSupportedPatterns())
|
||||||
|
{
|
||||||
|
patterns.Add(pattern.ProgrammaticName);
|
||||||
|
}
|
||||||
|
elementInfo.Add("patterns", patterns);
|
||||||
|
|
||||||
|
// 输出 JSON
|
||||||
|
var serializer = new JavaScriptSerializer();
|
||||||
|
Console.Write(serializer.Serialize(elementInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化为 JSON 字符串
|
||||||
|
private static string JsonSerialize(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return "null";
|
||||||
|
|
||||||
|
if (obj is string)
|
||||||
|
{
|
||||||
|
return string.Format("\"{0}\"", ((string)obj).Replace("\"", "\\\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is bool)
|
||||||
|
{
|
||||||
|
return obj.ToString().ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is int || obj is long || obj is float || obj is double)
|
||||||
|
{
|
||||||
|
return obj.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is Dictionary<string, object>)
|
||||||
|
{
|
||||||
|
var dict = obj as Dictionary<string, object>;
|
||||||
|
var pairs = dict.Select(kvp => string.Format("\"{0}\": {1}", kvp.Key, JsonSerialize(kvp.Value)));
|
||||||
|
return string.Format("{{{0}}}", string.Join(", ", pairs));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj is System.Collections.IEnumerable)
|
||||||
|
{
|
||||||
|
var items = ((System.Collections.IEnumerable)obj).Cast<object>().Select(item => JsonSerialize(item));
|
||||||
|
return string.Format("[{0}]", string.Join(", ", items));
|
||||||
|
}
|
||||||
|
|
||||||
|
return "null";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ const iconv = require("iconv-lite");
|
|||||||
const child_process = require("child_process");
|
const child_process = require("child_process");
|
||||||
const { getQuickcommandFolderFile } = require("../getQuickcommandFile");
|
const { getQuickcommandFolderFile } = require("../getQuickcommandFile");
|
||||||
|
|
||||||
|
let currentChild = null;
|
||||||
|
|
||||||
const getAssemblyPath = (assembly) => {
|
const getAssemblyPath = (assembly) => {
|
||||||
const { version } = getCscPath();
|
const { version } = getCscPath();
|
||||||
const is64bit = process.arch === "x64";
|
const is64bit = process.arch === "x64";
|
||||||
@ -37,15 +39,9 @@ const getAssemblyPath = (assembly) => {
|
|||||||
|
|
||||||
// v3.0/v3.5 路径
|
// v3.0/v3.5 路径
|
||||||
path.join(
|
path.join(
|
||||||
process.env["ProgramFiles(x86)"] || process.env.ProgramFiles,
|
process.arch === "x64"
|
||||||
"Reference Assemblies",
|
? process.env.ProgramFiles
|
||||||
"Microsoft",
|
: process.env["ProgramFiles(x86)"],
|
||||||
"Framework",
|
|
||||||
"v3.0",
|
|
||||||
assembly + ".dll"
|
|
||||||
),
|
|
||||||
path.join(
|
|
||||||
process.env.ProgramFiles,
|
|
||||||
"Reference Assemblies",
|
"Reference Assemblies",
|
||||||
"Microsoft",
|
"Microsoft",
|
||||||
"Framework",
|
"Framework",
|
||||||
@ -81,13 +77,16 @@ const getFeatureReferences = (feature) => {
|
|||||||
const formsDll = getAssemblyPath("System.Windows.Forms");
|
const formsDll = getAssemblyPath("System.Windows.Forms");
|
||||||
const typesDll = getAssemblyPath("UIAutomationTypes");
|
const typesDll = getAssemblyPath("UIAutomationTypes");
|
||||||
const baseDll = getAssemblyPath("WindowsBase");
|
const baseDll = getAssemblyPath("WindowsBase");
|
||||||
|
const drawingDll = getAssemblyPath("System.Drawing");
|
||||||
if (!automationDll) throw new Error("找不到UIAutomationClient.dll");
|
if (!automationDll) throw new Error("找不到UIAutomationClient.dll");
|
||||||
if (!formsDll) throw new Error("找不到System.Windows.Forms.dll");
|
if (!formsDll) throw new Error("找不到System.Windows.Forms.dll");
|
||||||
if (!typesDll) throw new Error("找不到UIAutomationTypes.dll");
|
if (!typesDll) throw new Error("找不到UIAutomationTypes.dll");
|
||||||
if (!baseDll) throw new Error("找不到WindowsBase.dll");
|
if (!baseDll) throw new Error("找不到WindowsBase.dll");
|
||||||
|
if (!drawingDll) throw new Error("找不到System.Drawing.dll");
|
||||||
references =
|
references =
|
||||||
`/reference:"${automationDll}" /reference:"${formsDll}" ` +
|
`/reference:"${automationDll}" /reference:"${formsDll}" ` +
|
||||||
`/reference:"${typesDll}" /reference:"${baseDll}" `;
|
`/reference:"${typesDll}" /reference:"${baseDll}" ` +
|
||||||
|
`/reference:"${drawingDll}" `;
|
||||||
}
|
}
|
||||||
return references;
|
return references;
|
||||||
};
|
};
|
||||||
@ -128,10 +127,11 @@ const buildCsharpFeature = async (feature) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getCscPath = () => {
|
const getCscPath = () => {
|
||||||
|
const is64bit = process.arch === "x64";
|
||||||
let cscPath = path.join(
|
let cscPath = path.join(
|
||||||
process.env.WINDIR,
|
process.env.WINDIR,
|
||||||
"Microsoft.NET",
|
"Microsoft.NET",
|
||||||
"Framework",
|
is64bit ? "Framework64" : "Framework",
|
||||||
"v4.0.30319",
|
"v4.0.30319",
|
||||||
"csc.exe"
|
"csc.exe"
|
||||||
);
|
);
|
||||||
@ -141,7 +141,7 @@ const getCscPath = () => {
|
|||||||
cscPath = path.join(
|
cscPath = path.join(
|
||||||
process.env.WINDIR,
|
process.env.WINDIR,
|
||||||
"Microsoft.NET",
|
"Microsoft.NET",
|
||||||
"Framework",
|
is64bit ? "Framework64" : "Framework",
|
||||||
"v3.5",
|
"v3.5",
|
||||||
"csc.exe"
|
"csc.exe"
|
||||||
);
|
);
|
||||||
@ -163,13 +163,17 @@ const getCscPath = () => {
|
|||||||
*/
|
*/
|
||||||
const runCsharpFeature = async (feature, args = [], options = {}) => {
|
const runCsharpFeature = async (feature, args = [], options = {}) => {
|
||||||
return new Promise(async (reslove, reject) => {
|
return new Promise(async (reslove, reject) => {
|
||||||
const { alwaysBuildNewExe = false } = options;
|
const { alwaysBuildNewExe = window.utools.isDev(), killPrevious = true } =
|
||||||
|
options;
|
||||||
try {
|
try {
|
||||||
const featureExePath = await getCsharpFeatureExe(
|
const featureExePath = await getCsharpFeatureExe(
|
||||||
feature,
|
feature,
|
||||||
alwaysBuildNewExe
|
alwaysBuildNewExe
|
||||||
);
|
);
|
||||||
child_process.execFile(
|
if (killPrevious && currentChild) {
|
||||||
|
quickcommand.kill(currentChild.pid, "SIGKILL");
|
||||||
|
}
|
||||||
|
currentChild = child_process.execFile(
|
||||||
featureExePath,
|
featureExePath,
|
||||||
args,
|
args,
|
||||||
{
|
{
|
||||||
|
165
plugin/lib/quickcomposer/windows/automation.js
Normal file
165
plugin/lib/quickcomposer/windows/automation.js
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
const { runCsharpFeature } = require("../../csharp");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出所有元素
|
||||||
|
* @param {string} method 窗口类型:title/handle/active
|
||||||
|
* @param {string} window 窗口标题/句柄
|
||||||
|
* @param {object} options 选项
|
||||||
|
* @param {string} options.filter 过滤条件
|
||||||
|
* @param {boolean} options.background 是否后台操作
|
||||||
|
* @returns {object[]} 元素列表
|
||||||
|
*/
|
||||||
|
const listElements = async function (method, window, options = {}) {
|
||||||
|
const { filter, scope } = options;
|
||||||
|
const args = ["-type", "list"];
|
||||||
|
|
||||||
|
args.push("-method", method);
|
||||||
|
if (method !== "active" && window) args.push("-window", window);
|
||||||
|
if (filter) args.push("-filter", filter);
|
||||||
|
if (scope) args.push("-scope", scope);
|
||||||
|
const result = await runCsharpFeature("automation", args);
|
||||||
|
if (result && result.startsWith("Error:")) {
|
||||||
|
throw new Error(result.substring(7));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析元素列表失败:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击元素
|
||||||
|
* @param {string} method 窗口类型:title/handle/active
|
||||||
|
* @param {string} window 窗口标题/句柄
|
||||||
|
* @param {string} by 查找方式:name/class/type/automationid
|
||||||
|
* @param {string} value 查找值
|
||||||
|
* @param {object} options 选项
|
||||||
|
* @param {string} options.pattern 点击模式:invoke/toggle
|
||||||
|
* @param {boolean} options.background 是否后台操作
|
||||||
|
* @returns {boolean} 是否成功
|
||||||
|
*/
|
||||||
|
const clickElement = async function (method, window, by, value, options = {}) {
|
||||||
|
const { pattern = "invoke", background = false } = options;
|
||||||
|
const args = ["-type", "click", "-by", by, "-value", value];
|
||||||
|
|
||||||
|
args.push("-method", method);
|
||||||
|
if (method !== "active" && window) args.push("-window", window);
|
||||||
|
if (pattern) args.push("-pattern", pattern);
|
||||||
|
if (background) args.push("-background");
|
||||||
|
|
||||||
|
const result = await runCsharpFeature("automation", args);
|
||||||
|
if (result && result.startsWith("Error:")) {
|
||||||
|
throw new Error(result.substring(7));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置元素值
|
||||||
|
* @param {string} method 窗口类型:title/handle/active
|
||||||
|
* @param {string} window 窗口标题/句柄
|
||||||
|
* @param {string} by 查找方式:name/class/type/automationid
|
||||||
|
* @param {string} value 查找值
|
||||||
|
* @param {string} newValue 要设置的值
|
||||||
|
* @param {object} options 选项
|
||||||
|
* @param {boolean} options.background 是否后台操作
|
||||||
|
* @returns {boolean} 是否成功
|
||||||
|
*/
|
||||||
|
const setElementValue = async function (
|
||||||
|
method,
|
||||||
|
window,
|
||||||
|
by,
|
||||||
|
value,
|
||||||
|
newValue,
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
|
const { background = false } = options;
|
||||||
|
const args = [
|
||||||
|
"-type",
|
||||||
|
"setvalue",
|
||||||
|
"-by",
|
||||||
|
by,
|
||||||
|
"-value",
|
||||||
|
value,
|
||||||
|
"-newvalue",
|
||||||
|
newValue,
|
||||||
|
];
|
||||||
|
|
||||||
|
args.push("-method", method);
|
||||||
|
if (method !== "active" && window) args.push("-window", window);
|
||||||
|
if (background) args.push("-background");
|
||||||
|
|
||||||
|
const result = await runCsharpFeature("automation", args);
|
||||||
|
if (result && result.startsWith("Error:")) {
|
||||||
|
throw new Error(result.substring(7));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取元素值
|
||||||
|
* @param {string} method 窗口类型:title/handle/active
|
||||||
|
* @param {string} window 窗口标题/句柄
|
||||||
|
* @param {string} by 查找方式:name/class/type/automationid
|
||||||
|
* @param {string} value 查找值
|
||||||
|
* @param {object} options 选项
|
||||||
|
* @param {boolean} options.background 是否后台操作
|
||||||
|
* @returns {object} 元素值
|
||||||
|
*/
|
||||||
|
const getElementValue = async function (
|
||||||
|
method,
|
||||||
|
window,
|
||||||
|
by,
|
||||||
|
value,
|
||||||
|
options = {}
|
||||||
|
) {
|
||||||
|
const { background = false } = options;
|
||||||
|
const args = ["-type", "getvalue", "-by", by, "-value", value];
|
||||||
|
|
||||||
|
args.push("-method", method);
|
||||||
|
if (method !== "active" && window) args.push("-window", window);
|
||||||
|
if (background) args.push("-background");
|
||||||
|
|
||||||
|
const result = await runCsharpFeature("automation", args);
|
||||||
|
if (result && result.startsWith("Error:")) {
|
||||||
|
throw new Error(result.substring(7));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析元素值失败:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查元素
|
||||||
|
* @param {object} options 选项
|
||||||
|
* @param {number} options.timeout 超时时间(秒)
|
||||||
|
* @param {boolean} options.background 是否后台操作
|
||||||
|
* @returns {object} 元素信息
|
||||||
|
*/
|
||||||
|
const inspectElement = async function () {
|
||||||
|
const args = ["-type", "inspect"];
|
||||||
|
|
||||||
|
const result = await runCsharpFeature("automation", args);
|
||||||
|
if (result && result.startsWith("Error:")) {
|
||||||
|
throw new Error(result.substring(7));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析元素信息失败:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
listElements,
|
||||||
|
clickElement,
|
||||||
|
setElementValue,
|
||||||
|
getElementValue,
|
||||||
|
inspectElement,
|
||||||
|
};
|
@ -6,6 +6,7 @@ const registry = require("./registry");
|
|||||||
const service = require("./service");
|
const service = require("./service");
|
||||||
const software = require("./software");
|
const software = require("./software");
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
|
const automation = require("./automation");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
window,
|
window,
|
||||||
@ -16,4 +17,5 @@ module.exports = {
|
|||||||
service,
|
service,
|
||||||
software,
|
software,
|
||||||
utils,
|
utils,
|
||||||
|
automation,
|
||||||
};
|
};
|
||||||
|
@ -48,7 +48,7 @@ const getQuickcommandIconPath = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 修改对话框函数,使用新的 dialog.cs
|
// 修改对话框函数,使用新的 dialog.cs
|
||||||
const showSystemMessageBox = async function (content, title = "") {
|
const showSystemMessageBox = async function (content, title = "提示") {
|
||||||
try {
|
try {
|
||||||
const iconPath = getQuickcommandIconPath();
|
const iconPath = getQuickcommandIconPath();
|
||||||
if (window.utools.isWindows()) {
|
if (window.utools.isWindows()) {
|
||||||
@ -97,7 +97,7 @@ const showSystemMessageBox = async function (content, title = "") {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSystemInputBox = async function (placeholders, title = "") {
|
const showSystemInputBox = async function (placeholders, title = "请输入") {
|
||||||
if (!Array.isArray(placeholders)) {
|
if (!Array.isArray(placeholders)) {
|
||||||
placeholders = [placeholders];
|
placeholders = [placeholders];
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ const showSystemInputBox = async function (placeholders, title = "") {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSystemConfirmBox = async function (content, title = "") {
|
const showSystemConfirmBox = async function (content, title = "请确认") {
|
||||||
const iconPath = getQuickcommandIconPath();
|
const iconPath = getQuickcommandIconPath();
|
||||||
if (window.utools.isMacOs()) {
|
if (window.utools.isMacOs()) {
|
||||||
let iconParam = "note";
|
let iconParam = "note";
|
||||||
@ -207,7 +207,7 @@ const showSystemConfirmBox = async function (content, title = "") {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSystemButtonBox = async function (buttons, title = "") {
|
const showSystemButtonBox = async function (buttons, title = "请选择") {
|
||||||
const iconPath = getQuickcommandIconPath();
|
const iconPath = getQuickcommandIconPath();
|
||||||
if (window.utools.isMacOs()) {
|
if (window.utools.isMacOs()) {
|
||||||
const itemList = buttons.map((item) => `"${item}"`).join(", ");
|
const itemList = buttons.map((item) => `"${item}"`).join(", ");
|
||||||
@ -250,7 +250,7 @@ const showSystemButtonBox = async function (buttons, title = "") {
|
|||||||
const itemsList = buttons
|
const itemsList = buttons
|
||||||
.map((btn, index) => `"${index}" "${btn}"`)
|
.map((btn, index) => `"${index}" "${btn}"`)
|
||||||
.join(" ");
|
.join(" ");
|
||||||
const script2 = `zenity --list --title="${title}" --text="请选择:" --column="序号" --column="选项" ${itemsList} --width=400 --height=300`;
|
const script2 = `zenity --list --title="${title}" --column="序号" --column="选项" ${itemsList} --width=400 --height=300`;
|
||||||
const result = await execCommand(script2);
|
const result = await execCommand(script2);
|
||||||
if (!result) return {};
|
if (!result) return {};
|
||||||
const text = result.trim();
|
const text = result.trim();
|
||||||
@ -263,7 +263,7 @@ const showSystemButtonBox = async function (buttons, title = "") {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showSystemTextArea = async function (defaultText = "", title = "") {
|
const showSystemTextArea = async function (defaultText = "", title = "请输入") {
|
||||||
const iconPath = getQuickcommandIconPath();
|
const iconPath = getQuickcommandIconPath();
|
||||||
if (window.utools.isWindows()) {
|
if (window.utools.isWindows()) {
|
||||||
const args = [
|
const args = [
|
||||||
|
@ -85,7 +85,9 @@ export default {
|
|||||||
},
|
},
|
||||||
// 判断是否是转为表格的结果,表格结果不需要换行,第二行一般包含分隔符---
|
// 判断是否是转为表格的结果,表格结果不需要换行,第二行一般包含分隔符---
|
||||||
isTable() {
|
isTable() {
|
||||||
return this.runResult?.[0]?.split("\n")?.[1]?.includes("---");
|
const result = this.runResult?.[0];
|
||||||
|
if (typeof result !== "string") return false;
|
||||||
|
return result.split("\n")?.[1]?.includes("---");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -37,13 +37,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
// 通用参数配置
|
// 通用参数配置
|
||||||
commonConfig() {
|
commonConfig() {
|
||||||
return (
|
return this.modelValue.config || [];
|
||||||
// 过滤掉特定函数排除的参数, excludeConfig格式为[要排除的参数索引]
|
|
||||||
this.modelValue.config?.filter(
|
|
||||||
(_, index) =>
|
|
||||||
!this.getSelectSubCommand()?.excludeConfig?.includes(index)
|
|
||||||
) || []
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// 特定函数独有参数配置,config格式和通用的config一致
|
// 特定函数独有参数配置,config格式和通用的config一致
|
||||||
subCommandConfig() {
|
subCommandConfig() {
|
||||||
|
@ -53,14 +53,7 @@ export const networkCommands = {
|
|||||||
label: "URL操作",
|
label: "URL操作",
|
||||||
desc: "URL解析、格式化和参数处理",
|
desc: "URL解析、格式化和参数处理",
|
||||||
icon: "link",
|
icon: "link",
|
||||||
config: [
|
config: [],
|
||||||
{
|
|
||||||
label: "URL",
|
|
||||||
component: "VariableInput",
|
|
||||||
icon: "link",
|
|
||||||
width: "auto",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
subCommands: [
|
subCommands: [
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.url.parse",
|
value: "quickcomposer.network.url.parse",
|
||||||
@ -71,7 +64,6 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.format",
|
value: "quickcomposer.network.url.format",
|
||||||
label: "格式化URL",
|
label: "格式化URL",
|
||||||
icon: "link",
|
icon: "link",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "协议",
|
label: "协议",
|
||||||
@ -120,7 +112,6 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.parseQuery",
|
value: "quickcomposer.network.url.parseQuery",
|
||||||
label: "解析查询字符串",
|
label: "解析查询字符串",
|
||||||
icon: "search",
|
icon: "search",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "查询字符串",
|
label: "查询字符串",
|
||||||
@ -133,7 +124,6 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.formatQuery",
|
value: "quickcomposer.network.url.formatQuery",
|
||||||
label: "格式化查询字符串",
|
label: "格式化查询字符串",
|
||||||
icon: "edit",
|
icon: "edit",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "参数",
|
label: "参数",
|
||||||
@ -146,7 +136,6 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.parsePath",
|
value: "quickcomposer.network.url.parsePath",
|
||||||
label: "解析路径",
|
label: "解析路径",
|
||||||
icon: "folder_open",
|
icon: "folder_open",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "路径",
|
label: "路径",
|
||||||
@ -159,7 +148,6 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.parseHost",
|
value: "quickcomposer.network.url.parseHost",
|
||||||
label: "解析主机名",
|
label: "解析主机名",
|
||||||
icon: "dns",
|
icon: "dns",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "主机名",
|
label: "主机名",
|
||||||
@ -173,6 +161,12 @@ export const networkCommands = {
|
|||||||
label: "获取参数",
|
label: "获取参数",
|
||||||
icon: "find_in_page",
|
icon: "find_in_page",
|
||||||
config: [
|
config: [
|
||||||
|
{
|
||||||
|
label: "URL",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "link",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "参数名",
|
label: "参数名",
|
||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
@ -186,6 +180,12 @@ export const networkCommands = {
|
|||||||
label: "添加参数",
|
label: "添加参数",
|
||||||
icon: "add_circle",
|
icon: "add_circle",
|
||||||
config: [
|
config: [
|
||||||
|
{
|
||||||
|
label: "URL",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "link",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "参数名",
|
label: "参数名",
|
||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
@ -205,6 +205,12 @@ export const networkCommands = {
|
|||||||
label: "移除参数",
|
label: "移除参数",
|
||||||
icon: "remove_circle",
|
icon: "remove_circle",
|
||||||
config: [
|
config: [
|
||||||
|
{
|
||||||
|
label: "URL",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "link",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "参数名",
|
label: "参数名",
|
||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
@ -217,11 +223,27 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.url.isAbsolute",
|
value: "quickcomposer.network.url.isAbsolute",
|
||||||
label: "检查绝对URL",
|
label: "检查绝对URL",
|
||||||
icon: "check_circle",
|
icon: "check_circle",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "URL",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "link",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.url.parseComponents",
|
value: "quickcomposer.network.url.parseComponents",
|
||||||
label: "解析组成部分",
|
label: "解析组成部分",
|
||||||
icon: "category",
|
icon: "category",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "URL",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "link",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -231,20 +253,19 @@ export const networkCommands = {
|
|||||||
desc: "DNS解析和查询",
|
desc: "DNS解析和查询",
|
||||||
icon: "dns",
|
icon: "dns",
|
||||||
isAsync: true,
|
isAsync: true,
|
||||||
config: [
|
config: [],
|
||||||
{
|
|
||||||
label: "要查询的域名",
|
|
||||||
icon: "dns",
|
|
||||||
component: "VariableInput",
|
|
||||||
width: "auto",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
subCommands: [
|
subCommands: [
|
||||||
{
|
{
|
||||||
label: "DNS查询",
|
label: "DNS查询",
|
||||||
value: "quickcomposer.network.dns.lookupHost",
|
value: "quickcomposer.network.dns.lookupHost",
|
||||||
icon: "search",
|
icon: "search",
|
||||||
config: [
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "IP版本",
|
label: "IP版本",
|
||||||
icon: "settings_ethernet",
|
icon: "settings_ethernet",
|
||||||
@ -269,42 +290,97 @@ export const networkCommands = {
|
|||||||
value: "quickcomposer.network.dns.resolveAll",
|
value: "quickcomposer.network.dns.resolveAll",
|
||||||
label: "解析所有记录",
|
label: "解析所有记录",
|
||||||
icon: "all_inclusive",
|
icon: "all_inclusive",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveIpv4",
|
value: "quickcomposer.network.dns.resolveIpv4",
|
||||||
label: "解析IPv4",
|
label: "解析IPv4",
|
||||||
icon: "filter_4",
|
icon: "filter_4",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveIpv6",
|
value: "quickcomposer.network.dns.resolveIpv6",
|
||||||
label: "解析IPv6",
|
label: "解析IPv6",
|
||||||
icon: "filter_6",
|
icon: "filter_6",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveMxRecords",
|
value: "quickcomposer.network.dns.resolveMxRecords",
|
||||||
label: "解析MX记录",
|
label: "解析MX记录",
|
||||||
icon: "mail",
|
icon: "mail",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveTxtRecords",
|
value: "quickcomposer.network.dns.resolveTxtRecords",
|
||||||
label: "解析TXT记录",
|
label: "解析TXT记录",
|
||||||
icon: "text_fields",
|
icon: "text_fields",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveNsRecords",
|
value: "quickcomposer.network.dns.resolveNsRecords",
|
||||||
label: "解析NS记录",
|
label: "解析NS记录",
|
||||||
icon: "dns",
|
icon: "dns",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.resolveCnameRecords",
|
value: "quickcomposer.network.dns.resolveCnameRecords",
|
||||||
label: "解析CNAME记录",
|
label: "解析CNAME记录",
|
||||||
icon: "link",
|
icon: "link",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "要查询的域名",
|
||||||
|
icon: "dns",
|
||||||
|
component: "VariableInput",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.network.dns.reverseResolve",
|
value: "quickcomposer.network.dns.reverseResolve",
|
||||||
label: "反向解析",
|
label: "反向解析",
|
||||||
icon: "swap_horiz",
|
icon: "swap_horiz",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "IP地址",
|
label: "IP地址",
|
||||||
|
@ -308,35 +308,58 @@ export const systemCommands = {
|
|||||||
label: "路径操作",
|
label: "路径操作",
|
||||||
desc: "路径解析和处理",
|
desc: "路径解析和处理",
|
||||||
icon: "folder",
|
icon: "folder",
|
||||||
config: [
|
config: [],
|
||||||
{
|
|
||||||
label: "路径",
|
|
||||||
component: "VariableInput",
|
|
||||||
icon: "folder",
|
|
||||||
width: "auto",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
subCommands: [
|
subCommands: [
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.normalize",
|
value: "quickcomposer.system.path.normalize",
|
||||||
label: "规范化路径",
|
label: "规范化路径",
|
||||||
icon: "straighten",
|
icon: "straighten",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.parse",
|
value: "quickcomposer.system.path.parse",
|
||||||
label: "解析路径",
|
label: "解析路径",
|
||||||
icon: "account_tree",
|
icon: "account_tree",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.dirname",
|
value: "quickcomposer.system.path.dirname",
|
||||||
label: "获取目录名",
|
label: "获取目录名",
|
||||||
icon: "folder",
|
icon: "folder",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.basename",
|
value: "quickcomposer.system.path.basename",
|
||||||
label: "获取文件名",
|
label: "获取文件名",
|
||||||
icon: "description",
|
icon: "description",
|
||||||
config: [
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "要移除的扩展名",
|
label: "要移除的扩展名",
|
||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
@ -349,17 +372,32 @@ export const systemCommands = {
|
|||||||
value: "quickcomposer.system.path.extname",
|
value: "quickcomposer.system.path.extname",
|
||||||
label: "获取扩展名",
|
label: "获取扩展名",
|
||||||
icon: "extension",
|
icon: "extension",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.isAbsolute",
|
value: "quickcomposer.system.path.isAbsolute",
|
||||||
label: "判断绝对路径",
|
label: "判断绝对路径",
|
||||||
icon: "check_circle",
|
icon: "check_circle",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "路径",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "folder",
|
||||||
|
width: "auto",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "quickcomposer.system.path.join",
|
value: "quickcomposer.system.path.join",
|
||||||
label: "连接路径",
|
label: "连接路径",
|
||||||
icon: "add_link",
|
icon: "add_link",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "路径片段",
|
label: "路径片段",
|
||||||
@ -379,7 +417,6 @@ export const systemCommands = {
|
|||||||
value: "quickcomposer.system.path.resolve",
|
value: "quickcomposer.system.path.resolve",
|
||||||
label: "解析绝对路径",
|
label: "解析绝对路径",
|
||||||
icon: "assistant_direction",
|
icon: "assistant_direction",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "路径片段",
|
label: "路径片段",
|
||||||
@ -399,7 +436,6 @@ export const systemCommands = {
|
|||||||
value: "quickcomposer.system.path.relative",
|
value: "quickcomposer.system.path.relative",
|
||||||
label: "计算相对路径",
|
label: "计算相对路径",
|
||||||
icon: "compare_arrows",
|
icon: "compare_arrows",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "起始路径",
|
label: "起始路径",
|
||||||
@ -419,7 +455,6 @@ export const systemCommands = {
|
|||||||
value: "quickcomposer.system.path.format",
|
value: "quickcomposer.system.path.format",
|
||||||
label: "格式化路径",
|
label: "格式化路径",
|
||||||
icon: "format_shapes",
|
icon: "format_shapes",
|
||||||
excludeConfig: [0],
|
|
||||||
config: [
|
config: [
|
||||||
{
|
{
|
||||||
label: "根路径",
|
label: "根路径",
|
||||||
|
@ -104,6 +104,32 @@ const registryPaths = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const searchWindowConfig = [
|
||||||
|
{
|
||||||
|
key: "method",
|
||||||
|
label: "查找方式",
|
||||||
|
component: "q-select",
|
||||||
|
icon: "search",
|
||||||
|
width: 3,
|
||||||
|
options: [
|
||||||
|
{ label: "标题", value: "title" },
|
||||||
|
// { label: "类名", value: "class" },
|
||||||
|
{ label: "句柄", value: "handle" },
|
||||||
|
// { label: "进程", value: "process" },
|
||||||
|
{ label: "活动窗口", value: "active" },
|
||||||
|
],
|
||||||
|
defaultValue: "title",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
label: "窗口标题/句柄",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "title",
|
||||||
|
width: 9,
|
||||||
|
placeholder: "标题支持模糊匹配,选择活动窗口无需输入",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export const windowsCommands = {
|
export const windowsCommands = {
|
||||||
label: "Win自动化",
|
label: "Win自动化",
|
||||||
icon: "window",
|
icon: "window",
|
||||||
@ -114,29 +140,7 @@ export const windowsCommands = {
|
|||||||
label: "窗口控制",
|
label: "窗口控制",
|
||||||
desc: "Windows窗口操作",
|
desc: "Windows窗口操作",
|
||||||
icon: "window",
|
icon: "window",
|
||||||
config: [
|
config: searchWindowConfig,
|
||||||
{
|
|
||||||
key: "method",
|
|
||||||
label: "查找方式",
|
|
||||||
component: "q-select",
|
|
||||||
icon: "search",
|
|
||||||
width: 3,
|
|
||||||
options: [
|
|
||||||
{ label: "标题", value: "title" },
|
|
||||||
{ label: "句柄", value: "handle" },
|
|
||||||
{ label: "活动窗口", value: "active" },
|
|
||||||
],
|
|
||||||
defaultValue: "title",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "value",
|
|
||||||
label: "窗口标题/句柄",
|
|
||||||
component: "VariableInput",
|
|
||||||
icon: "title",
|
|
||||||
width: 9,
|
|
||||||
placeholder: "标题支持模糊匹配,选择活动窗口无需输入",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
subCommands: [
|
subCommands: [
|
||||||
{
|
{
|
||||||
value: "quickcomposer.windows.window.getWindowInfo",
|
value: "quickcomposer.windows.window.getWindowInfo",
|
||||||
@ -357,7 +361,7 @@ export const windowsCommands = {
|
|||||||
items: controlClass,
|
items: controlClass,
|
||||||
},
|
},
|
||||||
width: 8,
|
width: 8,
|
||||||
placeholder: "可选,输入要过滤的控件类名或文本",
|
placeholder: "可选,输入要过滤的控件类型或文本",
|
||||||
},
|
},
|
||||||
background: {
|
background: {
|
||||||
label: "后台操作",
|
label: "后台操作",
|
||||||
@ -394,7 +398,7 @@ export const windowsCommands = {
|
|||||||
width: 12,
|
width: 12,
|
||||||
options: {
|
options: {
|
||||||
control: {
|
control: {
|
||||||
label: "控件类名",
|
label: "控件类型",
|
||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
icon: "class",
|
icon: "class",
|
||||||
options: {
|
options: {
|
||||||
@ -408,7 +412,7 @@ export const windowsCommands = {
|
|||||||
component: "VariableInput",
|
component: "VariableInput",
|
||||||
icon: "text_fields",
|
icon: "text_fields",
|
||||||
width: 6,
|
width: 6,
|
||||||
placeholder: "可选,和控件类名至少输入一个",
|
placeholder: "可选,和控件类型至少输入一个",
|
||||||
},
|
},
|
||||||
pos: {
|
pos: {
|
||||||
label: "坐标",
|
label: "坐标",
|
||||||
@ -1097,5 +1101,175 @@ export const windowsCommands = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.inspectElement",
|
||||||
|
label: "UI自动化",
|
||||||
|
desc: "Windows界面自动化操作",
|
||||||
|
icon: "smart_button",
|
||||||
|
isAsync: true,
|
||||||
|
config: [],
|
||||||
|
subCommands: [
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.inspectElement",
|
||||||
|
label: "检查元素",
|
||||||
|
icon: "search",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.listElements",
|
||||||
|
label: "列出元素",
|
||||||
|
icon: "list",
|
||||||
|
config: [
|
||||||
|
...searchWindowConfig,
|
||||||
|
{
|
||||||
|
component: "OptionEditor",
|
||||||
|
icon: "settings",
|
||||||
|
width: 12,
|
||||||
|
options: {
|
||||||
|
scope: {
|
||||||
|
label: "范围",
|
||||||
|
component: "q-select",
|
||||||
|
icon: "account_tree",
|
||||||
|
width: 3,
|
||||||
|
options: [
|
||||||
|
{ label: "子元素", value: "children" },
|
||||||
|
{ label: "所有后代", value: "descendants" },
|
||||||
|
{ label: "整个子树", value: "subtree" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
label: "过滤条件",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "filter_alt",
|
||||||
|
width: 9,
|
||||||
|
placeholder: "可选,按名称/类名/ControlType/AutomationId过滤",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultValue: {
|
||||||
|
scope: "children",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.clickElement",
|
||||||
|
label: "点击元素",
|
||||||
|
icon: "mouse",
|
||||||
|
config: [
|
||||||
|
...searchWindowConfig,
|
||||||
|
{
|
||||||
|
key: "by",
|
||||||
|
label: "查找方式",
|
||||||
|
component: "q-select",
|
||||||
|
icon: "search",
|
||||||
|
width: 4,
|
||||||
|
options: [
|
||||||
|
{ label: "名称", value: "name" },
|
||||||
|
{ label: "类名", value: "class" },
|
||||||
|
{ label: "类型", value: "type" },
|
||||||
|
{ label: "AutomationId", value: "automationid" },
|
||||||
|
],
|
||||||
|
defaultValue: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
label: "查找值",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "text_fields",
|
||||||
|
width: 8,
|
||||||
|
placeholder: "要点击的元素值",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "pattern",
|
||||||
|
label: "点击模式",
|
||||||
|
component: "ButtonGroup",
|
||||||
|
icon: "touch_app",
|
||||||
|
width: 12,
|
||||||
|
options: [
|
||||||
|
{ label: "普通点击", value: "invoke" },
|
||||||
|
{ label: "切换状态", value: "toggle" },
|
||||||
|
],
|
||||||
|
defaultValue: "invoke",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "background",
|
||||||
|
label: "后台操作",
|
||||||
|
component: "CheckButton",
|
||||||
|
icon: "back_hand",
|
||||||
|
width: 12,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.setElementValue",
|
||||||
|
label: "设置值",
|
||||||
|
icon: "edit",
|
||||||
|
config: [
|
||||||
|
...searchWindowConfig,
|
||||||
|
{
|
||||||
|
key: "by",
|
||||||
|
label: "查找方式",
|
||||||
|
component: "q-select",
|
||||||
|
icon: "search",
|
||||||
|
width: 4,
|
||||||
|
options: [
|
||||||
|
{ label: "名称", value: "name" },
|
||||||
|
{ label: "类名", value: "class" },
|
||||||
|
{ label: "类型", value: "type" },
|
||||||
|
{ label: "AutomationId", value: "automationid" },
|
||||||
|
],
|
||||||
|
defaultValue: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
label: "查找值",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "text_fields",
|
||||||
|
width: 8,
|
||||||
|
placeholder: "要设置值的元素",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "newValue",
|
||||||
|
label: "新值",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "edit",
|
||||||
|
width: 12,
|
||||||
|
placeholder: "要设置的新值",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.windows.automation.getElementValue",
|
||||||
|
label: "获取值",
|
||||||
|
icon: "content_paste",
|
||||||
|
outputVariable: "elementValue",
|
||||||
|
saveOutput: true,
|
||||||
|
config: [
|
||||||
|
...searchWindowConfig,
|
||||||
|
{
|
||||||
|
key: "by",
|
||||||
|
label: "查找方式",
|
||||||
|
component: "q-select",
|
||||||
|
icon: "search",
|
||||||
|
width: 4,
|
||||||
|
options: [
|
||||||
|
{ label: "名称", value: "name" },
|
||||||
|
{ label: "类名", value: "class" },
|
||||||
|
{ label: "类型", value: "type" },
|
||||||
|
{ label: "AutomationId", value: "automationid" },
|
||||||
|
],
|
||||||
|
defaultValue: "name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
label: "查找值",
|
||||||
|
component: "VariableInput",
|
||||||
|
icon: "text_fields",
|
||||||
|
width: 8,
|
||||||
|
placeholder: "要获取值的元素",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user