mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-28 11:52:46 +08:00
新增分类:浏览器控制,基于CDP对edge和chrome进行自动化操作
This commit is contained in:
parent
3b49adcd59
commit
1a1ec4b45f
400
plugin/lib/quickcomposer/browser/browser.js
Normal file
400
plugin/lib/quickcomposer/browser/browser.js
Normal file
@ -0,0 +1,400 @@
|
||||
const CDP = require("chrome-remote-interface");
|
||||
const { exec } = require("child_process");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
const http = require("http");
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
|
||||
let client = null;
|
||||
let Page = null;
|
||||
let Runtime = null;
|
||||
let DOM = null;
|
||||
|
||||
const getBrowserPath = (browser = "msedge") => {
|
||||
const platform = os.platform();
|
||||
let paths = null;
|
||||
if (platform === "win32") {
|
||||
paths = {
|
||||
chrome: [
|
||||
path.join(
|
||||
process.env["ProgramFiles"],
|
||||
"Google/Chrome/Application/chrome.exe"
|
||||
),
|
||||
path.join(
|
||||
process.env["ProgramFiles(x86)"],
|
||||
"Google/Chrome/Application/chrome.exe"
|
||||
),
|
||||
path.join(
|
||||
process.env["LocalAppData"],
|
||||
"Google/Chrome/Application/chrome.exe"
|
||||
),
|
||||
],
|
||||
msedge: [
|
||||
path.join(
|
||||
process.env["ProgramFiles"],
|
||||
"Microsoft/Edge/Application/msedge.exe"
|
||||
),
|
||||
path.join(
|
||||
process.env["ProgramFiles(x86)"],
|
||||
"Microsoft/Edge/Application/msedge.exe"
|
||||
),
|
||||
path.join(
|
||||
process.env["LocalAppData"],
|
||||
"Microsoft/Edge/Application/msedge.exe"
|
||||
),
|
||||
],
|
||||
};
|
||||
} else if (platform === "darwin") {
|
||||
paths = {
|
||||
chrome: ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"],
|
||||
msedge: [
|
||||
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
||||
],
|
||||
};
|
||||
} else if (platform === "linux") {
|
||||
paths = {
|
||||
chrome: [
|
||||
"/opt/google/chrome/chrome",
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/google-chrome-stable",
|
||||
],
|
||||
msedge: [
|
||||
"/opt/microsoft/msedge/msedge",
|
||||
"/usr/bin/microsoft-edge",
|
||||
"/usr/bin/microsoft-edge-stable",
|
||||
],
|
||||
};
|
||||
} else {
|
||||
throw new Error("不支持的操作系统");
|
||||
}
|
||||
return paths[browser].find((p) => fs.existsSync(p));
|
||||
};
|
||||
|
||||
const isPortAvailable = (port) => {
|
||||
return new Promise((resolve) => {
|
||||
const server = http.createServer();
|
||||
server.listen(port, () => {
|
||||
server.close(() => resolve(true));
|
||||
});
|
||||
server.on("error", () => resolve(false));
|
||||
});
|
||||
};
|
||||
|
||||
const waitForPort = async (port, timeout = 30000) => {
|
||||
const startTime = Date.now();
|
||||
while (Date.now() - startTime < timeout) {
|
||||
try {
|
||||
const response = await axios.get(`http://localhost:${port}/json/version`);
|
||||
if (response.status === 200) return true;
|
||||
} catch (e) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const findAvailablePort = async (startPort) => {
|
||||
let port = startPort;
|
||||
while (port < startPort + 100) {
|
||||
const available = await isPortAvailable(port);
|
||||
if (available) {
|
||||
return port;
|
||||
}
|
||||
port++;
|
||||
}
|
||||
throw new Error("无法找到可用的调试端口");
|
||||
};
|
||||
|
||||
const launchBrowser = async (options) => {
|
||||
const {
|
||||
browserType = "msedge",
|
||||
useSingleUserDataDir = true,
|
||||
headless = false,
|
||||
proxy = null,
|
||||
browserPath = getBrowserPath(browserType),
|
||||
windowSize = null,
|
||||
} = options;
|
||||
|
||||
if (!browserPath) {
|
||||
throw new Error("未找到浏览器,或未指定浏览器路径");
|
||||
}
|
||||
|
||||
// 查找可用端口
|
||||
const port = await findAvailablePort(9222);
|
||||
|
||||
const args = [
|
||||
`--remote-debugging-port=${port}`,
|
||||
"--no-first-run",
|
||||
"--no-default-browser-check",
|
||||
"--start-maximized",
|
||||
headless ? "--headless" : "",
|
||||
windowSize ? `--window-size=${windowSize}` : "",
|
||||
proxy ? `--proxy-server=${proxy}` : "",
|
||||
useSingleUserDataDir
|
||||
? `--user-data-dir=${path.join(
|
||||
os.tmpdir(),
|
||||
`${browserType}-debug-${port}`
|
||||
)}`
|
||||
: "",
|
||||
].filter(Boolean);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
// 如果使用独立用户数据目录,则需要先杀死已有的浏览器进程
|
||||
if (!useSingleUserDataDir) {
|
||||
try {
|
||||
await killRunningBrowser(browserType);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const child = exec(
|
||||
`"${browserPath}" ${args.join(" ")}`,
|
||||
{
|
||||
windowsHide: true,
|
||||
},
|
||||
async (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 等待端口可用
|
||||
waitForPort(port).then((success) => {
|
||||
if (success) {
|
||||
resolve({
|
||||
pid: child.pid,
|
||||
port,
|
||||
}); // 返回使用的端口号
|
||||
} else {
|
||||
reject(new Error("浏览器启动超时,请检查是否有权限问题或防火墙限制"));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const killRunningBrowser = (browserType = "msedge") => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (os.platform() === "win32") {
|
||||
exec(`taskkill /F /IM ${browserType}.exe`, (error) => {
|
||||
if (error) reject(error);
|
||||
else resolve();
|
||||
});
|
||||
} else {
|
||||
exec(`kill -9 $(pgrep ${browserType})`, (error) => {
|
||||
if (error) reject(error);
|
||||
else resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const initCDP = async (port) => {
|
||||
if (!client) {
|
||||
try {
|
||||
client = await CDP({ port });
|
||||
({ Page, Runtime, DOM } = client);
|
||||
await Promise.all([Page.enable(), Runtime.enable(), DOM.enable()]);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw new Error(
|
||||
`请确保浏览器已启动,且开启了远程调试端口(--remote-debugging-port=${port})`
|
||||
);
|
||||
}
|
||||
}
|
||||
return { Page, Runtime, DOM };
|
||||
};
|
||||
|
||||
const getUrl = async () => {
|
||||
const { Page } = await initCDP();
|
||||
const { frameTree } = await Page.getFrameTree();
|
||||
return frameTree.frame.url;
|
||||
};
|
||||
|
||||
const setUrl = async (url) => {
|
||||
const { Page } = await initCDP();
|
||||
await Page.navigate({ url });
|
||||
await Page.loadEventFired();
|
||||
};
|
||||
|
||||
const executeScript = async (script, args = {}) => {
|
||||
const { Runtime } = await initCDP();
|
||||
// 构建参数列表
|
||||
const argNames = Object.keys(args);
|
||||
const argValues = Object.values(args).map((v) => JSON.stringify(v));
|
||||
|
||||
const wrappedScript = `
|
||||
(function(${argNames.join(", ")}) {
|
||||
${script}
|
||||
})(${argValues.join(", ")})
|
||||
`;
|
||||
const { result } = await Runtime.evaluate({
|
||||
expression: wrappedScript,
|
||||
returnByValue: true,
|
||||
awaitPromise: true,
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
const getTabs = async () => {
|
||||
const targets = await CDP.List();
|
||||
return targets
|
||||
.filter((target) => target.type === "page")
|
||||
.map((target) => ({
|
||||
url: target.url,
|
||||
title: target.title,
|
||||
}));
|
||||
};
|
||||
|
||||
const activateTab = async (index) => {
|
||||
const targets = await CDP.List();
|
||||
const pages = targets.filter((target) => target.type === "page");
|
||||
if (index > 0 && index <= pages.length) {
|
||||
const targetId = pages[index - 1].id;
|
||||
await CDP.Activate({ id: targetId });
|
||||
}
|
||||
};
|
||||
|
||||
const clickElement = async (selector) => {
|
||||
return await executeScript(`document.querySelector('${selector}').click()`);
|
||||
};
|
||||
|
||||
const inputText = async (selector, text) => {
|
||||
return await executeScript(
|
||||
`
|
||||
const el = document.querySelector('${selector}');
|
||||
el.value = '${text}';
|
||||
el.dispatchEvent(new Event('input'));
|
||||
el.dispatchEvent(new Event('change'));
|
||||
`
|
||||
);
|
||||
};
|
||||
|
||||
const getText = async (selector) => {
|
||||
return await executeScript(
|
||||
`document.querySelector('${selector}')?.textContent || ''`
|
||||
);
|
||||
};
|
||||
|
||||
const getHtml = async (selector) => {
|
||||
return await executeScript(
|
||||
`const element = document.querySelector('${selector}');
|
||||
return element ? element.innerHTML : '';`
|
||||
);
|
||||
};
|
||||
|
||||
const hideElement = async (selector) => {
|
||||
return await executeScript(
|
||||
`document.querySelector('${selector}').style.display = 'none'`
|
||||
);
|
||||
};
|
||||
|
||||
const showElement = async (selector) => {
|
||||
return await executeScript(
|
||||
`document.querySelector('${selector}').style.display = ''`
|
||||
);
|
||||
};
|
||||
|
||||
const injectCSS = async (css) => {
|
||||
return await executeScript(
|
||||
`
|
||||
const style = document.createElement('style');
|
||||
style.textContent = \`${css}\`;
|
||||
document.head.appendChild(style);
|
||||
`
|
||||
);
|
||||
};
|
||||
|
||||
const setCookie = async (cookies, options = {}) => {
|
||||
const { Network } = await initCDP();
|
||||
for (const cookie of cookies) {
|
||||
await Network.setCookie({
|
||||
name: cookie.name,
|
||||
value: cookie.value,
|
||||
domain: options.domain || undefined,
|
||||
path: options.path || "/",
|
||||
secure: options.secure || false,
|
||||
expires: options.expires
|
||||
? Math.floor(Date.now() / 1000) + options.expires * 3600
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getCookie = async (name) => {
|
||||
const { Network } = await initCDP();
|
||||
const cookies = await Network.getCookies();
|
||||
return cookies.find((cookie) => cookie.name === name);
|
||||
};
|
||||
|
||||
const scrollTo = async (x, y) => {
|
||||
return await executeScript(`window.scrollTo(${x}, ${y})`);
|
||||
};
|
||||
|
||||
const scrollToElement = async (selector) => {
|
||||
return await executeScript(
|
||||
`document.querySelector('${selector}').scrollIntoView()`
|
||||
);
|
||||
};
|
||||
|
||||
const getScrollPosition = async () => {
|
||||
return await executeScript(`
|
||||
return JSON.stringify({
|
||||
x: window.pageXOffset || document.documentElement.scrollLeft,
|
||||
y: window.pageYOffset || document.documentElement.scrollTop
|
||||
});
|
||||
`);
|
||||
};
|
||||
|
||||
const getPageSize = async () => {
|
||||
return await executeScript(`
|
||||
return JSON.stringify({
|
||||
width: Math.max(
|
||||
document.documentElement.scrollWidth,
|
||||
document.documentElement.clientWidth
|
||||
),
|
||||
height: Math.max(
|
||||
document.documentElement.scrollHeight,
|
||||
document.documentElement.clientHeight
|
||||
)
|
||||
});
|
||||
`);
|
||||
};
|
||||
|
||||
const waitForElement = async (selector, timeout = 5000) => {
|
||||
const startTime = Date.now();
|
||||
while (Date.now() - startTime < timeout) {
|
||||
const result = await executeScript(
|
||||
`!!document.querySelector('${selector}')`
|
||||
);
|
||||
if (result) return;
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
throw new Error(`等待元素 ${selector} 超时`);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
launchBrowser,
|
||||
getUrl,
|
||||
setUrl,
|
||||
executeScript,
|
||||
getTabs,
|
||||
activateTab,
|
||||
clickElement,
|
||||
inputText,
|
||||
getText,
|
||||
getHtml,
|
||||
hideElement,
|
||||
showElement,
|
||||
injectCSS,
|
||||
setCookie,
|
||||
getCookie,
|
||||
scrollTo,
|
||||
scrollToElement,
|
||||
getScrollPosition,
|
||||
getPageSize,
|
||||
waitForElement,
|
||||
};
|
3
plugin/lib/quickcomposer/browser/index.js
Normal file
3
plugin/lib/quickcomposer/browser/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
const browser = require("./browser");
|
||||
|
||||
module.exports = browser;
|
58
plugin/package-lock.json
generated
58
plugin/package-lock.json
generated
@ -6,6 +6,7 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"chrome-remote-interface": "^0.33.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"exif-reader": "^2.0.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
@ -39,6 +40,18 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chrome-remote-interface": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.33.2.tgz",
|
||||
"integrity": "sha512-wvm9cOeBTrb218EC+6DteGt92iXr2iY0+XJP30f15JVDhqvWvJEVACh9GvUm8b9Yd8bxQivaLSb8k7mgrbyomQ==",
|
||||
"dependencies": {
|
||||
"commander": "2.11.x",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"chrome-remote-interface": "bin/client.js"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -51,6 +64,11 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
|
||||
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
@ -198,6 +216,26 @@
|
||||
"bin": {
|
||||
"tree-kill": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
@ -221,6 +259,15 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"chrome-remote-interface": {
|
||||
"version": "0.33.2",
|
||||
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.33.2.tgz",
|
||||
"integrity": "sha512-wvm9cOeBTrb218EC+6DteGt92iXr2iY0+XJP30f15JVDhqvWvJEVACh9GvUm8b9Yd8bxQivaLSb8k7mgrbyomQ==",
|
||||
"requires": {
|
||||
"commander": "2.11.x",
|
||||
"ws": "^7.2.0"
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -229,6 +276,11 @@
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
|
||||
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
@ -325,6 +377,12 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^1.7.9",
|
||||
"chrome-remote-interface": "^0.33.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"exif-reader": "^2.0.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
|
399
src/js/composer/commands/browserCommands.js
Normal file
399
src/js/composer/commands/browserCommands.js
Normal file
@ -0,0 +1,399 @@
|
||||
import { newVarInputVal } from "js/composer/varInputValManager";
|
||||
|
||||
export const browserCommands = {
|
||||
label: "浏览器控制",
|
||||
icon: "web",
|
||||
defaultOpened: false,
|
||||
commands: [
|
||||
{
|
||||
value: "quickcomposer.browser.launchBrowser",
|
||||
label: "启动浏览器",
|
||||
icon: "launch",
|
||||
isAsync: true,
|
||||
config: [
|
||||
{
|
||||
component: "OptionEditor",
|
||||
icon: "settings",
|
||||
width: 12,
|
||||
options: {
|
||||
browserType: {
|
||||
component: "ButtonGroup",
|
||||
defaultValue: "msedge",
|
||||
options: [
|
||||
{ label: "Edge", value: "msedge" },
|
||||
{ label: "Chrome", value: "chrome" },
|
||||
],
|
||||
width: 12,
|
||||
},
|
||||
useSingleUserDataDir: {
|
||||
label: "使用独立用户数据目录",
|
||||
component: "CheckButton",
|
||||
width: 6,
|
||||
},
|
||||
headless: {
|
||||
label: "启用无头模式",
|
||||
component: "CheckButton",
|
||||
width: 6,
|
||||
},
|
||||
windowSize: {
|
||||
label: "窗口尺寸",
|
||||
component: "VariableInput",
|
||||
icon: "window",
|
||||
width: 6,
|
||||
placeholder: "如1920x1080,不设置则最大化",
|
||||
},
|
||||
proxy: {
|
||||
label: "代理",
|
||||
component: "VariableInput",
|
||||
icon: "vpn_lock",
|
||||
width: 6,
|
||||
placeholder: "如 socks5://127.0.0.1:7890",
|
||||
},
|
||||
browserPath: {
|
||||
label: "浏览器路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: 12,
|
||||
options: {
|
||||
dialog: {
|
||||
type: "open",
|
||||
options: {
|
||||
title: "选择浏览器",
|
||||
properties: ["openFile"],
|
||||
},
|
||||
},
|
||||
},
|
||||
placeholder: "二进制绝对路径,留空则自动查找",
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
browserType: "msedge",
|
||||
useSingleUserDataDir: true,
|
||||
headless: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getUrl",
|
||||
label: "浏览器操作",
|
||||
icon: "web",
|
||||
isAsync: true,
|
||||
subCommands: [
|
||||
{
|
||||
value: "quickcomposer.browser.getUrl",
|
||||
label: "获取当前地址",
|
||||
icon: "link",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.setUrl",
|
||||
label: "设置当前地址",
|
||||
icon: "link",
|
||||
config: [
|
||||
{
|
||||
label: "网址",
|
||||
component: "VariableInput",
|
||||
icon: "link",
|
||||
width: 12,
|
||||
placeholder: "输入网址",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getTabs",
|
||||
label: "获取标签列表",
|
||||
icon: "tab",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.activateTab",
|
||||
label: "切换标签",
|
||||
icon: "tab_unselected",
|
||||
config: [
|
||||
{
|
||||
label: "标签索引",
|
||||
component: "NumberInput",
|
||||
icon: "tab",
|
||||
min: 1,
|
||||
defaultValue: 1,
|
||||
width: 12,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.executeScript",
|
||||
label: "执行脚本",
|
||||
icon: "code",
|
||||
config: [
|
||||
{
|
||||
label: "脚本内容",
|
||||
component: "CodeEditor",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入JavaScript代码",
|
||||
},
|
||||
{
|
||||
topLabel: "要传递的参数",
|
||||
component: "DictEditor",
|
||||
icon: "data_array",
|
||||
width: 12,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.clickElement",
|
||||
label: "点击元素",
|
||||
icon: "mouse",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.inputText",
|
||||
label: "输入文本",
|
||||
icon: "edit",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
{
|
||||
label: "文本内容",
|
||||
component: "VariableInput",
|
||||
icon: "edit",
|
||||
width: 12,
|
||||
placeholder: "输入要填写的文本",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getText",
|
||||
label: "获取文本",
|
||||
icon: "text_fields",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getHtml",
|
||||
label: "获取HTML",
|
||||
icon: "code",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.hideElement",
|
||||
label: "隐藏元素",
|
||||
icon: "visibility_off",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.showElement",
|
||||
label: "显示元素",
|
||||
icon: "visibility",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.injectCSS",
|
||||
label: "注入CSS",
|
||||
icon: "style",
|
||||
config: [
|
||||
{
|
||||
label: "CSS内容",
|
||||
component: "CodeEditor",
|
||||
icon: "style",
|
||||
width: 12,
|
||||
placeholder: "输入CSS代码",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.setCookie",
|
||||
label: "设置Cookie",
|
||||
icon: "cookie",
|
||||
config: [
|
||||
{
|
||||
label: "Cookie",
|
||||
component: "ArrayEditor",
|
||||
icon: "cookie",
|
||||
width: 12,
|
||||
columns: {
|
||||
name: {
|
||||
label: "名称",
|
||||
defaultValue: newVarInputVal("str"),
|
||||
},
|
||||
value: {
|
||||
label: "值",
|
||||
defaultValue: newVarInputVal("str"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "选项",
|
||||
component: "OptionEditor",
|
||||
icon: "settings",
|
||||
width: 12,
|
||||
options: {
|
||||
expires: {
|
||||
label: "过期时间",
|
||||
component: "QSelect",
|
||||
icon: "timer",
|
||||
width: 6,
|
||||
options: [
|
||||
{ label: "关闭浏览器失效", value: false },
|
||||
{ label: "1小时", value: 1 },
|
||||
{ label: "1天", value: 24 },
|
||||
{ label: "1年", value: 24 * 365 },
|
||||
],
|
||||
},
|
||||
path: {
|
||||
label: "路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: 6,
|
||||
},
|
||||
domain: {
|
||||
label: "域名",
|
||||
component: "VariableInput",
|
||||
icon: "domain",
|
||||
width: 6,
|
||||
},
|
||||
secure: {
|
||||
label: "安全",
|
||||
component: "CheckButton",
|
||||
icon: "lock",
|
||||
width: 6,
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
expires: false,
|
||||
path: newVarInputVal("str", "/"),
|
||||
domain: newVarInputVal("str", ""),
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getCookie",
|
||||
label: "获取Cookie",
|
||||
icon: "cookie",
|
||||
config: [
|
||||
{
|
||||
label: "名称",
|
||||
component: "VariableInput",
|
||||
icon: "label",
|
||||
width: 12,
|
||||
placeholder: "输入Cookie名称",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.scrollTo",
|
||||
label: "滚动到位置",
|
||||
icon: "open_in_full",
|
||||
config: [
|
||||
{
|
||||
label: "X坐标",
|
||||
component: "NumberInput",
|
||||
icon: "arrow_right",
|
||||
width: 12,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
label: "Y坐标",
|
||||
component: "NumberInput",
|
||||
icon: "arrow_drop_down",
|
||||
width: 12,
|
||||
defaultValue: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.scrollToElement",
|
||||
label: "滚动到元素",
|
||||
icon: "open_in_full",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getScrollPosition",
|
||||
label: "获取滚动位置",
|
||||
icon: "open_in_full",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.getPageSize",
|
||||
label: "获取页面尺寸",
|
||||
icon: "open_in_full",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.browser.waitForElement",
|
||||
label: "等待元素",
|
||||
icon: "hourglass_empty",
|
||||
config: [
|
||||
{
|
||||
label: "选择器",
|
||||
component: "VariableInput",
|
||||
icon: "code",
|
||||
width: 12,
|
||||
placeholder: "输入CSS选择器",
|
||||
},
|
||||
{
|
||||
label: "超时时间",
|
||||
component: "NumberInput",
|
||||
icon: "timer",
|
||||
width: 12,
|
||||
defaultValue: 5000,
|
||||
min: 1000,
|
||||
step: 1000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
@ -18,6 +18,7 @@ import { windowsCommands } from "./windowsCommands";
|
||||
import { statusCommands } from "./statusCommands";
|
||||
import { macosCommands } from "./macosCommands";
|
||||
import { scriptCommands } from "./scriptCommands";
|
||||
import { browserCommands } from "./browserCommands";
|
||||
|
||||
const platformCommands = {
|
||||
win32: [windowsCommands],
|
||||
@ -33,6 +34,7 @@ export const commandCategories = [
|
||||
imageCommands,
|
||||
utoolsCommands,
|
||||
...platformCommands[window.processPlatform],
|
||||
browserCommands,
|
||||
dataCommands,
|
||||
codingCommands,
|
||||
controlCommands,
|
||||
|
Loading…
x
Reference in New Issue
Block a user