autojs-解析查找节点表达式

145 阅读3分钟

parseQuery 函数详解

在进行 UI 自动化或界面元素查找时,查询条件的灵活组合尤为重要。本文将介绍一个常用的查询解析函数 parseQuery,它能根据特定的条件生成对应的查询对象,方便在自动化脚本中进行元素定位。

函数功能

parseQuery 函数用于解析查询字符串并根据解析结果生成查询对象。该函数支持多个查询条件,通过 ~ 符号连接,每个条件对应一个属性的匹配规则。支持的属性包括文本、描述、ID、类名、包名、边界等多个选项。

支持的条件格式

函数支持通过查询字符串指定多个条件,每个条件由一个选择器类型和一个匹配值组成,格式为:

多个条件可以用 ~ 分隔,表示节点多属性查找逻辑。例如:

const result = parseQuery("t=微信~i=wechat");
console.log(result);

表示查询文本为 微信 且 ID 为 wechat 的元素。

支持的选择器类型

选择器类型说明
t匹配文本
tC匹配包含指定文本
tS匹配以指定文本开头
tE匹配以指定文本结尾
tM匹配正则表达式
d匹配描述
dC匹配包含指定描述
dS匹配以指定描述开头
dE匹配以指定描述结尾
dM匹配描述的正则表达式
i匹配ID
iC匹配ID包含指定值
iS匹配ID以指定值开头
iE匹配ID以指定值结尾
iM匹配ID的正则表达式
c匹配类名
cC匹配类名包含指定值
cS匹配类名以指定值开头
cE匹配类名以指定值结尾
cM匹配类名的正则表达式
p匹配包名
pC匹配包名包含指定值
pS匹配包名以指定值开头
pE匹配包名以指定值结尾
pM匹配包名的正则表达式
b匹配边界
bI匹配边界内部元素
bC匹配边界包含指定元素
dO匹配绘制顺序

函数实现

/**
 * 解析单个表达式,支持 `t=微信~i=pay` 的形式进行多条件查询
 *
 * 该函数接收一个查询字符串,解析字符串中的多个查询条件,并根据条件生成查询对象。
 * 查询条件支持对文本、描述、id、类名、包名、边界等多个属性的匹配。
 * 
 * @param {string} str 查询字符串,格式如 "t=微信~i=pay",支持多个条件用 `~` 分隔
 * @returns {object} 返回一个查询对象,包含多个查询条件
 *
 * @example
 * const result = parseQuery("t=微信~i=pay");
 * console.log(result);
 */
parseQuery = (str) => {
    let query = $selector();  // 使用 $selector() 初始化查询对象
    let subConditions = str.split("~"); // 多属性 AND 逻辑,按 ~ 分割多个子条件
    // 定义一个对象,映射选择器类型到相应的函数
    const selectFn = {
        t: "text",
        tC: "textContains",
        tS: "textStartsWith",
        tE: "textEndsWith",
        tM: "textMatches",
        d: "desc",
        dC: "descContains",
        dS: "descStartsWith",
        dE: "descEndsWith",
        dM: "descMatches",
        i: "id",
        iC: "idContains",
        iS: "idStartsWith",
        iE: "idEndsWith",
        iM: "idMatches",
        c: "className",
        cC: "classNameContains",      // 类名包含
        cS: "classNameStartsWith",
        cE: "classNameEndsWith",
        cM: "classNameMatches",
        p: "packageName",             // 包名匹配
        pC: "packageNameContains",    // 包名包含
        pS: "packageNameStartsWith",
        pE: "packageNameEndsWith",
        pM: 'packageNameMatches',
        b: "bounds",
        bI: "boundsInside",
        bC: "boundsContains",
        dO: "drawingOrder"
    };
    
    // 遍历每个子条件
    for (let cond of subConditions) {
        let [key, value] = cond.split("=");  // 按照 '=' 分割条件,获取选择器类型和匹配值
        if (!key) continue; // 如果没有选择器类型,跳过当前条件
        if (!value) {
            // 如果没有给定值,默认为 `text` 类型
            value = key
            key = "t"
        }

        let objKey = selectFn[key] || (query[key] ? key : false);  // 获取对应的查询方法
        if (query[objKey]) {
            // 处理 `true` 和 `false` 的值
            if (value === 'true') {
                value = true
            } else if (value === 'false') {
                value = false
            } else if (objKey.includes("Matches")) {
                // 如果是正则匹配类型,处理为正则表达式
                if (new RegExp('^/.*\/$').test(value)) {
                    value = value.slice(1, -1)
                }
                value = new RegExp(value);
            }

            if (objKey.includes("bounds")) {
                // 处理边界值,期望四个数字(左,上,右,下)
                let arr = value.split(",").map(e => Number(e));
                if (arr && arr.length === 4) {
                    query[objKey](arr[0], arr[1], arr[2], arr[3]);
                } else {
                    console.error(objKey + " 参数错误,必须是 4 个数字");
                }
            } else {
                query = query[objKey](value);
            }
        } else {
            console.warn(`未知的选择器类型: ${key}`); // 当遇到无法识别的选择器类型时,给出警告
        }
    }

    // 返回查询对象
    return query;
}